@amplitude/session-replay-browser 1.47.0-sr-trc-debug-log.0 → 1.47.0-sr-trc-debug-log.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/config/joined-config.d.ts.map +1 -1
- package/lib/cjs/config/joined-config.js +23 -19
- package/lib/cjs/config/joined-config.js.map +1 -1
- package/lib/cjs/diagnostics.d.ts +5 -0
- package/lib/cjs/diagnostics.d.ts.map +1 -1
- package/lib/cjs/diagnostics.js +11 -1
- package/lib/cjs/diagnostics.js.map +1 -1
- package/lib/cjs/plugins/url-tracking-plugin.d.ts +6 -0
- package/lib/cjs/plugins/url-tracking-plugin.d.ts.map +1 -1
- package/lib/cjs/plugins/url-tracking-plugin.js +3 -1
- package/lib/cjs/plugins/url-tracking-plugin.js.map +1 -1
- package/lib/cjs/session-replay.d.ts +7 -0
- package/lib/cjs/session-replay.d.ts.map +1 -1
- package/lib/cjs/session-replay.js +123 -26
- package/lib/cjs/session-replay.js.map +1 -1
- package/lib/cjs/targeting/targeting-manager.d.ts.map +1 -1
- package/lib/cjs/targeting/targeting-manager.js +28 -15
- package/lib/cjs/targeting/targeting-manager.js.map +1 -1
- package/lib/cjs/version.d.ts +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/cjs/version.js.map +1 -1
- package/lib/esm/config/joined-config.d.ts.map +1 -1
- package/lib/esm/config/joined-config.js +23 -19
- package/lib/esm/config/joined-config.js.map +1 -1
- package/lib/esm/diagnostics.d.ts +5 -0
- package/lib/esm/diagnostics.d.ts.map +1 -1
- package/lib/esm/diagnostics.js +11 -1
- package/lib/esm/diagnostics.js.map +1 -1
- package/lib/esm/plugins/url-tracking-plugin.d.ts +6 -0
- package/lib/esm/plugins/url-tracking-plugin.d.ts.map +1 -1
- package/lib/esm/plugins/url-tracking-plugin.js +3 -1
- package/lib/esm/plugins/url-tracking-plugin.js.map +1 -1
- package/lib/esm/session-replay.d.ts +7 -0
- package/lib/esm/session-replay.d.ts.map +1 -1
- package/lib/esm/session-replay.js +123 -26
- package/lib/esm/session-replay.js.map +1 -1
- package/lib/esm/targeting/targeting-manager.d.ts.map +1 -1
- package/lib/esm/targeting/targeting-manager.js +28 -15
- package/lib/esm/targeting/targeting-manager.js.map +1 -1
- package/lib/esm/version.d.ts +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/esm/version.js.map +1 -1
- package/lib/scripts/index-min.js +1 -1
- package/lib/scripts/index-min.js.gz +0 -0
- package/lib/scripts/index-min.js.map +1 -1
- package/lib/scripts/session-replay-browser-min.js +1 -1
- package/lib/scripts/session-replay-browser-min.js.gz +0 -0
- package/lib/scripts/session-replay-browser-min.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-tracking-plugin.js","sourceRoot":"","sources":["../../../src/plugins/url-tracking-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { getPageUrl } from '../helpers';\nimport { UGCFilterRule } from '../config/types';\nimport { DEFAULT_URL_CHANGE_POLLING_INTERVAL } from '../constants';\nimport { RecordPlugin } from '@amplitude/rrweb-types';\n\n/**\n * Event emitted when URL changes are detected by the plugin\n * Contains the current page URL, title, and viewport dimensions\n */\nexport interface URLChangeEvent {\n /** The current page URL (may be filtered if UGC rules are applied) */\n href: string;\n /** The current page title */\n title: string;\n /** Viewport height in pixels */\n viewportHeight: number;\n /** Viewport width in pixels */\n viewportWidth: number;\n /** The type of URL change event */\n type: string;\n}\n\n/**\n * Configuration options for the URL tracking plugin\n */\nexport interface URLTrackingPluginOptions {\n /** Rules for filtering sensitive URLs (User Generated Content) */\n ugcFilterRules?: UGCFilterRule[];\n /** Whether to use polling instead of history API events for URL detection */\n enablePolling?: boolean;\n /** Interval in milliseconds for polling URL changes (default: 1000ms) */\n pollingInterval?: number;\n /** Whether to capture document title in URL change events (default: false) */\n captureDocumentTitle?: boolean;\n}\n\n/** Options for subscribeToUrlChanges (polling vs history + hash) */\nexport interface SubscribeToUrlChangesOptions {\n /** Use polling instead of history/popstate/hashchange (e.g. when history is unreliable) */\n enablePolling?: boolean;\n /** Polling interval in ms when enablePolling is true (default: 1000) */\n pollingInterval?: number;\n}\n\n/** Patch detection marker to prevent double-patching */\nconst PATCH_MARKER = '__amplitude_url_tracking_patched__';\n\ntype PatchableHistoryMethod<T extends (...args: any[]) => any> = T & { [PATCH_MARKER]?: boolean };\n\ninterface UrlChangeSubscriptionState {\n callbacks: Set<(href: string) => void>;\n onPopStateOrHashChange: () => void;\n notify: () => void;\n listenersAttached: boolean;\n}\n\n/** Per-globalScope subscription state; persists to avoid wrapper stacking across re-subscribe cycles */\nconst urlChangeSubscriptionsByScope = new WeakMap<Window, UrlChangeSubscriptionState>();\n\n/**\n * Single helper for URL change detection. Supports:\n * - History API (pushState/replaceState) + popstate + hashchange (shared patch per scope)\n * - Optional polling (setInterval on location.href)\n *\n * Used by session-replay targeting (re-evaluate on URL change) and the URL tracking plugin\n * (emit rrweb events). Call the returned function to unsubscribe.\n *\n * @param globalScope - window (or equivalent); no-op if undefined\n * @param onUrlChange - called when the URL changes, with the new href\n * @param options - optional polling (default: event-based only)\n * @returns cleanup function to remove this subscription\n */\nexport function subscribeToUrlChanges(\n globalScope: Window | undefined,\n onUrlChange: (href: string) => void,\n options: SubscribeToUrlChangesOptions = {},\n): () => void {\n if (!globalScope?.location) {\n return (): void => {\n return;\n };\n }\n\n const { enablePolling = false, pollingInterval = DEFAULT_URL_CHANGE_POLLING_INTERVAL } = options;\n\n if (enablePolling) {\n const getHref = (): string => globalScope.location.href ?? '';\n let lastHref = getHref();\n const id = globalScope.setInterval(() => {\n const href = getHref();\n if (href === lastHref) {\n return;\n }\n lastHref = href;\n onUrlChange(href);\n }, pollingInterval);\n return (): void => {\n if (id != null) {\n globalScope.clearInterval(id);\n }\n };\n }\n\n let subscriptionState = urlChangeSubscriptionsByScope.get(globalScope);\n if (!subscriptionState) {\n let lastHref: string | undefined = undefined;\n const callbacks = new Set<(href: string) => void>();\n\n const getHref = (): string => globalScope.location.href ?? '';\n\n const notify = (): void => {\n const href = getHref();\n if (lastHref !== undefined && href === lastHref) return;\n lastHref = href;\n callbacks.forEach((c) => c(href));\n };\n\n /**\n * Creates a patched version of history methods (pushState/replaceState)\n * that calls the original method and then emits a URL change event.\n */\n const createHistoryMethodPatch = <T extends typeof history.pushState | typeof history.replaceState>(\n originalMethod: T,\n ) => {\n const patchedMethod = function (this: History, ...args: Parameters<T>) {\n const result = originalMethod.apply(this, args);\n // Read from location.href after history call so we always notify with resolved absolute URL.\n notify();\n return result;\n } as PatchableHistoryMethod<T>;\n\n patchedMethod[PATCH_MARKER] = true;\n return patchedMethod;\n };\n\n const history = globalScope.history;\n if (history?.pushState) {\n const pushState = Reflect.get(history, 'pushState') as PatchableHistoryMethod<History['pushState']>;\n if (!pushState[PATCH_MARKER]) {\n history.pushState = createHistoryMethodPatch(pushState);\n }\n }\n if (history?.replaceState) {\n const replaceState = Reflect.get(history, 'replaceState') as PatchableHistoryMethod<History['replaceState']>;\n if (!replaceState[PATCH_MARKER]) {\n history.replaceState = createHistoryMethodPatch(replaceState);\n }\n }\n\n subscriptionState = {\n callbacks,\n notify,\n onPopStateOrHashChange: () => notify(),\n listenersAttached: false,\n };\n urlChangeSubscriptionsByScope.set(globalScope, subscriptionState);\n }\n\n const state = subscriptionState;\n if (!state.listenersAttached) {\n globalScope.addEventListener('popstate', state.onPopStateOrHashChange);\n globalScope.addEventListener('hashchange', state.onPopStateOrHashChange);\n state.listenersAttached = true;\n }\n\n state.callbacks.add(onUrlChange);\n return (): void => {\n state.callbacks.delete(onUrlChange);\n if (state.callbacks.size === 0) {\n // Do not restore history methods: we are not aware of patches applied by other scripts.\n if (state.listenersAttached) {\n globalScope.removeEventListener('popstate', state.onPopStateOrHashChange);\n globalScope.removeEventListener('hashchange', state.onPopStateOrHashChange);\n state.listenersAttached = false;\n }\n }\n };\n}\n\n/**\n * Creates a URL tracking plugin for rrweb record function\n *\n * This plugin monitors URL changes in the browser and emits events when the URL changes.\n * It supports three tracking modes:\n * 1. Polling (if explicitly enabled) - periodically checks for URL changes\n * 2. History API + Hash routing (default) - patches pushState/replaceState, listens to popstate and hashchange\n * 3. Hash routing only (fallback) - listens to hashchange events when History API is unavailable\n *\n * The plugin handles edge cases gracefully:\n * - Missing or null location objects\n * - Undefined, null, or empty location.href values\n * - Temporal dead zone issues with variable declarations\n * - Consistent URL normalization across all code paths\n *\n * @param options Configuration options for URL tracking\n * @returns RecordPlugin instance that can be used with rrweb\n */\nexport function createUrlTrackingPlugin(\n options: URLTrackingPluginOptions = {},\n): RecordPlugin<URLTrackingPluginOptions> {\n return {\n name: 'amplitude/url-tracking@1',\n observer(cb, globalScope, pluginOptions?: URLTrackingPluginOptions) {\n // Merge options with plugin-level options taking precedence over constructor options\n const config = { ...options, ...pluginOptions };\n const ugcFilterRules = config.ugcFilterRules || [];\n const enablePolling = config.enablePolling ?? false;\n const pollingInterval = config.pollingInterval ?? DEFAULT_URL_CHANGE_POLLING_INTERVAL;\n const captureDocumentTitle = config.captureDocumentTitle ?? false;\n\n // Early return if no global scope is available\n if (!globalScope) {\n return () => {\n // No cleanup needed if no global scope available\n };\n }\n\n // Track the last URL to prevent duplicate events\n // Initialize to undefined to ensure first call always emits an event\n let lastTrackedUrl: string | undefined = undefined;\n\n // Helper functions\n /**\n * Gets the current URL with proper normalization\n * Handles edge cases where location.href might be undefined, null, or empty\n * Ensures consistent behavior across all code paths\n * @returns Normalized URL string (empty string if location unavailable)\n */\n const getCurrentUrl = (): string => {\n if (!globalScope.location) return '';\n return globalScope.location.href || '';\n };\n\n /**\n * Creates a URL change event with current page information\n * Applies UGC filtering if rules are configured\n * Uses getCurrentUrl() for consistent URL normalization\n * @returns URLChangeEvent with current page state\n */\n const createUrlChangeEvent = (): URLChangeEvent => {\n const { innerHeight, innerWidth, document } = globalScope;\n const currentUrl = getCurrentUrl();\n let currentTitle = '';\n if (captureDocumentTitle) {\n currentTitle = document?.title || '';\n }\n\n // Apply UGC filtering if rules are provided, otherwise use original URL\n const filteredUrl = ugcFilterRules.length > 0 ? getPageUrl(currentUrl, ugcFilterRules) : currentUrl;\n\n return {\n href: filteredUrl,\n title: currentTitle,\n viewportHeight: innerHeight,\n viewportWidth: innerWidth,\n type: 'url-change-event',\n };\n };\n\n /**\n * Emits a URL change event if the URL has actually changed\n * Always emits on first call (when lastTrackedUrl is undefined)\n */\n const emitUrlChange = (): void => {\n const currentUrl = getCurrentUrl();\n if (lastTrackedUrl === undefined || currentUrl !== lastTrackedUrl) {\n lastTrackedUrl = currentUrl;\n const event = createUrlChangeEvent();\n cb(event);\n }\n };\n\n // Single helper: history + popstate + hashchange, or polling when enabled\n const unsubscribe = subscribeToUrlChanges(\n globalScope as Window,\n () => emitUrlChange(),\n enablePolling ? { enablePolling: true, pollingInterval } : {},\n );\n emitUrlChange();\n\n return (): void => unsubscribe();\n },\n options,\n };\n}\n\n/**\n * Default URL tracking plugin instance with default options\n * Can be used directly without custom configuration\n */\nexport const urlTrackingPlugin = createUrlTrackingPlugin();\n"]}
|
|
1
|
+
{"version":3,"file":"url-tracking-plugin.js","sourceRoot":"","sources":["../../../src/plugins/url-tracking-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { getPageUrl } from '../helpers';\nimport { UGCFilterRule } from '../config/types';\nimport { DEFAULT_URL_CHANGE_POLLING_INTERVAL } from '../constants';\nimport { RecordPlugin } from '@amplitude/rrweb-types';\n\n/**\n * Event emitted when URL changes are detected by the plugin\n * Contains the current page URL, title, and viewport dimensions\n */\nexport interface URLChangeEvent {\n /** The current page URL (may be filtered if UGC rules are applied) */\n href: string;\n /** The current page title */\n title: string;\n /** Viewport height in pixels */\n viewportHeight: number;\n /** Viewport width in pixels */\n viewportWidth: number;\n /** The type of URL change event */\n type: string;\n}\n\n/**\n * Configuration options for the URL tracking plugin\n */\nexport interface URLTrackingPluginOptions {\n /** Rules for filtering sensitive URLs (User Generated Content) */\n ugcFilterRules?: UGCFilterRule[];\n /** Whether to use polling instead of history API events for URL detection */\n enablePolling?: boolean;\n /** Interval in milliseconds for polling URL changes (default: 1000ms) */\n pollingInterval?: number;\n /** Whether to capture document title in URL change events (default: false) */\n captureDocumentTitle?: boolean;\n}\n\n/** Options for subscribeToUrlChanges (polling vs history + hash) */\nexport interface SubscribeToUrlChangesOptions {\n /** Use polling instead of history/popstate/hashchange (e.g. when history is unreliable) */\n enablePolling?: boolean;\n /** Polling interval in ms when enablePolling is true (default: 1000) */\n pollingInterval?: number;\n /**\n * Optional debug logger, called once per poll tick (only on the polling path). Lets the SDK\n * confirm in the browser console that the interval is actually firing — useful when verifying\n * that enableUrlChangePolling took effect for an SPA that bypasses the History API.\n */\n log?: (message: string) => void;\n}\n\n/** Patch detection marker to prevent double-patching */\nconst PATCH_MARKER = '__amplitude_url_tracking_patched__';\n\ntype PatchableHistoryMethod<T extends (...args: any[]) => any> = T & { [PATCH_MARKER]?: boolean };\n\ninterface UrlChangeSubscriptionState {\n callbacks: Set<(href: string) => void>;\n onPopStateOrHashChange: () => void;\n notify: () => void;\n listenersAttached: boolean;\n}\n\n/** Per-globalScope subscription state; persists to avoid wrapper stacking across re-subscribe cycles */\nconst urlChangeSubscriptionsByScope = new WeakMap<Window, UrlChangeSubscriptionState>();\n\n/**\n * Single helper for URL change detection. Supports:\n * - History API (pushState/replaceState) + popstate + hashchange (shared patch per scope)\n * - Optional polling (setInterval on location.href)\n *\n * Used by session-replay targeting (re-evaluate on URL change) and the URL tracking plugin\n * (emit rrweb events). Call the returned function to unsubscribe.\n *\n * @param globalScope - window (or equivalent); no-op if undefined\n * @param onUrlChange - called when the URL changes, with the new href\n * @param options - optional polling (default: event-based only)\n * @returns cleanup function to remove this subscription\n */\nexport function subscribeToUrlChanges(\n globalScope: Window | undefined,\n onUrlChange: (href: string) => void,\n options: SubscribeToUrlChangesOptions = {},\n): () => void {\n if (!globalScope?.location) {\n return (): void => {\n return;\n };\n }\n\n const { enablePolling = false, pollingInterval = DEFAULT_URL_CHANGE_POLLING_INTERVAL, log } = options;\n\n if (enablePolling) {\n const getHref = (): string => globalScope.location.href ?? '';\n let lastHref = getHref();\n const id = globalScope.setInterval(() => {\n const href = getHref();\n // Logged every tick (not just on change) so we can confirm the polling loop is alive.\n log?.(`URL polling tick (href=${href}, changed=${String(href !== lastHref)}).`);\n if (href === lastHref) {\n return;\n }\n lastHref = href;\n onUrlChange(href);\n }, pollingInterval);\n return (): void => {\n if (id != null) {\n globalScope.clearInterval(id);\n }\n };\n }\n\n let subscriptionState = urlChangeSubscriptionsByScope.get(globalScope);\n if (!subscriptionState) {\n let lastHref: string | undefined = undefined;\n const callbacks = new Set<(href: string) => void>();\n\n const getHref = (): string => globalScope.location.href ?? '';\n\n const notify = (): void => {\n const href = getHref();\n if (lastHref !== undefined && href === lastHref) return;\n lastHref = href;\n callbacks.forEach((c) => c(href));\n };\n\n /**\n * Creates a patched version of history methods (pushState/replaceState)\n * that calls the original method and then emits a URL change event.\n */\n const createHistoryMethodPatch = <T extends typeof history.pushState | typeof history.replaceState>(\n originalMethod: T,\n ) => {\n const patchedMethod = function (this: History, ...args: Parameters<T>) {\n const result = originalMethod.apply(this, args);\n // Read from location.href after history call so we always notify with resolved absolute URL.\n notify();\n return result;\n } as PatchableHistoryMethod<T>;\n\n patchedMethod[PATCH_MARKER] = true;\n return patchedMethod;\n };\n\n const history = globalScope.history;\n if (history?.pushState) {\n const pushState = Reflect.get(history, 'pushState') as PatchableHistoryMethod<History['pushState']>;\n if (!pushState[PATCH_MARKER]) {\n history.pushState = createHistoryMethodPatch(pushState);\n }\n }\n if (history?.replaceState) {\n const replaceState = Reflect.get(history, 'replaceState') as PatchableHistoryMethod<History['replaceState']>;\n if (!replaceState[PATCH_MARKER]) {\n history.replaceState = createHistoryMethodPatch(replaceState);\n }\n }\n\n subscriptionState = {\n callbacks,\n notify,\n onPopStateOrHashChange: () => notify(),\n listenersAttached: false,\n };\n urlChangeSubscriptionsByScope.set(globalScope, subscriptionState);\n }\n\n const state = subscriptionState;\n if (!state.listenersAttached) {\n globalScope.addEventListener('popstate', state.onPopStateOrHashChange);\n globalScope.addEventListener('hashchange', state.onPopStateOrHashChange);\n state.listenersAttached = true;\n }\n\n state.callbacks.add(onUrlChange);\n return (): void => {\n state.callbacks.delete(onUrlChange);\n if (state.callbacks.size === 0) {\n // Do not restore history methods: we are not aware of patches applied by other scripts.\n if (state.listenersAttached) {\n globalScope.removeEventListener('popstate', state.onPopStateOrHashChange);\n globalScope.removeEventListener('hashchange', state.onPopStateOrHashChange);\n state.listenersAttached = false;\n }\n }\n };\n}\n\n/**\n * Creates a URL tracking plugin for rrweb record function\n *\n * This plugin monitors URL changes in the browser and emits events when the URL changes.\n * It supports three tracking modes:\n * 1. Polling (if explicitly enabled) - periodically checks for URL changes\n * 2. History API + Hash routing (default) - patches pushState/replaceState, listens to popstate and hashchange\n * 3. Hash routing only (fallback) - listens to hashchange events when History API is unavailable\n *\n * The plugin handles edge cases gracefully:\n * - Missing or null location objects\n * - Undefined, null, or empty location.href values\n * - Temporal dead zone issues with variable declarations\n * - Consistent URL normalization across all code paths\n *\n * @param options Configuration options for URL tracking\n * @returns RecordPlugin instance that can be used with rrweb\n */\nexport function createUrlTrackingPlugin(\n options: URLTrackingPluginOptions = {},\n): RecordPlugin<URLTrackingPluginOptions> {\n return {\n name: 'amplitude/url-tracking@1',\n observer(cb, globalScope, pluginOptions?: URLTrackingPluginOptions) {\n // Merge options with plugin-level options taking precedence over constructor options\n const config = { ...options, ...pluginOptions };\n const ugcFilterRules = config.ugcFilterRules || [];\n const enablePolling = config.enablePolling ?? false;\n const pollingInterval = config.pollingInterval ?? DEFAULT_URL_CHANGE_POLLING_INTERVAL;\n const captureDocumentTitle = config.captureDocumentTitle ?? false;\n\n // Early return if no global scope is available\n if (!globalScope) {\n return () => {\n // No cleanup needed if no global scope available\n };\n }\n\n // Track the last URL to prevent duplicate events\n // Initialize to undefined to ensure first call always emits an event\n let lastTrackedUrl: string | undefined = undefined;\n\n // Helper functions\n /**\n * Gets the current URL with proper normalization\n * Handles edge cases where location.href might be undefined, null, or empty\n * Ensures consistent behavior across all code paths\n * @returns Normalized URL string (empty string if location unavailable)\n */\n const getCurrentUrl = (): string => {\n if (!globalScope.location) return '';\n return globalScope.location.href || '';\n };\n\n /**\n * Creates a URL change event with current page information\n * Applies UGC filtering if rules are configured\n * Uses getCurrentUrl() for consistent URL normalization\n * @returns URLChangeEvent with current page state\n */\n const createUrlChangeEvent = (): URLChangeEvent => {\n const { innerHeight, innerWidth, document } = globalScope;\n const currentUrl = getCurrentUrl();\n let currentTitle = '';\n if (captureDocumentTitle) {\n currentTitle = document?.title || '';\n }\n\n // Apply UGC filtering if rules are provided, otherwise use original URL\n const filteredUrl = ugcFilterRules.length > 0 ? getPageUrl(currentUrl, ugcFilterRules) : currentUrl;\n\n return {\n href: filteredUrl,\n title: currentTitle,\n viewportHeight: innerHeight,\n viewportWidth: innerWidth,\n type: 'url-change-event',\n };\n };\n\n /**\n * Emits a URL change event if the URL has actually changed\n * Always emits on first call (when lastTrackedUrl is undefined)\n */\n const emitUrlChange = (): void => {\n const currentUrl = getCurrentUrl();\n if (lastTrackedUrl === undefined || currentUrl !== lastTrackedUrl) {\n lastTrackedUrl = currentUrl;\n const event = createUrlChangeEvent();\n cb(event);\n }\n };\n\n // Single helper: history + popstate + hashchange, or polling when enabled\n const unsubscribe = subscribeToUrlChanges(\n globalScope as Window,\n () => emitUrlChange(),\n enablePolling ? { enablePolling: true, pollingInterval } : {},\n );\n emitUrlChange();\n\n return (): void => unsubscribe();\n },\n options,\n };\n}\n\n/**\n * Default URL tracking plugin instance with default options\n * Can be used directly without custom configuration\n */\nexport const urlTrackingPlugin = createUrlTrackingPlugin();\n"]}
|
|
@@ -67,6 +67,13 @@ export declare class SessionReplay implements AmplitudeSessionReplay {
|
|
|
67
67
|
*/
|
|
68
68
|
private isBelowMinSessionDuration;
|
|
69
69
|
private getCurrentPageForTargeting;
|
|
70
|
+
/**
|
|
71
|
+
* Best-effort navigation type from the Navigation Timing API: 'reload' | 'navigate' |
|
|
72
|
+
* 'back_forward' | 'prerender'. Surfaced in the init diagnostic so a page refresh ('reload')
|
|
73
|
+
* is distinguishable from a fresh load ('navigate') — neither changes the session id, so this
|
|
74
|
+
* is the only way to tell them apart. Returns undefined when the API is unavailable.
|
|
75
|
+
*/
|
|
76
|
+
private getNavigationType;
|
|
70
77
|
protected _init(apiKey: string, options: SessionReplayOptions): Promise<void>;
|
|
71
78
|
setSessionId(sessionId: string | number, deviceId?: string): import("@amplitude/analytics-core").AmplitudeReturn<void>;
|
|
72
79
|
asyncSetSessionId(sessionId: string | number, deviceId?: string, options?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,OAAO,EASR,MAAM,2BAA2B,CAAC;AAKnC,OAAO,EACL,aAAa,EACb,yBAAyB,EACzB,kCAAkC,EAInC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,gBAAgB,EASjB,MAAM,aAAa,CAAC;AAWrB,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAuB,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAcvF,OAAO,EACL,sBAAsB,EACtB,0BAA0B,IAAI,mCAAmC,EAIjE,kBAAkB,IAAI,mBAAmB,EACzC,oBAAoB,EACpB,2BAA2B,EAC5B,MAAM,0BAA0B,CAAC;AAOlC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,KAAK,WAAW,GAAG,CAAC,CAAC,EAAE,mBAAmB,GAAG,KAAK,KAAK,IAAI,CAAC;AAE5D,qBAAa,aAAc,YAAW,sBAAsB;IAC1D,IAAI,SAAuC;IAC3C,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAC9C,qBAAqB,EAAE,kCAAkC,GAAG,SAAS,CAAC;IACtE,WAAW,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC7C,aAAa,CAAC,EAAE,mCAAmC,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;IACtF,cAAc,EAAE,OAAO,CAAC;IACxB,oBAAoB,EAAE,UAAU,CAAC,cAAc,CAAC,GAAG,IAAI,CAAQ;IAC/D,UAAU,SAAK;IACf,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;IAC7C,qBAAqB,UAAS;IAC9B,OAAO,CAAC,mBAAmB,CAAC,CAA8B;IAC1D,OAAO,CAAC,wBAAwB,CAAC,CAAU;IAI3C,OAAO,CAAC,sBAAsB,CAA0C;IAOxE,YAAY,EAAE,WAAW,EAAE,CAAM;IACjC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,iBAAiB,EAAE,uBAAuB,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;IACjE;;;OAGG;IACH,OAAO,CAAC,mBAAmB,CAAK;IAChC,+EAA+E;IAC/E,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,UAAU,CAAC,CAAiB;IACpC,OAAO,CAAC,YAAY,CAAC,CAAe;IACpC,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAoC;IAGpD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,iBAAiB,CAAmE;IAE5F,gFAAgF;IAChF,OAAO,CAAC,cAAc,CAAM;IAE5B,OAAO,CAAC,oCAAoC,CAAwB;IAEpE,yFAAyF;IACzF,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,4BAA4B,CAA6C;IACjF,OAAO,CAAC,8BAA8B,CAA6B;IACnE,qEAAqE;IACrE,OAAO,CAAC,oCAAoC,CAAK;;IAMjD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB;IAIlD,OAAO,CAAC,sBAAsB,CAmB5B;IAEF;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;
|
|
1
|
+
{"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -169,6 +169,17 @@ var SessionReplay = /** @class */ (function () {
|
|
|
169
169
|
}
|
|
170
170
|
pageUrl = (_f = (_c = (_b = targetingParams.page) === null || _b === void 0 ? void 0 : _b.url) !== null && _c !== void 0 ? _c : (_e = (_d = getGlobalScope()) === null || _d === void 0 ? void 0 : _d.location) === null || _e === void 0 ? void 0 : _e.href) !== null && _f !== void 0 ? _f : '';
|
|
171
171
|
pageForTargeting = (_g = targetingParams.page) !== null && _g !== void 0 ? _g : (pageUrl !== '' ? { url: pageUrl } : undefined);
|
|
172
|
+
// Record the targeting trigger event
|
|
173
|
+
this.recordDiagnosticEvent(SrDiagnostic.targetingTrigger, {
|
|
174
|
+
sessionId: this.identifiers.sessionId,
|
|
175
|
+
deviceId: this.getDeviceId(),
|
|
176
|
+
targetingConfig: this.config.targetingConfig,
|
|
177
|
+
targetingParams: {
|
|
178
|
+
userProperties: targetingParams.userProperties,
|
|
179
|
+
event: eventForTargeting,
|
|
180
|
+
page: pageForTargeting,
|
|
181
|
+
},
|
|
182
|
+
});
|
|
172
183
|
evalStart = Date.now();
|
|
173
184
|
return [4 /*yield*/, evaluateTargetingAndStore({
|
|
174
185
|
sessionId: this.identifiers.sessionId,
|
|
@@ -326,12 +337,17 @@ var SessionReplay = /** @class */ (function () {
|
|
|
326
337
|
*/
|
|
327
338
|
SessionReplay.prototype.setupUrlChangeListener = function () {
|
|
328
339
|
var _this = this;
|
|
329
|
-
var _a, _b;
|
|
340
|
+
var _a, _b, _c, _d, _e, _f;
|
|
330
341
|
// If init() runs multiple times, remove the previous URL-change subscription first
|
|
331
342
|
// so we don't leak callbacks and trigger duplicate targeting evaluations.
|
|
332
343
|
(_a = this.urlChangeCleanup) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
333
344
|
var globalScope = getGlobalScope();
|
|
334
345
|
if (!(globalScope === null || globalScope === void 0 ? void 0 : globalScope.location)) {
|
|
346
|
+
// No window/location (SSR, worker, or pre-render) — the listener can't attach, so TRC will
|
|
347
|
+
// never re-evaluate on navigation. Surface it rather than failing silently.
|
|
348
|
+
this.incrementDiagnostic(SrDiagnostic.urlListenerSkipped);
|
|
349
|
+
this.recordDiagnosticEvent(SrDiagnostic.urlListenerSkipped, { reason: 'no_global_scope' });
|
|
350
|
+
this.loggerProvider.debug('URL-change listener not attached: no global scope/location.');
|
|
335
351
|
return;
|
|
336
352
|
}
|
|
337
353
|
var hasTargeting = !!((_b = this.config) === null || _b === void 0 ? void 0 : _b.targetingConfig);
|
|
@@ -356,7 +372,27 @@ var SessionReplay = /** @class */ (function () {
|
|
|
356
372
|
_this.loggerProvider.debug("Queued URL-change targeting re-evaluation #".concat(evaluationId, " for ").concat(href, "."));
|
|
357
373
|
}
|
|
358
374
|
};
|
|
359
|
-
|
|
375
|
+
// Pass the polling options so targeting re-evaluation also respects `enableUrlChangePolling`.
|
|
376
|
+
// Without this, the targeting listener only sees history.pushState/replaceState + popstate +
|
|
377
|
+
// hashchange — so SPA navigations that bypass the history API never re-evaluate TRC and
|
|
378
|
+
// recording never starts on the new URL (enableUrlChangePolling previously only affected the
|
|
379
|
+
// rrweb URL-tracking plugin, which runs only once recording is already active).
|
|
380
|
+
var enablePolling = (_d = (_c = this.config) === null || _c === void 0 ? void 0 : _c.enableUrlChangePolling) !== null && _d !== void 0 ? _d : false;
|
|
381
|
+
var unsubscribe = subscribeToUrlChanges(globalScope, onUrlChange, {
|
|
382
|
+
enablePolling: enablePolling,
|
|
383
|
+
pollingInterval: (_e = this.config) === null || _e === void 0 ? void 0 : _e.urlChangePollingInterval,
|
|
384
|
+
// Mirror each poll tick to the console (Debug level) so we can confirm polling is firing.
|
|
385
|
+
log: this.loggerProvider.debug.bind(this.loggerProvider),
|
|
386
|
+
});
|
|
387
|
+
// Confirm the listener is actually live, and under what settings — if recording never starts on
|
|
388
|
+
// navigation, this tells us whether the listener existed and whether polling (the fallback for
|
|
389
|
+
// SPAs that bypass the History API) was on.
|
|
390
|
+
this.recordDiagnosticEvent(SrDiagnostic.urlListenerAttached, {
|
|
391
|
+
hasTargeting: hasTargeting,
|
|
392
|
+
enablePolling: enablePolling,
|
|
393
|
+
pollingInterval: (_f = this.config) === null || _f === void 0 ? void 0 : _f.urlChangePollingInterval,
|
|
394
|
+
});
|
|
395
|
+
this.loggerProvider.debug("URL-change listener attached (polling: ".concat(String(enablePolling), ")."));
|
|
360
396
|
this.urlChangeCleanup = function () {
|
|
361
397
|
unsubscribe();
|
|
362
398
|
_this.urlChangeCleanup = null;
|
|
@@ -385,14 +421,34 @@ var SessionReplay = /** @class */ (function () {
|
|
|
385
421
|
var currentUrl = (_b = (_a = getGlobalScope()) === null || _a === void 0 ? void 0 : _a.location) === null || _b === void 0 ? void 0 : _b.href;
|
|
386
422
|
return currentUrl != null ? { url: currentUrl } : undefined;
|
|
387
423
|
};
|
|
424
|
+
/**
|
|
425
|
+
* Best-effort navigation type from the Navigation Timing API: 'reload' | 'navigate' |
|
|
426
|
+
* 'back_forward' | 'prerender'. Surfaced in the init diagnostic so a page refresh ('reload')
|
|
427
|
+
* is distinguishable from a fresh load ('navigate') — neither changes the session id, so this
|
|
428
|
+
* is the only way to tell them apart. Returns undefined when the API is unavailable.
|
|
429
|
+
*/
|
|
430
|
+
SessionReplay.prototype.getNavigationType = function () {
|
|
431
|
+
try {
|
|
432
|
+
var globalScope = getGlobalScope();
|
|
433
|
+
var performance_1 = globalScope && globalScope.performance;
|
|
434
|
+
if (!performance_1 || typeof performance_1.getEntriesByType !== 'function') {
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
var navEntries = performance_1.getEntriesByType('navigation');
|
|
438
|
+
return navEntries.length > 0 ? navEntries[0].type : undefined;
|
|
439
|
+
}
|
|
440
|
+
catch (_a) {
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
388
444
|
SessionReplay.prototype._init = function (apiKey, options) {
|
|
389
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
445
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
390
446
|
return __awaiter(this, void 0, void 0, function () {
|
|
391
|
-
var now,
|
|
392
|
-
var e_2,
|
|
447
|
+
var now, _u, _v, joinedConfig, localConfig, remoteConfig, scrollWatcher, managers, storeType, compressionWorkerScript, trackDestinationWorkerScript, globalScope, _w, compressionScript, trackDestinationScript, rrwebEventManager, error_1, typedError, payloadBatcher, interactionEventManager, error_2, typedError, onFullSnapshotProcessed, pending_2, pending_1, pending_1_1, _x, event_1, sessionId, messenger, needsUrlTracking;
|
|
448
|
+
var e_2, _y;
|
|
393
449
|
var _this = this;
|
|
394
|
-
return __generator(this, function (
|
|
395
|
-
switch (
|
|
450
|
+
return __generator(this, function (_z) {
|
|
451
|
+
switch (_z.label) {
|
|
396
452
|
case 0:
|
|
397
453
|
// Re-init should always tear down any previous URL-change subscription, even when the
|
|
398
454
|
// next config has no targeting config and we don't subscribe again.
|
|
@@ -408,13 +464,13 @@ var SessionReplay = /** @class */ (function () {
|
|
|
408
464
|
options.sessionId !== undefined
|
|
409
465
|
? (_b = getOrInitReplayStartTime(apiKey, options.sessionId, now, this.loggerProvider)) !== null && _b !== void 0 ? _b : now
|
|
410
466
|
: now;
|
|
411
|
-
|
|
467
|
+
_u = this;
|
|
412
468
|
return [4 /*yield*/, createSessionReplayJoinedConfigGenerator(apiKey, options)];
|
|
413
469
|
case 1:
|
|
414
|
-
|
|
470
|
+
_u.joinedConfigGenerator = _z.sent();
|
|
415
471
|
return [4 /*yield*/, this.joinedConfigGenerator.generateJoinedConfig()];
|
|
416
472
|
case 2:
|
|
417
|
-
|
|
473
|
+
_v = _z.sent(), joinedConfig = _v.joinedConfig, localConfig = _v.localConfig, remoteConfig = _v.remoteConfig;
|
|
418
474
|
this.config = joinedConfig;
|
|
419
475
|
this.setMetadata(options.sessionId, joinedConfig, localConfig, remoteConfig, (_c = options.version) === null || _c === void 0 ? void 0 : _c.version, VERSION, (_d = options.version) === null || _d === void 0 ? void 0 : _d.type);
|
|
420
476
|
this.pageLeaveFns = [];
|
|
@@ -438,12 +494,12 @@ var SessionReplay = /** @class */ (function () {
|
|
|
438
494
|
if (!(this.config.useWebWorker && globalScope && globalScope.Worker)) return [3 /*break*/, 4];
|
|
439
495
|
return [4 /*yield*/, import('./worker')];
|
|
440
496
|
case 3:
|
|
441
|
-
|
|
497
|
+
_w = _z.sent(), compressionScript = _w.compressionScript, trackDestinationScript = _w.trackDestinationScript;
|
|
442
498
|
compressionWorkerScript = compressionScript;
|
|
443
499
|
trackDestinationWorkerScript = trackDestinationScript;
|
|
444
|
-
|
|
500
|
+
_z.label = 4;
|
|
445
501
|
case 4:
|
|
446
|
-
|
|
502
|
+
_z.trys.push([4, 6, , 7]);
|
|
447
503
|
return [4 /*yield*/, createEventsManager({
|
|
448
504
|
config: this.config,
|
|
449
505
|
type: 'replay',
|
|
@@ -455,21 +511,21 @@ var SessionReplay = /** @class */ (function () {
|
|
|
455
511
|
shouldSend: function () { return !_this.isBelowMinSessionDuration(); },
|
|
456
512
|
})];
|
|
457
513
|
case 5:
|
|
458
|
-
rrwebEventManager =
|
|
514
|
+
rrwebEventManager = _z.sent();
|
|
459
515
|
this.rrwebEventManager = rrwebEventManager;
|
|
460
516
|
managers.push({ name: 'replay', manager: rrwebEventManager });
|
|
461
517
|
return [3 /*break*/, 7];
|
|
462
518
|
case 6:
|
|
463
|
-
error_1 =
|
|
519
|
+
error_1 = _z.sent();
|
|
464
520
|
typedError = error_1;
|
|
465
521
|
this.loggerProvider.warn("Error occurred while creating replay events manager: ".concat(typedError.toString()));
|
|
466
522
|
return [3 /*break*/, 7];
|
|
467
523
|
case 7:
|
|
468
524
|
if (!((_j = this.config.interactionConfig) === null || _j === void 0 ? void 0 : _j.enabled)) return [3 /*break*/, 11];
|
|
469
525
|
payloadBatcher = this.config.interactionConfig.batch ? clickBatcher : clickNonBatcher;
|
|
470
|
-
|
|
526
|
+
_z.label = 8;
|
|
471
527
|
case 8:
|
|
472
|
-
|
|
528
|
+
_z.trys.push([8, 10, , 11]);
|
|
473
529
|
return [4 /*yield*/, createEventsManager({
|
|
474
530
|
config: this.config,
|
|
475
531
|
type: 'interaction',
|
|
@@ -481,11 +537,11 @@ var SessionReplay = /** @class */ (function () {
|
|
|
481
537
|
trackDestinationWorkerScript: trackDestinationWorkerScript,
|
|
482
538
|
})];
|
|
483
539
|
case 9:
|
|
484
|
-
interactionEventManager =
|
|
540
|
+
interactionEventManager = _z.sent();
|
|
485
541
|
managers.push({ name: 'interaction', manager: interactionEventManager });
|
|
486
542
|
return [3 /*break*/, 11];
|
|
487
543
|
case 10:
|
|
488
|
-
error_2 =
|
|
544
|
+
error_2 = _z.sent();
|
|
489
545
|
typedError = error_2;
|
|
490
546
|
this.loggerProvider.warn("Error occurred while creating interaction events manager: ".concat(typedError.toString()));
|
|
491
547
|
return [3 /*break*/, 11];
|
|
@@ -503,14 +559,14 @@ var SessionReplay = /** @class */ (function () {
|
|
|
503
559
|
pending_2 = this.pendingEmitEvents.splice(0);
|
|
504
560
|
try {
|
|
505
561
|
for (pending_1 = __values(pending_2), pending_1_1 = pending_1.next(); !pending_1_1.done; pending_1_1 = pending_1.next()) {
|
|
506
|
-
|
|
562
|
+
_x = pending_1_1.value, event_1 = _x.event, sessionId = _x.sessionId;
|
|
507
563
|
this.eventCompressor.enqueueEvent(event_1, sessionId);
|
|
508
564
|
}
|
|
509
565
|
}
|
|
510
566
|
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
511
567
|
finally {
|
|
512
568
|
try {
|
|
513
|
-
if (pending_1_1 && !pending_1_1.done && (
|
|
569
|
+
if (pending_1_1 && !pending_1_1.done && (_y = pending_1.return)) _y.call(pending_1);
|
|
514
570
|
}
|
|
515
571
|
finally { if (e_2) throw e_2.error; }
|
|
516
572
|
}
|
|
@@ -547,7 +603,7 @@ var SessionReplay = /** @class */ (function () {
|
|
|
547
603
|
], false);
|
|
548
604
|
return [4 /*yield*/, this.initializeNetworkObservers()];
|
|
549
605
|
case 12:
|
|
550
|
-
|
|
606
|
+
_z.sent();
|
|
551
607
|
// Enable background capture when this page is opened by the Amplitude app
|
|
552
608
|
// (window.opener exists). Uses the shared messenger singleton so that if
|
|
553
609
|
// autocapture is also loaded, both share a single messenger and script load.
|
|
@@ -569,11 +625,27 @@ var SessionReplay = /** @class */ (function () {
|
|
|
569
625
|
sampleRate: this.config.sampleRate,
|
|
570
626
|
optOut: this.shouldOptOut(),
|
|
571
627
|
currentUrl: (_m = this.getCurrentPageForTargeting()) === null || _m === void 0 ? void 0 : _m.url,
|
|
628
|
+
// 'reload' tells a page refresh apart from a fresh load ('navigate') or back/forward
|
|
629
|
+
// ('back_forward'). New tabs and refreshes keep the same session id, so this is the only
|
|
630
|
+
// way to distinguish a refresh from a brand-new init within a session.
|
|
631
|
+
navigationType: this.getNavigationType(),
|
|
572
632
|
});
|
|
573
633
|
return [4 /*yield*/, this.evaluateTargetingAndCapture({ userProperties: options.userProperties, page: this.getCurrentPageForTargeting() }, true)];
|
|
574
634
|
case 13:
|
|
575
|
-
|
|
635
|
+
_z.sent();
|
|
576
636
|
needsUrlTracking = this.config.targetingConfig || ((_q = (_p = (_o = this.config.privacyConfig) === null || _o === void 0 ? void 0 : _o.urlMaskLevels) === null || _p === void 0 ? void 0 : _p.length) !== null && _q !== void 0 ? _q : 0) > 0;
|
|
637
|
+
// Record whether we even attempt to wire up the URL-change listener, and the inputs behind that
|
|
638
|
+
// decision. If `needsUrlTracking` is false the listener is never attached, so SPA navigations are
|
|
639
|
+
// invisible to TRC — this is the first thing to check when "TRC is on but never re-evaluates".
|
|
640
|
+
this.recordDiagnosticEvent(SrDiagnostic.urlListenerSetup, {
|
|
641
|
+
needsUrlTracking: !!needsUrlTracking,
|
|
642
|
+
hasTargetingConfig: !!this.config.targetingConfig,
|
|
643
|
+
urlMaskLevels: (_t = (_s = (_r = this.config.privacyConfig) === null || _r === void 0 ? void 0 : _r.urlMaskLevels) === null || _s === void 0 ? void 0 : _s.length) !== null && _t !== void 0 ? _t : 0,
|
|
644
|
+
// config always defaults this to a boolean (see local-config), so no `?? false` needed here.
|
|
645
|
+
enableUrlChangePolling: this.config.enableUrlChangePolling,
|
|
646
|
+
urlChangePollingInterval: this.config.urlChangePollingInterval,
|
|
647
|
+
});
|
|
648
|
+
this.loggerProvider.debug("URL-change listener needed: ".concat(String(!!needsUrlTracking), "."));
|
|
577
649
|
if (needsUrlTracking) {
|
|
578
650
|
this.setupUrlChangeListener();
|
|
579
651
|
}
|
|
@@ -613,6 +685,18 @@ var SessionReplay = /** @class */ (function () {
|
|
|
613
685
|
this.sendEvents(previousSessionId);
|
|
614
686
|
}
|
|
615
687
|
isSessionChange = previousSessionId !== sessionId;
|
|
688
|
+
// A real session transition (not the first set): recording stops/restarts and targeting is
|
|
689
|
+
// re-evaluated here, so session churn (timeout, multi-tab custom ids, explicit setSessionId)
|
|
690
|
+
// is a prime suspect for "sometimes records, sometimes not". recordDiagnosticEvent ships to
|
|
691
|
+
// DataDog AND mirrors to loggerProvider.debug. New tabs / refreshes keep the same id and won't
|
|
692
|
+
// hit this — use the init diagnostic's navigationType for those.
|
|
693
|
+
if (isSessionChange && previousSessionId !== undefined) {
|
|
694
|
+
this.recordDiagnosticEvent(SrDiagnostic.sessionChanged, {
|
|
695
|
+
from: previousSessionId,
|
|
696
|
+
to: sessionId,
|
|
697
|
+
deviceIdChanged: deviceId !== undefined && deviceId !== currentDeviceId,
|
|
698
|
+
});
|
|
699
|
+
}
|
|
616
700
|
// Drop any beacon-buffered events from the previous session BEFORE installing the
|
|
617
701
|
// new identifiers / start time. Otherwise the page-leave beacon path could attribute
|
|
618
702
|
// old-session events to the new session id, and the gate (using the new start time)
|
|
@@ -777,6 +861,9 @@ var SessionReplay = /** @class */ (function () {
|
|
|
777
861
|
SessionReplay.prototype.incrementDiagnostic = function (counter) {
|
|
778
862
|
var _a, _b;
|
|
779
863
|
try {
|
|
864
|
+
// Mirror to the console (Debug level) so the full set of diagnostics is visible in the
|
|
865
|
+
// browser, not only in DataDog — helps debugging when a customer can't share a repro env.
|
|
866
|
+
this.loggerProvider.debug("[SR diagnostics] ".concat(counter));
|
|
780
867
|
(_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.diagnosticsClient) === null || _b === void 0 ? void 0 : _b.increment(counter);
|
|
781
868
|
}
|
|
782
869
|
catch (_c) {
|
|
@@ -790,6 +877,8 @@ var SessionReplay = /** @class */ (function () {
|
|
|
790
877
|
SessionReplay.prototype.recordDiagnosticHistogram = function (name, value) {
|
|
791
878
|
var _a, _b;
|
|
792
879
|
try {
|
|
880
|
+
// Mirror to the console (Debug level) — see incrementDiagnostic.
|
|
881
|
+
this.loggerProvider.debug("[SR diagnostics] ".concat(name, "=").concat(value));
|
|
793
882
|
// The only caller runs inside evaluateTargetingAndCapture's `this.config` guard, so the
|
|
794
883
|
// `config == null` arm of this optional chain is unreachable here (kept as defensive parity
|
|
795
884
|
// with recordDiagnosticEvent). The diagnosticsClient-absent arm IS exercised by no-client tests.
|
|
@@ -807,16 +896,24 @@ var SessionReplay = /** @class */ (function () {
|
|
|
807
896
|
* meaningful decision points, not per-call tallies. Best-effort, never throws.
|
|
808
897
|
*/
|
|
809
898
|
SessionReplay.prototype.recordDiagnosticEvent = function (name, properties) {
|
|
810
|
-
var _a, _b, _c;
|
|
899
|
+
var _a, _b, _c, _d, _e, _f;
|
|
811
900
|
try {
|
|
812
901
|
var sessionId = (_a = this.identifiers) === null || _a === void 0 ? void 0 : _a.sessionId;
|
|
813
902
|
var deviceId = this.getDeviceId();
|
|
814
903
|
// Always stamp sessionId, deviceId and srId so every diagnostics event (→ DataDog Logs) can
|
|
815
904
|
// be correlated to a single session/device — and to the actual replay, since the Session
|
|
816
905
|
// Replay ID is `${deviceId}/${sessionId}`. Caller props override if they pass their own.
|
|
817
|
-
|
|
906
|
+
var enriched = __assign({ sessionId: sessionId, deviceId: deviceId, srId: deviceId != null && sessionId != null ? "".concat(deviceId, "/").concat(sessionId) : undefined }, properties);
|
|
907
|
+
// Mirror to the console (Debug level) so the full event + props are visible in the browser,
|
|
908
|
+
// not only in DataDog — see incrementDiagnostic.
|
|
909
|
+
this.loggerProvider.debug("[SR diagnostics] ".concat(name, " ").concat(JSON.stringify(enriched)));
|
|
910
|
+
(_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.diagnosticsClient) === null || _c === void 0 ? void 0 : _c.recordEvent(name, enriched);
|
|
911
|
+
// Flush immediately so the event ships now rather than on the client's ~5-min timer (and so
|
|
912
|
+
// short sessions that never reach the timer aren't lost). NOTE: this sends one capture POST
|
|
913
|
+
// per event — higher request volume; revisit/gate before production.
|
|
914
|
+
void ((_f = (_e = (_d = this.config) === null || _d === void 0 ? void 0 : _d.diagnosticsClient) === null || _e === void 0 ? void 0 : _e._flush) === null || _f === void 0 ? void 0 : _f.call(_e));
|
|
818
915
|
}
|
|
819
|
-
catch (
|
|
916
|
+
catch (_g) {
|
|
820
917
|
// swallow — diagnostics is best-effort
|
|
821
918
|
}
|
|
822
919
|
};
|