diff options
Diffstat (limited to 'node_modules/@jet/environment/metrics')
11 files changed, 1228 insertions, 0 deletions
diff --git a/node_modules/@jet/environment/metrics/builder.js b/node_modules/@jet/environment/metrics/builder.js new file mode 100644 index 0000000..935dd50 --- /dev/null +++ b/node_modules/@jet/environment/metrics/builder.js @@ -0,0 +1,456 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createMetricsMediaClickData = exports.createMetricsMediaData = exports.createMetricsImpressionsData = exports.createMetricsSearchData = exports.createMetricsPageData = exports.createMetricsBackClickData = exports.createMetricsClickData = exports.MediaClickEventBuilder = exports.MediaEventBuilder = exports.ImpressionsEventBuilder = exports.SearchEventBuilder = exports.PageEventBuilder = exports.BackClickEventBuilder = exports.ClickEventBuilder = exports.EventBuilder = void 0; +const optional_1 = require("../types/optional"); +// region Builders +/** + * Base event builder. + */ +class EventBuilder { + /** + * Create base event builder with metrics configuration. + * + * @param configuration - Metrics configuration used to build metrics events. + */ + constructor(configuration) { + this.configuration = configuration; + this.eventFields = {}; + } + /** + * Updates event builder with given event fields. + * @param eventFields - The events fields to update in builder. + * @returns Event builder with updated event fields. + */ + withEventFields(eventFields) { + this.eventFields = eventFields; + return this; + } + /** + * Updates event builder with given configuration. + * @param configuration - The configuration to update in builder. + * @returns Event builder with updated configuration. + */ + withConfiguration(configuration) { + this.configuration = configuration; + return this; + } + /** + * Updates event builder with given default topic. + * @param topic - The default topic to update in builder. + * @returns Event builder with updated default topic. + */ + withDefaultTopic(topic) { + this.configuration = { + ...this.configuration, + defaultTopic: topic, + }; + return this; + } + /** + * Updates event builder with given include fields requests. + * @param includeRequests - The include fields requests to update in builder. + * @returns Event builder with updated include fields requests. + */ + withDefaultIncludeRequests(includeRequests) { + this.configuration = { + ...this.configuration, + defaultIncludeRequests: includeRequests, + }; + return this; + } + /** + * Updates event builder with given exclude fields requests. + * @param excludeRequests - The exclude fields requests to update in builder. + * @returns Event builder with updated exclude fields requests. + */ + withDefaultExcludeRequests(excludeRequests) { + this.configuration = { + ...this.configuration, + defaultExcludeRequests: excludeRequests, + }; + return this; + } + /** + * Updates event builder with given flush behavior. + * @param shouldFlush - The flush behavior to update in builder. + * @returns Event builder with updated flush behavior. + */ + withShouldFlush(shouldFlush) { + this.configuration = { + ...this.configuration, + shouldFlush: shouldFlush, + }; + return this; + } +} +exports.EventBuilder = EventBuilder; +/** + * Builder for click events. + */ +class ClickEventBuilder extends EventBuilder { + /** + * Create click metrics event builder with all required properties. + * + * @param options - Object containing options required to create the builder. + * + * Options: + * - targetId - Target ID used for building events. + * - targetType - Target type used for building events. + * - configuration - Metrics configuration used to build metrics events. + */ + constructor(options) { + super(options.configuration); + this.targetId = options.targetId; + this.targetType = options.targetType; + } + /** + * Updates event builder with given target ID. + * @param targetId - The target ID to update in builder. + * @returns Event builder with updated target ID. + */ + withTargetId(targetId) { + this.targetId = targetId; + return this; + } + /** + * Updates event builder with given target type. + * @param targetType - The target type to update in builder. + * @returns Event builder with updated target type. + */ + withTargetType(targetType) { + this.targetType = targetType; + return this; + } + build() { + return createMetricsClickData(this.targetId, this.targetType, this.eventFields, this.configuration); + } +} +exports.ClickEventBuilder = ClickEventBuilder; +/** + * Builder for back click events. + */ +class BackClickEventBuilder extends EventBuilder { + build() { + return createMetricsBackClickData(this.eventFields, this.configuration); + } +} +exports.BackClickEventBuilder = BackClickEventBuilder; +/** + * Builder for page events. + */ +class PageEventBuilder extends EventBuilder { + /** + * Create page metrics event builder with all required properties. + * + * @param configuration - Metrics configuration used to build metrics events. + */ + constructor(configuration) { + super(configuration); + this.timingMetrics = {}; + } + /** + * Updates event builder with given fetch timing metrics. + * @param timingMetrics - The fetch timing metrics to update in builder. + * @returns Event builder with updated fetch timing metrics. + */ + withTimingMetrics(timingMetrics) { + this.timingMetrics = timingMetrics; + return this; + } + build() { + return createMetricsPageData(this.eventFields, this.timingMetrics, this.configuration); + } +} +exports.PageEventBuilder = PageEventBuilder; +/** + * Builder for search metrics events. + */ +class SearchEventBuilder extends EventBuilder { + /** + * Create search metrics event builder with all required properties. + * + * @param options - Object containing options required to create the builder. + * + * Options: + * - term - Search term used to build metrics events. + * - targetType - Target type used to build metrics events. + * - actionType - Action type used to build metrics events. + * - configuration - Metrics configuration used to build metrics events. + */ + constructor(options) { + super(options.configuration); + this.term = options.term; + this.targetType = options.targetType; + this.actionType = options.actionType; + this.actionUrl = null; + } + /** + * Updates event builder with given search term. + * @param term - The search term to update in builder. + * @returns Event builder with updated search term. + */ + withTerm(term) { + this.term = term; + return this; + } + /** + * Updates event builder with given target type. + * @param targetType - The target type to update in builder. + * @returns Event builder with updated target type. + */ + withTargetType(targetType) { + this.targetType = targetType; + return this; + } + /** + * Updates event builder with given action type. + * @param actionType - The action type to update in builder. + * @returns Event builder with updated action type. + */ + withActionType(actionType) { + this.actionType = actionType; + return this; + } + /** + * Updates event builder with given action URL. + * @param actionUrl - The action URL to update in builder. + * @returns Event builder with updated action URL. + */ + withActionUrl(actionUrl) { + this.actionUrl = actionUrl; + return this; + } + build() { + return createMetricsSearchData(this.term, this.targetType, this.actionType, this.actionUrl, this.eventFields, this.configuration); + } +} +exports.SearchEventBuilder = SearchEventBuilder; +/** + * Builder for impressions events. + */ +class ImpressionsEventBuilder extends EventBuilder { + constructor() { + super(...arguments); + /** + * Impressions event version. + */ + this.impressionsEventVersion = 4; + } + /** + * Updates event builder with given impressions event version. + * @param version - The impressions event version to update in builder. + * @returns Event builder with updated impressions event version. + */ + withImpressionsEventVersion(version) { + this.impressionsEventVersion = version; + return this; + } + build() { + return createMetricsImpressionsData(this.eventFields, this.configuration, this.impressionsEventVersion); + } +} +exports.ImpressionsEventBuilder = ImpressionsEventBuilder; +/** + * Builder for media events. + */ +class MediaEventBuilder extends EventBuilder { + build() { + return createMetricsMediaData(this.eventFields, this.configuration); + } +} +exports.MediaEventBuilder = MediaEventBuilder; +/** + * Builder for media click events. + */ +class MediaClickEventBuilder extends EventBuilder { + /** + * Create media click event builder with all required properties. + * + * @param options - Object containing options required to create the builder. + * + * Options: + * - targetId - Target ID used for building events. + * - targetType - Target type used to build metrics events. + * - configuration - Metrics configuration used to build metrics events. + */ + constructor(options) { + super(options.configuration); + this.targetId = options.targetId; + this.targetType = options.targetType; + } + /** + * Updates event builder with given target ID. + * @param targetId - The target ID to update in builder. + * @returns Event builder with updated target ID. + */ + withTargetId(targetId) { + this.targetId = targetId; + return this; + } + /** + * Updates event builder with given target type. + * @param targetType - The target type to update in builder. + * @returns Event builder with updated target type. + */ + withTargetType(targetType) { + this.targetType = targetType; + return this; + } + build() { + return createMetricsMediaClickData(this.targetId, this.targetType, this.eventFields, this.configuration); + } +} +exports.MediaClickEventBuilder = MediaClickEventBuilder; +// region Metrics Data +/** + * Create metrics data for the click event. + * + * @param targetId - The ID of the click event target. + * @param targetType - The type of the click target. + * @param eventFields - Event fields to create metrics data from. + * @param configuration - Metrics configuration used to create metrics data. + */ +function createMetricsClickData(targetId, targetType, eventFields, configuration) { + const eventType = "click" /* MetricsEventType.click */; + const fields = { + ...eventFields, + eventType: eventType, + targetType: targetType, + targetId: targetId, + }; + return createMetricsData(fields, configuration, eventType); +} +exports.createMetricsClickData = createMetricsClickData; +/** + * Create metrics data for the Back button click event. + * + * @param eventFields - Event fields to create metrics data from. + * @param configuration - Metrics configuration used to create metrics data. + */ +function createMetricsBackClickData(eventFields, configuration) { + const fields = { + ...eventFields, + actionType: "back", + }; + return createMetricsClickData("back", "button" /* MetricsClickTargetType.button */, fields, configuration); +} +exports.createMetricsBackClickData = createMetricsBackClickData; +/** + * Create metrics data for the page event. + * @param eventFields - Event fields to create metrics data from. + * @param timingMetrics - The timing metrics for page data fetching. + * @param configuration - Metrics configuration used to create metrics data. + */ +function createMetricsPageData(eventFields, timingMetrics, configuration) { + const eventType = "page" /* MetricsEventType.page */; + const fields = { + ...eventFields, + eventType: eventType, + ...timingMetrics, + }; + return createMetricsData(fields, configuration, eventType); +} +exports.createMetricsPageData = createMetricsPageData; +/** + * Create metrics data for search event. + * + * @param term - The search term. + * @param target - The type of the acton target. + * @param actionType - The type of the action. + * @param actionUrl - An optional action URL. + * @param eventFields - Event fields to create metrics data from. + * @param configuration - Metrics configuration used to create metrics data. + */ +function createMetricsSearchData(term, targetType, actionType, actionUrl, eventFields, configuration) { + const eventType = "search" /* MetricsEventType.search */; + const fields = { + ...eventFields, + eventType: eventType, + term: term, + targetType: targetType, + actionType: actionType, + }; + if ((0, optional_1.isSome)(actionUrl)) { + // actionUrl is defined for `hints` but not for searches fired from elsewhere. + fields["actionUrl"] = actionUrl; + } + return createMetricsData(fields, configuration, eventType); +} +exports.createMetricsSearchData = createMetricsSearchData; +/** + * Create metrics data for impressions event. + * + * @param eventFields - Event fields to create metrics data from. + * @param configuration - Metrics configuration used to create metrics data. + * @param impressionsEventVersion - The version of the impressions event. + */ +function createMetricsImpressionsData(eventFields, configuration, impressionsEventVersion = 4) { + const eventType = "impressions" /* MetricsEventType.impressions */; + const fields = { + ...eventFields, + eventType: eventType, + impressionQueue: "data-metrics", + eventVersion: impressionsEventVersion, + }; + return createMetricsData(fields, configuration, eventType); +} +exports.createMetricsImpressionsData = createMetricsImpressionsData; +/** + * Create metrics data for media event. + * + * @param eventFields - Event fields to create metrics data from. + * @param configuration - Metrics configuration used to create metrics data. + */ +function createMetricsMediaData(eventFields, configuration) { + const eventType = "media" /* MetricsEventType.media */; + const fields = { + ...eventFields, + eventType: eventType, + }; + return createMetricsData(fields, configuration, eventType); +} +exports.createMetricsMediaData = createMetricsMediaData; +/** + * Create metrics data for media event. + * + * @param targetId - The ID of the click target. + * @param targetType - The type of the click target. + * @param eventFields - Event fields to create metrics data from. + * @param configuration - Metrics configuration used to create metrics data. + */ +function createMetricsMediaClickData(targetId, targetType, eventFields, configuration) { + const eventType = "click" /* MetricsEventType.click */; + const fields = { + ...eventFields, + eventType: eventType, + targetType: targetType, + targetId: targetId, + }; + return createMetricsData(fields, configuration, eventType); +} +exports.createMetricsMediaClickData = createMetricsMediaClickData; +// endregion +// region Helpers +function createMetricsData(fields, configuration, eventType) { + return { + fields: fields, + includingFields: configuration.defaultIncludeRequests[eventType], + excludingFields: configuration.defaultExcludeRequests[eventType], + topic: topicFromEventFields(fields, configuration.defaultTopic), + shouldFlush: (0, optional_1.isSome)(configuration.shouldFlush) ? configuration.shouldFlush(fields) : false, + }; +} +/** + * Returns event topic for the given event fields. + * @param eventFields - Event fields. + * @param defaultTopic - An optional default topic to use if event fields doesn't have one. + */ +function topicFromEventFields(eventFields, defaultTopic) { + const topic = eventFields["topic"]; + if ((0, optional_1.isSome)(topic)) { + return topic; + } + return defaultTopic; +} +// endregion +//# sourceMappingURL=builder.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/cookies.js b/node_modules/@jet/environment/metrics/cookies.js new file mode 100644 index 0000000..ba46ef1 --- /dev/null +++ b/node_modules/@jet/environment/metrics/cookies.js @@ -0,0 +1,46 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.cookieValueForKey = exports.cookiesOf = void 0; +const optional_1 = require("../types/optional"); +/** + * Iterate the cookies contained in a string. + * + * @param cookie - A string containing zero or more cookies. + */ +function* cookiesOf(cookie) { + if ((0, optional_1.isNothing)(cookie)) { + return; + } + const rawEntries = cookie.split(";"); + for (const rawEntry of rawEntries) { + const keyEndIndex = rawEntry.indexOf("="); + if (keyEndIndex === -1) { + // If there's no splitter, treat the whole raw + // entry as the key and provide an empty value. + const key = decodeURIComponent(rawEntry).trim(); + yield { key, value: "" }; + } + else { + const key = decodeURIComponent(rawEntry.substring(0, keyEndIndex)).trim(); + const value = decodeURIComponent(rawEntry.substring(keyEndIndex + 1)).trim(); + yield { key, value }; + } + } +} +exports.cookiesOf = cookiesOf; +/** + * Returns value of the cookie with the given key or `null` if there's no such cookie. + * + * @param cookies - Cookies. + * @param key - The key to return cookie value for. + */ +function cookieValueForKey(cookies, key) { + for (const cookie of cookies) { + if (cookie.key === key) { + return cookie.value; + } + } + return null; +} +exports.cookieValueForKey = cookieValueForKey; +//# sourceMappingURL=cookies.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/event-linter.js b/node_modules/@jet/environment/metrics/event-linter.js new file mode 100644 index 0000000..172afe7 --- /dev/null +++ b/node_modules/@jet/environment/metrics/event-linter.js @@ -0,0 +1,155 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventLinter = void 0; +const object_reader_1 = require("../json/reader/object-reader"); +const optional_1 = require("../types/optional"); +const numerics = require("./helpers/numerics"); +/** + * A type which applies common business rules to metrics fields + * and generates events which are ready for posting to Figaro. + * + * The common business rules are: + * - Add base event fields provided by linter configuration provider object + * - Set clientBuiltType and resourceRevNum fields based on EventLinterEnvironment object values + * - Set xpSendMethod field to "jet-js" + * - Combine pageType and pageId using compound separator provided by linter configuration provider + * and set result to "page" field. + * - Apply event field de-resolution rules provided by linter configuration provider object + * - Set the "position" field for events of "media" type. + */ +class EventLinter { + /** + * Create an event linter. + * + * @param options - The options which specify various behaviors of the new linter. + * This object will be frozen. + */ + constructor(options) { + this.options = Object.freeze(options); + } + // MARK: Public Properties + /** + * Topic to use if an event fields blob does not specify one. + */ + get defaultTopic() { + return this.options.defaultTopic; + } + // MARK: Utilities + /** + * Reduce the accuracy of fields in a blob according to + * a given array of rules provided by linter configuration object. + * + * @param eventFields - The fields of an event to reduce the accuracy of. + * @param rules - An array of de-resolution rules to apply to event fields. + */ + applyDeResolutionRules(eventFields, rules) { + const eventFieldsReader = new object_reader_1.ObjectReader(eventFields); + for (const rule of rules) { + const value = eventFieldsReader.asNumber(rule.fieldName); + if ((0, optional_1.isNothing)(value)) { + continue; + } + let magnitude = rule.magnitude; + if ((0, optional_1.isNothing)(magnitude)) { + magnitude = 1024 * 1024; + } + let significantDigits = rule.significantDigits; + if ((0, optional_1.isNothing)(significantDigits)) { + significantDigits = 2; + } + if (magnitude <= 0.0 || significantDigits < 0.0) { + // This is the failure mode from MetricsKit. + eventFields[rule.fieldName] = Number.NaN; + continue; + } + const scaledValue = value / magnitude; + eventFields[rule.fieldName] = numerics.reduceSignificantDigits(scaledValue, significantDigits); + } + } + // MARK: Rules + /** + * Apply the rules which are universal to all metrics events + * to a given metrics fields linter. + * + * @param eventFields - The fields which will be used to construct a built event. + * @param topic - The topic the built event will be submitted to. + */ + decorateCommonEventFields(eventFields, topic) { + const eventFieldsReader = new object_reader_1.ObjectReader(eventFields); + const configurationProvider = this.options.configuration; + // - Base metrics fields. + const baseFields = configurationProvider.baseFields(topic); + if ((0, optional_1.isSome)(baseFields)) { + Object.assign(eventFields, baseFields); + } + // - Universal basic fields. + eventFields["clientBuildType"] = this.options.environment.buildType; + eventFields["resourceRevNum"] = this.options.environment.jsVersion; + eventFields["xpSendMethod"] = "jet-js"; + // - Page. + const pageType = eventFieldsReader.asString("pageType"); + const pageId = eventFieldsReader.asString("pageId"); + if ((0, optional_1.isSome)(pageType) && (0, optional_1.isSome)(pageId) && (0, optional_1.isNothing)(eventFields["page"])) { + const bagValue = configurationProvider.compoundSeparator(topic); + const separator = (0, optional_1.isSome)(bagValue) ? (0, optional_1.unwrapOptional)(bagValue) : "_"; + eventFields["page"] = `${pageType}${separator}${pageId}`; + } + // - Field value resolution reduction. + const rules = configurationProvider.deResolutionRules(topic); + this.applyDeResolutionRules(eventFields, rules); + } + /** + * Apply the rules specific to the `media` event. + * + * @param eventFields - The fields which will be used to construct a built event. + */ + decorateMediaEventEvents(eventFields) { + const eventFieldsReader = new object_reader_1.ObjectReader(eventFields); + const position = eventFieldsReader.asNumber("position"); + if ((0, optional_1.isSome)(position)) { + eventFields["position"] = Math.round(position); + } + } + // MARK: Decorating Event Fields + /** + * Lint metrics event fields by applying the common business rules to a given fields blob. + * + * @remarks + * + * Note: A deep copy of event fields is created by linter using `JSON.parse(JSON.stringify(eventFields))`. + * The original event fields are not modified. + * + * @param eventFields - The fields to decorate. + * @param context - The additional event linter context to be passed to all + * event linter rules. This is a free-form object so clients can pass custom + * context information. + * @returns Decorated fields ready for creating a metrics event. + */ + lint(eventFields, context = {}) { + const eventFieldsReader = new object_reader_1.ObjectReader(eventFields); + const eventType = eventFieldsReader.asString("eventType"); + if (this.options.isLoggingEnabled) { + console.log(`Building event for event type: ${eventType !== null && eventType !== void 0 ? eventType : "<null>"}`); + } + // Make sure we have a deep copy of an object. + const decoratedEventFields = JSON.parse(JSON.stringify(eventFields)); + const value = eventFieldsReader.asString("topic"); + const topic = (0, optional_1.isSome)(value) ? (0, optional_1.unwrapOptional)(value) : this.options.defaultTopic; + this.decorateCommonEventFields(decoratedEventFields, topic); + switch (eventType) { + case "media" /* MetricsEventType.media */: + this.decorateMediaEventEvents(decoratedEventFields); + break; + default: + break; + } + for (const rule of this.options.rules) { + rule.apply(decoratedEventFields, context); + } + return { + fields: decoratedEventFields, + }; + } +} +exports.EventLinter = EventLinter; +//# sourceMappingURL=event-linter.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/fetch-timing-metrics-builder.js b/node_modules/@jet/environment/metrics/fetch-timing-metrics-builder.js new file mode 100644 index 0000000..7a658ff --- /dev/null +++ b/node_modules/@jet/environment/metrics/fetch-timing-metrics-builder.js @@ -0,0 +1,96 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.fetchTimingMetricsBuilderType = exports.FetchTimingMetricsBuilder = void 0; +const optional_1 = require("../types/optional"); +const metatype_1 = require("../util/metatype"); +const promise_1 = require("../util/promise"); +/** Object to collect `FetchTimingMetrics`, in order to decorate a model object with these metrics. */ +class FetchTimingMetricsBuilder { + constructor() { + this.metrics = []; + } + /** + * Collects the timing metrics from the response, + * then execute code for parsing while capturing timing points around it, + * adding those parse times to the first timing metrics. + */ + measureParsing(response, body) { + const responseTimingMetrics = response.metrics.length > 0 ? [...response.metrics] : []; + const parseStartTime = Date.now(); + const result = body(response); + const parseEndTime = Date.now(); + // MAINTAINERS NOTE: + // Follows app store approach to add additional timing points to the first item. + // This may not be strictly correct, and we should revisit this later. + if (responseTimingMetrics.length > 0) { + responseTimingMetrics[0].parseStartTime = parseStartTime; + responseTimingMetrics[0].parseEndTime = parseEndTime; + } + this.metrics.push(...responseTimingMetrics); + return result; + } + /** + * Execute code for model construction while capturing timing points around it, + * adding those model construction times to the first timing metrics. + * + * Use this method when model construction is a synchronous operation. + * Use `measureModelConstructionAsync()` when model construction is an asynchronous operation. + */ + measureModelConstruction(body) { + const buildModelStartTime = Date.now(); + const result = body(); + const buildModelEndTime = Date.now(); + this.saveModelConstructionTimes(buildModelStartTime, buildModelEndTime); + return result; + } + /** + * Execute and await code for an asynchronous model construction operation while capturing timing points around it, + * adding those model construction times to the first timing metrics. + * + * Use this method when model construction is an asynchronous operation. + * Use `measureModelConstruction()` when model construction is a synchronous operation. + */ + async measureModelConstructionAsync(body) { + const buildModelStartTime = Date.now(); + const result = await body(); + const buildModelEndTime = Date.now(); + this.saveModelConstructionTimes(buildModelStartTime, buildModelEndTime); + return result; + } + saveModelConstructionTimes(startTime, endTime) { + // MAINTAINERS NOTE: + // Follows app store approach to add additional timing points to the first item. + // This may not be strictly correct, and we should revisit this later. + if (this.metrics.length > 0) { + this.metrics[0].modelConstructionStartTime = startTime; + this.metrics[0].modelConstructionEndTime = endTime; + } + else { + this.metrics.push({ + modelConstructionStartTime: startTime, + modelConstructionEndTime: endTime, + }); + } + } + /** Add the recorded `FetchTimingMetrics` as an additional property on the model. */ + decorate(model) { + if ((0, optional_1.isNothing)(model)) { + throw new Error("Cannot decorate null or undefined"); + } + if (typeof model !== "object") { + throw new Error("View model to decorate must be an object"); + } + if ((0, promise_1.isPromise)(model)) { + // TypeScript compiler may be able to enforce this at compile time. + // A newer version of TypeScript which supports `Awaited` type may help. + throw new Error("Cannot decorate a Promise object"); + } + if (this.metrics.length > 0) { + model["$networkPerformance"] = this.metrics; + } + } +} +exports.FetchTimingMetricsBuilder = FetchTimingMetricsBuilder; +/** Metatype for the `FetchTimingMetricsBuilder`, for adding to an object graph. */ +exports.fetchTimingMetricsBuilderType = (0, metatype_1.makeMetatype)("jet-engine:fetchTimingMetricsBuilder"); +//# sourceMappingURL=fetch-timing-metrics-builder.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/helpers/index.js b/node_modules/@jet/environment/metrics/helpers/index.js new file mode 100644 index 0000000..ee6bdeb --- /dev/null +++ b/node_modules/@jet/environment/metrics/helpers/index.js @@ -0,0 +1,21 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./location"), exports); +__exportStar(require("./models"), exports); +__exportStar(require("./numerics"), exports); +__exportStar(require("./util"), exports); +//# sourceMappingURL=index.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/helpers/location.js b/node_modules/@jet/environment/metrics/helpers/location.js new file mode 100644 index 0000000..7bccd40 --- /dev/null +++ b/node_modules/@jet/environment/metrics/helpers/location.js @@ -0,0 +1,213 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MetricsLocationTracker = void 0; +const validation = require("../../json/validation"); +const optional_1 = require("../../types/optional"); +const metricsUtil = require("./util"); +/** + * A type describing metrics location tracker. + * + * The tracker manages stack of metrics location items. + */ +class MetricsLocationTracker { + // endregion + // region Initialization + /** + * Create new metrics location tracker with all required attributes. + * @param rootPosition - Current root position of the tracker. + * @param locations - Array of metrics location to track. + */ + constructor(rootPosition = 0, locations = []) { + this.rootPosition = rootPosition; + this.locationStack = locations.map((location) => new MetricsLocationStackItem(location)); + } + // endregion + // region Location Stack Management + /** + * Check whether location stack is empty or not. + */ + get isEmpty() { + return this.locationStack.length === 0; + } + /** + * Push new location to location tracker's stack. + * @param location - Location to push to stack. + */ + pushLocation(location) { + this.locationStack.push(new MetricsLocationStackItem(location)); + } + /** + * Pop location from location tracker's stack. + */ + popLocation() { + var _a; + if (this.locationStack.length === 0) { + validation.unexpectedType("ignoredValue", "non-empty location stack", "empty location stack"); + return null; + } + return (_a = this.locationStack.pop()) === null || _a === void 0 ? void 0 : _a.location; + } + /** + * Returns tracker's current position. + */ + get currentPosition() { + const stackItem = this.lastStackItem; + if ((0, optional_1.isSome)(stackItem)) { + return stackItem.position; + } + else { + return this.rootPosition; + } + } + /** + * Set current position of tracker. + * This is necessary when large today modules are broken apart into multipart shelves. + * We need to preserve the position of content within server-response, not our logical shelves. + * @param position - Position to set to. + */ + setCurrentPosition(position) { + const stackItem = this.lastStackItem; + if ((0, optional_1.isSome)(stackItem)) { + stackItem.position = position; + } + else { + this.rootPosition = position; + } + } + /** + * Advance tracker's position. + */ + nextPosition() { + const stackItem = this.lastStackItem; + if ((0, optional_1.isSome)(stackItem)) { + stackItem.position += 1; + } + else { + this.rootPosition += 1; + } + } + /** + * Convert location tracker's stack items to array of metric location objects. + */ + get stackItemsToLocations() { + return this.locationStack.map((stackItem) => stackItem.location); + } + /** + * Returns last stack item in location stack or `null` if stack is empty. + */ + get lastStackItem() { + const length = this.locationStack.length; + if (length === 0) { + return null; + } + return this.locationStack[length - 1]; + } + // endregion + // region Adding Location + /** + * Create new basic location and add it to existing locations of the location tracker + * and return resulting array of locations. + * @param options - Base metrics options which include location tracker to get current locations from. + * @param title - New location title. + */ + static locationsByAddingBasicLocation(options, title) { + const locations = options.locationTracker.stackItemsToLocations; + locations.push(MetricsLocationTracker.buildBasicLocation(options, title)); + return locations; + } + /** + * Create new content location and add it to existing locations of the location tracker + * and return resulting array of locations. + * @param options - Content metrics options which include location tracker to get current locations from. + * @param title - New location title. + */ + static locationsByAddingContentLocation(options, title) { + const locations = options.locationTracker.stackItemsToLocations; + locations.push(MetricsLocationTracker.buildContentLocation(options, title)); + return locations; + } + // endregion + // region Metrics Options + /** + * Create new basic location from base metrics options + * and push it to the stack of location tracker included into options. + * @param options - Base metrics options which include location tracker to push new location to. + * @param title - Location title. + */ + static pushBasicLocation(options, title) { + options.locationTracker.pushLocation(MetricsLocationTracker.buildBasicLocation(options, title)); + } + /** + * Create new content location from content metrics options + * and push it to the stack of location tracker included into options. + * @param options - Content metrics options which include location tracker to push new location to. + * @param title - Location title. + */ + static pushContentLocation(options, title) { + options.locationTracker.pushLocation(MetricsLocationTracker.buildContentLocation(options, title)); + } + /** + * Pop last location from location tracker contained in metrics options. + * @param options - Metrics options containing the location tracker. + */ + static popLocation(options) { + return options.locationTracker.popLocation(); + } + // endregion + // region Location Builders + static buildBasicLocation(options, title) { + let name = title; + if ((0, optional_1.isSome)(options.anonymizationOptions)) { + name = options.anonymizationOptions.anonymizationString; + } + const location = { + locationPosition: options.locationTracker.currentPosition, + locationType: metricsUtil.targetTypeForMetricsOptions(options), + name: name, + }; + if ((0, optional_1.isSome)(options.recoMetricsData)) { + Object.assign(location, options.recoMetricsData); + } + return location; + } + static buildContentLocation(options, title) { + const base = MetricsLocationTracker.buildBasicLocation(options, title); + // Use the location tracker if there is no id override. + if ((0, optional_1.isNothing)(options.id)) { + base.idType = "sequential" /* MetricsIDType.sequential */; + base.id = options.locationTracker.currentPosition.toString(); + } + else { + // If there is a id specified, use that. + base.idType = metricsUtil.idTypeForMetricsOptions(options); + let id = options.id; + if ((0, optional_1.isSome)(options.anonymizationOptions)) { + id = options.anonymizationOptions.anonymizationString; + } + base.id = id; + } + if ((0, optional_1.isSome)(options.fcKind)) { + base.fcKind = options.fcKind; + } + if ((0, optional_1.isSome)(options.displayStyle)) { + base.displayStyle = options.displayStyle; + } + return base; + } +} +exports.MetricsLocationTracker = MetricsLocationTracker; +/** + * A type describing a metrics location item in location tracking stack. + */ +class MetricsLocationStackItem { + /** + * Create new metrics location stack item with all required attributes. + * @param location - The metrics location associated with this item. + * @param position - The position of the item. + */ + constructor(location, position = 0) { + this.location = location; + this.position = position; + } +} +//# sourceMappingURL=location.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/helpers/models.js b/node_modules/@jet/environment/metrics/helpers/models.js new file mode 100644 index 0000000..b2dccd6 --- /dev/null +++ b/node_modules/@jet/environment/metrics/helpers/models.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=models.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/helpers/numerics.js b/node_modules/@jet/environment/metrics/helpers/numerics.js new file mode 100644 index 0000000..822f51f --- /dev/null +++ b/node_modules/@jet/environment/metrics/helpers/numerics.js @@ -0,0 +1,23 @@ +"use strict"; +/** + * Number related helper functions for metrics. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.reduceSignificantDigits = void 0; +/** + * Reduce significant figures of `value` by `significantDigits`. + * @param value - Value to reduce precision of. + * @param significantDigits - Number of significant digits to reduce precision by. + * + * Examples: + * value = 123.5, significantDigits = 0, result = 120 (no significant digit reduced) + * value = 123.5, significantDigits = 1, result = 120 (1 significant digit reduced) + * value = 123.5, significantDigits = 2, result = 100 (2 significant digit reduced) + */ +function reduceSignificantDigits(value, significantDigits) { + const roundFactor = Math.pow(10.0, significantDigits); + const roundingFunction = value > 0.0 ? Math.floor : Math.ceil; + return roundingFunction(value / roundFactor) * roundFactor; +} +exports.reduceSignificantDigits = reduceSignificantDigits; +//# sourceMappingURL=numerics.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/helpers/util.js b/node_modules/@jet/environment/metrics/helpers/util.js new file mode 100644 index 0000000..00c739a --- /dev/null +++ b/node_modules/@jet/environment/metrics/helpers/util.js @@ -0,0 +1,76 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.searchTermFromRefURL = exports.extractSiriRefAppFromRefURL = exports.idTypeForMetricsOptions = exports.targetTypeForMetricsOptions = void 0; +const optional_1 = require("../../types/optional"); +const urls = require("../../util/urls"); +/** + * Returns click target type for given base metrics options. + * @param options - Base metrics options to derive click target type for. + */ +function targetTypeForMetricsOptions(options) { + let type = options.targetType; + if ((0, optional_1.isNothing)(type)) { + type = "lockup" /* MetricsClickTargetType.lockup */; + } + return type; +} +exports.targetTypeForMetricsOptions = targetTypeForMetricsOptions; +/** + * Returns metrics ID type for given content metrics options. + * @param options - Content metrics options to derive metrics ID type for. + */ +function idTypeForMetricsOptions(options) { + let type = options.idType; + if ((0, optional_1.isNothing)(type)) { + type = "its_id" /* MetricsIDType.itsID */; + } + return type; +} +exports.idTypeForMetricsOptions = idTypeForMetricsOptions; +/** + * Extract and return Siri reference app from URL string. + * @param refUrlString - URL string. + * @returns An optional Siri reference app string. + */ +function extractSiriRefAppFromRefURL(urlString) { + const refUrl = new urls.URL(urlString); + if ((0, optional_1.isNothing)(refUrl.query)) { + return null; + } + let extractedRefApp = null; + for (const key of Object.keys(refUrl.query)) { + if (key === "referrer") { + if (refUrl.query[key] === "siri") { + extractedRefApp = "com.apple.siri"; + } + break; + } + } + return extractedRefApp; +} +exports.extractSiriRefAppFromRefURL = extractSiriRefAppFromRefURL; +/** + * Extract and return search term from reference URL string. + * @param refUrlString - Reference URL string. + * @returns An optional search term string. + */ +function searchTermFromRefURL(refUrlString) { + const refUrl = new urls.URL(refUrlString); + const queryItems = refUrl.query; + if ((0, optional_1.isNothing)(queryItems)) { + return null; + } + const searchTerm = queryItems["term"]; + const path = refUrl.pathname; + if ((0, optional_1.isNothing)(searchTerm) || (0, optional_1.isNothing)(path)) { + return null; + } + if (!path.endsWith("/search")) { + return null; + } + // the url object has already url-decoded this query parameter + const plainTerm = searchTerm; + return plainTerm; +} +exports.searchTermFromRefURL = searchTermFromRefURL; +//# sourceMappingURL=util.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/index.js b/node_modules/@jet/environment/metrics/index.js new file mode 100644 index 0000000..44c4a8f --- /dev/null +++ b/node_modules/@jet/environment/metrics/index.js @@ -0,0 +1,23 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./builder"), exports); +__exportStar(require("./cookies"), exports); +__exportStar(require("./event-linter"), exports); +__exportStar(require("./fetch-timing-metrics-builder"), exports); +__exportStar(require("./helpers"), exports); +__exportStar(require("./parse-and-build-model"), exports); +//# sourceMappingURL=index.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/metrics/parse-and-build-model.js b/node_modules/@jet/environment/metrics/parse-and-build-model.js new file mode 100644 index 0000000..6f81a59 --- /dev/null +++ b/node_modules/@jet/environment/metrics/parse-and-build-model.js @@ -0,0 +1,116 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.parseAndBuildModel = exports.requestAndBuildModel = void 0; +const dependencies_1 = require("../dependencies"); +const optional_1 = require("../types/optional"); +const fetch_timing_metrics_builder_1 = require("./fetch-timing-metrics-builder"); +/** + * Returns an object graph guaranteed to have a `FetchTimingMetricsBuilder` object. + * + * If a `FetchTimingMetricsBuilder` already exists, the same object graph is returned. + * + * If it doesn't exist, a new object graph is returned, which has a new `FetchTimingMetricsBuilder` added to it. + */ +function ensureFetchTimingMetricsBuilderExists(objectGraph, errorIfNotFound) { + const optionalBuilder = objectGraph.optional(fetch_timing_metrics_builder_1.fetchTimingMetricsBuilderType); + if ((0, optional_1.isSome)(optionalBuilder)) { + return objectGraph; + } + else { + if (errorIfNotFound) { + throw new Error("Could not find FetchTimingMetricsBuilder in the object graph"); + } + return objectGraph.adding(fetch_timing_metrics_builder_1.fetchTimingMetricsBuilderType, new fetch_timing_metrics_builder_1.FetchTimingMetricsBuilder()); + } +} +/** + * Orchestrates the flow to make a network request and then build the view model, + * capturing timing points around the model building step, and then + * adding performance metrics data to the final result. + * + * **Important** + * + * When using this method, it is the responsibility of the `requester` to instrument the network times and parsing times + * using a `FetchTimingMetricsBuilder` which is in the object graph. + * + * A simplified implementation of a requester function might look like this: +``` + import * as types from "@jet/environment/types/globals/types" + import { fetchTimingMetricsBuilderType } from "@jet/environment/metrics"; + + export async function requestFromAPI(request: FetchRequest, objectGraph: ObjectGraph): Promise<MyParsedModel> { + const net = inject(types.net, objectGraph); + const fetchResponse = await net.fetch(request); + const fetchTimingMetricsBuilder = inject(fetchTimingMetricsBuilderType, objectGraph); + return fetchTimingMetricsBuilder.measureParsing(fetchResponse, (response) => { + const parsedBody = JSON.parse(response.body); + return new MyParsedModel(parsedBody); + }); + } +``` + * This approach supports a common pattern in our adopters in which the code that coordinates the fetch of the network request + * is tightly coupled to the code which parses the response. + * + * For an alternative approach which includes orchestrating the parsing step, use `parseAndBuildModel()`. + * + * @param request - The request object to pass to the `requester` function. + * @param objectGraph - Object graph that can be used to pass data to the `requester` and `modelBuilder` steps. + * The object graph must contain a `FetchTimingMetricsBuilder` that the `requester` will use to collect network times and parsing times. + * @param requester - A function which can make a network request, and return a `ResponseType` which can be used by the `modelBuilder`. + * @param modelBuilder - A step that takes the output of the `requester` step, and builds the view model. + * This parameter supports both synchronous and asynchronous functions. + * @returns The view model. + */ +async function requestAndBuildModel(request, objectGraph, requester, modelBuilder) { + const modifiedObjectGraph = ensureFetchTimingMetricsBuilderExists(objectGraph, true); + const response = await requester(request, modifiedObjectGraph); + return await constructAndDecorateModel(response, modifiedObjectGraph, modelBuilder); +} +exports.requestAndBuildModel = requestAndBuildModel; +/** + * Orchestrates the flow to parse a `FetchResponse` and then build the view model, + * adding performance metrics data to the final result. + * + * Timing points are captured around the parsing and model building steps, + * and this is added to the metrics which were returned from the `FetchResponse`. + * + * When using this method, it is the responsibility of the caller + * to perform the network fetch in order to have a `FetchResponse` object. + * The parsing logic which transforms the `FetchResponse` object into an intermediate model object + * will need to be decoupled so that it can be implemented in the `parser` function. + * + * @param response - The response from a `Network.fetch()` call. + * @param objectGraph - Object graph that can be used to pass data to the `parser` and `renderer` steps. + * @param parser - A step that parses a `FetchResponse` to an intermediate model object. + * @param modelBuilder - A step that takes the output of the `parser` step, and builds the view model. + * This parameter supports both synchronous and asynchronous functions. + * @returns The view model. + */ +async function parseAndBuildModel(response, objectGraph, parser, modelBuilder) { + const modifiedObjectGraph = ensureFetchTimingMetricsBuilderExists(objectGraph, false); + const fetchTimingMetricsBuilder = (0, dependencies_1.inject)(fetch_timing_metrics_builder_1.fetchTimingMetricsBuilderType, modifiedObjectGraph); + // Add metrics from FetchResponse, and run the parser, capturing timing points. + const parsedObject = fetchTimingMetricsBuilder.measureParsing(response, () => { + return parser(response, modifiedObjectGraph); + }); + if ((0, optional_1.isNothing)(parsedObject)) { + throw new Error("parser function returned null or undefined"); + } + return await constructAndDecorateModel(parsedObject, modifiedObjectGraph, modelBuilder); +} +exports.parseAndBuildModel = parseAndBuildModel; +async function constructAndDecorateModel(parsedObject, objectGraph, modelBuilder) { + const fetchTimingMetricsBuilder = (0, dependencies_1.inject)(fetch_timing_metrics_builder_1.fetchTimingMetricsBuilderType, objectGraph); + // Run the model builder, capturing timing points. + const model = await fetchTimingMetricsBuilder.measureModelConstructionAsync(async () => { + const maybePromise = modelBuilder(parsedObject, objectGraph); // The actual model, or a Promise of the model. + return await Promise.resolve(maybePromise); // If it was promise, it will await until the promise is resolved. + }); + if ((0, optional_1.isNothing)(model)) { + throw new Error("model builder function returned null or undefined"); + } + // Decorate the final output with the metrics data. + fetchTimingMetricsBuilder.decorate(model); + return model; +} +//# sourceMappingURL=parse-and-build-model.js.map
\ No newline at end of file |
