@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.
Files changed (49) hide show
  1. package/lib/cjs/config/joined-config.d.ts.map +1 -1
  2. package/lib/cjs/config/joined-config.js +23 -19
  3. package/lib/cjs/config/joined-config.js.map +1 -1
  4. package/lib/cjs/diagnostics.d.ts +5 -0
  5. package/lib/cjs/diagnostics.d.ts.map +1 -1
  6. package/lib/cjs/diagnostics.js +11 -1
  7. package/lib/cjs/diagnostics.js.map +1 -1
  8. package/lib/cjs/plugins/url-tracking-plugin.d.ts +6 -0
  9. package/lib/cjs/plugins/url-tracking-plugin.d.ts.map +1 -1
  10. package/lib/cjs/plugins/url-tracking-plugin.js +3 -1
  11. package/lib/cjs/plugins/url-tracking-plugin.js.map +1 -1
  12. package/lib/cjs/session-replay.d.ts +7 -0
  13. package/lib/cjs/session-replay.d.ts.map +1 -1
  14. package/lib/cjs/session-replay.js +123 -26
  15. package/lib/cjs/session-replay.js.map +1 -1
  16. package/lib/cjs/targeting/targeting-manager.d.ts.map +1 -1
  17. package/lib/cjs/targeting/targeting-manager.js +28 -15
  18. package/lib/cjs/targeting/targeting-manager.js.map +1 -1
  19. package/lib/cjs/version.d.ts +1 -1
  20. package/lib/cjs/version.js +1 -1
  21. package/lib/cjs/version.js.map +1 -1
  22. package/lib/esm/config/joined-config.d.ts.map +1 -1
  23. package/lib/esm/config/joined-config.js +23 -19
  24. package/lib/esm/config/joined-config.js.map +1 -1
  25. package/lib/esm/diagnostics.d.ts +5 -0
  26. package/lib/esm/diagnostics.d.ts.map +1 -1
  27. package/lib/esm/diagnostics.js +11 -1
  28. package/lib/esm/diagnostics.js.map +1 -1
  29. package/lib/esm/plugins/url-tracking-plugin.d.ts +6 -0
  30. package/lib/esm/plugins/url-tracking-plugin.d.ts.map +1 -1
  31. package/lib/esm/plugins/url-tracking-plugin.js +3 -1
  32. package/lib/esm/plugins/url-tracking-plugin.js.map +1 -1
  33. package/lib/esm/session-replay.d.ts +7 -0
  34. package/lib/esm/session-replay.d.ts.map +1 -1
  35. package/lib/esm/session-replay.js +123 -26
  36. package/lib/esm/session-replay.js.map +1 -1
  37. package/lib/esm/targeting/targeting-manager.d.ts.map +1 -1
  38. package/lib/esm/targeting/targeting-manager.js +28 -15
  39. package/lib/esm/targeting/targeting-manager.js.map +1 -1
  40. package/lib/esm/version.d.ts +1 -1
  41. package/lib/esm/version.js +1 -1
  42. package/lib/esm/version.js.map +1 -1
  43. package/lib/scripts/index-min.js +1 -1
  44. package/lib/scripts/index-min.js.gz +0 -0
  45. package/lib/scripts/index-min.js.map +1 -1
  46. package/lib/scripts/session-replay-browser-min.js +1 -1
  47. package/lib/scripts/session-replay-browser-min.js.gz +0 -0
  48. package/lib/scripts/session-replay-browser-min.js.map +1 -1
  49. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"joined-config.d.ts","sourceRoot":"","sources":["../../../src/config/joined-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAA4C,MAAM,2BAA2B,CAAC;AAGnH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,OAAO,EACL,wBAAwB,IAAI,yBAAyB,EACrD,aAAa,EACb,oBAAoB,EAGrB,MAAM,SAAS,CAAC;AAajB,eAAO,MAAM,uCAAuC,kBAAmB,aAAa,kBAAkB,OAAO,kBA0B5G,CAAC;AACF,qBAAa,kCAAkC;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4B;IACxD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAsB;IAEzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;gBAGjC,kBAAkB,EAAE,mBAAmB,EACvC,WAAW,EAAE,yBAAyB,EACtC,QAAQ,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;IAQzD,oBAAoB,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAoO3D;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;CAmBrC;AAED;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,eAAO,MAAM,wCAAwC,WAAkB,MAAM,WAAW,oBAAoB,gDAc3G,CAAC"}
1
+ {"version":3,"file":"joined-config.d.ts","sourceRoot":"","sources":["../../../src/config/joined-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAA4C,MAAM,2BAA2B,CAAC;AAGnH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,OAAO,EACL,wBAAwB,IAAI,yBAAyB,EACrD,aAAa,EACb,oBAAoB,EAGrB,MAAM,SAAS,CAAC;AAajB,eAAO,MAAM,uCAAuC,kBAAmB,aAAa,kBAAkB,OAAO,kBA0B5G,CAAC;AACF,qBAAa,kCAAkC;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4B;IACxD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAsB;IAEzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;gBAGjC,kBAAkB,EAAE,mBAAmB,EACvC,WAAW,EAAE,yBAAyB,EACtC,QAAQ,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;IAQzD,oBAAoB,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAwO3D;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;CAmBrC;AAED;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,eAAO,MAAM,wCAAwC,WAAkB,MAAM,WAAW,oBAAoB,gDAc3G,CAAC"}
@@ -53,13 +53,13 @@ var SessionReplayJoinedConfigGenerator = /** @class */ (function () {
53
53
  this.deviceId = identity === null || identity === void 0 ? void 0 : identity.deviceId;
54
54
  }
55
55
  SessionReplayJoinedConfigGenerator.prototype.generateJoinedConfig = function () {
56
- var _a, _b, _c, _d, _e, _f, _g, _h;
56
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
57
57
  return tslib_1.__awaiter(this, void 0, void 0, function () {
58
- var config, sessionReplayRemoteConfig, error_1, samplingConfig, remotePrivacyConfig, targetingConfig, localPrivacyConfig, joinedPrivacyConfig, privacyConfigSelectorMap, selectorMap, _j, _k, _l, selector, selectorType;
59
- var e_1, _m;
58
+ var config, sessionReplayRemoteConfig, error_1, samplingConfig, remotePrivacyConfig, targetingConfig, localPrivacyConfig, joinedPrivacyConfig, privacyConfigSelectorMap, selectorMap, _l, _m, _o, selector, selectorType;
59
+ var e_1, _p;
60
60
  var _this = this;
61
- return tslib_1.__generator(this, function (_o) {
62
- switch (_o.label) {
61
+ return tslib_1.__generator(this, function (_q) {
62
+ switch (_q.label) {
63
63
  case 0:
64
64
  config = tslib_1.__assign({}, this.localConfig);
65
65
  // Special case here as optOut is implemented via getter/setter
@@ -67,16 +67,16 @@ var SessionReplayJoinedConfigGenerator = /** @class */ (function () {
67
67
  // We always want captureEnabled to be true, unless there's an override
68
68
  // in the remote config.
69
69
  config.captureEnabled = true;
70
- _o.label = 1;
70
+ _q.label = 1;
71
71
  case 1:
72
- _o.trys.push([1, 3, , 4]);
72
+ _q.trys.push([1, 3, , 4]);
73
73
  // Subscribe with a timeout so the SDK prefers the remote response and only falls back
74
74
  // to cache after the budget elapses. 'all' mode would race a synchronous cache read
75
75
  // against the network and resolve on whichever fires first — cache always wins, so a
76
76
  // stale cache silently overrides the live config (SR-4234).
77
77
  return [4 /*yield*/, new Promise(function (resolve, reject) {
78
78
  _this.remoteConfigClient.subscribe('configs.sessionReplay', { timeout: REMOTE_CONFIG_TIMEOUT_MS }, function (remoteConfig, source) {
79
- var _a;
79
+ var _a, _b;
80
80
  _this.localConfig.loggerProvider.debug("Session Replay remote configuration received from ".concat(source, ":"), JSON.stringify(remoteConfig, null, 2));
81
81
  if (!remoteConfig) {
82
82
  reject(new Error('No remote config received'));
@@ -119,8 +119,11 @@ var SessionReplayJoinedConfigGenerator = /** @class */ (function () {
119
119
  targetingSegmentCount: Array.isArray(targetingSegments) ? targetingSegments.length : undefined,
120
120
  hasPrivacy: !!privacyConfig,
121
121
  });
122
+ // Flush now (vs the client's ~5-min timer). One capture POST per event — higher
123
+ // volume; revisit/gate before production.
124
+ void ((_b = diagnosticsClient === null || diagnosticsClient === void 0 ? void 0 : diagnosticsClient._flush) === null || _b === void 0 ? void 0 : _b.call(diagnosticsClient));
122
125
  }
123
- catch (_b) {
126
+ catch (_c) {
124
127
  // diagnostics is best-effort
125
128
  }
126
129
  if (samplingConfig || privacyConfig || targetingConfig) {
@@ -143,15 +146,16 @@ var SessionReplayJoinedConfigGenerator = /** @class */ (function () {
143
146
  // to cache after the budget elapses. 'all' mode would race a synchronous cache read
144
147
  // against the network and resolve on whichever fires first — cache always wins, so a
145
148
  // stale cache silently overrides the live config (SR-4234).
146
- _o.sent();
149
+ _q.sent();
147
150
  return [3 /*break*/, 4];
148
151
  case 3:
149
- error_1 = _o.sent();
152
+ error_1 = _q.sent();
150
153
  this.localConfig.loggerProvider.error('Failed to generate joined config: ', error_1);
151
154
  try {
152
155
  (_a = this.localConfig.diagnosticsClient) === null || _a === void 0 ? void 0 : _a.increment(diagnostics_1.SrDiagnostic.configFetchFailed);
156
+ void ((_c = (_b = this.localConfig.diagnosticsClient) === null || _b === void 0 ? void 0 : _b._flush) === null || _c === void 0 ? void 0 : _c.call(_b));
153
157
  }
154
- catch (_p) {
158
+ catch (_r) {
155
159
  // diagnostics is best-effort
156
160
  }
157
161
  config.captureEnabled = false;
@@ -204,14 +208,14 @@ var SessionReplayJoinedConfigGenerator = /** @class */ (function () {
204
208
  // ...remotePrivacyConfig,
205
209
  // };
206
210
  if (remotePrivacyConfig) {
207
- localPrivacyConfig = (_b = config.privacyConfig) !== null && _b !== void 0 ? _b : {};
211
+ localPrivacyConfig = (_d = config.privacyConfig) !== null && _d !== void 0 ? _d : {};
208
212
  joinedPrivacyConfig = {
209
- defaultMaskLevel: (_d = (_c = remotePrivacyConfig.defaultMaskLevel) !== null && _c !== void 0 ? _c : localPrivacyConfig.defaultMaskLevel) !== null && _d !== void 0 ? _d : 'medium',
213
+ defaultMaskLevel: (_f = (_e = remotePrivacyConfig.defaultMaskLevel) !== null && _e !== void 0 ? _e : localPrivacyConfig.defaultMaskLevel) !== null && _f !== void 0 ? _f : 'medium',
210
214
  blockSelector: [],
211
215
  maskSelector: [],
212
216
  unmaskSelector: [],
213
- maskAttributes: tslib_1.__spreadArray([], tslib_1.__read(new Set(tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(((_e = localPrivacyConfig.maskAttributes) !== null && _e !== void 0 ? _e : [])), false), tslib_1.__read(((_f = remotePrivacyConfig.maskAttributes) !== null && _f !== void 0 ? _f : [])), false))), false),
214
- urlMaskLevels: tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(((_g = remotePrivacyConfig.urlMaskLevels) !== null && _g !== void 0 ? _g : [])), false), tslib_1.__read(((_h = localPrivacyConfig.urlMaskLevels) !== null && _h !== void 0 ? _h : [])), false),
217
+ maskAttributes: tslib_1.__spreadArray([], tslib_1.__read(new Set(tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(((_g = localPrivacyConfig.maskAttributes) !== null && _g !== void 0 ? _g : [])), false), tslib_1.__read(((_h = remotePrivacyConfig.maskAttributes) !== null && _h !== void 0 ? _h : [])), false))), false),
218
+ urlMaskLevels: tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(((_j = remotePrivacyConfig.urlMaskLevels) !== null && _j !== void 0 ? _j : [])), false), tslib_1.__read(((_k = localPrivacyConfig.urlMaskLevels) !== null && _k !== void 0 ? _k : [])), false),
215
219
  };
216
220
  privacyConfigSelectorMap = function (privacyConfig) {
217
221
  var e_2, _a, e_3, _b, e_4, _c;
@@ -263,8 +267,8 @@ var SessionReplayJoinedConfigGenerator = /** @class */ (function () {
263
267
  };
264
268
  selectorMap = tslib_1.__assign(tslib_1.__assign({}, privacyConfigSelectorMap(localPrivacyConfig)), privacyConfigSelectorMap(remotePrivacyConfig));
265
269
  try {
266
- for (_j = tslib_1.__values(Object.entries(selectorMap)), _k = _j.next(); !_k.done; _k = _j.next()) {
267
- _l = tslib_1.__read(_k.value, 2), selector = _l[0], selectorType = _l[1];
270
+ for (_l = tslib_1.__values(Object.entries(selectorMap)), _m = _l.next(); !_m.done; _m = _l.next()) {
271
+ _o = tslib_1.__read(_m.value, 2), selector = _o[0], selectorType = _o[1];
268
272
  if (selectorType === 'mask') {
269
273
  joinedPrivacyConfig.maskSelector.push(selector);
270
274
  }
@@ -279,7 +283,7 @@ var SessionReplayJoinedConfigGenerator = /** @class */ (function () {
279
283
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
280
284
  finally {
281
285
  try {
282
- if (_k && !_k.done && (_m = _j.return)) _m.call(_j);
286
+ if (_m && !_m.done && (_p = _l.return)) _p.call(_l);
283
287
  }
284
288
  finally { if (e_1) throw e_1.error; }
285
289
  }
@@ -1 +1 @@
1
- {"version":3,"file":"joined-config.js","sourceRoot":"","sources":["../../../src/config/joined-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { ILogger, IRemoteConfigClient, RemoteConfigClient, RemoteConfig, Source } from '@amplitude/analytics-core';\nimport { getDebugConfig } from '../helpers';\nimport { SrDiagnostic } from '../diagnostics';\nimport { SessionReplayOptions } from '../typings/session-replay';\nimport { SessionReplayLocalConfig } from './local-config';\nimport {\n SessionReplayLocalConfig as ISessionReplayLocalConfig,\n PrivacyConfig,\n SessionReplayConfigs,\n SessionReplayJoinedConfig,\n SessionReplayRemoteConfig,\n} from './types';\n\n// Budget for waiting on the remote config response before falling back to the local cache.\n// The inner fetch in analytics-core uses a 1000ms per-attempt AbortController timeout with\n// up to 3 retries — but this outer timeout is the only thing the SR plugin waits on, so\n// only the first attempt can possibly complete in time (attempt 2 starts at ~1000-1333ms\n// after backoff jitter and runs to ~2000-2333ms, well past any reasonable outer budget).\n// 1500ms is set above the 1000ms inner abort to avoid a tie with the inner cutoff and\n// give the first attempt's resolution path room to finish; everything else falls through\n// to the cache fallback. See SR-4234: prefer remote over a stale cache that would\n// otherwise win the synchronous race in 'all' mode.\nconst REMOTE_CONFIG_TIMEOUT_MS = 1500;\n\nexport const removeInvalidSelectorsFromPrivacyConfig = (privacyConfig: PrivacyConfig, loggerProvider: ILogger) => {\n // This allows us to not search the DOM.\n const fragment = document.createDocumentFragment();\n\n const dropInvalidSelectors = (selectors: string[] | string = []): string[] | undefined => {\n if (typeof selectors === 'string') {\n selectors = [selectors];\n }\n selectors = selectors.filter((selector: string) => {\n try {\n fragment.querySelector(selector);\n } catch {\n loggerProvider.warn(`[session-replay-browser] omitting selector \"${selector}\" because it is invalid`);\n return false;\n }\n return true;\n });\n if (selectors.length === 0) {\n return undefined;\n }\n return selectors;\n };\n privacyConfig.blockSelector = dropInvalidSelectors(privacyConfig.blockSelector);\n privacyConfig.maskSelector = dropInvalidSelectors(privacyConfig.maskSelector);\n privacyConfig.unmaskSelector = dropInvalidSelectors(privacyConfig.unmaskSelector);\n return privacyConfig;\n};\nexport class SessionReplayJoinedConfigGenerator {\n private readonly localConfig: ISessionReplayLocalConfig;\n private readonly remoteConfigClient: IRemoteConfigClient;\n // Identity for diagnostics correlation (config fetch is per-session). Sourced from init options.\n private readonly sessionId?: string | number;\n private readonly deviceId?: string;\n\n constructor(\n remoteConfigClient: IRemoteConfigClient,\n localConfig: ISessionReplayLocalConfig,\n identity?: { sessionId?: string | number; deviceId?: string },\n ) {\n this.localConfig = localConfig;\n this.remoteConfigClient = remoteConfigClient;\n this.sessionId = identity?.sessionId;\n this.deviceId = identity?.deviceId;\n }\n\n async generateJoinedConfig(): Promise<SessionReplayConfigs> {\n const config: SessionReplayJoinedConfig = { ...this.localConfig };\n // Special case here as optOut is implemented via getter/setter\n config.optOut = this.localConfig.optOut;\n // We always want captureEnabled to be true, unless there's an override\n // in the remote config.\n config.captureEnabled = true;\n let sessionReplayRemoteConfig: SessionReplayRemoteConfig | undefined;\n\n try {\n // Subscribe with a timeout so the SDK prefers the remote response and only falls back\n // to cache after the budget elapses. 'all' mode would race a synchronous cache read\n // against the network and resolve on whichever fires first — cache always wins, so a\n // stale cache silently overrides the live config (SR-4234).\n await new Promise<void>((resolve, reject) => {\n this.remoteConfigClient.subscribe(\n 'configs.sessionReplay',\n { timeout: REMOTE_CONFIG_TIMEOUT_MS },\n (remoteConfig: RemoteConfig | null, source: Source) => {\n this.localConfig.loggerProvider.debug(\n `Session Replay remote configuration received from ${source}:`,\n JSON.stringify(remoteConfig, null, 2),\n );\n\n if (!remoteConfig) {\n reject(new Error('No remote config received'));\n return;\n }\n\n // remoteConfig is already filtered to 'configs.sessionReplay' namespace\n const namespaceConfig = remoteConfig as SessionReplayRemoteConfig;\n const samplingConfig = namespaceConfig.sr_sampling_config;\n const privacyConfig = namespaceConfig.sr_privacy_config;\n const targetingConfig = namespaceConfig.sr_targeting_config;\n\n // Captured for the sr.trc.config.received diagnostics event below (remote vs cache is\n // the crux of SR-4234 stale-cache reports).\n const samplingForLog = samplingConfig as { capture_enabled?: boolean; sample_rate?: number } | undefined;\n const targetingSegments = (targetingConfig as unknown as { segments?: unknown[] } | undefined)?.segments;\n\n const ugcFilterRules = config.interactionConfig?.ugcFilterRules;\n // This is intentionally forced to only be set through the remote config.\n config.interactionConfig = namespaceConfig.sr_interaction_config;\n if (config.interactionConfig && ugcFilterRules) {\n config.interactionConfig.ugcFilterRules = ugcFilterRules;\n }\n\n // This is intentionally forced to only be set through the remote config.\n config.loggingConfig = namespaceConfig.sr_logging_config;\n\n // Team-visible diagnostics: which SOURCE the config came from (remote vs cache — the\n // crux of SR-4234) and whether targeting was present. Counters aggregate across the\n // customer's sessions; the event carries the full context (source, sample rate,\n // segment count) as queryable log fields. No-op when diagnostics isn't configured.\n try {\n const diagnosticsClient = this.localConfig.diagnosticsClient;\n diagnosticsClient?.increment(SrDiagnostic.configSource(String(source)));\n diagnosticsClient?.increment(\n targetingConfig ? SrDiagnostic.configHasTargeting : SrDiagnostic.configNoTargeting,\n );\n diagnosticsClient?.recordEvent(SrDiagnostic.configReceived, {\n sessionId: this.sessionId,\n deviceId: this.deviceId,\n srId:\n this.deviceId != null && this.sessionId != null ? `${this.deviceId}/${this.sessionId}` : undefined,\n source: String(source),\n hasSampling: !!samplingConfig,\n captureEnabled: samplingForLog?.capture_enabled,\n sampleRate: samplingForLog?.sample_rate,\n hasTargeting: !!targetingConfig,\n targetingSegmentCount: Array.isArray(targetingSegments) ? targetingSegments.length : undefined,\n hasPrivacy: !!privacyConfig,\n });\n } catch {\n // diagnostics is best-effort\n }\n\n if (samplingConfig || privacyConfig || targetingConfig) {\n sessionReplayRemoteConfig = {};\n if (samplingConfig) {\n sessionReplayRemoteConfig.sr_sampling_config = samplingConfig;\n }\n if (privacyConfig) {\n sessionReplayRemoteConfig.sr_privacy_config = privacyConfig;\n }\n if (targetingConfig) {\n sessionReplayRemoteConfig.sr_targeting_config = targetingConfig;\n }\n }\n\n resolve();\n },\n );\n });\n } catch (error) {\n this.localConfig.loggerProvider.error('Failed to generate joined config: ', error);\n try {\n this.localConfig.diagnosticsClient?.increment(SrDiagnostic.configFetchFailed);\n } catch {\n // diagnostics is best-effort\n }\n config.captureEnabled = false;\n return {\n localConfig: this.localConfig,\n joinedConfig: config,\n remoteConfig: undefined,\n };\n }\n\n if (!sessionReplayRemoteConfig) {\n return {\n localConfig: this.localConfig,\n joinedConfig: config,\n remoteConfig: sessionReplayRemoteConfig,\n };\n }\n\n const {\n sr_sampling_config: samplingConfig,\n sr_privacy_config: remotePrivacyConfig,\n sr_targeting_config: targetingConfig,\n } = sessionReplayRemoteConfig;\n if (samplingConfig && Object.keys(samplingConfig).length > 0) {\n if (Object.prototype.hasOwnProperty.call(samplingConfig, 'capture_enabled')) {\n config.captureEnabled = samplingConfig.capture_enabled;\n } else {\n config.captureEnabled = false;\n }\n\n if (Object.prototype.hasOwnProperty.call(samplingConfig, 'sample_rate')) {\n config.sampleRate = samplingConfig.sample_rate;\n }\n\n if (Object.prototype.hasOwnProperty.call(samplingConfig, 'min_session_duration_ms')) {\n config.minSessionDurationMs = this.sanitizeMinSessionDurationMs(samplingConfig.min_session_duration_ms);\n }\n } else {\n // If config API response was valid (ie 200), but no config returned, assume that\n // customer has not yet set up config, and use sample rate from SDK options,\n // allowing for immediate replay capture\n config.captureEnabled = true;\n this.localConfig.loggerProvider.debug(\n 'Remote config successfully fetched, but no values set for project, Session Replay capture enabled.',\n );\n }\n\n // Remote config join acts somewhat like a left join between the remote and the local\n // config. That is, remote config has precedence over local values as with sampling.\n // However, non conflicting values will be added to the lists.\n // Here's an example to illustrate:\n //\n // Remote config: {'.selector1': 'MASK', '.selector2': 'UNMASK'}\n // Local config: {'.selector1': 'UNMASK', '.selector3': 'MASK'}\n //\n // Resolved config: {'.selector1': 'MASK', '.selector2': 'UNMASK', '.selector3': 'MASK'}\n // config.privacyConfig = {\n // ...(config.privacyConfig ?? {}),\n // ...remotePrivacyConfig,\n // };\n\n if (remotePrivacyConfig) {\n const localPrivacyConfig: PrivacyConfig = config.privacyConfig ?? {};\n\n const joinedPrivacyConfig: Required<PrivacyConfig> & { blockSelector: string[] } = {\n defaultMaskLevel: remotePrivacyConfig.defaultMaskLevel ?? localPrivacyConfig.defaultMaskLevel ?? 'medium',\n blockSelector: [],\n maskSelector: [],\n unmaskSelector: [],\n maskAttributes: [\n ...new Set([...(localPrivacyConfig.maskAttributes ?? []), ...(remotePrivacyConfig.maskAttributes ?? [])]),\n ],\n urlMaskLevels: [...(remotePrivacyConfig.urlMaskLevels ?? []), ...(localPrivacyConfig.urlMaskLevels ?? [])],\n };\n\n const privacyConfigSelectorMap = (privacyConfig: PrivacyConfig): Record<string, 'mask' | 'unmask' | 'block'> => {\n const selectorMap: Record<string, 'mask' | 'unmask' | 'block'> = {};\n if (typeof privacyConfig.blockSelector === 'string') {\n privacyConfig.blockSelector = [privacyConfig.blockSelector];\n }\n\n for (const selector of privacyConfig.blockSelector ?? []) {\n selectorMap[selector] = 'block';\n }\n for (const selector of privacyConfig.maskSelector ?? []) {\n selectorMap[selector] = 'mask';\n }\n for (const selector of privacyConfig.unmaskSelector ?? []) {\n selectorMap[selector] = 'unmask';\n }\n return selectorMap;\n };\n\n const selectorMap: Record<string, 'mask' | 'unmask' | 'block'> = {\n ...privacyConfigSelectorMap(localPrivacyConfig),\n ...privacyConfigSelectorMap(remotePrivacyConfig),\n };\n\n for (const [selector, selectorType] of Object.entries(selectorMap)) {\n if (selectorType === 'mask') {\n joinedPrivacyConfig.maskSelector.push(selector);\n } else if (selectorType === 'block') {\n joinedPrivacyConfig.blockSelector.push(selector);\n } else if (selectorType === 'unmask') {\n joinedPrivacyConfig.unmaskSelector.push(selector);\n }\n }\n\n config.privacyConfig = removeInvalidSelectorsFromPrivacyConfig(\n joinedPrivacyConfig,\n this.localConfig.loggerProvider,\n );\n }\n\n if (targetingConfig && Object.keys(targetingConfig).length > 0) {\n config.targetingConfig = targetingConfig;\n }\n\n this.localConfig.loggerProvider.debug(\n JSON.stringify({ name: 'session replay joined config', config: getDebugConfig(config) }, null, 2),\n );\n\n return {\n localConfig: this.localConfig,\n joinedConfig: config,\n remoteConfig: sessionReplayRemoteConfig,\n };\n }\n\n /**\n * Defensive bounds for the remote-supplied min_session_duration_ms. A misconfigured\n * value (e.g. 30_000_000) would silently suppress every replay until the config is\n * pushed again, so we clamp to a sane ceiling and warn on out-of-range inputs.\n * Returns undefined for clearly invalid values so the gate falls back to disabled\n * rather than carrying a NaN through downstream comparisons.\n */\n private sanitizeMinSessionDurationMs(raw: unknown): number | undefined {\n if (typeof raw !== 'number' || !Number.isFinite(raw)) {\n this.localConfig.loggerProvider.warn(\n `min_session_duration_ms remote value is not a finite number (got ${String(raw)}); ignoring.`,\n );\n return undefined;\n }\n if (raw < 0) {\n this.localConfig.loggerProvider.warn(`min_session_duration_ms remote value is negative (${raw}); ignoring.`);\n return undefined;\n }\n if (raw > MAX_MIN_SESSION_DURATION_MS) {\n this.localConfig.loggerProvider.warn(\n `min_session_duration_ms remote value ${raw} exceeds ${MAX_MIN_SESSION_DURATION_MS}ms ceiling; clamping.`,\n );\n return MAX_MIN_SESSION_DURATION_MS;\n }\n return raw;\n }\n}\n\n/**\n * Upper bound for the remote-configured replay min duration. 60 seconds is well above\n * any reasonable bounce threshold; values higher than this are almost certainly typos\n * (e.g. seconds confused for milliseconds, or an extra zero) and would otherwise\n * suppress every replay until the config is corrected.\n */\nexport const MAX_MIN_SESSION_DURATION_MS = 60_000;\n\nexport const createSessionReplayJoinedConfigGenerator = async (apiKey: string, options: SessionReplayOptions) => {\n const localConfig = new SessionReplayLocalConfig(apiKey, options);\n\n const remoteConfigClient = new RemoteConfigClient(\n apiKey,\n localConfig.loggerProvider,\n localConfig.serverZone,\n options.configServerUrl,\n );\n\n return new SessionReplayJoinedConfigGenerator(remoteConfigClient, localConfig, {\n sessionId: options.sessionId,\n deviceId: options.deviceId,\n });\n};\n"]}
1
+ {"version":3,"file":"joined-config.js","sourceRoot":"","sources":["../../../src/config/joined-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { ILogger, IRemoteConfigClient, RemoteConfigClient, RemoteConfig, Source } from '@amplitude/analytics-core';\nimport { getDebugConfig } from '../helpers';\nimport { SrDiagnostic } from '../diagnostics';\nimport { SessionReplayOptions } from '../typings/session-replay';\nimport { SessionReplayLocalConfig } from './local-config';\nimport {\n SessionReplayLocalConfig as ISessionReplayLocalConfig,\n PrivacyConfig,\n SessionReplayConfigs,\n SessionReplayJoinedConfig,\n SessionReplayRemoteConfig,\n} from './types';\n\n// Budget for waiting on the remote config response before falling back to the local cache.\n// The inner fetch in analytics-core uses a 1000ms per-attempt AbortController timeout with\n// up to 3 retries — but this outer timeout is the only thing the SR plugin waits on, so\n// only the first attempt can possibly complete in time (attempt 2 starts at ~1000-1333ms\n// after backoff jitter and runs to ~2000-2333ms, well past any reasonable outer budget).\n// 1500ms is set above the 1000ms inner abort to avoid a tie with the inner cutoff and\n// give the first attempt's resolution path room to finish; everything else falls through\n// to the cache fallback. See SR-4234: prefer remote over a stale cache that would\n// otherwise win the synchronous race in 'all' mode.\nconst REMOTE_CONFIG_TIMEOUT_MS = 1500;\n\nexport const removeInvalidSelectorsFromPrivacyConfig = (privacyConfig: PrivacyConfig, loggerProvider: ILogger) => {\n // This allows us to not search the DOM.\n const fragment = document.createDocumentFragment();\n\n const dropInvalidSelectors = (selectors: string[] | string = []): string[] | undefined => {\n if (typeof selectors === 'string') {\n selectors = [selectors];\n }\n selectors = selectors.filter((selector: string) => {\n try {\n fragment.querySelector(selector);\n } catch {\n loggerProvider.warn(`[session-replay-browser] omitting selector \"${selector}\" because it is invalid`);\n return false;\n }\n return true;\n });\n if (selectors.length === 0) {\n return undefined;\n }\n return selectors;\n };\n privacyConfig.blockSelector = dropInvalidSelectors(privacyConfig.blockSelector);\n privacyConfig.maskSelector = dropInvalidSelectors(privacyConfig.maskSelector);\n privacyConfig.unmaskSelector = dropInvalidSelectors(privacyConfig.unmaskSelector);\n return privacyConfig;\n};\nexport class SessionReplayJoinedConfigGenerator {\n private readonly localConfig: ISessionReplayLocalConfig;\n private readonly remoteConfigClient: IRemoteConfigClient;\n // Identity for diagnostics correlation (config fetch is per-session). Sourced from init options.\n private readonly sessionId?: string | number;\n private readonly deviceId?: string;\n\n constructor(\n remoteConfigClient: IRemoteConfigClient,\n localConfig: ISessionReplayLocalConfig,\n identity?: { sessionId?: string | number; deviceId?: string },\n ) {\n this.localConfig = localConfig;\n this.remoteConfigClient = remoteConfigClient;\n this.sessionId = identity?.sessionId;\n this.deviceId = identity?.deviceId;\n }\n\n async generateJoinedConfig(): Promise<SessionReplayConfigs> {\n const config: SessionReplayJoinedConfig = { ...this.localConfig };\n // Special case here as optOut is implemented via getter/setter\n config.optOut = this.localConfig.optOut;\n // We always want captureEnabled to be true, unless there's an override\n // in the remote config.\n config.captureEnabled = true;\n let sessionReplayRemoteConfig: SessionReplayRemoteConfig | undefined;\n\n try {\n // Subscribe with a timeout so the SDK prefers the remote response and only falls back\n // to cache after the budget elapses. 'all' mode would race a synchronous cache read\n // against the network and resolve on whichever fires first — cache always wins, so a\n // stale cache silently overrides the live config (SR-4234).\n await new Promise<void>((resolve, reject) => {\n this.remoteConfigClient.subscribe(\n 'configs.sessionReplay',\n { timeout: REMOTE_CONFIG_TIMEOUT_MS },\n (remoteConfig: RemoteConfig | null, source: Source) => {\n this.localConfig.loggerProvider.debug(\n `Session Replay remote configuration received from ${source}:`,\n JSON.stringify(remoteConfig, null, 2),\n );\n\n if (!remoteConfig) {\n reject(new Error('No remote config received'));\n return;\n }\n\n // remoteConfig is already filtered to 'configs.sessionReplay' namespace\n const namespaceConfig = remoteConfig as SessionReplayRemoteConfig;\n const samplingConfig = namespaceConfig.sr_sampling_config;\n const privacyConfig = namespaceConfig.sr_privacy_config;\n const targetingConfig = namespaceConfig.sr_targeting_config;\n\n // Captured for the sr.trc.config.received diagnostics event below (remote vs cache is\n // the crux of SR-4234 stale-cache reports).\n const samplingForLog = samplingConfig as { capture_enabled?: boolean; sample_rate?: number } | undefined;\n const targetingSegments = (targetingConfig as unknown as { segments?: unknown[] } | undefined)?.segments;\n\n const ugcFilterRules = config.interactionConfig?.ugcFilterRules;\n // This is intentionally forced to only be set through the remote config.\n config.interactionConfig = namespaceConfig.sr_interaction_config;\n if (config.interactionConfig && ugcFilterRules) {\n config.interactionConfig.ugcFilterRules = ugcFilterRules;\n }\n\n // This is intentionally forced to only be set through the remote config.\n config.loggingConfig = namespaceConfig.sr_logging_config;\n\n // Team-visible diagnostics: which SOURCE the config came from (remote vs cache — the\n // crux of SR-4234) and whether targeting was present. Counters aggregate across the\n // customer's sessions; the event carries the full context (source, sample rate,\n // segment count) as queryable log fields. No-op when diagnostics isn't configured.\n try {\n const diagnosticsClient = this.localConfig.diagnosticsClient;\n diagnosticsClient?.increment(SrDiagnostic.configSource(String(source)));\n diagnosticsClient?.increment(\n targetingConfig ? SrDiagnostic.configHasTargeting : SrDiagnostic.configNoTargeting,\n );\n diagnosticsClient?.recordEvent(SrDiagnostic.configReceived, {\n sessionId: this.sessionId,\n deviceId: this.deviceId,\n srId:\n this.deviceId != null && this.sessionId != null ? `${this.deviceId}/${this.sessionId}` : undefined,\n source: String(source),\n hasSampling: !!samplingConfig,\n captureEnabled: samplingForLog?.capture_enabled,\n sampleRate: samplingForLog?.sample_rate,\n hasTargeting: !!targetingConfig,\n targetingSegmentCount: Array.isArray(targetingSegments) ? targetingSegments.length : undefined,\n hasPrivacy: !!privacyConfig,\n });\n // Flush now (vs the client's ~5-min timer). One capture POST per event — higher\n // volume; revisit/gate before production.\n void diagnosticsClient?._flush?.();\n } catch {\n // diagnostics is best-effort\n }\n\n if (samplingConfig || privacyConfig || targetingConfig) {\n sessionReplayRemoteConfig = {};\n if (samplingConfig) {\n sessionReplayRemoteConfig.sr_sampling_config = samplingConfig;\n }\n if (privacyConfig) {\n sessionReplayRemoteConfig.sr_privacy_config = privacyConfig;\n }\n if (targetingConfig) {\n sessionReplayRemoteConfig.sr_targeting_config = targetingConfig;\n }\n }\n\n resolve();\n },\n );\n });\n } catch (error) {\n this.localConfig.loggerProvider.error('Failed to generate joined config: ', error);\n try {\n this.localConfig.diagnosticsClient?.increment(SrDiagnostic.configFetchFailed);\n void this.localConfig.diagnosticsClient?._flush?.();\n } catch {\n // diagnostics is best-effort\n }\n config.captureEnabled = false;\n return {\n localConfig: this.localConfig,\n joinedConfig: config,\n remoteConfig: undefined,\n };\n }\n\n if (!sessionReplayRemoteConfig) {\n return {\n localConfig: this.localConfig,\n joinedConfig: config,\n remoteConfig: sessionReplayRemoteConfig,\n };\n }\n\n const {\n sr_sampling_config: samplingConfig,\n sr_privacy_config: remotePrivacyConfig,\n sr_targeting_config: targetingConfig,\n } = sessionReplayRemoteConfig;\n if (samplingConfig && Object.keys(samplingConfig).length > 0) {\n if (Object.prototype.hasOwnProperty.call(samplingConfig, 'capture_enabled')) {\n config.captureEnabled = samplingConfig.capture_enabled;\n } else {\n config.captureEnabled = false;\n }\n\n if (Object.prototype.hasOwnProperty.call(samplingConfig, 'sample_rate')) {\n config.sampleRate = samplingConfig.sample_rate;\n }\n\n if (Object.prototype.hasOwnProperty.call(samplingConfig, 'min_session_duration_ms')) {\n config.minSessionDurationMs = this.sanitizeMinSessionDurationMs(samplingConfig.min_session_duration_ms);\n }\n } else {\n // If config API response was valid (ie 200), but no config returned, assume that\n // customer has not yet set up config, and use sample rate from SDK options,\n // allowing for immediate replay capture\n config.captureEnabled = true;\n this.localConfig.loggerProvider.debug(\n 'Remote config successfully fetched, but no values set for project, Session Replay capture enabled.',\n );\n }\n\n // Remote config join acts somewhat like a left join between the remote and the local\n // config. That is, remote config has precedence over local values as with sampling.\n // However, non conflicting values will be added to the lists.\n // Here's an example to illustrate:\n //\n // Remote config: {'.selector1': 'MASK', '.selector2': 'UNMASK'}\n // Local config: {'.selector1': 'UNMASK', '.selector3': 'MASK'}\n //\n // Resolved config: {'.selector1': 'MASK', '.selector2': 'UNMASK', '.selector3': 'MASK'}\n // config.privacyConfig = {\n // ...(config.privacyConfig ?? {}),\n // ...remotePrivacyConfig,\n // };\n\n if (remotePrivacyConfig) {\n const localPrivacyConfig: PrivacyConfig = config.privacyConfig ?? {};\n\n const joinedPrivacyConfig: Required<PrivacyConfig> & { blockSelector: string[] } = {\n defaultMaskLevel: remotePrivacyConfig.defaultMaskLevel ?? localPrivacyConfig.defaultMaskLevel ?? 'medium',\n blockSelector: [],\n maskSelector: [],\n unmaskSelector: [],\n maskAttributes: [\n ...new Set([...(localPrivacyConfig.maskAttributes ?? []), ...(remotePrivacyConfig.maskAttributes ?? [])]),\n ],\n urlMaskLevels: [...(remotePrivacyConfig.urlMaskLevels ?? []), ...(localPrivacyConfig.urlMaskLevels ?? [])],\n };\n\n const privacyConfigSelectorMap = (privacyConfig: PrivacyConfig): Record<string, 'mask' | 'unmask' | 'block'> => {\n const selectorMap: Record<string, 'mask' | 'unmask' | 'block'> = {};\n if (typeof privacyConfig.blockSelector === 'string') {\n privacyConfig.blockSelector = [privacyConfig.blockSelector];\n }\n\n for (const selector of privacyConfig.blockSelector ?? []) {\n selectorMap[selector] = 'block';\n }\n for (const selector of privacyConfig.maskSelector ?? []) {\n selectorMap[selector] = 'mask';\n }\n for (const selector of privacyConfig.unmaskSelector ?? []) {\n selectorMap[selector] = 'unmask';\n }\n return selectorMap;\n };\n\n const selectorMap: Record<string, 'mask' | 'unmask' | 'block'> = {\n ...privacyConfigSelectorMap(localPrivacyConfig),\n ...privacyConfigSelectorMap(remotePrivacyConfig),\n };\n\n for (const [selector, selectorType] of Object.entries(selectorMap)) {\n if (selectorType === 'mask') {\n joinedPrivacyConfig.maskSelector.push(selector);\n } else if (selectorType === 'block') {\n joinedPrivacyConfig.blockSelector.push(selector);\n } else if (selectorType === 'unmask') {\n joinedPrivacyConfig.unmaskSelector.push(selector);\n }\n }\n\n config.privacyConfig = removeInvalidSelectorsFromPrivacyConfig(\n joinedPrivacyConfig,\n this.localConfig.loggerProvider,\n );\n }\n\n if (targetingConfig && Object.keys(targetingConfig).length > 0) {\n config.targetingConfig = targetingConfig;\n }\n\n this.localConfig.loggerProvider.debug(\n JSON.stringify({ name: 'session replay joined config', config: getDebugConfig(config) }, null, 2),\n );\n\n return {\n localConfig: this.localConfig,\n joinedConfig: config,\n remoteConfig: sessionReplayRemoteConfig,\n };\n }\n\n /**\n * Defensive bounds for the remote-supplied min_session_duration_ms. A misconfigured\n * value (e.g. 30_000_000) would silently suppress every replay until the config is\n * pushed again, so we clamp to a sane ceiling and warn on out-of-range inputs.\n * Returns undefined for clearly invalid values so the gate falls back to disabled\n * rather than carrying a NaN through downstream comparisons.\n */\n private sanitizeMinSessionDurationMs(raw: unknown): number | undefined {\n if (typeof raw !== 'number' || !Number.isFinite(raw)) {\n this.localConfig.loggerProvider.warn(\n `min_session_duration_ms remote value is not a finite number (got ${String(raw)}); ignoring.`,\n );\n return undefined;\n }\n if (raw < 0) {\n this.localConfig.loggerProvider.warn(`min_session_duration_ms remote value is negative (${raw}); ignoring.`);\n return undefined;\n }\n if (raw > MAX_MIN_SESSION_DURATION_MS) {\n this.localConfig.loggerProvider.warn(\n `min_session_duration_ms remote value ${raw} exceeds ${MAX_MIN_SESSION_DURATION_MS}ms ceiling; clamping.`,\n );\n return MAX_MIN_SESSION_DURATION_MS;\n }\n return raw;\n }\n}\n\n/**\n * Upper bound for the remote-configured replay min duration. 60 seconds is well above\n * any reasonable bounce threshold; values higher than this are almost certainly typos\n * (e.g. seconds confused for milliseconds, or an extra zero) and would otherwise\n * suppress every replay until the config is corrected.\n */\nexport const MAX_MIN_SESSION_DURATION_MS = 60_000;\n\nexport const createSessionReplayJoinedConfigGenerator = async (apiKey: string, options: SessionReplayOptions) => {\n const localConfig = new SessionReplayLocalConfig(apiKey, options);\n\n const remoteConfigClient = new RemoteConfigClient(\n apiKey,\n localConfig.loggerProvider,\n localConfig.serverZone,\n options.configServerUrl,\n );\n\n return new SessionReplayJoinedConfigGenerator(remoteConfigClient, localConfig, {\n sessionId: options.sessionId,\n deviceId: options.deviceId,\n });\n};\n"]}
@@ -10,11 +10,13 @@
10
10
  export declare const SR_DIAGNOSTIC_PREFIX = "sr.trc";
11
11
  export declare const SrDiagnostic: {
12
12
  readonly init: "sr.trc.init";
13
+ readonly sessionChanged: "sr.trc.session.changed";
13
14
  readonly configSource: (source: string) => string;
14
15
  readonly configHasTargeting: "sr.trc.config.has_targeting";
15
16
  readonly configNoTargeting: "sr.trc.config.no_targeting";
16
17
  readonly configFetchFailed: "sr.trc.config.fetch_failed";
17
18
  readonly configReceived: "sr.trc.config.received";
19
+ readonly targetingTrigger: "sr.trc.targeting.trigger";
18
20
  readonly evalTrigger: (trigger: string) => string;
19
21
  readonly evalNoConfig: "sr.trc.eval.no_config";
20
22
  readonly evalMissingPrereq: "sr.trc.eval.missing_prereq";
@@ -39,5 +41,8 @@ export declare const SrDiagnostic: {
39
41
  readonly decision: "sr.trc.decision";
40
42
  readonly urlChange: "sr.trc.url_change";
41
43
  readonly urlChangeEvent: "sr.trc.url_change";
44
+ readonly urlListenerSetup: "sr.trc.url_listener.setup";
45
+ readonly urlListenerAttached: "sr.trc.url_listener.attached";
46
+ readonly urlListenerSkipped: "sr.trc.url_listener.skipped";
42
47
  };
43
48
  //# sourceMappingURL=diagnostics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAI7C,eAAO,MAAM,YAAY;;oCAKA,MAAM;;;;;oCAQN,MAAM;;;;;;;;;;;;;;;;;;;;;;;;CA8BrB,CAAC"}
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAI7C,eAAO,MAAM,YAAY;;;oCAUA,MAAM;;;;;;oCAUN,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCrB,CAAC"}
@@ -15,12 +15,17 @@ var p = exports.SR_DIAGNOSTIC_PREFIX;
15
15
  exports.SrDiagnostic = {
16
16
  // ── Init (Q1: did init even happen?) ─────────────────────────────────────
17
17
  init: "".concat(p, ".init"),
18
+ // ── Session lifecycle ────────────────────────────────────────────────────
19
+ // SR session id changed (timeout / explicit setSessionId / custom session id). New tabs and
20
+ // page refreshes do NOT change the session id, so this is distinct from a fresh init.
21
+ sessionChanged: "".concat(p, ".session.changed"),
18
22
  // ── Remote config fetch (joined-config) ──────────────────────────────────
19
23
  configSource: function (source) { return "".concat(p, ".config.source.").concat(source); },
20
24
  configHasTargeting: "".concat(p, ".config.has_targeting"),
21
25
  configNoTargeting: "".concat(p, ".config.no_targeting"),
22
26
  configFetchFailed: "".concat(p, ".config.fetch_failed"),
23
27
  configReceived: "".concat(p, ".config.received"),
28
+ targetingTrigger: "".concat(p, ".targeting.trigger"),
24
29
  // ── Targeting evaluation (evaluateTargetingAndCapture) ────────────────────
25
30
  // Q2 (did eval run?), Q3 (working/failed?), Q4 (missing value?), Q5 (all params).
26
31
  evalTrigger: function (trigger) { return "".concat(p, ".eval.").concat(trigger); },
@@ -49,6 +54,11 @@ exports.SrDiagnostic = {
49
54
  decision: "".concat(p, ".decision"),
50
55
  // ── SPA URL change (setupUrlChangeListener) ──────────────────────────────
51
56
  urlChange: "".concat(p, ".url_change"),
52
- urlChangeEvent: "".concat(p, ".url_change"), // event (with props); same name, logs vs metric
57
+ urlChangeEvent: "".concat(p, ".url_change"),
58
+ // Was the URL-change listener even wired up? (covers "the SDK never saw any navigation"
59
+ // because the listener was never attached — e.g. no targeting config, or no global scope.)
60
+ urlListenerSetup: "".concat(p, ".url_listener.setup"),
61
+ urlListenerAttached: "".concat(p, ".url_listener.attached"),
62
+ urlListenerSkipped: "".concat(p, ".url_listener.skipped"), // counter + event: listener NOT attached (with reason)
53
63
  };
54
64
  //# sourceMappingURL=diagnostics.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/diagnostics.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;GAQG;AACU,QAAA,oBAAoB,GAAG,QAAQ,CAAC;AAE7C,IAAM,CAAC,GAAG,4BAAoB,CAAC;AAElB,QAAA,YAAY,GAAG;IAC1B,4EAA4E;IAC5E,IAAI,EAAE,UAAG,CAAC,UAAO;IAEjB,4EAA4E;IAC5E,YAAY,EAAE,UAAC,MAAc,IAAK,OAAA,UAAG,CAAC,4BAAkB,MAAM,CAAE,EAA9B,CAA8B;IAChE,kBAAkB,EAAE,UAAG,CAAC,0BAAuB;IAC/C,iBAAiB,EAAE,UAAG,CAAC,yBAAsB;IAC7C,iBAAiB,EAAE,UAAG,CAAC,yBAAsB;IAC7C,cAAc,EAAE,UAAG,CAAC,qBAAkB;IAEtC,6EAA6E;IAC7E,kFAAkF;IAClF,WAAW,EAAE,UAAC,OAAe,IAAK,OAAA,UAAG,CAAC,mBAAS,OAAO,CAAE,EAAtB,CAAsB;IACxD,YAAY,EAAE,UAAG,CAAC,oBAAiB;IACnC,iBAAiB,EAAE,UAAG,CAAC,yBAAsB;IAC7C,SAAS,EAAE,UAAG,CAAC,gBAAa;IAC5B,WAAW,EAAE,UAAG,CAAC,mBAAgB;IACjC,SAAS,EAAE,UAAG,CAAC,gBAAa;IAC5B,kBAAkB,EAAE,UAAG,CAAC,0BAAuB;IAC/C,yBAAyB,EAAE,UAAG,CAAC,kCAA+B;IAC9D,cAAc,EAAE,UAAG,CAAC,sBAAmB;IACvC,SAAS,EAAE,UAAG,CAAC,UAAO;IACtB,UAAU,EAAE,UAAG,CAAC,iBAAc;IAE9B,mFAAmF;IACnF,aAAa,EAAE,UAAG,CAAC,oBAAiB;IACpC,gBAAgB,EAAE,UAAG,CAAC,yBAAsB;IAC5C,yBAAyB,EAAE,UAAG,CAAC,kCAA+B;IAE9D,4EAA4E;IAC5E,iBAAiB,EAAE,UAAG,CAAC,yBAAsB;IAC7C,mBAAmB,EAAE,UAAG,CAAC,2BAAwB;IACjD,UAAU,EAAE,UAAG,CAAC,iBAAc;IAC9B,YAAY,EAAE,UAAG,CAAC,oBAAiB;IACnC,cAAc,EAAE,UAAG,CAAC,uBAAoB;IACxC,YAAY,EAAE,UAAG,CAAC,oBAAiB;IACnC,aAAa,EAAE,UAAG,CAAC,qBAAkB;IACrC,QAAQ,EAAE,UAAG,CAAC,cAAW;IAEzB,4EAA4E;IAC5E,SAAS,EAAE,UAAG,CAAC,gBAAa;IAC5B,cAAc,EAAE,UAAG,CAAC,gBAAa,EAAE,gDAAgD;CAC3E,CAAC","sourcesContent":["/**\n * Centralized names for Session Replay diagnostics shipped via the analytics DiagnosticsClient.\n *\n * Every name shares SR_DIAGNOSTIC_PREFIX so they group together in DataDog as\n * `sdk.diagnostics.sr.trc.*`. The diagnostics pipeline prepends `sdk.diagnostics.` and appends\n * `.count` to counters / `.{min,max,avg,count}` to histograms; events are forwarded to DataDog\n * Logs keyed by `event_name`. Keep ALL SR diagnostic names here so the prefix stays unified —\n * changing the namespace is then a one-line edit.\n */\nexport const SR_DIAGNOSTIC_PREFIX = 'sr.trc';\n\nconst p = SR_DIAGNOSTIC_PREFIX;\n\nexport const SrDiagnostic = {\n // ── Init (Q1: did init even happen?) ─────────────────────────────────────\n init: `${p}.init`, // counter + event(with props): fires once per init()\n\n // ── Remote config fetch (joined-config) ──────────────────────────────────\n configSource: (source: string) => `${p}.config.source.${source}`,\n configHasTargeting: `${p}.config.has_targeting`,\n configNoTargeting: `${p}.config.no_targeting`,\n configFetchFailed: `${p}.config.fetch_failed`,\n configReceived: `${p}.config.received`, // event (with props)\n\n // ── Targeting evaluation (evaluateTargetingAndCapture) ────────────────────\n // Q2 (did eval run?), Q3 (working/failed?), Q4 (missing value?), Q5 (all params).\n evalTrigger: (trigger: string) => `${p}.eval.${trigger}`, // init | urlchange | event\n evalNoConfig: `${p}.eval.no_config`,\n evalMissingPrereq: `${p}.eval.missing_prereq`, // Q4: sessionId/config/deviceId missing\n evalMatch: `${p}.eval.match`,\n evalNoMatch: `${p}.eval.no_match`,\n evalError: `${p}.eval.error`, // Q3: targeting engine threw\n evalStaleDiscarded: `${p}.eval.stale_discarded`,\n evalSkippedAlreadyMatched: `${p}.eval.skipped_already_matched`,\n evalDurationMs: `${p}.eval.duration_ms`, // histogram\n evalEvent: `${p}.eval`, // event (with ALL eval params — Q5)\n evalResult: `${p}.eval.result`, // event: raw engine verdict (variantKey) — why match/no-match\n\n // ── Recording execution (getShouldRecord said yes — did rrweb actually start?) ──\n recordStarted: `${p}.record.started`, // event: capture began (carries the srId the replay uploads under)\n recordNoRecordFn: `${p}.record.no_record_fn`, // counter + event: rrweb import returned nothing\n sendSuppressedMinDuration: `${p}.send.suppressed_min_duration`, // counter: events held back by min_session_duration\n\n // ── Record / no-record gate (getShouldRecord) ────────────────────────────\n gateNoIdentifiers: `${p}.gate.no_identifiers`, // Q4: no config/sessionId at gate time\n gateCaptureDisabled: `${p}.gate.capture_disabled`,\n gateOptOut: `${p}.gate.optout`,\n gateTrcMatch: `${p}.gate.trc_match`,\n gateTrcNoMatch: `${p}.gate.trc_no_match`,\n gateSampleIn: `${p}.gate.sample_in`,\n gateSampleOut: `${p}.gate.sample_out`,\n decision: `${p}.decision`, // event (with props)\n\n // ── SPA URL change (setupUrlChangeListener) ──────────────────────────────\n urlChange: `${p}.url_change`,\n urlChangeEvent: `${p}.url_change`, // event (with props); same name, logs vs metric\n} as const;\n"]}
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/diagnostics.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;GAQG;AACU,QAAA,oBAAoB,GAAG,QAAQ,CAAC;AAE7C,IAAM,CAAC,GAAG,4BAAoB,CAAC;AAElB,QAAA,YAAY,GAAG;IAC1B,4EAA4E;IAC5E,IAAI,EAAE,UAAG,CAAC,UAAO;IAEjB,4EAA4E;IAC5E,4FAA4F;IAC5F,sFAAsF;IACtF,cAAc,EAAE,UAAG,CAAC,qBAAkB;IAEtC,4EAA4E;IAC5E,YAAY,EAAE,UAAC,MAAc,IAAK,OAAA,UAAG,CAAC,4BAAkB,MAAM,CAAE,EAA9B,CAA8B;IAChE,kBAAkB,EAAE,UAAG,CAAC,0BAAuB;IAC/C,iBAAiB,EAAE,UAAG,CAAC,yBAAsB;IAC7C,iBAAiB,EAAE,UAAG,CAAC,yBAAsB;IAC7C,cAAc,EAAE,UAAG,CAAC,qBAAkB;IAEtC,gBAAgB,EAAE,UAAG,CAAC,uBAAoB;IAE1C,6EAA6E;IAC7E,kFAAkF;IAClF,WAAW,EAAE,UAAC,OAAe,IAAK,OAAA,UAAG,CAAC,mBAAS,OAAO,CAAE,EAAtB,CAAsB;IACxD,YAAY,EAAE,UAAG,CAAC,oBAAiB;IACnC,iBAAiB,EAAE,UAAG,CAAC,yBAAsB;IAC7C,SAAS,EAAE,UAAG,CAAC,gBAAa;IAC5B,WAAW,EAAE,UAAG,CAAC,mBAAgB;IACjC,SAAS,EAAE,UAAG,CAAC,gBAAa;IAC5B,kBAAkB,EAAE,UAAG,CAAC,0BAAuB;IAC/C,yBAAyB,EAAE,UAAG,CAAC,kCAA+B;IAC9D,cAAc,EAAE,UAAG,CAAC,sBAAmB;IACvC,SAAS,EAAE,UAAG,CAAC,UAAO;IACtB,UAAU,EAAE,UAAG,CAAC,iBAAc;IAE9B,mFAAmF;IACnF,aAAa,EAAE,UAAG,CAAC,oBAAiB;IACpC,gBAAgB,EAAE,UAAG,CAAC,yBAAsB;IAC5C,yBAAyB,EAAE,UAAG,CAAC,kCAA+B;IAE9D,4EAA4E;IAC5E,iBAAiB,EAAE,UAAG,CAAC,yBAAsB;IAC7C,mBAAmB,EAAE,UAAG,CAAC,2BAAwB;IACjD,UAAU,EAAE,UAAG,CAAC,iBAAc;IAC9B,YAAY,EAAE,UAAG,CAAC,oBAAiB;IACnC,cAAc,EAAE,UAAG,CAAC,uBAAoB;IACxC,YAAY,EAAE,UAAG,CAAC,oBAAiB;IACnC,aAAa,EAAE,UAAG,CAAC,qBAAkB;IACrC,QAAQ,EAAE,UAAG,CAAC,cAAW;IAEzB,4EAA4E;IAC5E,SAAS,EAAE,UAAG,CAAC,gBAAa;IAC5B,cAAc,EAAE,UAAG,CAAC,gBAAa;IACjC,wFAAwF;IACxF,2FAA2F;IAC3F,gBAAgB,EAAE,UAAG,CAAC,wBAAqB;IAC3C,mBAAmB,EAAE,UAAG,CAAC,2BAAwB;IACjD,kBAAkB,EAAE,UAAG,CAAC,0BAAuB,EAAE,uDAAuD;CAChG,CAAC","sourcesContent":["/**\n * Centralized names for Session Replay diagnostics shipped via the analytics DiagnosticsClient.\n *\n * Every name shares SR_DIAGNOSTIC_PREFIX so they group together in DataDog as\n * `sdk.diagnostics.sr.trc.*`. The diagnostics pipeline prepends `sdk.diagnostics.` and appends\n * `.count` to counters / `.{min,max,avg,count}` to histograms; events are forwarded to DataDog\n * Logs keyed by `event_name`. Keep ALL SR diagnostic names here so the prefix stays unified —\n * changing the namespace is then a one-line edit.\n */\nexport const SR_DIAGNOSTIC_PREFIX = 'sr.trc';\n\nconst p = SR_DIAGNOSTIC_PREFIX;\n\nexport const SrDiagnostic = {\n // ── Init (Q1: did init even happen?) ─────────────────────────────────────\n init: `${p}.init`, // counter + event(with props): fires once per init()\n\n // ── Session lifecycle ────────────────────────────────────────────────────\n // SR session id changed (timeout / explicit setSessionId / custom session id). New tabs and\n // page refreshes do NOT change the session id, so this is distinct from a fresh init.\n sessionChanged: `${p}.session.changed`, // event (with from/to)\n\n // ── Remote config fetch (joined-config) ──────────────────────────────────\n configSource: (source: string) => `${p}.config.source.${source}`,\n configHasTargeting: `${p}.config.has_targeting`,\n configNoTargeting: `${p}.config.no_targeting`,\n configFetchFailed: `${p}.config.fetch_failed`,\n configReceived: `${p}.config.received`, // event (with props)\n\n targetingTrigger: `${p}.targeting.trigger`, // event: targeting evaluation triggered\n\n // ── Targeting evaluation (evaluateTargetingAndCapture) ────────────────────\n // Q2 (did eval run?), Q3 (working/failed?), Q4 (missing value?), Q5 (all params).\n evalTrigger: (trigger: string) => `${p}.eval.${trigger}`, // init | urlchange | event\n evalNoConfig: `${p}.eval.no_config`,\n evalMissingPrereq: `${p}.eval.missing_prereq`, // Q4: sessionId/config/deviceId missing\n evalMatch: `${p}.eval.match`,\n evalNoMatch: `${p}.eval.no_match`,\n evalError: `${p}.eval.error`, // Q3: targeting engine threw\n evalStaleDiscarded: `${p}.eval.stale_discarded`,\n evalSkippedAlreadyMatched: `${p}.eval.skipped_already_matched`,\n evalDurationMs: `${p}.eval.duration_ms`, // histogram\n evalEvent: `${p}.eval`, // event (with ALL eval params — Q5)\n evalResult: `${p}.eval.result`, // event: raw engine verdict (variantKey) — why match/no-match\n\n // ── Recording execution (getShouldRecord said yes — did rrweb actually start?) ──\n recordStarted: `${p}.record.started`, // event: capture began (carries the srId the replay uploads under)\n recordNoRecordFn: `${p}.record.no_record_fn`, // counter + event: rrweb import returned nothing\n sendSuppressedMinDuration: `${p}.send.suppressed_min_duration`, // counter: events held back by min_session_duration\n\n // ── Record / no-record gate (getShouldRecord) ────────────────────────────\n gateNoIdentifiers: `${p}.gate.no_identifiers`, // Q4: no config/sessionId at gate time\n gateCaptureDisabled: `${p}.gate.capture_disabled`,\n gateOptOut: `${p}.gate.optout`,\n gateTrcMatch: `${p}.gate.trc_match`,\n gateTrcNoMatch: `${p}.gate.trc_no_match`,\n gateSampleIn: `${p}.gate.sample_in`,\n gateSampleOut: `${p}.gate.sample_out`,\n decision: `${p}.decision`, // event (with props)\n\n // ── SPA URL change (setupUrlChangeListener) ──────────────────────────────\n urlChange: `${p}.url_change`,\n urlChangeEvent: `${p}.url_change`, // event (with props); same name, logs vs metric\n // Was the URL-change listener even wired up? (covers \"the SDK never saw any navigation\"\n // because the listener was never attached — e.g. no targeting config, or no global scope.)\n urlListenerSetup: `${p}.url_listener.setup`, // event: the needsUrlTracking decision + its inputs\n urlListenerAttached: `${p}.url_listener.attached`, // event: subscribeToUrlChanges succeeded (with polling opts)\n urlListenerSkipped: `${p}.url_listener.skipped`, // counter + event: listener NOT attached (with reason)\n} as const;\n"]}
@@ -35,6 +35,12 @@ export interface SubscribeToUrlChangesOptions {
35
35
  enablePolling?: boolean;
36
36
  /** Polling interval in ms when enablePolling is true (default: 1000) */
37
37
  pollingInterval?: number;
38
+ /**
39
+ * Optional debug logger, called once per poll tick (only on the polling path). Lets the SDK
40
+ * confirm in the browser console that the interval is actually firing — useful when verifying
41
+ * that enableUrlChangePolling took effect for an SPA that bypasses the History API.
42
+ */
43
+ log?: (message: string) => void;
38
44
  }
39
45
  /**
40
46
  * Single helper for URL change detection. Supports:
@@ -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,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
+ {"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;IACzB;;;;OAIG;IACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;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,CAuGZ;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,YAAY,CAAC,wBAAwB,CAAC,CAqFxC;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,wCAA4B,CAAC"}
@@ -28,12 +28,14 @@ function subscribeToUrlChanges(globalScope, onUrlChange, options) {
28
28
  return;
29
29
  };
30
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;
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, log = options.log;
32
32
  if (enablePolling) {
33
33
  var getHref_1 = function () { var _a; return (_a = globalScope.location.href) !== null && _a !== void 0 ? _a : ''; };
34
34
  var lastHref_1 = getHref_1();
35
35
  var id_1 = globalScope.setInterval(function () {
36
36
  var href = getHref_1();
37
+ // Logged every tick (not just on change) so we can confirm the polling loop is alive.
38
+ log === null || log === void 0 ? void 0 : log("URL polling tick (href=".concat(href, ", changed=").concat(String(href !== lastHref_1), ")."));
37
39
  if (href === lastHref_1) {
38
40
  return;
39
41
  }
@@ -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":"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"}
1
+ {"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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"}