@amplitude/session-replay-browser 1.31.7 → 1.32.1

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.
Files changed (56) hide show
  1. package/lib/cjs/events/event-compressor.d.ts.map +1 -1
  2. package/lib/cjs/events/event-compressor.js +1 -0
  3. package/lib/cjs/events/event-compressor.js.map +1 -1
  4. package/lib/cjs/index.d.ts +1 -1
  5. package/lib/cjs/index.d.ts.map +1 -1
  6. package/lib/cjs/plugins/url-tracking-plugin.d.ts +21 -0
  7. package/lib/cjs/plugins/url-tracking-plugin.d.ts.map +1 -1
  8. package/lib/cjs/plugins/url-tracking-plugin.js +117 -104
  9. package/lib/cjs/plugins/url-tracking-plugin.js.map +1 -1
  10. package/lib/cjs/session-replay.d.ts +11 -1
  11. package/lib/cjs/session-replay.d.ts.map +1 -1
  12. package/lib/cjs/session-replay.js +108 -47
  13. package/lib/cjs/session-replay.js.map +1 -1
  14. package/lib/cjs/targeting/targeting-manager.d.ts +3 -2
  15. package/lib/cjs/targeting/targeting-manager.d.ts.map +1 -1
  16. package/lib/cjs/targeting/targeting-manager.js +16 -13
  17. package/lib/cjs/targeting/targeting-manager.js.map +1 -1
  18. package/lib/cjs/typings/session-replay.d.ts +2 -2
  19. package/lib/cjs/typings/session-replay.d.ts.map +1 -1
  20. package/lib/cjs/typings/session-replay.js.map +1 -1
  21. package/lib/cjs/version.d.ts +1 -1
  22. package/lib/cjs/version.js +1 -1
  23. package/lib/cjs/version.js.map +1 -1
  24. package/lib/esm/events/event-compressor.d.ts.map +1 -1
  25. package/lib/esm/events/event-compressor.js +1 -0
  26. package/lib/esm/events/event-compressor.js.map +1 -1
  27. package/lib/esm/index.d.ts +1 -1
  28. package/lib/esm/index.d.ts.map +1 -1
  29. package/lib/esm/plugins/url-tracking-plugin.d.ts +21 -0
  30. package/lib/esm/plugins/url-tracking-plugin.d.ts.map +1 -1
  31. package/lib/esm/plugins/url-tracking-plugin.js +115 -103
  32. package/lib/esm/plugins/url-tracking-plugin.js.map +1 -1
  33. package/lib/esm/session-replay.d.ts +11 -1
  34. package/lib/esm/session-replay.d.ts.map +1 -1
  35. package/lib/esm/session-replay.js +109 -48
  36. package/lib/esm/session-replay.js.map +1 -1
  37. package/lib/esm/targeting/targeting-manager.d.ts +3 -2
  38. package/lib/esm/targeting/targeting-manager.d.ts.map +1 -1
  39. package/lib/esm/targeting/targeting-manager.js +16 -13
  40. package/lib/esm/targeting/targeting-manager.js.map +1 -1
  41. package/lib/esm/typings/session-replay.d.ts +2 -2
  42. package/lib/esm/typings/session-replay.d.ts.map +1 -1
  43. package/lib/esm/typings/session-replay.js.map +1 -1
  44. package/lib/esm/version.d.ts +1 -1
  45. package/lib/esm/version.js +1 -1
  46. package/lib/esm/version.js.map +1 -1
  47. package/lib/scripts/index-min.js +1 -1
  48. package/lib/scripts/index-min.js.gz +0 -0
  49. package/lib/scripts/index-min.js.map +1 -1
  50. package/lib/scripts/session-replay-browser-min.js +1 -1
  51. package/lib/scripts/session-replay-browser-min.js.gz +0 -0
  52. package/lib/scripts/session-replay-browser-min.js.map +1 -1
  53. package/lib/scripts/targeting-min.js +1 -1
  54. package/lib/scripts/targeting-min.js.gz +0 -0
  55. package/lib/scripts/targeting-min.js.map +1 -1
  56. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"event-compressor.d.ts","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAEvE,UAAU,SAAS;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAGD,qBAAa,eAAe;IAC1B,SAAS,EAAE,SAAS,EAAE,CAAM;IAC5B,YAAY,UAAS;IACrB,aAAa,CAAC,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;IAC7E,MAAM,EAAE,yBAAyB,CAAC;IAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,kBAAkB,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;gBAGd,aAAa,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,EAC3E,MAAM,EAAE,yBAAyB,EACjC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,CAAC,EAAE,MAAM;IAmChB,sBAAsB,IAAI,IAAI;IAa9B,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAYpE,YAAY,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI;IAuBrD,aAAa,UAAW,aAAa,YAGnC;IAEF,OAAO,CAAC,2BAA2B,CAQjC;IAEK,kBAAkB,UAAW,aAAa,aAAa,MAAM,GAAG,MAAM,UAkB3E;IAEK,SAAS,aAEd;CACH"}
1
+ {"version":3,"file":"event-compressor.d.ts","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAEvE,UAAU,SAAS;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAGD,qBAAa,eAAe;IAC1B,SAAS,EAAE,SAAS,EAAE,CAAM;IAC5B,YAAY,UAAS;IACrB,aAAa,CAAC,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,CAAC;IAC7E,MAAM,EAAE,yBAAyB,CAAC;IAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,kBAAkB,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;gBAGd,aAAa,EAAE,0BAA0B,CAAC,QAAQ,GAAG,aAAa,EAAE,MAAM,CAAC,EAC3E,MAAM,EAAE,yBAAyB,EACjC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,CAAC,EAAE,MAAM;IAoChB,sBAAsB,IAAI,IAAI;IAa9B,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAYpE,YAAY,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI;IAuBrD,aAAa,UAAW,aAAa,YAGnC;IAEF,OAAO,CAAC,2BAA2B,CAQjC;IAEK,kBAAkB,UAAW,aAAa,aAAa,MAAM,GAAG,MAAM,UAkB3E;IAEK,SAAS,aAEd;CACH"}
@@ -62,6 +62,7 @@ var EventCompressor = /** @class */ (function () {
62
62
  var blobUrl = URL.createObjectURL(blob);
63
63
  var worker_1 = new Worker(blobUrl);
64
64
  worker_1.onerror = function (e) {
65
+ e.preventDefault();
65
66
  config.loggerProvider.error('Worker failed, falling back to non-worker compression:', e);
66
67
  worker_1.terminate();
67
68
  _this.worker = undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"event-compressor.js","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\nimport { pack } from '@amplitude/rrweb-packer';\nimport type { eventWithTime } from '@amplitude/rrweb-types';\nimport { SessionReplayJoinedConfig } from '../config/types';\nimport { SessionReplayEventsManager } from '../typings/session-replay';\n\ninterface TaskQueue {\n event: eventWithTime;\n sessionId: string | number;\n}\n\nconst DEFAULT_TIMEOUT = 2000;\nexport class EventCompressor {\n taskQueue: TaskQueue[] = [];\n isProcessing = false;\n eventsManager?: SessionReplayEventsManager<'replay' | 'interaction', string>;\n config: SessionReplayJoinedConfig;\n deviceId: string | undefined;\n canUseIdleCallback: boolean | undefined;\n timeout: number;\n worker?: Worker;\n\n constructor(\n eventsManager: SessionReplayEventsManager<'replay' | 'interaction', string>,\n config: SessionReplayJoinedConfig,\n deviceId: string | undefined,\n workerScript?: string,\n ) {\n const globalScope = getGlobalScope();\n this.canUseIdleCallback = globalScope && 'requestIdleCallback' in globalScope;\n this.eventsManager = eventsManager;\n this.config = config;\n this.deviceId = deviceId;\n this.timeout = config.performanceConfig?.timeout || DEFAULT_TIMEOUT;\n\n if (workerScript) {\n config.loggerProvider.log('Enabling web worker for compression');\n\n try {\n const blob = new Blob([workerScript], { type: 'application/javascript' });\n const blobUrl = URL.createObjectURL(blob);\n const worker = new Worker(blobUrl);\n\n worker.onerror = (e) => {\n config.loggerProvider.error('Worker failed, falling back to non-worker compression:', e);\n worker.terminate();\n this.worker = undefined;\n };\n worker.onmessage = (e) => {\n const { compressedEvent, sessionId } = e.data as Record<string, string>;\n this.addCompressedEventToManager(compressedEvent, sessionId);\n };\n\n this.worker = worker;\n } catch (error) {\n config.loggerProvider.error('Failed to create worker, falling back to non-worker compression:', error);\n }\n }\n }\n\n // Schedule processing during idle time\n public scheduleIdleProcessing(): void {\n if (!this.isProcessing) {\n this.isProcessing = true;\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n }\n }\n\n // Add an event to the task queue if idle callback is supported or compress the event directly\n public enqueueEvent(event: eventWithTime, sessionId: string | number): void {\n if (this.canUseIdleCallback && this.config.performanceConfig?.enabled) {\n this.config.loggerProvider.debug('Enqueuing event for processing during idle time.');\n this.taskQueue.push({ event, sessionId });\n this.scheduleIdleProcessing();\n } else {\n this.config.loggerProvider.debug('Processing event without idle callback.');\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // Process the task queue during idle time\n public processQueue(idleDeadline: IdleDeadline): void {\n // Process tasks while there's idle time or until the max number of tasks is reached\n while (this.taskQueue.length > 0 && (idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout)) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // If there are still tasks in the queue, schedule the next idle callback\n if (this.taskQueue.length > 0) {\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n } else {\n this.isProcessing = false;\n }\n }\n\n compressEvent = (event: eventWithTime) => {\n const packedEvent = pack(event);\n return JSON.stringify(packedEvent);\n };\n\n private addCompressedEventToManager = (compressedEvent: string, sessionId: string | number) => {\n if (this.eventsManager && this.deviceId) {\n this.eventsManager.addEvent({\n event: { type: 'replay', data: compressedEvent },\n sessionId,\n deviceId: this.deviceId,\n });\n }\n };\n\n public addCompressedEvent = (event: eventWithTime, sessionId: string | number) => {\n if (this.worker) {\n // This indirectly compresses the event.\n try {\n this.worker.postMessage({ event, sessionId });\n } catch (err: any) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (err.name === 'DataCloneError') {\n // fallback: serialize\n this.worker.postMessage(JSON.stringify({ event, sessionId }));\n } else {\n this.config.loggerProvider.warn('Unexpected error while posting message to worker:', err);\n }\n }\n } else {\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n }\n };\n\n public terminate = () => {\n this.worker?.terminate();\n };\n}\n"]}
1
+ {"version":3,"file":"event-compressor.js","sourceRoot":"","sources":["../../../src/events/event-compressor.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\nimport { pack } from '@amplitude/rrweb-packer';\nimport type { eventWithTime } from '@amplitude/rrweb-types';\nimport { SessionReplayJoinedConfig } from '../config/types';\nimport { SessionReplayEventsManager } from '../typings/session-replay';\n\ninterface TaskQueue {\n event: eventWithTime;\n sessionId: string | number;\n}\n\nconst DEFAULT_TIMEOUT = 2000;\nexport class EventCompressor {\n taskQueue: TaskQueue[] = [];\n isProcessing = false;\n eventsManager?: SessionReplayEventsManager<'replay' | 'interaction', string>;\n config: SessionReplayJoinedConfig;\n deviceId: string | undefined;\n canUseIdleCallback: boolean | undefined;\n timeout: number;\n worker?: Worker;\n\n constructor(\n eventsManager: SessionReplayEventsManager<'replay' | 'interaction', string>,\n config: SessionReplayJoinedConfig,\n deviceId: string | undefined,\n workerScript?: string,\n ) {\n const globalScope = getGlobalScope();\n this.canUseIdleCallback = globalScope && 'requestIdleCallback' in globalScope;\n this.eventsManager = eventsManager;\n this.config = config;\n this.deviceId = deviceId;\n this.timeout = config.performanceConfig?.timeout || DEFAULT_TIMEOUT;\n\n if (workerScript) {\n config.loggerProvider.log('Enabling web worker for compression');\n\n try {\n const blob = new Blob([workerScript], { type: 'application/javascript' });\n const blobUrl = URL.createObjectURL(blob);\n const worker = new Worker(blobUrl);\n\n worker.onerror = (e) => {\n e.preventDefault();\n config.loggerProvider.error('Worker failed, falling back to non-worker compression:', e);\n worker.terminate();\n this.worker = undefined;\n };\n worker.onmessage = (e) => {\n const { compressedEvent, sessionId } = e.data as Record<string, string>;\n this.addCompressedEventToManager(compressedEvent, sessionId);\n };\n\n this.worker = worker;\n } catch (error) {\n config.loggerProvider.error('Failed to create worker, falling back to non-worker compression:', error);\n }\n }\n }\n\n // Schedule processing during idle time\n public scheduleIdleProcessing(): void {\n if (!this.isProcessing) {\n this.isProcessing = true;\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n }\n }\n\n // Add an event to the task queue if idle callback is supported or compress the event directly\n public enqueueEvent(event: eventWithTime, sessionId: string | number): void {\n if (this.canUseIdleCallback && this.config.performanceConfig?.enabled) {\n this.config.loggerProvider.debug('Enqueuing event for processing during idle time.');\n this.taskQueue.push({ event, sessionId });\n this.scheduleIdleProcessing();\n } else {\n this.config.loggerProvider.debug('Processing event without idle callback.');\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // Process the task queue during idle time\n public processQueue(idleDeadline: IdleDeadline): void {\n // Process tasks while there's idle time or until the max number of tasks is reached\n while (this.taskQueue.length > 0 && (idleDeadline.timeRemaining() > 0 || idleDeadline.didTimeout)) {\n const task = this.taskQueue.shift();\n if (task) {\n const { event, sessionId } = task;\n this.addCompressedEvent(event, sessionId);\n }\n }\n\n // If there are still tasks in the queue, schedule the next idle callback\n if (this.taskQueue.length > 0) {\n requestIdleCallback(\n (idleDeadline) => {\n this.processQueue(idleDeadline);\n },\n { timeout: this.timeout },\n );\n } else {\n this.isProcessing = false;\n }\n }\n\n compressEvent = (event: eventWithTime) => {\n const packedEvent = pack(event);\n return JSON.stringify(packedEvent);\n };\n\n private addCompressedEventToManager = (compressedEvent: string, sessionId: string | number) => {\n if (this.eventsManager && this.deviceId) {\n this.eventsManager.addEvent({\n event: { type: 'replay', data: compressedEvent },\n sessionId,\n deviceId: this.deviceId,\n });\n }\n };\n\n public addCompressedEvent = (event: eventWithTime, sessionId: string | number) => {\n if (this.worker) {\n // This indirectly compresses the event.\n try {\n this.worker.postMessage({ event, sessionId });\n } catch (err: any) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (err.name === 'DataCloneError') {\n // fallback: serialize\n this.worker.postMessage(JSON.stringify({ event, sessionId }));\n } else {\n this.config.loggerProvider.warn('Unexpected error while posting message to worker:', err);\n }\n }\n } else {\n const compressedEvent = this.compressEvent(event);\n this.addCompressedEventToManager(compressedEvent, sessionId);\n }\n };\n\n public terminate = () => {\n this.worker?.terminate();\n };\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  export declare const init: (apiKey: string, options: import("./typings/session-replay").SessionReplayOptions) => import("@amplitude/analytics-core").AmplitudeReturn<void>, setSessionId: (sessionId: string | number, deviceId?: string | undefined) => import("@amplitude/analytics-core").AmplitudeReturn<void>, getSessionId: () => string | number | undefined, getSessionReplayProperties: () => {
2
2
  [key: string]: string | boolean | null;
3
- }, flush: (useRetry: boolean) => Promise<void>, shutdown: () => void, evaluateTargetingAndCapture: (targetingParams: Pick<import("@amplitude/targeting").TargetingParameters, "event" | "userProperties">, isInit?: boolean | undefined, forceRestart?: boolean | undefined) => Promise<void>;
3
+ }, flush: (useRetry: boolean) => Promise<void>, shutdown: () => void, evaluateTargetingAndCapture: (targetingParams: Pick<import("@amplitude/targeting").TargetingParameters, "event" | "userProperties" | "page">, isInit?: boolean | undefined, forceRestart?: boolean | undefined, forceTargetingReevaluation?: boolean | undefined) => Promise<void>;
4
4
  export { SessionReplayOptions, StoreType } from './typings/session-replay';
5
5
  export { SafeLoggerProvider } from './logger';
6
6
  export { AmplitudeSessionReplay } from './typings/session-replay';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,eAAO,MACL,IAAI,mJACJ,YAAY,4HACZ,YAAY,qCACZ,0BAA0B;;GAC1B,KAAK,wCACL,QAAQ,cACR,2BAA2B,4LACZ,CAAC;AAClB,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,eAAO,MACL,IAAI,mJACJ,YAAY,4HACZ,YAAY,qCACZ,0BAA0B;;GAC1B,KAAK,wCACL,QAAQ,cACR,2BAA2B,uPACZ,CAAC;AAClB,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -29,6 +29,27 @@ export interface URLTrackingPluginOptions {
29
29
  /** Whether to capture document title in URL change events (default: false) */
30
30
  captureDocumentTitle?: boolean;
31
31
  }
32
+ /** Options for subscribeToUrlChanges (polling vs history + hash) */
33
+ export interface SubscribeToUrlChangesOptions {
34
+ /** Use polling instead of history/popstate/hashchange (e.g. when history is unreliable) */
35
+ enablePolling?: boolean;
36
+ /** Polling interval in ms when enablePolling is true (default: 1000) */
37
+ pollingInterval?: number;
38
+ }
39
+ /**
40
+ * Single helper for URL change detection. Supports:
41
+ * - History API (pushState/replaceState) + popstate + hashchange (shared patch per scope)
42
+ * - Optional polling (setInterval on location.href)
43
+ *
44
+ * Used by session-replay targeting (re-evaluate on URL change) and the URL tracking plugin
45
+ * (emit rrweb events). Call the returned function to unsubscribe.
46
+ *
47
+ * @param globalScope - window (or equivalent); no-op if undefined
48
+ * @param onUrlChange - called when the URL changes, with the new href
49
+ * @param options - optional polling (default: event-based only)
50
+ * @returns cleanup function to remove this subscription
51
+ */
52
+ export declare function subscribeToUrlChanges(globalScope: Window | undefined, onUrlChange: (href: string) => void, options?: SubscribeToUrlChangesOptions): () => void;
32
53
  /**
33
54
  * Creates a URL tracking plugin for rrweb record function
34
55
  *
@@ -1 +1 @@
1
- {"version":3,"file":"url-tracking-plugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/url-tracking-plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,kEAAkE;IAClE,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,YAAY,CAAC,wBAAwB,CAAC,CAyMxC;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,wCAA4B,CAAC"}
1
+ {"version":3,"file":"url-tracking-plugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/url-tracking-plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,kEAAkE;IAClE,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,oEAAoE;AACpE,MAAM,WAAW,4BAA4B;IAC3C,2FAA2F;IAC3F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAiBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EACnC,OAAO,GAAE,4BAAiC,GACzC,MAAM,IAAI,CAqGZ;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,YAAY,CAAC,wBAAwB,CAAC,CAqFxC;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,wCAA4B,CAAC"}
@@ -1,9 +1,122 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.urlTrackingPlugin = exports.createUrlTrackingPlugin = void 0;
3
+ exports.urlTrackingPlugin = exports.createUrlTrackingPlugin = exports.subscribeToUrlChanges = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var helpers_1 = require("../helpers");
6
6
  var constants_1 = require("../constants");
7
+ /** Patch detection marker to prevent double-patching */
8
+ var PATCH_MARKER = '__amplitude_url_tracking_patched__';
9
+ /** Per-globalScope subscription state; persists to avoid wrapper stacking across re-subscribe cycles */
10
+ var urlChangeSubscriptionsByScope = new WeakMap();
11
+ /**
12
+ * Single helper for URL change detection. Supports:
13
+ * - History API (pushState/replaceState) + popstate + hashchange (shared patch per scope)
14
+ * - Optional polling (setInterval on location.href)
15
+ *
16
+ * Used by session-replay targeting (re-evaluate on URL change) and the URL tracking plugin
17
+ * (emit rrweb events). Call the returned function to unsubscribe.
18
+ *
19
+ * @param globalScope - window (or equivalent); no-op if undefined
20
+ * @param onUrlChange - called when the URL changes, with the new href
21
+ * @param options - optional polling (default: event-based only)
22
+ * @returns cleanup function to remove this subscription
23
+ */
24
+ function subscribeToUrlChanges(globalScope, onUrlChange, options) {
25
+ if (options === void 0) { options = {}; }
26
+ if (!(globalScope === null || globalScope === void 0 ? void 0 : globalScope.location)) {
27
+ return function () {
28
+ return;
29
+ };
30
+ }
31
+ var _a = options.enablePolling, enablePolling = _a === void 0 ? false : _a, _b = options.pollingInterval, pollingInterval = _b === void 0 ? constants_1.DEFAULT_URL_CHANGE_POLLING_INTERVAL : _b;
32
+ if (enablePolling) {
33
+ var getHref_1 = function () { var _a; return (_a = globalScope.location.href) !== null && _a !== void 0 ? _a : ''; };
34
+ var lastHref_1 = getHref_1();
35
+ var id_1 = globalScope.setInterval(function () {
36
+ var href = getHref_1();
37
+ if (href === lastHref_1) {
38
+ return;
39
+ }
40
+ lastHref_1 = href;
41
+ onUrlChange(href);
42
+ }, pollingInterval);
43
+ return function () {
44
+ if (id_1 != null) {
45
+ globalScope.clearInterval(id_1);
46
+ }
47
+ };
48
+ }
49
+ var subscriptionState = urlChangeSubscriptionsByScope.get(globalScope);
50
+ if (!subscriptionState) {
51
+ var lastHref_2 = undefined;
52
+ var callbacks_1 = new Set();
53
+ var getHref_2 = function () { var _a; return (_a = globalScope.location.href) !== null && _a !== void 0 ? _a : ''; };
54
+ var notify_1 = function () {
55
+ var href = getHref_2();
56
+ if (lastHref_2 !== undefined && href === lastHref_2)
57
+ return;
58
+ lastHref_2 = href;
59
+ callbacks_1.forEach(function (c) { return c(href); });
60
+ };
61
+ /**
62
+ * Creates a patched version of history methods (pushState/replaceState)
63
+ * that calls the original method and then emits a URL change event.
64
+ */
65
+ var createHistoryMethodPatch = function (originalMethod) {
66
+ var patchedMethod = function () {
67
+ var args = [];
68
+ for (var _i = 0; _i < arguments.length; _i++) {
69
+ args[_i] = arguments[_i];
70
+ }
71
+ var result = originalMethod.apply(this, args);
72
+ // Read from location.href after history call so we always notify with resolved absolute URL.
73
+ notify_1();
74
+ return result;
75
+ };
76
+ patchedMethod[PATCH_MARKER] = true;
77
+ return patchedMethod;
78
+ };
79
+ var history_1 = globalScope.history;
80
+ if (history_1 === null || history_1 === void 0 ? void 0 : history_1.pushState) {
81
+ var pushState = Reflect.get(history_1, 'pushState');
82
+ if (!pushState[PATCH_MARKER]) {
83
+ history_1.pushState = createHistoryMethodPatch(pushState);
84
+ }
85
+ }
86
+ if (history_1 === null || history_1 === void 0 ? void 0 : history_1.replaceState) {
87
+ var replaceState = Reflect.get(history_1, 'replaceState');
88
+ if (!replaceState[PATCH_MARKER]) {
89
+ history_1.replaceState = createHistoryMethodPatch(replaceState);
90
+ }
91
+ }
92
+ subscriptionState = {
93
+ callbacks: callbacks_1,
94
+ notify: notify_1,
95
+ onPopStateOrHashChange: function () { return notify_1(); },
96
+ listenersAttached: false,
97
+ };
98
+ urlChangeSubscriptionsByScope.set(globalScope, subscriptionState);
99
+ }
100
+ var state = subscriptionState;
101
+ if (!state.listenersAttached) {
102
+ globalScope.addEventListener('popstate', state.onPopStateOrHashChange);
103
+ globalScope.addEventListener('hashchange', state.onPopStateOrHashChange);
104
+ state.listenersAttached = true;
105
+ }
106
+ state.callbacks.add(onUrlChange);
107
+ return function () {
108
+ state.callbacks.delete(onUrlChange);
109
+ if (state.callbacks.size === 0) {
110
+ // Do not restore history methods: we are not aware of patches applied by other scripts.
111
+ if (state.listenersAttached) {
112
+ globalScope.removeEventListener('popstate', state.onPopStateOrHashChange);
113
+ globalScope.removeEventListener('hashchange', state.onPopStateOrHashChange);
114
+ state.listenersAttached = false;
115
+ }
116
+ }
117
+ };
118
+ }
119
+ exports.subscribeToUrlChanges = subscribeToUrlChanges;
7
120
  /**
8
121
  * Creates a URL tracking plugin for rrweb record function
9
122
  *
@@ -43,12 +156,6 @@ function createUrlTrackingPlugin(options) {
43
156
  // Track the last URL to prevent duplicate events
44
157
  // Initialize to undefined to ensure first call always emits an event
45
158
  var lastTrackedUrl = undefined;
46
- // Patch detection marker to prevent double-patching
47
- var PATCH_MARKER = '__amplitude_url_tracking_patched__';
48
- // Global flag key on globalScope to track if the plugin has been reset/cleaned up
49
- // When true, prevents emitUrlChange from being called from patched history methods
50
- // even if other plugins have also patched them and we can't restore the originals
51
- var RESET_FLAG_KEY = '__amplitude_url_tracking_reset__';
52
159
  // Helper functions
53
160
  /**
54
161
  * Gets the current URL with proper normalization
@@ -87,113 +194,19 @@ function createUrlTrackingPlugin(options) {
87
194
  /**
88
195
  * Emits a URL change event if the URL has actually changed
89
196
  * Always emits on first call (when lastTrackedUrl is undefined)
90
- * Prevents duplicate events for the same URL on subsequent calls
91
- * Handles edge cases like undefined/null/empty URLs gracefully
92
197
  */
93
198
  var emitUrlChange = function () {
94
199
  var currentUrl = getCurrentUrl();
95
- // Always emit on first call, or if URL actually changed
96
200
  if (lastTrackedUrl === undefined || currentUrl !== lastTrackedUrl) {
97
201
  lastTrackedUrl = currentUrl;
98
202
  var event_1 = createUrlChangeEvent();
99
203
  cb(event_1);
100
204
  }
101
205
  };
102
- /**
103
- * Creates a patched version of history methods (pushState/replaceState)
104
- * that calls the original method and then emits a URL change event
105
- * Ensures URL changes are detected even when history methods are called programmatically
106
- * Checks global reset flag to prevent emitting after plugin cleanup
107
- * @param originalMethod The original history method to patch
108
- * @returns Patched function that calls original method then emits URL change event
109
- */
110
- var createHistoryMethodPatch = function (originalMethod) {
111
- var patchedMethod = function () {
112
- var args = [];
113
- for (var _i = 0; _i < arguments.length; _i++) {
114
- args[_i] = arguments[_i];
115
- }
116
- // Call the original history method first
117
- var result = originalMethod.apply(this, args);
118
- // Then emit URL change event only if plugin hasn't been reset
119
- if (!globalScope[RESET_FLAG_KEY]) {
120
- emitUrlChange();
121
- }
122
- return result;
123
- };
124
- // Mark the patched method to prevent double-patching
125
- patchedMethod[PATCH_MARKER] = true;
126
- return patchedMethod;
127
- };
128
- // Hashchange event handler - delegates to emitUrlChange for consistency
129
- var hashChangeHandler = function () {
130
- emitUrlChange();
131
- };
132
- // 1. if explicitly enable polling → use polling
133
- if (enablePolling) {
134
- // Use polling (covers everything)
135
- var urlChangeInterval_1 = globalScope.setInterval(function () {
136
- emitUrlChange();
137
- }, pollingInterval);
138
- // Emit initial URL immediately
139
- emitUrlChange();
140
- // Return cleanup function to stop polling
141
- return function () {
142
- if (urlChangeInterval_1) {
143
- globalScope.clearInterval(urlChangeInterval_1);
144
- }
145
- };
146
- }
147
- // 2. if polling not enabled → check history, if exist, use history
148
- if (globalScope.history) {
149
- // Use History API + hashchange (covers History API + hash routing)
150
- // Store original history methods for restoration during cleanup
151
- var originalPushState_1 = globalScope.history.pushState.bind(globalScope.history);
152
- var originalReplaceState_1 = globalScope.history.replaceState.bind(globalScope.history);
153
- /**
154
- * Sets up history method patching to intercept pushState and replaceState calls
155
- * This ensures URL changes are detected even when history methods are called programmatically
156
- * Includes patch detection to prevent double-patching by the same plugin
157
- */
158
- var setupHistoryPatching = function () {
159
- // Check if we already patched these methods
160
- if (globalScope.history.pushState[PATCH_MARKER]) {
161
- // Already patched by this plugin, skip patching
162
- return;
163
- }
164
- // Patch pushState to emit URL change events
165
- globalScope.history.pushState = createHistoryMethodPatch(originalPushState_1);
166
- // Patch replaceState to emit URL change events
167
- globalScope.history.replaceState = createHistoryMethodPatch(originalReplaceState_1);
168
- };
169
- // Apply history method patches
170
- setupHistoryPatching();
171
- // Listen to popstate events for browser back/forward navigation
172
- globalScope.addEventListener('popstate', emitUrlChange);
173
- // Listen to hashchange events for hash routing
174
- globalScope.addEventListener('hashchange', hashChangeHandler);
175
- // Emit initial URL immediately
176
- emitUrlChange();
177
- // Return cleanup function to restore original state
178
- return function () {
179
- // Restore original history methods - cannot be done here
180
- // because the plugin is not aware of the history methods modified by other plugins
181
- // so we need to set a flag on the globalScope to prevent further emitUrlChange calls from patched methods
182
- // Set reset flag on globalScope to prevent further emitUrlChange calls from patched methods
183
- globalScope[RESET_FLAG_KEY] = true;
184
- // Remove popstate event listener
185
- globalScope.removeEventListener('popstate', emitUrlChange);
186
- // Remove hashchange event listener
187
- globalScope.removeEventListener('hashchange', hashChangeHandler);
188
- };
189
- }
190
- // 3. if not, then the framework is probably using hash router → do hash
191
- // Fallback: just hashchange (for pure hash routing)
192
- globalScope.addEventListener('hashchange', hashChangeHandler);
206
+ // Single helper: history + popstate + hashchange, or polling when enabled
207
+ var unsubscribe = subscribeToUrlChanges(globalScope, function () { return emitUrlChange(); }, enablePolling ? { enablePolling: true, pollingInterval: pollingInterval } : {});
193
208
  emitUrlChange();
194
- return function () {
195
- globalScope.removeEventListener('hashchange', hashChangeHandler);
196
- };
209
+ return function () { return unsubscribe(); };
197
210
  },
198
211
  options: options,
199
212
  };
@@ -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/**\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 // Patch detection marker to prevent double-patching\n const PATCH_MARKER = '__amplitude_url_tracking_patched__';\n\n // Global flag key on globalScope to track if the plugin has been reset/cleaned up\n // When true, prevents emitUrlChange from being called from patched history methods\n // even if other plugins have also patched them and we can't restore the originals\n const RESET_FLAG_KEY = '__amplitude_url_tracking_reset__';\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 * Prevents duplicate events for the same URL on subsequent calls\n * Handles edge cases like undefined/null/empty URLs gracefully\n */\n const emitUrlChange = (): void => {\n const currentUrl = getCurrentUrl();\n\n // Always emit on first call, or if URL actually changed\n if (lastTrackedUrl === undefined || currentUrl !== lastTrackedUrl) {\n lastTrackedUrl = currentUrl;\n const event = createUrlChangeEvent();\n cb(event);\n }\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 * Ensures URL changes are detected even when history methods are called programmatically\n * Checks global reset flag to prevent emitting after plugin cleanup\n * @param originalMethod The original history method to patch\n * @returns Patched function that calls original method then emits 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 // Call the original history method first\n const result = originalMethod.apply(this, args);\n // Then emit URL change event only if plugin hasn't been reset\n if (!(globalScope as Record<string, any>)[RESET_FLAG_KEY]) {\n emitUrlChange();\n }\n return result;\n } as T & { [PATCH_MARKER]: boolean };\n\n // Mark the patched method to prevent double-patching\n patchedMethod[PATCH_MARKER] = true;\n\n return patchedMethod;\n };\n\n // Hashchange event handler - delegates to emitUrlChange for consistency\n const hashChangeHandler = () => {\n emitUrlChange();\n };\n\n // 1. if explicitly enable polling → use polling\n if (enablePolling) {\n // Use polling (covers everything)\n const urlChangeInterval = globalScope.setInterval(() => {\n emitUrlChange();\n }, pollingInterval);\n\n // Emit initial URL immediately\n emitUrlChange();\n\n // Return cleanup function to stop polling\n return () => {\n if (urlChangeInterval) {\n globalScope.clearInterval(urlChangeInterval);\n }\n };\n }\n\n // 2. if polling not enabled → check history, if exist, use history\n if (globalScope.history) {\n // Use History API + hashchange (covers History API + hash routing)\n // Store original history methods for restoration during cleanup\n const originalPushState = globalScope.history.pushState.bind(globalScope.history);\n const originalReplaceState = globalScope.history.replaceState.bind(globalScope.history);\n\n /**\n * Sets up history method patching to intercept pushState and replaceState calls\n * This ensures URL changes are detected even when history methods are called programmatically\n * Includes patch detection to prevent double-patching by the same plugin\n */\n const setupHistoryPatching = (): void => {\n // Check if we already patched these methods\n if (\n (globalScope.history.pushState as typeof globalScope.history.pushState & { [PATCH_MARKER]?: boolean })[\n PATCH_MARKER\n ]\n ) {\n // Already patched by this plugin, skip patching\n return;\n }\n\n // Patch pushState to emit URL change events\n globalScope.history.pushState = createHistoryMethodPatch(originalPushState);\n\n // Patch replaceState to emit URL change events\n globalScope.history.replaceState = createHistoryMethodPatch(originalReplaceState);\n };\n\n // Apply history method patches\n setupHistoryPatching();\n\n // Listen to popstate events for browser back/forward navigation\n globalScope.addEventListener('popstate', emitUrlChange);\n // Listen to hashchange events for hash routing\n globalScope.addEventListener('hashchange', hashChangeHandler);\n\n // Emit initial URL immediately\n emitUrlChange();\n\n // Return cleanup function to restore original state\n return () => {\n // Restore original history methods - cannot be done here\n // because the plugin is not aware of the history methods modified by other plugins\n // so we need to set a flag on the globalScope to prevent further emitUrlChange calls from patched methods\n // Set reset flag on globalScope to prevent further emitUrlChange calls from patched methods\n (globalScope as Record<string, any>)[RESET_FLAG_KEY] = true;\n\n // Remove popstate event listener\n globalScope.removeEventListener('popstate', emitUrlChange);\n // Remove hashchange event listener\n globalScope.removeEventListener('hashchange', hashChangeHandler);\n };\n }\n\n // 3. if not, then the framework is probably using hash router → do hash\n // Fallback: just hashchange (for pure hash routing)\n globalScope.addEventListener('hashchange', hashChangeHandler);\n emitUrlChange();\n return () => {\n globalScope.removeEventListener('hashchange', hashChangeHandler);\n };\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\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"]}
@@ -25,9 +25,19 @@ export declare class SessionReplay implements AmplitudeSessionReplay {
25
25
  private networkObservers?;
26
26
  private metadata;
27
27
  private recordFunction;
28
+ /** Cleanup for URL change listener used to re-evaluate targeting on SPA route changes */
29
+ private urlChangeCleanup;
30
+ /** Monotonic counter to ignore stale URL-change targeting results */
31
+ private latestUrlChangeTargetingEvaluationId;
28
32
  constructor();
29
33
  init(apiKey: string, options: SessionReplayOptions): import("@amplitude/analytics-core").AmplitudeReturn<void>;
30
34
  private teardownEventListeners;
35
+ /**
36
+ * Subscribes to SPA URL changes via the URL tracking plugin and re-evaluates targeting so we
37
+ * can start/stop recording when the user navigates to a page that matches or no longer matches.
38
+ */
39
+ private setupUrlChangeListenerForTargeting;
40
+ private getCurrentPageForTargeting;
31
41
  protected _init(apiKey: string, options: SessionReplayOptions): Promise<void>;
32
42
  setSessionId(sessionId: string | number, deviceId?: string): import("@amplitude/analytics-core").AmplitudeReturn<void>;
33
43
  asyncSetSessionId(sessionId: string | number, deviceId?: string, options?: {
@@ -46,7 +56,7 @@ export declare class SessionReplay implements AmplitudeSessionReplay {
46
56
  * prevent duplicate listener actions from firing.
47
57
  */
48
58
  private pageLeaveListener;
49
- evaluateTargetingAndCapture: (targetingParams: Pick<TargetingParameters, 'event' | 'userProperties'>, isInit?: boolean, forceRestart?: boolean) => Promise<void>;
59
+ evaluateTargetingAndCapture: (targetingParams: Pick<TargetingParameters, 'event' | 'userProperties' | 'page'>, isInit?: boolean, forceRestart?: boolean, forceTargetingReevaluation?: boolean) => Promise<void>;
50
60
  sendEvents(sessionId?: string | number): void;
51
61
  initialize(shouldSendStoredEvents?: boolean): Promise<void>;
52
62
  shouldOptOut(): boolean | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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"}