@amplitude/session-replay-browser 1.40.0 → 1.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -0
- package/lib/cjs/config/local-config.d.ts +2 -1
- package/lib/cjs/config/local-config.d.ts.map +1 -1
- package/lib/cjs/config/local-config.js +3 -0
- package/lib/cjs/config/local-config.js.map +1 -1
- package/lib/cjs/config/types.d.ts +26 -0
- package/lib/cjs/config/types.d.ts.map +1 -1
- package/lib/cjs/config/types.js.map +1 -1
- package/lib/cjs/constants.d.ts +2 -0
- package/lib/cjs/constants.d.ts.map +1 -1
- package/lib/cjs/constants.js +3 -1
- package/lib/cjs/constants.js.map +1 -1
- package/lib/cjs/cross-origin-iframes.d.ts +28 -0
- package/lib/cjs/cross-origin-iframes.d.ts.map +1 -0
- package/lib/cjs/cross-origin-iframes.js +175 -0
- package/lib/cjs/cross-origin-iframes.js.map +1 -0
- package/lib/cjs/events/events-idb-store.d.ts +25 -4
- package/lib/cjs/events/events-idb-store.d.ts.map +1 -1
- package/lib/cjs/events/events-idb-store.js +257 -61
- package/lib/cjs/events/events-idb-store.js.map +1 -1
- package/lib/cjs/session-replay.d.ts +4 -0
- package/lib/cjs/session-replay.d.ts.map +1 -1
- package/lib/cjs/session-replay.js +118 -51
- package/lib/cjs/session-replay.js.map +1 -1
- package/lib/cjs/track-destination.d.ts.map +1 -1
- package/lib/cjs/track-destination.js +5 -1
- package/lib/cjs/track-destination.js.map +1 -1
- package/lib/cjs/utils/rrweb.d.ts +1 -0
- package/lib/cjs/utils/rrweb.d.ts.map +1 -1
- package/lib/cjs/utils/rrweb.js.map +1 -1
- package/lib/cjs/version.d.ts +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/cjs/version.js.map +1 -1
- package/lib/cjs/worker/index.js +1 -1
- package/lib/esm/config/local-config.d.ts +2 -1
- package/lib/esm/config/local-config.d.ts.map +1 -1
- package/lib/esm/config/local-config.js +3 -0
- package/lib/esm/config/local-config.js.map +1 -1
- package/lib/esm/config/types.d.ts +26 -0
- package/lib/esm/config/types.d.ts.map +1 -1
- package/lib/esm/config/types.js.map +1 -1
- package/lib/esm/constants.d.ts +2 -0
- package/lib/esm/constants.d.ts.map +1 -1
- package/lib/esm/constants.js +2 -0
- package/lib/esm/constants.js.map +1 -1
- package/lib/esm/cross-origin-iframes.d.ts +28 -0
- package/lib/esm/cross-origin-iframes.d.ts.map +1 -0
- package/lib/esm/cross-origin-iframes.js +170 -0
- package/lib/esm/cross-origin-iframes.js.map +1 -0
- package/lib/esm/events/events-idb-store.d.ts +25 -4
- package/lib/esm/events/events-idb-store.d.ts.map +1 -1
- package/lib/esm/events/events-idb-store.js +255 -61
- package/lib/esm/events/events-idb-store.js.map +1 -1
- package/lib/esm/session-replay.d.ts +4 -0
- package/lib/esm/session-replay.d.ts.map +1 -1
- package/lib/esm/session-replay.js +118 -51
- package/lib/esm/session-replay.js.map +1 -1
- package/lib/esm/track-destination.d.ts.map +1 -1
- package/lib/esm/track-destination.js +6 -2
- package/lib/esm/track-destination.js.map +1 -1
- package/lib/esm/utils/rrweb.d.ts +1 -0
- package/lib/esm/utils/rrweb.d.ts.map +1 -1
- package/lib/esm/utils/rrweb.js.map +1 -1
- package/lib/esm/version.d.ts +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/esm/version.js.map +1 -1
- package/lib/esm/worker/index.js +1 -1
- package/lib/scripts/index-min.js +1 -1
- package/lib/scripts/index-min.js.gz +0 -0
- package/lib/scripts/index-min.js.map +1 -1
- package/lib/scripts/session-replay-browser-min.js +1 -1
- package/lib/scripts/session-replay-browser-min.js.gz +0 -0
- package/lib/scripts/session-replay-browser-min.js.map +1 -1
- package/lib/scripts/targeting-min.js +1 -1
- package/lib/scripts/targeting-min.js.gz +0 -0
- package/lib/scripts/targeting-min.js.map +1 -1
- package/lib/scripts/worker-min.js +1 -1
- package/lib/scripts/worker-min.js.gz +0 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -104,6 +104,91 @@ sessionReplay.shutdown()
|
|
|
104
104
|
|`performanceConfig.enabled`|`boolean`|No|`true`|If enabled, event compression will be deferred to occur during the browser's idle periods.|
|
|
105
105
|
|`performanceConfig.timeout`|`number`|No|`undefined`|Optional timeout in milliseconds for the `requestIdleCallback` API. If specified, this value will be used to set a maximum time for the browser to wait before executing the deferred compression task, even if the browser is not idle.|
|
|
106
106
|
|`useWebWorker`|`boolean`|No|`false`|If true, the SDK will compress replay events using a web worker. This offloads compression to a separate thread, improving performance on the main thread.|
|
|
107
|
+
|`crossOriginIframes.enabled`|`boolean`|No|`false`|Enables cross-origin iframe recording. Must be set to `true` on both the parent page and each child iframe page. See [Cross-Origin Iframe Recording](#cross-origin-iframe-recording).|
|
|
108
|
+
|`crossOriginIframes.coordinateChildren`|`boolean`|No|`true`|When `true`, the parent SDK automatically sends start/stop signals to child iframes via `postMessage`. Set to `false` to manage child recording lifecycle yourself.|
|
|
109
|
+
|
|
110
|
+
## Cross-Origin Iframe Recording
|
|
111
|
+
|
|
112
|
+
The SDK can capture events inside cross-origin `<iframe>` elements and merge them into the parent page's session replay.
|
|
113
|
+
|
|
114
|
+
### How it works
|
|
115
|
+
|
|
116
|
+
Both the parent page and each iframe page must load the SDK with `crossOriginIframes.enabled: true`. The parent SDK coordinates recording across all child iframes using `postMessage` signals and rrweb's built-in cross-origin relay.
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Parent page (yoursite.com)
|
|
120
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
121
|
+
│ sessionReplay.init(API_KEY, { crossOriginIframes: { enabled: true } })
|
|
122
|
+
│ │
|
|
123
|
+
│ CrossOriginIframeCoordinator │
|
|
124
|
+
│ ┌────────────────────────┐ │
|
|
125
|
+
│ │ MutationObserver │ postMessage("start") ──────────────► │
|
|
126
|
+
│ │ watches for <iframe> │ │
|
|
127
|
+
│ │ additions/removals │ postMessage("stop") ──────────────► │
|
|
128
|
+
│ └────────────────────────┘ │
|
|
129
|
+
│ │
|
|
130
|
+
│ rrweb (recordCrossOriginIframes: true) │
|
|
131
|
+
│ ◄─────────────── child rrweb events relayed via postMessage ──── │
|
|
132
|
+
│ Merges child events into parent snapshot stream │
|
|
133
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
134
|
+
│ postMessage("start" / "stop")
|
|
135
|
+
▼
|
|
136
|
+
Child iframe page (payments.example.com)
|
|
137
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
138
|
+
│ sessionReplay.init(API_KEY, { crossOriginIframes: { enabled: true } })
|
|
139
|
+
│ │
|
|
140
|
+
│ isInIframe() === true → child mode │
|
|
141
|
+
│ listenForParentSignals() │
|
|
142
|
+
│ ┌──────────────────────────────────┐ │
|
|
143
|
+
│ │ Waits for "start" signal │ │
|
|
144
|
+
│ │ → initialises rrweb recording │ │
|
|
145
|
+
│ │ On "stop" signal │ │
|
|
146
|
+
│ │ → stops rrweb, flushes events │ │
|
|
147
|
+
│ └──────────────────────────────────┘ │
|
|
148
|
+
│ │
|
|
149
|
+
│ rrweb events ──► postMessage relay ──► parent rrweb │
|
|
150
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Child events are serialized by the child page's own rrweb instance and relayed to the parent via `postMessage`. The parent rrweb stream stores them inline, so the replay viewer can reconstruct both frames from a single session.
|
|
154
|
+
|
|
155
|
+
### Setup
|
|
156
|
+
|
|
157
|
+
**Parent page:**
|
|
158
|
+
```typescript
|
|
159
|
+
sessionReplay.init(API_KEY, {
|
|
160
|
+
deviceId: DEVICE_ID,
|
|
161
|
+
sessionId: SESSION_ID,
|
|
162
|
+
crossOriginIframes: { enabled: true },
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Child iframe page** (must also load the SDK):
|
|
167
|
+
```typescript
|
|
168
|
+
sessionReplay.init(API_KEY, {
|
|
169
|
+
deviceId: DEVICE_ID,
|
|
170
|
+
sessionId: SESSION_ID,
|
|
171
|
+
crossOriginIframes: { enabled: true },
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The child SDK detects it is running inside an iframe and automatically enters child mode — it will wait for a start signal from the parent rather than begin recording immediately.
|
|
176
|
+
|
|
177
|
+
### Options
|
|
178
|
+
|
|
179
|
+
| Name | Type | Required | Default | Description |
|
|
180
|
+
|-|-|-|-|-|
|
|
181
|
+
| `crossOriginIframes.enabled` | `boolean` | Yes | — | Enables cross-origin iframe recording on both parent and child pages. |
|
|
182
|
+
| `crossOriginIframes.coordinateChildren` | `boolean` | No | `true` | When `true`, the parent SDK sends start/stop signals to child iframes and keeps their recording lifecycle in sync. Set to `false` to manage child recording yourself. |
|
|
183
|
+
|
|
184
|
+
### Privacy
|
|
185
|
+
|
|
186
|
+
The child page's rrweb instance performs its own DOM serialisation. The parent's privacy config (mask levels, block selectors, etc.) does **not** automatically apply inside the iframe — configure privacy settings independently on the child page.
|
|
187
|
+
|
|
188
|
+
### Limitations
|
|
189
|
+
|
|
190
|
+
- **Third-party iframes** (e.g. Stripe, Google Maps) cannot be captured. Both the parent and child pages must load the SDK with `crossOriginIframes.enabled: true`.
|
|
191
|
+
- `coordinateChildren: false` opts out of the coordinator; in this mode the child SDK will not start recording until you manage its lifecycle directly.
|
|
107
192
|
|
|
108
193
|
## Network Request Capture
|
|
109
194
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Config, Logger, FetchTransport, LogLevel } from '@amplitude/analytics-core';
|
|
2
2
|
import { SessionReplayOptions, StoreType } from '../typings/session-replay';
|
|
3
|
-
import { SessionReplayLocalConfig as ISessionReplayLocalConfig, InteractionConfig, PrivacyConfig, SessionReplayPerformanceConfig, SessionReplayVersion } from './types';
|
|
3
|
+
import { SessionReplayLocalConfig as ISessionReplayLocalConfig, CrossOriginIframesConfig, InteractionConfig, PrivacyConfig, SessionReplayPerformanceConfig, SessionReplayVersion } from './types';
|
|
4
4
|
export declare const getDefaultConfig: () => {
|
|
5
5
|
flushMaxRetries: number;
|
|
6
6
|
logLevel: LogLevel;
|
|
@@ -29,6 +29,7 @@ export declare class SessionReplayLocalConfig extends Config implements ISession
|
|
|
29
29
|
urlChangePollingInterval?: number;
|
|
30
30
|
captureDocumentTitle?: boolean;
|
|
31
31
|
captureAdoptedStyleSheets?: boolean;
|
|
32
|
+
crossOriginIframes?: CrossOriginIframesConfig;
|
|
32
33
|
constructor(apiKey: string, options: SessionReplayOptions);
|
|
33
34
|
}
|
|
34
35
|
//# sourceMappingURL=local-config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAOrF,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EACL,wBAAwB,IAAI,yBAAyB,EACrD,iBAAiB,EACjB,aAAa,EACb,8BAA8B,EAC9B,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAIjB,eAAO,MAAM,gBAAgB;;;;;CAK3B,CAAC;AAEH,qBAAa,wBAAyB,SAAQ,MAAO,YAAW,yBAAyB;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,SAAS,CAAC;IACrB,iBAAiB,CAAC,EAAE,8BAA8B,CAAC;IACnD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,eAAe,CAAC,EAAE;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yBAAyB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAOrF,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EACL,wBAAwB,IAAI,yBAAyB,EACrD,wBAAwB,EACxB,iBAAiB,EACjB,aAAa,EACb,8BAA8B,EAC9B,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAIjB,eAAO,MAAM,gBAAgB;;;;;CAK3B,CAAC;AAEH,qBAAa,wBAAyB,SAAQ,MAAO,YAAW,yBAAyB;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,SAAS,CAAC;IACrB,iBAAiB,CAAC,EAAE,8BAA8B,CAAC;IACnD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,eAAe,CAAC,EAAE;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;gBAElC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB;CA0D1D"}
|
|
@@ -64,6 +64,9 @@ var SessionReplayLocalConfig = /** @class */ (function (_super) {
|
|
|
64
64
|
_this.omitElementTags = options.omitElementTags;
|
|
65
65
|
}
|
|
66
66
|
_this.captureAdoptedStyleSheets = (_g = options.captureAdoptedStyleSheets) !== null && _g !== void 0 ? _g : true;
|
|
67
|
+
if (options.crossOriginIframes) {
|
|
68
|
+
_this.crossOriginIframes = options.crossOriginIframes;
|
|
69
|
+
}
|
|
67
70
|
return _this;
|
|
68
71
|
}
|
|
69
72
|
return SessionReplayLocalConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":";;;;AAAA,4DAAqF;AACrF,0CAKsB;
|
|
1
|
+
{"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../../src/config/local-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { Config, Logger, FetchTransport, LogLevel } from '@amplitude/analytics-core';\nimport {\n DEFAULT_PERFORMANCE_CONFIG,\n DEFAULT_SAMPLE_RATE,\n DEFAULT_SERVER_ZONE,\n DEFAULT_URL_CHANGE_POLLING_INTERVAL,\n} from '../constants';\nimport { SessionReplayOptions, StoreType } from '../typings/session-replay';\nimport {\n SessionReplayLocalConfig as ISessionReplayLocalConfig,\n CrossOriginIframesConfig,\n InteractionConfig,\n PrivacyConfig,\n SessionReplayPerformanceConfig,\n SessionReplayVersion,\n} from './types';\nimport { SafeLoggerProvider } from '../logger';\nimport { validateUGCFilterRules } from '../helpers';\n\nexport const getDefaultConfig = () => ({\n flushMaxRetries: 2,\n logLevel: LogLevel.Warn,\n loggerProvider: new Logger(),\n transportProvider: new FetchTransport(),\n});\n\nexport class SessionReplayLocalConfig extends Config implements ISessionReplayLocalConfig {\n apiKey: string;\n sampleRate: number;\n privacyConfig?: PrivacyConfig;\n interactionConfig?: InteractionConfig;\n debugMode?: boolean;\n configServerUrl?: string;\n trackServerUrl?: string;\n shouldInlineStylesheet?: boolean;\n version?: SessionReplayVersion;\n storeType: StoreType;\n performanceConfig?: SessionReplayPerformanceConfig;\n useWebWorker?: boolean;\n applyBackgroundColorToBlockedElements?: boolean;\n omitElementTags?: {\n script?: boolean;\n comment?: boolean;\n };\n enableUrlChangePolling?: boolean;\n urlChangePollingInterval?: number;\n captureDocumentTitle?: boolean;\n captureAdoptedStyleSheets?: boolean;\n crossOriginIframes?: CrossOriginIframesConfig;\n\n constructor(apiKey: string, options: SessionReplayOptions) {\n const defaultConfig = getDefaultConfig();\n super({\n transportProvider: defaultConfig.transportProvider,\n loggerProvider: new SafeLoggerProvider(options.loggerProvider || defaultConfig.loggerProvider),\n ...options,\n apiKey,\n });\n this.flushMaxRetries =\n options.flushMaxRetries !== undefined && options.flushMaxRetries <= defaultConfig.flushMaxRetries\n ? options.flushMaxRetries\n : defaultConfig.flushMaxRetries;\n\n this.apiKey = apiKey;\n this.sampleRate = options.sampleRate || DEFAULT_SAMPLE_RATE;\n this.serverZone = options.serverZone || DEFAULT_SERVER_ZONE;\n this.configServerUrl = options.configServerUrl;\n this.trackServerUrl = options.trackServerUrl;\n this.shouldInlineStylesheet = options.shouldInlineStylesheet;\n this.version = options.version;\n this.performanceConfig = options.performanceConfig || DEFAULT_PERFORMANCE_CONFIG;\n this.storeType = options.storeType ?? 'idb';\n this.applyBackgroundColorToBlockedElements = options.applyBackgroundColorToBlockedElements ?? false;\n this.enableUrlChangePolling = options.enableUrlChangePolling ?? false;\n this.urlChangePollingInterval = options.urlChangePollingInterval ?? DEFAULT_URL_CHANGE_POLLING_INTERVAL;\n this.captureDocumentTitle = options.captureDocumentTitle ?? false;\n\n if (options.privacyConfig) {\n this.privacyConfig = options.privacyConfig;\n }\n if (options.interactionConfig) {\n this.interactionConfig = options.interactionConfig;\n\n // validate ugcFilterRules, throw error if invalid - throw error at the beginning of the config\n if (this.interactionConfig.ugcFilterRules) {\n validateUGCFilterRules(this.interactionConfig.ugcFilterRules);\n }\n }\n if (options.debugMode) {\n this.debugMode = options.debugMode;\n }\n // Support both new useWebWorker and legacy experimental.useWebWorker for backwards compatibility\n if (options.useWebWorker !== undefined) {\n this.useWebWorker = options.useWebWorker;\n } else {\n const legacyOptions = options as { experimental?: { useWebWorker?: boolean } };\n if (legacyOptions.experimental?.useWebWorker !== undefined) {\n this.useWebWorker = legacyOptions.experimental.useWebWorker;\n }\n }\n if (options.omitElementTags) {\n this.omitElementTags = options.omitElementTags;\n }\n this.captureAdoptedStyleSheets = options.captureAdoptedStyleSheets ?? true;\n if (options.crossOriginIframes) {\n this.crossOriginIframes = options.crossOriginIframes;\n }\n }\n}\n"]}
|
|
@@ -79,6 +79,24 @@ export type UGCFilterRule = {
|
|
|
79
79
|
*/
|
|
80
80
|
replacement: string;
|
|
81
81
|
};
|
|
82
|
+
export interface CrossOriginIframesConfig {
|
|
83
|
+
enabled: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* When true (default), the parent SDK sends start/stop signals to child iframes via
|
|
86
|
+
* postMessage, keeping their recording lifecycle in sync with the parent.
|
|
87
|
+
*
|
|
88
|
+
* **Privacy note:** The child page's rrweb instance performs its own DOM serialization,
|
|
89
|
+
* so the parent's privacy config (mask levels, block selectors) does NOT automatically
|
|
90
|
+
* apply inside the iframe. Privacy settings must be configured independently on the child page.
|
|
91
|
+
*
|
|
92
|
+
* **Third-party iframes:** Cannot capture iframes you don't control (e.g. Stripe, Google
|
|
93
|
+
* Maps) — both parent and child pages must load the SDK with `crossOriginIframes.enabled: true`.
|
|
94
|
+
*
|
|
95
|
+
* Set to `false` to skip coordination and manage the child recording lifecycle yourself.
|
|
96
|
+
* @defaultValue true
|
|
97
|
+
*/
|
|
98
|
+
coordinateChildren?: boolean;
|
|
99
|
+
}
|
|
82
100
|
export interface SessionReplayLocalConfig extends IConfig {
|
|
83
101
|
apiKey: string;
|
|
84
102
|
loggerProvider: ILogger;
|
|
@@ -207,6 +225,14 @@ export interface SessionReplayLocalConfig extends IConfig {
|
|
|
207
225
|
* @defaultValue true
|
|
208
226
|
*/
|
|
209
227
|
captureAdoptedStyleSheets?: boolean;
|
|
228
|
+
/**
|
|
229
|
+
* Enables recording of cross-origin iframes. Both the parent page and each child iframe
|
|
230
|
+
* page must load the Amplitude Session Replay SDK with this option enabled.
|
|
231
|
+
*
|
|
232
|
+
* When enabled, rrweb uses `postMessage` to relay child DOM events to the parent, which
|
|
233
|
+
* merges them into a single unified event stream.
|
|
234
|
+
*/
|
|
235
|
+
crossOriginIframes?: CrossOriginIframesConfig;
|
|
210
236
|
}
|
|
211
237
|
export interface SessionReplayJoinedConfig extends SessionReplayLocalConfig {
|
|
212
238
|
captureEnabled?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":";;;AAuDa,QAAA,kBAAkB,GAAG,QAAQ,CAAC","sourcesContent":["import { IConfig, LogLevel, ILogger } from '@amplitude/analytics-core';\nimport { StoreType, ConsoleLogLevel } from '../typings/session-replay';\nimport { TargetingFlag } from '@amplitude/targeting';\n\nexport interface SamplingConfig {\n sample_rate: number;\n capture_enabled: boolean;\n}\n\nexport interface InteractionConfig {\n trackEveryNms?: number;\n enabled: boolean; // defaults to false\n batch: boolean; // defaults to false\n /**\n * UGC filter rules.\n */\n ugcFilterRules?: UGCFilterRule[];\n}\n\nexport interface LoggingConfig {\n console: {\n enabled: boolean;\n levels: ConsoleLogLevel[];\n };\n network?: {\n enabled: boolean;\n body?: {\n request?: boolean;\n response?: boolean;\n maxBodySizeBytes?: number;\n };\n };\n}\n\nexport type TargetingConfig = TargetingFlag;\n\nexport type SessionReplayRemoteConfig = {\n sr_sampling_config?: SamplingConfig;\n sr_privacy_config?: PrivacyConfig;\n sr_interaction_config?: InteractionConfig;\n sr_logging_config?: LoggingConfig;\n sr_targeting_config?: TargetingConfig;\n};\n\nexport interface SessionReplayRemoteConfigAPIResponse {\n configs: {\n sessionReplay: SessionReplayRemoteConfig;\n };\n}\n\nexport type MaskLevel =\n | 'light' // only mask a subset of inputs that's deemed sensitive - password, credit card, telephone #, email. These are information we never want to capture.\n | 'medium' // mask all inputs\n | 'conservative'; // mask all inputs and all texts\n\nexport const DEFAULT_MASK_LEVEL = 'medium';\n\n// err on the side of excluding more\nexport type PrivacyConfig = {\n blockSelector?: string | string[]; // exclude in the UI\n defaultMaskLevel?: MaskLevel;\n maskSelector?: string[];\n unmaskSelector?: string[];\n maskAttributes?: string[]; // HTML attribute names to mask (e.g. [\"placeholder\", \"aria-label\"])\n /**\n * Per-URL overrides for `defaultMaskLevel`. Each entry contains a glob pattern (`match`)\n * and a `maskLevel` to apply when the current page URL matches that pattern.\n * Rules are evaluated in order; the first match wins. Remote rules take precedence\n * over local rules (remote entries are prepended before local entries).\n *\n * @example\n * urlMaskLevels: [\n * { match: 'https://example.com/checkout/*', maskLevel: 'conservative' },\n * { match: 'https://example.com/public/*', maskLevel: 'light' },\n * ]\n */\n urlMaskLevels?: Array<{ match: string; maskLevel: MaskLevel }>;\n};\n\n/**\n * UGC filter rule.\n */\nexport type UGCFilterRule = {\n /**\n * The selector of the UGC element.\n */\n selector: string;\n /**\n * The replacement text for the UGC element.\n */\n replacement: string;\n};\n\nexport interface SessionReplayLocalConfig extends IConfig {\n apiKey: string;\n loggerProvider: ILogger;\n /**\n * LogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug.\n * Sets the log level.\n *\n * @defaultValue LogLevel.Warn\n */\n logLevel: LogLevel;\n /**\n * The maximum number of retries allowed for sending replay events.\n * Once this limit is reached, failed events will no longer be sent.\n *\n * @defaultValue 2\n */\n flushMaxRetries: number;\n /**\n * Use this option to control how many sessions to select for replay collection.\n * The number should be a decimal between 0 and 1, for example 0.4, representing\n * the fraction of sessions to have randomly selected for replay collection.\n * Over a large number of sessions, 0.4 would select 40% of those sessions.\n * Sample rates as small as six decimal places (0.000001) are supported.\n *\n * @defaultValue 0\n */\n sampleRate: number;\n privacyConfig?: PrivacyConfig;\n /**\n * Adds additional debug event property to help debug instrumentation issues\n * (such as mismatching apps). Only recommended for debugging initial setup,\n * and not recommended for production.\n */\n debugMode?: boolean;\n /**\n * Specifies the endpoint URL to fetch remote configuration.\n * If provided, it overrides the default server zone configuration.\n */\n configServerUrl?: string;\n /**\n * Specifies the endpoint URL for sending session replay data.\n * If provided, it overrides the default server zone configuration.\n */\n trackServerUrl?: string;\n /**\n * If stylesheets are inlined, the contents of the stylesheet will be stored.\n * During replay, the stored stylesheet will be used instead of attempting to fetch it remotely.\n * This prevents replays from appearing broken due to missing stylesheets.\n * Note: Inlining stylesheets may not work in all cases.\n */\n shouldInlineStylesheet?: boolean;\n version?: SessionReplayVersion;\n /**\n * Performance configuration config. If enabled, we will defer compression\n * to be done during the browser's idle periods.\n */\n performanceConfig?: SessionReplayPerformanceConfig;\n /**\n * Specifies how replay events should be stored. `idb` uses IndexedDB to persist replay events\n * when all events cannot be sent during capture. `memory` stores replay events only in memory,\n * meaning events are lost when the page is closed. If IndexedDB is unavailable, the system falls back to `memory`.\n */\n storeType: StoreType;\n\n /**\n * If true, the SDK will compress replay events using a web worker.\n * This offloads compression to a separate thread, improving performance on the main thread.\n *\n * @defaultValue false\n */\n useWebWorker?: boolean;\n\n userProperties?: { [key: string]: any };\n\n /**\n * Remove certain parts of the DOM from being captured. These are typically ignored when blocking by selectors.\n */\n omitElementTags?: {\n /**\n * If true, removes script tags from the DOM, but not noscript tags.\n */\n script?: boolean;\n /**\n * If true, removes comment tags from the DOM.\n */\n comment?: boolean;\n };\n\n /**\n * If true, applies a background color to blocked elements in the replay.\n * This helps visualize which elements are blocked from being captured.\n */\n applyBackgroundColorToBlockedElements?: boolean;\n /**\n * Enables URL change polling as a fallback for SPA route tracking.\n * When enabled, the SDK will periodically check for URL changes every second\n * in addition to patching the History API. This is useful for edge cases where\n * route changes might bypass the standard History API methods.\n *\n * @defaultValue false\n */\n enableUrlChangePolling?: boolean;\n /**\n * Specifies the interval in milliseconds for URL change polling when enableUrlChangePolling is true.\n * The SDK will check for URL changes at this interval as a fallback for SPA route tracking.\n *\n * @defaultValue 1000\n */\n urlChangePollingInterval?: number;\n /**\n * Whether to capture document title in URL change events.\n * When disabled, the title field will be empty in URL change events.\n *\n * @defaultValue false\n */\n captureDocumentTitle?: boolean;\n interactionConfig?: InteractionConfig;\n /**\n * When true (default), the CSS rules of any `adoptedStyleSheets` on shadow roots and\n * the document are serialized **inline** within the full snapshot. This makes the snapshot\n * self-contained so that shadow DOM styles are replayed correctly even if subsequent\n * incremental `AdoptedStyleSheet` events are dropped in transit.\n *\n * Set to `false` to revert to the legacy behavior where adopted stylesheet rules are\n * emitted as separate incremental events (which may be lost if delivery is unreliable).\n * Only consider opting out if snapshot payload size is a critical concern.\n *\n * @defaultValue true\n */\n captureAdoptedStyleSheets?: boolean;\n}\n\nexport interface SessionReplayJoinedConfig extends SessionReplayLocalConfig {\n captureEnabled?: boolean;\n interactionConfig?: InteractionConfig;\n loggingConfig?: LoggingConfig;\n targetingConfig?: TargetingConfig;\n}\n\nexport interface SessionReplayConfigs {\n localConfig: SessionReplayLocalConfig;\n joinedConfig: SessionReplayJoinedConfig;\n remoteConfig: SessionReplayRemoteConfig | undefined;\n}\nexport interface SessionReplayJoinedConfigGenerator {\n generateJoinedConfig: () => Promise<SessionReplayConfigs>;\n}\n\nexport interface SessionReplayMetadata {\n remoteConfig: SessionReplayRemoteConfig | undefined;\n localConfig: SessionReplayLocalConfig;\n joinedConfig: SessionReplayJoinedConfig;\n framework?: {\n name: string;\n version: string;\n };\n sessionId: string | number | undefined;\n hashValue?: number;\n sampleRate: number;\n replaySDKType: string | null;\n replaySDKVersion: string | undefined;\n standaloneSDKType: string;\n standaloneSDKVersion: string | undefined;\n}\n\nexport interface SessionReplayVersion {\n version: string;\n type: SessionReplayType;\n}\n\n/**\n * Configuration options for session replay performance.\n */\nexport interface SessionReplayPerformanceConfig {\n /**\n * If enabled, event compression will be deferred to occur during the browser's idle periods.\n */\n enabled: boolean;\n /**\n * Optional timeout in milliseconds for the `requestIdleCallback` API.\n * If specified, this value will be used to set a maximum time for the browser to wait\n * before executing the deferred compression task, even if the browser is not idle.\n */\n timeout?: number;\n /**\n * If enabled, consecutive mutation events will be merged into a single event before\n * compression, reducing stored event count without changing replay semantics.\n * Defaults to false.\n */\n mergeMutations?: boolean;\n /**\n * Performance configuration for interaction tracking (clicks, scrolls).\n */\n interaction?: InteractionPerformanceConfig;\n}\n\n/**\n * Performance configuration for interaction tracking, specifically for CSS selector generation.\n */\nexport interface InteractionPerformanceConfig {\n /**\n * Maximum time in milliseconds allowed for CSS selector generation.\n * If selector generation takes longer than this, it will throw a timeout error.\n * Default: undefined (no timeout limit)\n */\n timeoutMs?: number;\n /**\n * Maximum number of attempts to optimize/simplify the CSS selector path.\n * Higher values may produce shorter selectors but take longer to compute.\n * Default: 10000\n */\n maxNumberOfTries?: number;\n /**\n * Maximum number of CSS selector combinations to test for uniqueness.\n * If more combinations would be generated, falls back to a simpler strategy.\n * Default: 1000\n */\n threshold?: number;\n}\n\nexport type SessionReplayType = 'standalone' | 'plugin' | 'segment';\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":";;;AAuDa,QAAA,kBAAkB,GAAG,QAAQ,CAAC","sourcesContent":["import { IConfig, LogLevel, ILogger } from '@amplitude/analytics-core';\nimport { StoreType, ConsoleLogLevel } from '../typings/session-replay';\nimport { TargetingFlag } from '@amplitude/targeting';\n\nexport interface SamplingConfig {\n sample_rate: number;\n capture_enabled: boolean;\n}\n\nexport interface InteractionConfig {\n trackEveryNms?: number;\n enabled: boolean; // defaults to false\n batch: boolean; // defaults to false\n /**\n * UGC filter rules.\n */\n ugcFilterRules?: UGCFilterRule[];\n}\n\nexport interface LoggingConfig {\n console: {\n enabled: boolean;\n levels: ConsoleLogLevel[];\n };\n network?: {\n enabled: boolean;\n body?: {\n request?: boolean;\n response?: boolean;\n maxBodySizeBytes?: number;\n };\n };\n}\n\nexport type TargetingConfig = TargetingFlag;\n\nexport type SessionReplayRemoteConfig = {\n sr_sampling_config?: SamplingConfig;\n sr_privacy_config?: PrivacyConfig;\n sr_interaction_config?: InteractionConfig;\n sr_logging_config?: LoggingConfig;\n sr_targeting_config?: TargetingConfig;\n};\n\nexport interface SessionReplayRemoteConfigAPIResponse {\n configs: {\n sessionReplay: SessionReplayRemoteConfig;\n };\n}\n\nexport type MaskLevel =\n | 'light' // only mask a subset of inputs that's deemed sensitive - password, credit card, telephone #, email. These are information we never want to capture.\n | 'medium' // mask all inputs\n | 'conservative'; // mask all inputs and all texts\n\nexport const DEFAULT_MASK_LEVEL = 'medium';\n\n// err on the side of excluding more\nexport type PrivacyConfig = {\n blockSelector?: string | string[]; // exclude in the UI\n defaultMaskLevel?: MaskLevel;\n maskSelector?: string[];\n unmaskSelector?: string[];\n maskAttributes?: string[]; // HTML attribute names to mask (e.g. [\"placeholder\", \"aria-label\"])\n /**\n * Per-URL overrides for `defaultMaskLevel`. Each entry contains a glob pattern (`match`)\n * and a `maskLevel` to apply when the current page URL matches that pattern.\n * Rules are evaluated in order; the first match wins. Remote rules take precedence\n * over local rules (remote entries are prepended before local entries).\n *\n * @example\n * urlMaskLevels: [\n * { match: 'https://example.com/checkout/*', maskLevel: 'conservative' },\n * { match: 'https://example.com/public/*', maskLevel: 'light' },\n * ]\n */\n urlMaskLevels?: Array<{ match: string; maskLevel: MaskLevel }>;\n};\n\n/**\n * UGC filter rule.\n */\nexport type UGCFilterRule = {\n /**\n * The selector of the UGC element.\n */\n selector: string;\n /**\n * The replacement text for the UGC element.\n */\n replacement: string;\n};\n\nexport interface CrossOriginIframesConfig {\n enabled: boolean;\n /**\n * When true (default), the parent SDK sends start/stop signals to child iframes via\n * postMessage, keeping their recording lifecycle in sync with the parent.\n *\n * **Privacy note:** The child page's rrweb instance performs its own DOM serialization,\n * so the parent's privacy config (mask levels, block selectors) does NOT automatically\n * apply inside the iframe. Privacy settings must be configured independently on the child page.\n *\n * **Third-party iframes:** Cannot capture iframes you don't control (e.g. Stripe, Google\n * Maps) — both parent and child pages must load the SDK with `crossOriginIframes.enabled: true`.\n *\n * Set to `false` to skip coordination and manage the child recording lifecycle yourself.\n * @defaultValue true\n */\n coordinateChildren?: boolean;\n}\n\nexport interface SessionReplayLocalConfig extends IConfig {\n apiKey: string;\n loggerProvider: ILogger;\n /**\n * LogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug.\n * Sets the log level.\n *\n * @defaultValue LogLevel.Warn\n */\n logLevel: LogLevel;\n /**\n * The maximum number of retries allowed for sending replay events.\n * Once this limit is reached, failed events will no longer be sent.\n *\n * @defaultValue 2\n */\n flushMaxRetries: number;\n /**\n * Use this option to control how many sessions to select for replay collection.\n * The number should be a decimal between 0 and 1, for example 0.4, representing\n * the fraction of sessions to have randomly selected for replay collection.\n * Over a large number of sessions, 0.4 would select 40% of those sessions.\n * Sample rates as small as six decimal places (0.000001) are supported.\n *\n * @defaultValue 0\n */\n sampleRate: number;\n privacyConfig?: PrivacyConfig;\n /**\n * Adds additional debug event property to help debug instrumentation issues\n * (such as mismatching apps). Only recommended for debugging initial setup,\n * and not recommended for production.\n */\n debugMode?: boolean;\n /**\n * Specifies the endpoint URL to fetch remote configuration.\n * If provided, it overrides the default server zone configuration.\n */\n configServerUrl?: string;\n /**\n * Specifies the endpoint URL for sending session replay data.\n * If provided, it overrides the default server zone configuration.\n */\n trackServerUrl?: string;\n /**\n * If stylesheets are inlined, the contents of the stylesheet will be stored.\n * During replay, the stored stylesheet will be used instead of attempting to fetch it remotely.\n * This prevents replays from appearing broken due to missing stylesheets.\n * Note: Inlining stylesheets may not work in all cases.\n */\n shouldInlineStylesheet?: boolean;\n version?: SessionReplayVersion;\n /**\n * Performance configuration config. If enabled, we will defer compression\n * to be done during the browser's idle periods.\n */\n performanceConfig?: SessionReplayPerformanceConfig;\n /**\n * Specifies how replay events should be stored. `idb` uses IndexedDB to persist replay events\n * when all events cannot be sent during capture. `memory` stores replay events only in memory,\n * meaning events are lost when the page is closed. If IndexedDB is unavailable, the system falls back to `memory`.\n */\n storeType: StoreType;\n\n /**\n * If true, the SDK will compress replay events using a web worker.\n * This offloads compression to a separate thread, improving performance on the main thread.\n *\n * @defaultValue false\n */\n useWebWorker?: boolean;\n\n userProperties?: { [key: string]: any };\n\n /**\n * Remove certain parts of the DOM from being captured. These are typically ignored when blocking by selectors.\n */\n omitElementTags?: {\n /**\n * If true, removes script tags from the DOM, but not noscript tags.\n */\n script?: boolean;\n /**\n * If true, removes comment tags from the DOM.\n */\n comment?: boolean;\n };\n\n /**\n * If true, applies a background color to blocked elements in the replay.\n * This helps visualize which elements are blocked from being captured.\n */\n applyBackgroundColorToBlockedElements?: boolean;\n /**\n * Enables URL change polling as a fallback for SPA route tracking.\n * When enabled, the SDK will periodically check for URL changes every second\n * in addition to patching the History API. This is useful for edge cases where\n * route changes might bypass the standard History API methods.\n *\n * @defaultValue false\n */\n enableUrlChangePolling?: boolean;\n /**\n * Specifies the interval in milliseconds for URL change polling when enableUrlChangePolling is true.\n * The SDK will check for URL changes at this interval as a fallback for SPA route tracking.\n *\n * @defaultValue 1000\n */\n urlChangePollingInterval?: number;\n /**\n * Whether to capture document title in URL change events.\n * When disabled, the title field will be empty in URL change events.\n *\n * @defaultValue false\n */\n captureDocumentTitle?: boolean;\n interactionConfig?: InteractionConfig;\n /**\n * When true (default), the CSS rules of any `adoptedStyleSheets` on shadow roots and\n * the document are serialized **inline** within the full snapshot. This makes the snapshot\n * self-contained so that shadow DOM styles are replayed correctly even if subsequent\n * incremental `AdoptedStyleSheet` events are dropped in transit.\n *\n * Set to `false` to revert to the legacy behavior where adopted stylesheet rules are\n * emitted as separate incremental events (which may be lost if delivery is unreliable).\n * Only consider opting out if snapshot payload size is a critical concern.\n *\n * @defaultValue true\n */\n captureAdoptedStyleSheets?: boolean;\n /**\n * Enables recording of cross-origin iframes. Both the parent page and each child iframe\n * page must load the Amplitude Session Replay SDK with this option enabled.\n *\n * When enabled, rrweb uses `postMessage` to relay child DOM events to the parent, which\n * merges them into a single unified event stream.\n */\n crossOriginIframes?: CrossOriginIframesConfig;\n}\n\nexport interface SessionReplayJoinedConfig extends SessionReplayLocalConfig {\n captureEnabled?: boolean;\n interactionConfig?: InteractionConfig;\n loggingConfig?: LoggingConfig;\n targetingConfig?: TargetingConfig;\n}\n\nexport interface SessionReplayConfigs {\n localConfig: SessionReplayLocalConfig;\n joinedConfig: SessionReplayJoinedConfig;\n remoteConfig: SessionReplayRemoteConfig | undefined;\n}\nexport interface SessionReplayJoinedConfigGenerator {\n generateJoinedConfig: () => Promise<SessionReplayConfigs>;\n}\n\nexport interface SessionReplayMetadata {\n remoteConfig: SessionReplayRemoteConfig | undefined;\n localConfig: SessionReplayLocalConfig;\n joinedConfig: SessionReplayJoinedConfig;\n framework?: {\n name: string;\n version: string;\n };\n sessionId: string | number | undefined;\n hashValue?: number;\n sampleRate: number;\n replaySDKType: string | null;\n replaySDKVersion: string | undefined;\n standaloneSDKType: string;\n standaloneSDKVersion: string | undefined;\n}\n\nexport interface SessionReplayVersion {\n version: string;\n type: SessionReplayType;\n}\n\n/**\n * Configuration options for session replay performance.\n */\nexport interface SessionReplayPerformanceConfig {\n /**\n * If enabled, event compression will be deferred to occur during the browser's idle periods.\n */\n enabled: boolean;\n /**\n * Optional timeout in milliseconds for the `requestIdleCallback` API.\n * If specified, this value will be used to set a maximum time for the browser to wait\n * before executing the deferred compression task, even if the browser is not idle.\n */\n timeout?: number;\n /**\n * If enabled, consecutive mutation events will be merged into a single event before\n * compression, reducing stored event count without changing replay semantics.\n * Defaults to false.\n */\n mergeMutations?: boolean;\n /**\n * Performance configuration for interaction tracking (clicks, scrolls).\n */\n interaction?: InteractionPerformanceConfig;\n}\n\n/**\n * Performance configuration for interaction tracking, specifically for CSS selector generation.\n */\nexport interface InteractionPerformanceConfig {\n /**\n * Maximum time in milliseconds allowed for CSS selector generation.\n * If selector generation takes longer than this, it will throw a timeout error.\n * Default: undefined (no timeout limit)\n */\n timeoutMs?: number;\n /**\n * Maximum number of attempts to optimize/simplify the CSS selector path.\n * Higher values may produce shorter selectors but take longer to compute.\n * Default: 10000\n */\n maxNumberOfTries?: number;\n /**\n * Maximum number of CSS selector combinations to test for uniqueness.\n * If more combinations would be generated, falls back to a simpler strategy.\n * Default: 1000\n */\n threshold?: number;\n}\n\nexport type SessionReplayType = 'standalone' | 'plugin' | 'segment';\n"]}
|
package/lib/cjs/constants.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export declare const MAX_IDB_STORAGE_LENGTH: number;
|
|
|
26
26
|
export declare const KB_SIZE = 1024;
|
|
27
27
|
export declare const MAX_URL_LENGTH = 1000;
|
|
28
28
|
export declare const RETRY_TIMEOUT_MS = 1000;
|
|
29
|
+
export declare const MAX_KEEPALIVE_BYTES: number;
|
|
30
|
+
export declare const CROSS_ORIGIN_IFRAME_MESSAGE_TYPE = "amplitude-sr-iframe";
|
|
29
31
|
export declare enum CustomRRwebEvent {
|
|
30
32
|
GET_SR_PROPS = "get-sr-props",
|
|
31
33
|
DEBUG_INFO = "debug-info",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEzE,eAAO,MAAM,6BAA6B,gBAAgB,CAAC;AAE3D,eAAO,MAAM,+BAA+B,QAAuD,CAAC;AACpG,eAAO,MAAM,2BAA2B,kBAAkB,CAAC;AAC3D,eAAO,MAAM,yBAAyB,gBAAgB,CAAC;AACvD,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,0BAA0B;;CAAoB,CAAC;AAC5D,eAAO,MAAM,mCAAmC,OAAO,CAAC;AAExD,eAAO,MAAM,6BAA6B,QAA0D,CAAC;AAErG,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,eAAe,aAAa,CAAC;AAC1C,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAC9C,eAAO,MAAM,yBAAyB,mDAAmD,CAAC;AAC1F,eAAO,MAAM,qBAAqB,sDAAsD,CAAC;AACzF,eAAO,MAAM,0BAA0B,yDAAyD,CAAC;AACjG,eAAO,MAAM,cAAc,QAAsC,CAAC;AAClE,eAAO,MAAM,mBAAmB,QAAc,CAAC;AAC/C,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,YAAY,QAAY,CAAC;AACtC,eAAO,MAAM,sBAAsB,QAA0B,CAAC;AAC9D,eAAO,MAAM,OAAO,OAAO,CAAC;AAC5B,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,gBAAgB,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEzE,eAAO,MAAM,6BAA6B,gBAAgB,CAAC;AAE3D,eAAO,MAAM,+BAA+B,QAAuD,CAAC;AACpG,eAAO,MAAM,2BAA2B,kBAAkB,CAAC;AAC3D,eAAO,MAAM,yBAAyB,gBAAgB,CAAC;AACvD,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,0BAA0B;;CAAoB,CAAC;AAC5D,eAAO,MAAM,mCAAmC,OAAO,CAAC;AAExD,eAAO,MAAM,6BAA6B,QAA0D,CAAC;AAErG,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,eAAe,aAAa,CAAC;AAC1C,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAC9C,eAAO,MAAM,yBAAyB,mDAAmD,CAAC;AAC1F,eAAO,MAAM,qBAAqB,sDAAsD,CAAC;AACzF,eAAO,MAAM,0BAA0B,yDAAyD,CAAC;AACjG,eAAO,MAAM,cAAc,QAAsC,CAAC;AAClE,eAAO,MAAM,mBAAmB,QAAc,CAAC;AAC/C,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,YAAY,QAAY,CAAC;AACtC,eAAO,MAAM,sBAAsB,QAA0B,CAAC;AAC9D,eAAO,MAAM,OAAO,OAAO,CAAC;AAC5B,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAE7C,eAAO,MAAM,gCAAgC,wBAAwB,CAAC;AAEtE,oBAAY,gBAAgB;IAC1B,YAAY,iBAAiB;IAC7B,UAAU,eAAe;IACzB,aAAa,kBAAkB;IAC/B,QAAQ,aAAa;IACrB,kBAAkB,uBAAuB;CAC1C"}
|
package/lib/cjs/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CustomRRwebEvent = exports.RETRY_TIMEOUT_MS = exports.MAX_URL_LENGTH = exports.KB_SIZE = exports.MAX_IDB_STORAGE_LENGTH = exports.MAX_INTERVAL = exports.MIN_INTERVAL = exports.INTERACTION_MAX_INTERVAL = exports.INTERACTION_MIN_INTERVAL = exports.MAX_EVENT_LIST_SIZE = exports.STORAGE_PREFIX = exports.SESSION_REPLAY_STAGING_URL = exports.SESSION_REPLAY_EU_URL = exports.SESSION_REPLAY_SERVER_URL = exports.UNMASK_TEXT_CLASS = exports.MASK_TEXT_CLASS = exports.BLOCK_CLASS = exports.SESSION_REPLAY_DEBUG_PROPERTY = exports.DEFAULT_URL_CHANGE_POLLING_INTERVAL = exports.DEFAULT_PERFORMANCE_CONFIG = exports.DEFAULT_SERVER_ZONE = exports.DEFAULT_SAMPLE_RATE = exports.DEFAULT_SESSION_END_EVENT = exports.DEFAULT_SESSION_START_EVENT = exports.DEFAULT_SESSION_REPLAY_PROPERTY = exports.DEFAULT_EVENT_PROPERTY_PREFIX = void 0;
|
|
3
|
+
exports.CustomRRwebEvent = exports.CROSS_ORIGIN_IFRAME_MESSAGE_TYPE = exports.MAX_KEEPALIVE_BYTES = exports.RETRY_TIMEOUT_MS = exports.MAX_URL_LENGTH = exports.KB_SIZE = exports.MAX_IDB_STORAGE_LENGTH = exports.MAX_INTERVAL = exports.MIN_INTERVAL = exports.INTERACTION_MAX_INTERVAL = exports.INTERACTION_MIN_INTERVAL = exports.MAX_EVENT_LIST_SIZE = exports.STORAGE_PREFIX = exports.SESSION_REPLAY_STAGING_URL = exports.SESSION_REPLAY_EU_URL = exports.SESSION_REPLAY_SERVER_URL = exports.UNMASK_TEXT_CLASS = exports.MASK_TEXT_CLASS = exports.BLOCK_CLASS = exports.SESSION_REPLAY_DEBUG_PROPERTY = exports.DEFAULT_URL_CHANGE_POLLING_INTERVAL = exports.DEFAULT_PERFORMANCE_CONFIG = exports.DEFAULT_SERVER_ZONE = exports.DEFAULT_SAMPLE_RATE = exports.DEFAULT_SESSION_END_EVENT = exports.DEFAULT_SESSION_START_EVENT = exports.DEFAULT_SESSION_REPLAY_PROPERTY = exports.DEFAULT_EVENT_PROPERTY_PREFIX = void 0;
|
|
4
4
|
var analytics_core_1 = require("@amplitude/analytics-core");
|
|
5
5
|
exports.DEFAULT_EVENT_PROPERTY_PREFIX = '[Amplitude]';
|
|
6
6
|
exports.DEFAULT_SESSION_REPLAY_PROPERTY = "".concat(exports.DEFAULT_EVENT_PROPERTY_PREFIX, " Session Replay ID");
|
|
@@ -27,6 +27,8 @@ exports.MAX_IDB_STORAGE_LENGTH = 1000 * 60 * 60 * 24 * 3; // 3 days
|
|
|
27
27
|
exports.KB_SIZE = 1024;
|
|
28
28
|
exports.MAX_URL_LENGTH = 1000;
|
|
29
29
|
exports.RETRY_TIMEOUT_MS = 1000;
|
|
30
|
+
exports.MAX_KEEPALIVE_BYTES = 64 * 1024; // browser keepalive budget shared with sendBeacon
|
|
31
|
+
exports.CROSS_ORIGIN_IFRAME_MESSAGE_TYPE = 'amplitude-sr-iframe';
|
|
30
32
|
var CustomRRwebEvent;
|
|
31
33
|
(function (CustomRRwebEvent) {
|
|
32
34
|
CustomRRwebEvent["GET_SR_PROPS"] = "get-sr-props";
|
package/lib/cjs/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":";;;AAAA,4DAAyE;AAE5D,QAAA,6BAA6B,GAAG,aAAa,CAAC;AAE9C,QAAA,+BAA+B,GAAG,UAAG,qCAA6B,uBAAoB,CAAC;AACvF,QAAA,2BAA2B,GAAG,eAAe,CAAC;AAC9C,QAAA,yBAAyB,GAAG,aAAa,CAAC;AAC1C,QAAA,mBAAmB,GAAG,CAAC,CAAC;AACxB,QAAA,mBAAmB,GAAG,2BAAU,CAAC,EAAE,CAAC;AACpC,QAAA,0BAA0B,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC/C,QAAA,mCAAmC,GAAG,IAAI,CAAC;AAE3C,QAAA,6BAA6B,GAAG,UAAG,qCAA6B,0BAAuB,CAAC;AAExF,QAAA,WAAW,GAAG,WAAW,CAAC;AAC1B,QAAA,eAAe,GAAG,UAAU,CAAC;AAC7B,QAAA,iBAAiB,GAAG,YAAY,CAAC;AACjC,QAAA,yBAAyB,GAAG,gDAAgD,CAAC;AAC7E,QAAA,qBAAqB,GAAG,mDAAmD,CAAC;AAC5E,QAAA,0BAA0B,GAAG,sDAAsD,CAAC;AACpF,QAAA,cAAc,GAAG,UAAG,iCAAgB,mBAAgB,CAAC;AACrD,QAAA,mBAAmB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,iDAAiD;AACpF,QAAA,wBAAwB,GAAG,KAAM,CAAC,CAAC,aAAa;AAChD,QAAA,wBAAwB,GAAG,KAAM,CAAC,CAAC,WAAW;AAC9C,QAAA,YAAY,GAAG,GAAG,CAAC,CAAC,SAAS;AAC7B,QAAA,YAAY,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AACvC,QAAA,sBAAsB,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS;AAC3D,QAAA,OAAO,GAAG,IAAI,CAAC;AACf,QAAA,cAAc,GAAG,IAAI,CAAC;AACtB,QAAA,gBAAgB,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":";;;AAAA,4DAAyE;AAE5D,QAAA,6BAA6B,GAAG,aAAa,CAAC;AAE9C,QAAA,+BAA+B,GAAG,UAAG,qCAA6B,uBAAoB,CAAC;AACvF,QAAA,2BAA2B,GAAG,eAAe,CAAC;AAC9C,QAAA,yBAAyB,GAAG,aAAa,CAAC;AAC1C,QAAA,mBAAmB,GAAG,CAAC,CAAC;AACxB,QAAA,mBAAmB,GAAG,2BAAU,CAAC,EAAE,CAAC;AACpC,QAAA,0BAA0B,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC/C,QAAA,mCAAmC,GAAG,IAAI,CAAC;AAE3C,QAAA,6BAA6B,GAAG,UAAG,qCAA6B,0BAAuB,CAAC;AAExF,QAAA,WAAW,GAAG,WAAW,CAAC;AAC1B,QAAA,eAAe,GAAG,UAAU,CAAC;AAC7B,QAAA,iBAAiB,GAAG,YAAY,CAAC;AACjC,QAAA,yBAAyB,GAAG,gDAAgD,CAAC;AAC7E,QAAA,qBAAqB,GAAG,mDAAmD,CAAC;AAC5E,QAAA,0BAA0B,GAAG,sDAAsD,CAAC;AACpF,QAAA,cAAc,GAAG,UAAG,iCAAgB,mBAAgB,CAAC;AACrD,QAAA,mBAAmB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,iDAAiD;AACpF,QAAA,wBAAwB,GAAG,KAAM,CAAC,CAAC,aAAa;AAChD,QAAA,wBAAwB,GAAG,KAAM,CAAC,CAAC,WAAW;AAC9C,QAAA,YAAY,GAAG,GAAG,CAAC,CAAC,SAAS;AAC7B,QAAA,YAAY,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AACvC,QAAA,sBAAsB,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS;AAC3D,QAAA,OAAO,GAAG,IAAI,CAAC;AACf,QAAA,cAAc,GAAG,IAAI,CAAC;AACtB,QAAA,gBAAgB,GAAG,IAAI,CAAC;AACxB,QAAA,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,kDAAkD;AAEnF,QAAA,gCAAgC,GAAG,qBAAqB,CAAC;AAEtE,IAAY,gBAMX;AAND,WAAY,gBAAgB;IAC1B,iDAA6B,CAAA;IAC7B,6CAAyB,CAAA;IACzB,mDAA+B,CAAA;IAC/B,yCAAqB,CAAA;IACrB,6DAAyC,CAAA;AAC3C,CAAC,EANW,gBAAgB,GAAhB,wBAAgB,KAAhB,wBAAgB,QAM3B","sourcesContent":["import { AMPLITUDE_PREFIX, ServerZone } from '@amplitude/analytics-core';\n\nexport const DEFAULT_EVENT_PROPERTY_PREFIX = '[Amplitude]';\n\nexport const DEFAULT_SESSION_REPLAY_PROPERTY = `${DEFAULT_EVENT_PROPERTY_PREFIX} Session Replay ID`;\nexport const DEFAULT_SESSION_START_EVENT = 'session_start';\nexport const DEFAULT_SESSION_END_EVENT = 'session_end';\nexport const DEFAULT_SAMPLE_RATE = 0;\nexport const DEFAULT_SERVER_ZONE = ServerZone.US;\nexport const DEFAULT_PERFORMANCE_CONFIG = { enabled: true };\nexport const DEFAULT_URL_CHANGE_POLLING_INTERVAL = 1000;\n\nexport const SESSION_REPLAY_DEBUG_PROPERTY = `${DEFAULT_EVENT_PROPERTY_PREFIX} Session Replay Debug`;\n\nexport const BLOCK_CLASS = 'amp-block';\nexport const MASK_TEXT_CLASS = 'amp-mask';\nexport const UNMASK_TEXT_CLASS = 'amp-unmask';\nexport const SESSION_REPLAY_SERVER_URL = 'https://api-sr.amplitude.com/sessions/v2/track';\nexport const SESSION_REPLAY_EU_URL = 'https://api-sr.eu.amplitude.com/sessions/v2/track';\nexport const SESSION_REPLAY_STAGING_URL = 'https://api-sr.stag2.amplitude.com/sessions/v2/track';\nexport const STORAGE_PREFIX = `${AMPLITUDE_PREFIX}_replay_unsent`;\nexport const MAX_EVENT_LIST_SIZE = 1 * 1000000; // ~1 MB limit for JSON serialized events payload\nexport const INTERACTION_MIN_INTERVAL = 30_000; // 30 seconds\nexport const INTERACTION_MAX_INTERVAL = 60_000; // 1 minute\nexport const MIN_INTERVAL = 500; // 500 ms\nexport const MAX_INTERVAL = 10 * 1000; // 10 seconds\nexport const MAX_IDB_STORAGE_LENGTH = 1000 * 60 * 60 * 24 * 3; // 3 days\nexport const KB_SIZE = 1024;\nexport const MAX_URL_LENGTH = 1000;\nexport const RETRY_TIMEOUT_MS = 1000;\nexport const MAX_KEEPALIVE_BYTES = 64 * 1024; // browser keepalive budget shared with sendBeacon\n\nexport const CROSS_ORIGIN_IFRAME_MESSAGE_TYPE = 'amplitude-sr-iframe';\n\nexport enum CustomRRwebEvent {\n GET_SR_PROPS = 'get-sr-props',\n DEBUG_INFO = 'debug-info',\n FETCH_REQUEST = 'fetch-request',\n METADATA = 'metadata',\n TARGETING_DECISION = 'targeting-decision',\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare function isInIframe(): boolean;
|
|
2
|
+
/**
|
|
3
|
+
* Manages the parent side of cross-origin iframe recording coordination.
|
|
4
|
+
*
|
|
5
|
+
* When the parent starts recording, it sends a start signal to all current child
|
|
6
|
+
* iframes and watches for dynamically added iframes via MutationObserver. When the
|
|
7
|
+
* parent stops, it sends a stop signal.
|
|
8
|
+
*/
|
|
9
|
+
export declare class CrossOriginIframeCoordinator {
|
|
10
|
+
private mutationObserver;
|
|
11
|
+
private pendingLoadListeners;
|
|
12
|
+
start(): void;
|
|
13
|
+
stop(): void;
|
|
14
|
+
private sendToIframeAfterLoad;
|
|
15
|
+
private sendToAllIframes;
|
|
16
|
+
private sendToIframe;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Listens for start/stop signals from the parent page and invokes the provided
|
|
20
|
+
* callbacks. Only messages from `window.parent` are accepted.
|
|
21
|
+
*
|
|
22
|
+
* Returns a cleanup function that removes the message listener.
|
|
23
|
+
*/
|
|
24
|
+
export declare function listenForParentSignals(callbacks: {
|
|
25
|
+
onStart: () => void;
|
|
26
|
+
onStop: () => void;
|
|
27
|
+
}): () => void;
|
|
28
|
+
//# sourceMappingURL=cross-origin-iframes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-origin-iframes.d.ts","sourceRoot":"","sources":["../../src/cross-origin-iframes.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,IAAI,OAAO,CAWpC;AAID;;;;;;GAMG;AACH,qBAAa,4BAA4B;IACvC,OAAO,CAAC,gBAAgB,CAA+B;IAEvD,OAAO,CAAC,oBAAoB,CAA4C;IAExE,KAAK;IAwCL,IAAI;IAWJ,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,YAAY;CAOrB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE;IAAE,OAAO,EAAE,MAAM,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,MAAM,IAAI,CA0BzG"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listenForParentSignals = exports.CrossOriginIframeCoordinator = exports.isInIframe = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var analytics_core_1 = require("@amplitude/analytics-core");
|
|
6
|
+
var constants_1 = require("./constants");
|
|
7
|
+
function isInIframe() {
|
|
8
|
+
try {
|
|
9
|
+
var globalScope = (0, analytics_core_1.getGlobalScope)();
|
|
10
|
+
if (!globalScope) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return globalScope.parent !== globalScope;
|
|
14
|
+
}
|
|
15
|
+
catch (_a) {
|
|
16
|
+
// SecurityError accessing window.parent in some sandboxed environments
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.isInIframe = isInIframe;
|
|
21
|
+
/**
|
|
22
|
+
* Manages the parent side of cross-origin iframe recording coordination.
|
|
23
|
+
*
|
|
24
|
+
* When the parent starts recording, it sends a start signal to all current child
|
|
25
|
+
* iframes and watches for dynamically added iframes via MutationObserver. When the
|
|
26
|
+
* parent stops, it sends a stop signal.
|
|
27
|
+
*/
|
|
28
|
+
var CrossOriginIframeCoordinator = /** @class */ (function () {
|
|
29
|
+
function CrossOriginIframeCoordinator() {
|
|
30
|
+
// Tracks pending load listeners for dynamically added iframes so stop() can cancel them.
|
|
31
|
+
this.pendingLoadListeners = new Map();
|
|
32
|
+
}
|
|
33
|
+
CrossOriginIframeCoordinator.prototype.start = function () {
|
|
34
|
+
var _this = this;
|
|
35
|
+
var _a;
|
|
36
|
+
(_a = this.mutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect(); // guard against double-start
|
|
37
|
+
var globalScope = (0, analytics_core_1.getGlobalScope)();
|
|
38
|
+
if (!globalScope) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.sendToAllIframes({ type: constants_1.CROSS_ORIGIN_IFRAME_MESSAGE_TYPE, action: 'start' });
|
|
42
|
+
this.mutationObserver = new MutationObserver(function (mutations) {
|
|
43
|
+
var e_1, _a, e_2, _b, e_3, _c;
|
|
44
|
+
try {
|
|
45
|
+
for (var mutations_1 = tslib_1.__values(mutations), mutations_1_1 = mutations_1.next(); !mutations_1_1.done; mutations_1_1 = mutations_1.next()) {
|
|
46
|
+
var mutation = mutations_1_1.value;
|
|
47
|
+
try {
|
|
48
|
+
for (var _d = (e_2 = void 0, tslib_1.__values(Array.from(mutation.addedNodes))), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
49
|
+
var node = _e.value;
|
|
50
|
+
if (node instanceof HTMLIFrameElement) {
|
|
51
|
+
// Send the start signal after the child page has loaded, not at insertion
|
|
52
|
+
// time. At insertion the contentWindow is still about:blank; the message
|
|
53
|
+
// sent there is discarded when the iframe navigates to its src, and the
|
|
54
|
+
// child SDK never receives it.
|
|
55
|
+
_this.sendToIframeAfterLoad(node);
|
|
56
|
+
}
|
|
57
|
+
else if (node instanceof Element) {
|
|
58
|
+
// A container element (e.g. a React-rendered div) may already have
|
|
59
|
+
// iframe descendants when it is inserted. These iframes do NOT appear
|
|
60
|
+
// in addedNodes — only the container does. Query the subtree so we
|
|
61
|
+
// don't miss them.
|
|
62
|
+
node.querySelectorAll('iframe').forEach(function (iframe) {
|
|
63
|
+
_this.sendToIframeAfterLoad(iframe);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
69
|
+
finally {
|
|
70
|
+
try {
|
|
71
|
+
if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
|
|
72
|
+
}
|
|
73
|
+
finally { if (e_2) throw e_2.error; }
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
for (var _f = (e_3 = void 0, tslib_1.__values(Array.from(mutation.removedNodes))), _g = _f.next(); !_g.done; _g = _f.next()) {
|
|
77
|
+
var node = _g.value;
|
|
78
|
+
if (node instanceof HTMLIFrameElement) {
|
|
79
|
+
var listener = _this.pendingLoadListeners.get(node);
|
|
80
|
+
if (listener) {
|
|
81
|
+
node.removeEventListener('load', listener);
|
|
82
|
+
_this.pendingLoadListeners.delete(node);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
88
|
+
finally {
|
|
89
|
+
try {
|
|
90
|
+
if (_g && !_g.done && (_c = _f.return)) _c.call(_f);
|
|
91
|
+
}
|
|
92
|
+
finally { if (e_3) throw e_3.error; }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
97
|
+
finally {
|
|
98
|
+
try {
|
|
99
|
+
if (mutations_1_1 && !mutations_1_1.done && (_a = mutations_1.return)) _a.call(mutations_1);
|
|
100
|
+
}
|
|
101
|
+
finally { if (e_1) throw e_1.error; }
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
this.mutationObserver.observe(document.documentElement, { childList: true, subtree: true });
|
|
105
|
+
};
|
|
106
|
+
CrossOriginIframeCoordinator.prototype.stop = function () {
|
|
107
|
+
var _a;
|
|
108
|
+
(_a = this.mutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
109
|
+
this.mutationObserver = undefined;
|
|
110
|
+
// Cancel any start signals that were waiting for a pending iframe load.
|
|
111
|
+
this.pendingLoadListeners.forEach(function (listener, iframe) {
|
|
112
|
+
iframe.removeEventListener('load', listener);
|
|
113
|
+
});
|
|
114
|
+
this.pendingLoadListeners.clear();
|
|
115
|
+
this.sendToAllIframes({ type: constants_1.CROSS_ORIGIN_IFRAME_MESSAGE_TYPE, action: 'stop' });
|
|
116
|
+
};
|
|
117
|
+
CrossOriginIframeCoordinator.prototype.sendToIframeAfterLoad = function (iframe) {
|
|
118
|
+
var _this = this;
|
|
119
|
+
var sendStart = function () {
|
|
120
|
+
_this.pendingLoadListeners.delete(iframe);
|
|
121
|
+
_this.sendToIframe(iframe, { type: constants_1.CROSS_ORIGIN_IFRAME_MESSAGE_TYPE, action: 'start' });
|
|
122
|
+
};
|
|
123
|
+
this.pendingLoadListeners.set(iframe, sendStart);
|
|
124
|
+
iframe.addEventListener('load', sendStart, { once: true });
|
|
125
|
+
};
|
|
126
|
+
CrossOriginIframeCoordinator.prototype.sendToAllIframes = function (message) {
|
|
127
|
+
var _this = this;
|
|
128
|
+
var iframes = document.querySelectorAll('iframe');
|
|
129
|
+
iframes.forEach(function (iframe) { return _this.sendToIframe(iframe, message); });
|
|
130
|
+
};
|
|
131
|
+
CrossOriginIframeCoordinator.prototype.sendToIframe = function (iframe, message) {
|
|
132
|
+
var _a;
|
|
133
|
+
try {
|
|
134
|
+
(_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(message, '*');
|
|
135
|
+
}
|
|
136
|
+
catch (_b) {
|
|
137
|
+
// Cross-origin postMessage can throw in some sandboxed environments; ignore.
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
return CrossOriginIframeCoordinator;
|
|
141
|
+
}());
|
|
142
|
+
exports.CrossOriginIframeCoordinator = CrossOriginIframeCoordinator;
|
|
143
|
+
/**
|
|
144
|
+
* Listens for start/stop signals from the parent page and invokes the provided
|
|
145
|
+
* callbacks. Only messages from `window.parent` are accepted.
|
|
146
|
+
*
|
|
147
|
+
* Returns a cleanup function that removes the message listener.
|
|
148
|
+
*/
|
|
149
|
+
function listenForParentSignals(callbacks) {
|
|
150
|
+
var globalScope = (0, analytics_core_1.getGlobalScope)();
|
|
151
|
+
if (!globalScope) {
|
|
152
|
+
return function () { return undefined; };
|
|
153
|
+
}
|
|
154
|
+
var parentFrame = globalScope.parent;
|
|
155
|
+
function handler(event) {
|
|
156
|
+
// Only accept messages from the direct parent frame.
|
|
157
|
+
if (event.source !== parentFrame) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
var data = event.data;
|
|
161
|
+
if ((data === null || data === void 0 ? void 0 : data.type) !== constants_1.CROSS_ORIGIN_IFRAME_MESSAGE_TYPE) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (data.action === 'start') {
|
|
165
|
+
callbacks.onStart();
|
|
166
|
+
}
|
|
167
|
+
else if (data.action === 'stop') {
|
|
168
|
+
callbacks.onStop();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
globalScope.addEventListener('message', handler);
|
|
172
|
+
return function () { return globalScope.removeEventListener('message', handler); };
|
|
173
|
+
}
|
|
174
|
+
exports.listenForParentSignals = listenForParentSignals;
|
|
175
|
+
//# sourceMappingURL=cross-origin-iframes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-origin-iframes.js","sourceRoot":"","sources":["../../src/cross-origin-iframes.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\nimport { CROSS_ORIGIN_IFRAME_MESSAGE_TYPE } from './constants';\n\nexport function isInIframe(): boolean {\n try {\n const globalScope = getGlobalScope() as Window | undefined;\n if (!globalScope) {\n return false;\n }\n return globalScope.parent !== globalScope;\n } catch {\n // SecurityError accessing window.parent in some sandboxed environments\n return true;\n }\n}\n\ntype IframeMessage = { type: typeof CROSS_ORIGIN_IFRAME_MESSAGE_TYPE; action: 'start' | 'stop' };\n\n/**\n * Manages the parent side of cross-origin iframe recording coordination.\n *\n * When the parent starts recording, it sends a start signal to all current child\n * iframes and watches for dynamically added iframes via MutationObserver. When the\n * parent stops, it sends a stop signal.\n */\nexport class CrossOriginIframeCoordinator {\n private mutationObserver: MutationObserver | undefined;\n // Tracks pending load listeners for dynamically added iframes so stop() can cancel them.\n private pendingLoadListeners = new Map<HTMLIFrameElement, () => void>();\n\n start() {\n this.mutationObserver?.disconnect(); // guard against double-start\n const globalScope = getGlobalScope();\n if (!globalScope) {\n return;\n }\n this.sendToAllIframes({ type: CROSS_ORIGIN_IFRAME_MESSAGE_TYPE, action: 'start' });\n this.mutationObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of Array.from(mutation.addedNodes)) {\n if (node instanceof HTMLIFrameElement) {\n // Send the start signal after the child page has loaded, not at insertion\n // time. At insertion the contentWindow is still about:blank; the message\n // sent there is discarded when the iframe navigates to its src, and the\n // child SDK never receives it.\n this.sendToIframeAfterLoad(node);\n } else if (node instanceof Element) {\n // A container element (e.g. a React-rendered div) may already have\n // iframe descendants when it is inserted. These iframes do NOT appear\n // in addedNodes — only the container does. Query the subtree so we\n // don't miss them.\n node.querySelectorAll<HTMLIFrameElement>('iframe').forEach((iframe) => {\n this.sendToIframeAfterLoad(iframe);\n });\n }\n }\n for (const node of Array.from(mutation.removedNodes)) {\n if (node instanceof HTMLIFrameElement) {\n const listener = this.pendingLoadListeners.get(node);\n if (listener) {\n node.removeEventListener('load', listener);\n this.pendingLoadListeners.delete(node);\n }\n }\n }\n }\n });\n this.mutationObserver.observe(document.documentElement, { childList: true, subtree: true });\n }\n\n stop() {\n this.mutationObserver?.disconnect();\n this.mutationObserver = undefined;\n // Cancel any start signals that were waiting for a pending iframe load.\n this.pendingLoadListeners.forEach((listener, iframe) => {\n iframe.removeEventListener('load', listener);\n });\n this.pendingLoadListeners.clear();\n this.sendToAllIframes({ type: CROSS_ORIGIN_IFRAME_MESSAGE_TYPE, action: 'stop' });\n }\n\n private sendToIframeAfterLoad(iframe: HTMLIFrameElement) {\n const sendStart = () => {\n this.pendingLoadListeners.delete(iframe);\n this.sendToIframe(iframe, { type: CROSS_ORIGIN_IFRAME_MESSAGE_TYPE, action: 'start' });\n };\n this.pendingLoadListeners.set(iframe, sendStart);\n iframe.addEventListener('load', sendStart, { once: true });\n }\n\n private sendToAllIframes(message: IframeMessage) {\n const iframes = document.querySelectorAll<HTMLIFrameElement>('iframe');\n iframes.forEach((iframe) => this.sendToIframe(iframe, message));\n }\n\n private sendToIframe(iframe: HTMLIFrameElement, message: IframeMessage) {\n try {\n iframe.contentWindow?.postMessage(message, '*');\n } catch {\n // Cross-origin postMessage can throw in some sandboxed environments; ignore.\n }\n }\n}\n\n/**\n * Listens for start/stop signals from the parent page and invokes the provided\n * callbacks. Only messages from `window.parent` are accepted.\n *\n * Returns a cleanup function that removes the message listener.\n */\nexport function listenForParentSignals(callbacks: { onStart: () => void; onStop: () => void }): () => void {\n const globalScope = getGlobalScope() as Window | undefined;\n if (!globalScope) {\n return () => undefined;\n }\n\n const parentFrame = globalScope.parent;\n\n function handler(event: MessageEvent) {\n // Only accept messages from the direct parent frame.\n if (event.source !== parentFrame) {\n return;\n }\n const data = event.data as Partial<IframeMessage>;\n if (data?.type !== CROSS_ORIGIN_IFRAME_MESSAGE_TYPE) {\n return;\n }\n if (data.action === 'start') {\n callbacks.onStart();\n } else if (data.action === 'stop') {\n callbacks.onStop();\n }\n }\n\n globalScope.addEventListener('message', handler);\n return () => globalScope.removeEventListener('message', handler);\n}\n"]}
|