@amplitude/session-replay-browser 1.32.2 → 1.33.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 +84 -0
- package/lib/cjs/config/types.d.ts +5 -0
- package/lib/cjs/config/types.d.ts.map +1 -1
- package/lib/cjs/config/types.js.map +1 -1
- package/lib/cjs/observers.d.ts +15 -1
- package/lib/cjs/observers.d.ts.map +1 -1
- package/lib/cjs/observers.js +93 -9
- package/lib/cjs/observers.js.map +1 -1
- package/lib/cjs/session-replay.d.ts.map +1 -1
- package/lib/cjs/session-replay.js +28 -23
- package/lib/cjs/session-replay.js.map +1 -1
- package/lib/cjs/version.d.ts +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/cjs/version.js.map +1 -1
- package/lib/esm/config/types.d.ts +5 -0
- package/lib/esm/config/types.d.ts.map +1 -1
- package/lib/esm/config/types.js.map +1 -1
- package/lib/esm/observers.d.ts +15 -1
- package/lib/esm/observers.d.ts.map +1 -1
- package/lib/esm/observers.js +93 -9
- package/lib/esm/observers.js.map +1 -1
- package/lib/esm/session-replay.d.ts.map +1 -1
- package/lib/esm/session-replay.js +29 -24
- package/lib/esm/session-replay.js.map +1 -1
- package/lib/esm/version.d.ts +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/esm/version.js.map +1 -1
- package/lib/scripts/index-min.js +1 -1
- package/lib/scripts/index-min.js.gz +0 -0
- package/lib/scripts/index-min.js.map +1 -1
- package/lib/scripts/observers-min.js +1 -1
- package/lib/scripts/observers-min.js.gz +0 -0
- package/lib/scripts/observers-min.js.map +1 -1
- package/lib/scripts/session-replay-browser-min.js +1 -1
- package/lib/scripts/session-replay-browser-min.js.gz +0 -0
- package/lib/scripts/session-replay-browser-min.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -105,6 +105,90 @@ sessionReplay.shutdown()
|
|
|
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
107
|
|
|
108
|
+
## Network Request Capture
|
|
109
|
+
|
|
110
|
+
The SDK can capture network requests made via `fetch` and include them as events in the session replay. This is opt-in and disabled by default.
|
|
111
|
+
|
|
112
|
+
### Basic setup
|
|
113
|
+
|
|
114
|
+
Enable network capture via remote configuration by setting `sr_logging_config.network.enabled: true`. No code changes are required beyond the standard SDK initialization.
|
|
115
|
+
|
|
116
|
+
### Capturing request and response bodies
|
|
117
|
+
|
|
118
|
+
Body capture is a separate opt-in within network capture. To enable it, set `body.request` and/or `body.response` in the network config:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// This is configured server-side via sr_logging_config, not in the SDK init call.
|
|
122
|
+
// The shape of the remote config that enables body capture:
|
|
123
|
+
{
|
|
124
|
+
sr_logging_config: {
|
|
125
|
+
network: {
|
|
126
|
+
enabled: true,
|
|
127
|
+
body: {
|
|
128
|
+
request: true, // capture fetch request bodies
|
|
129
|
+
response: true, // capture fetch response bodies
|
|
130
|
+
maxBodySizeBytes: 10240, // optional, defaults to 10KB
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### Behavior
|
|
138
|
+
|
|
139
|
+
- **Request bodies**: Captured for `string`, `URLSearchParams`, and `FormData` body types. `Blob`, `ArrayBuffer`, and `ReadableStream` bodies are skipped.
|
|
140
|
+
- **Response bodies**: Captured after the response is received. Binary content types (`image/*`, `audio/*`, `video/*`, `application/octet-stream`, `font/*`) are skipped and the event will have `responseBodyStatus: 'skipped_binary'`.
|
|
141
|
+
- **Truncation**: Bodies exceeding `maxBodySizeBytes` are truncated to fit within the limit (byte-accurate for multi-byte characters). The event will have `responseBodyStatus: 'truncated'`.
|
|
142
|
+
- **Errors**: If reading the response body fails, the event will have `responseBodyStatus: 'error'`.
|
|
143
|
+
|
|
144
|
+
#### Network event fields
|
|
145
|
+
|
|
146
|
+
| Field | Type | Description |
|
|
147
|
+
|-|-|-|
|
|
148
|
+
| `url` | `string` | Request URL |
|
|
149
|
+
| `method` | `string` | HTTP method |
|
|
150
|
+
| `status` | `number` | Response status code |
|
|
151
|
+
| `duration` | `number` | Round-trip time in milliseconds |
|
|
152
|
+
| `requestHeaders` | `object` | Request headers |
|
|
153
|
+
| `responseHeaders` | `object` | Response headers |
|
|
154
|
+
| `requestBody` | `string` | Request body (if body capture enabled) |
|
|
155
|
+
| `responseBody` | `string` | Response body (if body capture enabled) |
|
|
156
|
+
| `responseBodyStatus` | `string` | `'captured'`, `'truncated'`, `'skipped_binary'`, or `'error'` |
|
|
157
|
+
| `error` | `object` | Error name and message if the request failed |
|
|
158
|
+
|
|
159
|
+
## Releasing a Prerelease
|
|
160
|
+
|
|
161
|
+
Prereleases are published to npm via the **Publish v2.x** GitHub Actions workflow. The published package will be tagged with the branch name (e.g. `@amplitude/session-replay-browser@SR-2728`) so it doesn't affect the `latest` dist-tag.
|
|
162
|
+
|
|
163
|
+
### Steps
|
|
164
|
+
|
|
165
|
+
1. Go to **Actions → Publish v2.x** in the GitHub repo
|
|
166
|
+
2. Click **Run workflow**
|
|
167
|
+
3. Set the inputs:
|
|
168
|
+
- **Use workflow from**: `main` (required — the workflow enforces this)
|
|
169
|
+
- **Release type**: `prerelease`
|
|
170
|
+
- **Branch to create pre-release from**: your feature branch (e.g. `SR-2728`)
|
|
171
|
+
4. Click **Run workflow**
|
|
172
|
+
|
|
173
|
+
The workflow will:
|
|
174
|
+
- Check out your feature branch
|
|
175
|
+
- Build and run tests
|
|
176
|
+
- Bump the version using conventional commits with your branch name as the preid (e.g. `1.33.0-SR-2728.0`)
|
|
177
|
+
- Publish to npm with `--tag <branch-name>`
|
|
178
|
+
|
|
179
|
+
### Installing a prerelease
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
npm install @amplitude/session-replay-browser@SR-2728
|
|
183
|
+
# or a specific version:
|
|
184
|
+
npm install @amplitude/session-replay-browser@1.33.0-SR-2728.0
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Notes
|
|
188
|
+
|
|
189
|
+
- The `--tag` value is derived from the branch name with non-alphanumeric characters (except `-`) stripped, so `SR-2728` → tag `SR-2728`, `feature/my-branch` → tag `featuremy-branch`
|
|
190
|
+
- Triggering from a branch other than `main` will fail the authorization check — always use `main` in the "Use workflow from" dropdown
|
|
191
|
+
|
|
108
192
|
## Bundle Size Optimization
|
|
109
193
|
The Session Replay SDK uses dynamic imports to optimize bundle size and improve initial page load performance. Key modules are loaded on-demand rather than being included in the initial bundle:
|
|
110
194
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,eAAe,EAAE,CAAC;KAC3B,CAAC;IACF,OAAO,CAAC,EAAE;QACR,OAAO,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,eAAe,EAAE,CAAC;KAC3B,CAAC;IACF,OAAO,CAAC,EAAE;QACR,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,CAAC,EAAE;YACL,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;YACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;SAC3B,CAAC;KACH,CAAC;CACH;AAED,MAAM,MAAM,eAAe,GAAG,aAAa,CAAC;AAE5C,MAAM,MAAM,yBAAyB,GAAG;IACtC,kBAAkB,CAAC,EAAE,cAAc,CAAC;IACpC,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,qBAAqB,CAAC,EAAE,iBAAiB,CAAC;IAC1C,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,mBAAmB,CAAC,EAAE,eAAe,CAAC;CACvC,CAAC;AAEF,MAAM,WAAW,oCAAoC;IACnD,OAAO,EAAE;QACP,aAAa,EAAE,yBAAyB,CAAC;KAC1C,CAAC;CACH;AAED,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,QAAQ,GACR,cAAc,CAAC;AAEnB,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAG3C,MAAM,MAAM,aAAa,GAAG;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,wBAAyB,SAAQ,OAAO;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;;OAKG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,8BAA8B,CAAC;IACnD;;;;OAIG;IACH,SAAS,EAAE,SAAS,CAAC;IAErB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,cAAc,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IAExC;;OAEG;IACH,eAAe,CAAC,EAAE;QAChB;;WAEG;QACH,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB;;WAEG;QACH,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IAEF;;;OAGG;IACH,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAED,MAAM,WAAW,yBAA0B,SAAQ,wBAAwB;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,wBAAwB,CAAC;IACtC,YAAY,EAAE,yBAAyB,CAAC;IACxC,YAAY,EAAE,yBAAyB,GAAG,SAAS,CAAC;CACrD;AACD,MAAM,WAAW,kCAAkC;IACjD,oBAAoB,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC3D;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,yBAAyB,GAAG,SAAS,CAAC;IACpD,WAAW,EAAE,wBAAwB,CAAC;IACtC,YAAY,EAAE,yBAAyB,CAAC;IACxC,SAAS,CAAC,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,4BAA4B,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":";;;
|
|
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};\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\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 * 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/observers.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type ResponseBodyStatus = 'captured' | 'truncated' | 'skipped_binary' | 'error';
|
|
1
2
|
export interface NetworkRequestEvent {
|
|
2
3
|
timestamp: number;
|
|
3
4
|
type: 'fetch';
|
|
@@ -7,16 +8,29 @@ export interface NetworkRequestEvent {
|
|
|
7
8
|
duration?: number;
|
|
8
9
|
requestHeaders?: Record<string, string>;
|
|
9
10
|
responseHeaders?: Record<string, string>;
|
|
11
|
+
requestBody?: string;
|
|
12
|
+
responseBody?: string;
|
|
13
|
+
responseBodyStatus?: ResponseBodyStatus;
|
|
10
14
|
error?: {
|
|
11
15
|
name: string;
|
|
12
16
|
message: string;
|
|
13
17
|
};
|
|
14
18
|
}
|
|
15
19
|
export type NetworkEventCallback = (event: NetworkRequestEvent) => void;
|
|
20
|
+
export interface NetworkBodyConfig {
|
|
21
|
+
request?: boolean;
|
|
22
|
+
response?: boolean;
|
|
23
|
+
maxBodySizeBytes?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface NetworkConfig {
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
body?: NetworkBodyConfig;
|
|
28
|
+
}
|
|
16
29
|
export declare class NetworkObservers {
|
|
17
30
|
private fetchObserver;
|
|
18
31
|
private eventCallback?;
|
|
19
|
-
|
|
32
|
+
private networkConfig?;
|
|
33
|
+
start(eventCallback: NetworkEventCallback, networkConfig?: NetworkConfig): void;
|
|
20
34
|
stop(): void;
|
|
21
35
|
protected notifyEvent(event: NetworkRequestEvent): void;
|
|
22
36
|
private observeFetch;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observers.d.ts","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAExE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,aAAa,CAAC,CAAuB;
|
|
1
|
+
{"version":3,"file":"observers.d.ts","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,WAAW,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAEvF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAkDD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,aAAa,CAAC,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAgB;IAEtC,KAAK,CAAC,aAAa,EAAE,oBAAoB,EAAE,aAAa,CAAC,EAAE,aAAa;IAMxE,IAAI;IAOJ,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,mBAAmB;IAIhD,OAAO,CAAC,YAAY;CAwFrB"}
|
package/lib/cjs/observers.js
CHANGED
|
@@ -3,12 +3,61 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.NetworkObservers = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var analytics_core_1 = require("@amplitude/analytics-core");
|
|
6
|
+
var DEFAULT_MAX_BODY_SIZE_BYTES = 10240; // 10KB
|
|
7
|
+
var BINARY_CONTENT_TYPE_PREFIXES = ['image/', 'audio/', 'video/', 'application/octet-stream', 'font/'];
|
|
8
|
+
function isBinaryContentType(contentType) {
|
|
9
|
+
if (!contentType)
|
|
10
|
+
return false;
|
|
11
|
+
return BINARY_CONTENT_TYPE_PREFIXES.some(function (prefix) { return contentType.toLowerCase().startsWith(prefix); });
|
|
12
|
+
}
|
|
13
|
+
function serializeRequestBody(body) {
|
|
14
|
+
if (body === null || body === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
if (typeof body === 'string')
|
|
17
|
+
return body;
|
|
18
|
+
if (body instanceof URLSearchParams)
|
|
19
|
+
return body.toString();
|
|
20
|
+
if (body instanceof FormData) {
|
|
21
|
+
var parts_1 = [];
|
|
22
|
+
body.forEach(function (value, key) {
|
|
23
|
+
parts_1.push("".concat(key, "=").concat(typeof value === 'string' ? value : '[File]'));
|
|
24
|
+
});
|
|
25
|
+
return parts_1.join('&');
|
|
26
|
+
}
|
|
27
|
+
// Blob, ArrayBuffer, ArrayBufferView, ReadableStream — skip
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
function truncateToByteLimit(str, maxBytes) {
|
|
31
|
+
if (new Blob([str]).size <= maxBytes) {
|
|
32
|
+
return { value: str, truncated: false };
|
|
33
|
+
}
|
|
34
|
+
// Binary search for the longest prefix whose UTF-8 byte length fits within maxBytes.
|
|
35
|
+
// Cap hi at maxBytes since each UTF-8 character is at least 1 byte, so no more than
|
|
36
|
+
// maxBytes characters can ever fit — this avoids large intermediate Blob allocations.
|
|
37
|
+
var lo = 0;
|
|
38
|
+
var hi = Math.min(str.length, maxBytes);
|
|
39
|
+
while (lo < hi) {
|
|
40
|
+
var mid = Math.ceil((lo + hi) / 2);
|
|
41
|
+
if (new Blob([str.slice(0, mid)]).size <= maxBytes) {
|
|
42
|
+
lo = mid;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
hi = mid - 1;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Avoid splitting a surrogate pair: if lo landed after a high surrogate, back up one position
|
|
49
|
+
if (lo > 0 && str.charCodeAt(lo - 1) >= 0xd800 && str.charCodeAt(lo - 1) <= 0xdbff) {
|
|
50
|
+
lo -= 1;
|
|
51
|
+
}
|
|
52
|
+
return { value: str.slice(0, lo), truncated: true };
|
|
53
|
+
}
|
|
6
54
|
var NetworkObservers = /** @class */ (function () {
|
|
7
55
|
function NetworkObservers() {
|
|
8
56
|
this.fetchObserver = null;
|
|
9
57
|
}
|
|
10
|
-
NetworkObservers.prototype.start = function (eventCallback) {
|
|
58
|
+
NetworkObservers.prototype.start = function (eventCallback, networkConfig) {
|
|
11
59
|
this.eventCallback = eventCallback;
|
|
60
|
+
this.networkConfig = networkConfig;
|
|
12
61
|
this.observeFetch();
|
|
13
62
|
};
|
|
14
63
|
NetworkObservers.prototype.stop = function () {
|
|
@@ -16,6 +65,7 @@ var NetworkObservers = /** @class */ (function () {
|
|
|
16
65
|
(_a = this.fetchObserver) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
17
66
|
this.fetchObserver = null;
|
|
18
67
|
this.eventCallback = undefined;
|
|
68
|
+
this.networkConfig = undefined;
|
|
19
69
|
};
|
|
20
70
|
NetworkObservers.prototype.notifyEvent = function (event) {
|
|
21
71
|
var _a;
|
|
@@ -30,9 +80,11 @@ var NetworkObservers = /** @class */ (function () {
|
|
|
30
80
|
if (!originalFetch)
|
|
31
81
|
return;
|
|
32
82
|
globalScope.fetch = function (input, init) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
33
|
-
var startTime, requestEvent, response, endTime, headers_1, error_1, endTime, typedError;
|
|
34
|
-
|
|
35
|
-
|
|
83
|
+
var startTime, requestEvent, bodyConfig, serialized, maxBytes, response, endTime, headers_1, contentType, cloned, error_1, endTime, typedError;
|
|
84
|
+
var _this = this;
|
|
85
|
+
var _a, _b;
|
|
86
|
+
return tslib_1.__generator(this, function (_c) {
|
|
87
|
+
switch (_c.label) {
|
|
36
88
|
case 0:
|
|
37
89
|
startTime = Date.now();
|
|
38
90
|
requestEvent = {
|
|
@@ -42,12 +94,20 @@ var NetworkObservers = /** @class */ (function () {
|
|
|
42
94
|
url: input.toString(),
|
|
43
95
|
requestHeaders: init === null || init === void 0 ? void 0 : init.headers,
|
|
44
96
|
};
|
|
45
|
-
_a
|
|
97
|
+
bodyConfig = (_a = this.networkConfig) === null || _a === void 0 ? void 0 : _a.body;
|
|
98
|
+
if (bodyConfig === null || bodyConfig === void 0 ? void 0 : bodyConfig.request) {
|
|
99
|
+
serialized = serializeRequestBody(init === null || init === void 0 ? void 0 : init.body);
|
|
100
|
+
if (serialized !== undefined) {
|
|
101
|
+
maxBytes = (_b = bodyConfig.maxBodySizeBytes) !== null && _b !== void 0 ? _b : DEFAULT_MAX_BODY_SIZE_BYTES;
|
|
102
|
+
requestEvent.requestBody = truncateToByteLimit(serialized, maxBytes).value;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
_c.label = 1;
|
|
46
106
|
case 1:
|
|
47
|
-
|
|
107
|
+
_c.trys.push([1, 3, , 4]);
|
|
48
108
|
return [4 /*yield*/, originalFetch(input, init)];
|
|
49
109
|
case 2:
|
|
50
|
-
response =
|
|
110
|
+
response = _c.sent();
|
|
51
111
|
endTime = Date.now();
|
|
52
112
|
requestEvent.status = response.status;
|
|
53
113
|
requestEvent.duration = endTime - startTime;
|
|
@@ -56,10 +116,34 @@ var NetworkObservers = /** @class */ (function () {
|
|
|
56
116
|
headers_1[key] = value;
|
|
57
117
|
});
|
|
58
118
|
requestEvent.responseHeaders = headers_1;
|
|
59
|
-
|
|
119
|
+
if (bodyConfig === null || bodyConfig === void 0 ? void 0 : bodyConfig.response) {
|
|
120
|
+
contentType = headers_1['content-type'] || null;
|
|
121
|
+
if (isBinaryContentType(contentType)) {
|
|
122
|
+
requestEvent.responseBodyStatus = 'skipped_binary';
|
|
123
|
+
this.notifyEvent(requestEvent);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
cloned = response.clone();
|
|
127
|
+
// Read body without blocking the response return to the caller
|
|
128
|
+
cloned.text().then(function (text) {
|
|
129
|
+
var _a;
|
|
130
|
+
var maxBytes = (_a = bodyConfig.maxBodySizeBytes) !== null && _a !== void 0 ? _a : DEFAULT_MAX_BODY_SIZE_BYTES;
|
|
131
|
+
var _b = truncateToByteLimit(text, maxBytes), value = _b.value, truncated = _b.truncated;
|
|
132
|
+
requestEvent.responseBody = value;
|
|
133
|
+
requestEvent.responseBodyStatus = truncated ? 'truncated' : 'captured';
|
|
134
|
+
_this.notifyEvent(requestEvent);
|
|
135
|
+
}, function () {
|
|
136
|
+
requestEvent.responseBodyStatus = 'error';
|
|
137
|
+
_this.notifyEvent(requestEvent);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.notifyEvent(requestEvent);
|
|
143
|
+
}
|
|
60
144
|
return [2 /*return*/, response];
|
|
61
145
|
case 3:
|
|
62
|
-
error_1 =
|
|
146
|
+
error_1 = _c.sent();
|
|
63
147
|
endTime = Date.now();
|
|
64
148
|
requestEvent.duration = endTime - startTime;
|
|
65
149
|
typedError = error_1;
|
package/lib/cjs/observers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observers.js","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":";;;;AAAA,4DAA2D;AAmB3D;IAAA;QACU,kBAAa,GAAwB,IAAI,CAAC;IAuEpD,CAAC;IApEC,gCAAK,GAAL,UAAM,aAAmC;QACvC,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED,+BAAI,GAAJ;;QACE,MAAA,IAAI,CAAC,aAAa,oDAAI,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;IACjC,CAAC;IAES,sCAAW,GAArB,UAAsB,KAA0B;;QAC9C,MAAA,IAAI,CAAC,aAAa,qDAAG,KAAK,CAAC,CAAC;IAC9B,CAAC;IAEO,uCAAY,GAApB;QAAA,iBAoDC;QAnDC,IAAM,WAAW,GAAG,IAAA,+BAAc,GAAE,CAAC;QACrC,IAAI,CAAC,WAAW;YAAE,OAAO;QAEzB,IAAM,aAAa,GAAG,WAAW,CAAC,KAAK,CAAC;QACxC,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3B,WAAW,CAAC,KAAK,GAAG,UAAO,KAAwB,EAAE,IAAkB;;;;;wBAC/D,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;wBACvB,YAAY,GAAwB;4BACxC,SAAS,EAAE,SAAS;4BACpB,IAAI,EAAE,OAAO;4BACb,MAAM,EAAE,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,MAAM,KAAI,KAAK;4BAC7B,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE;4BACrB,cAAc,EAAE,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,OAAiC;yBACxD,CAAC;;;;wBAGiB,qBAAM,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,EAAA;;wBAA3C,QAAQ,GAAG,SAAgC;wBAC3C,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;wBAE3B,YAAY,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;wBACtC,YAAY,CAAC,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;wBAGtC,YAAkC,EAAE,CAAC;wBAC3C,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,UAAC,KAAK,EAAE,GAAG;4BAClC,SAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;wBACvB,CAAC,CAAC,CAAC;wBACH,YAAY,CAAC,eAAe,GAAG,SAAO,CAAC;wBAEvC,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;wBAC/B,sBAAO,QAAQ,EAAC;;;wBAEV,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;wBAC3B,YAAY,CAAC,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;wBAGtC,UAAU,GAAG,OAAc,CAAC;wBAClC,YAAY,CAAC,KAAK,GAAG;4BACnB,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,cAAc;4BACvC,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,2BAA2B;yBAC3D,CAAC;wBAEF,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;wBAC/B,MAAM,OAAK,CAAC;;;;aAEf,CAAC;QAEF,IAAI,CAAC,aAAa,GAAG;YACnB,WAAW,CAAC,KAAK,GAAG,aAAa,CAAC;QACpC,CAAC,CAAC;IACJ,CAAC;IACH,uBAAC;AAAD,CAAC,AAxED,IAwEC;AAxEY,4CAAgB","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\n\nexport interface NetworkRequestEvent {\n timestamp: number;\n type: 'fetch';\n method: string;\n url: string;\n status?: number;\n duration?: number;\n requestHeaders?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n error?: {\n name: string;\n message: string;\n };\n}\n\nexport type NetworkEventCallback = (event: NetworkRequestEvent) => void;\n\nexport class NetworkObservers {\n private fetchObserver: (() => void) | null = null;\n private eventCallback?: NetworkEventCallback;\n\n start(eventCallback: NetworkEventCallback) {\n this.eventCallback = eventCallback;\n this.observeFetch();\n }\n\n stop() {\n this.fetchObserver?.();\n this.fetchObserver = null;\n this.eventCallback = undefined;\n }\n\n protected notifyEvent(event: NetworkRequestEvent) {\n this.eventCallback?.(event);\n }\n\n private observeFetch() {\n const globalScope = getGlobalScope();\n if (!globalScope) return;\n\n const originalFetch = globalScope.fetch;\n if (!originalFetch) return;\n\n globalScope.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const startTime = Date.now();\n const requestEvent: NetworkRequestEvent = {\n timestamp: startTime,\n type: 'fetch',\n method: init?.method || 'GET', // Fetch API defaulted to GET when no method is provided\n url: input.toString(),\n requestHeaders: init?.headers as Record<string, string>,\n };\n\n try {\n const response = await originalFetch(input, init);\n const endTime = Date.now();\n\n requestEvent.status = response.status;\n requestEvent.duration = endTime - startTime;\n\n // Convert Headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n requestEvent.responseHeaders = headers;\n\n this.notifyEvent(requestEvent);\n return response;\n } catch (error) {\n const endTime = Date.now();\n requestEvent.duration = endTime - startTime;\n\n // Capture error information\n const typedError = error as Error;\n requestEvent.error = {\n name: typedError.name || 'UnknownError',\n message: typedError.message || 'An unknown error occurred',\n };\n\n this.notifyEvent(requestEvent);\n throw error;\n }\n };\n\n this.fetchObserver = () => {\n globalScope.fetch = originalFetch;\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"observers.js","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\n\nexport type ResponseBodyStatus = 'captured' | 'truncated' | 'skipped_binary' | 'error';\n\nexport interface NetworkRequestEvent {\n timestamp: number;\n type: 'fetch';\n method: string;\n url: string;\n status?: number;\n duration?: number;\n requestHeaders?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n requestBody?: string;\n responseBody?: string;\n responseBodyStatus?: ResponseBodyStatus;\n error?: {\n name: string;\n message: string;\n };\n}\n\nexport type NetworkEventCallback = (event: NetworkRequestEvent) => void;\n\nexport interface NetworkBodyConfig {\n request?: boolean;\n response?: boolean;\n maxBodySizeBytes?: number;\n}\n\nexport interface NetworkConfig {\n enabled: boolean;\n body?: NetworkBodyConfig;\n}\n\nconst DEFAULT_MAX_BODY_SIZE_BYTES = 10240; // 10KB\n\nconst BINARY_CONTENT_TYPE_PREFIXES = ['image/', 'audio/', 'video/', 'application/octet-stream', 'font/'];\n\nfunction isBinaryContentType(contentType: string | null): boolean {\n if (!contentType) return false;\n return BINARY_CONTENT_TYPE_PREFIXES.some((prefix) => contentType.toLowerCase().startsWith(prefix));\n}\n\nfunction serializeRequestBody(body: BodyInit | null | undefined): string | undefined {\n if (body === null || body === undefined) return undefined;\n if (typeof body === 'string') return body;\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof FormData) {\n const parts: string[] = [];\n body.forEach((value, key) => {\n parts.push(`${key}=${typeof value === 'string' ? value : '[File]'}`);\n });\n return parts.join('&');\n }\n // Blob, ArrayBuffer, ArrayBufferView, ReadableStream — skip\n return undefined;\n}\n\nfunction truncateToByteLimit(str: string, maxBytes: number): { value: string; truncated: boolean } {\n if (new Blob([str]).size <= maxBytes) {\n return { value: str, truncated: false };\n }\n // Binary search for the longest prefix whose UTF-8 byte length fits within maxBytes.\n // Cap hi at maxBytes since each UTF-8 character is at least 1 byte, so no more than\n // maxBytes characters can ever fit — this avoids large intermediate Blob allocations.\n let lo = 0;\n let hi = Math.min(str.length, maxBytes);\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (new Blob([str.slice(0, mid)]).size <= maxBytes) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n // Avoid splitting a surrogate pair: if lo landed after a high surrogate, back up one position\n if (lo > 0 && str.charCodeAt(lo - 1) >= 0xd800 && str.charCodeAt(lo - 1) <= 0xdbff) {\n lo -= 1;\n }\n return { value: str.slice(0, lo), truncated: true };\n}\n\nexport class NetworkObservers {\n private fetchObserver: (() => void) | null = null;\n private eventCallback?: NetworkEventCallback;\n private networkConfig?: NetworkConfig;\n\n start(eventCallback: NetworkEventCallback, networkConfig?: NetworkConfig) {\n this.eventCallback = eventCallback;\n this.networkConfig = networkConfig;\n this.observeFetch();\n }\n\n stop() {\n this.fetchObserver?.();\n this.fetchObserver = null;\n this.eventCallback = undefined;\n this.networkConfig = undefined;\n }\n\n protected notifyEvent(event: NetworkRequestEvent) {\n this.eventCallback?.(event);\n }\n\n private observeFetch() {\n const globalScope = getGlobalScope();\n if (!globalScope) return;\n\n const originalFetch = globalScope.fetch;\n if (!originalFetch) return;\n\n globalScope.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const startTime = Date.now();\n const requestEvent: NetworkRequestEvent = {\n timestamp: startTime,\n type: 'fetch',\n method: init?.method || 'GET', // Fetch API defaulted to GET when no method is provided\n url: input.toString(),\n requestHeaders: init?.headers as Record<string, string>,\n };\n\n // Capture request body if configured\n const bodyConfig = this.networkConfig?.body;\n if (bodyConfig?.request) {\n const serialized = serializeRequestBody(init?.body);\n if (serialized !== undefined) {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n requestEvent.requestBody = truncateToByteLimit(serialized, maxBytes).value;\n }\n }\n\n try {\n const response = await originalFetch(input, init);\n const endTime = Date.now();\n\n requestEvent.status = response.status;\n requestEvent.duration = endTime - startTime;\n\n // Convert Headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n requestEvent.responseHeaders = headers;\n\n if (bodyConfig?.response) {\n const contentType = headers['content-type'] || null;\n if (isBinaryContentType(contentType)) {\n requestEvent.responseBodyStatus = 'skipped_binary';\n this.notifyEvent(requestEvent);\n } else {\n const cloned = response.clone();\n // Read body without blocking the response return to the caller\n cloned.text().then(\n (text) => {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n const { value, truncated } = truncateToByteLimit(text, maxBytes);\n requestEvent.responseBody = value;\n requestEvent.responseBodyStatus = truncated ? 'truncated' : 'captured';\n this.notifyEvent(requestEvent);\n },\n () => {\n requestEvent.responseBodyStatus = 'error';\n this.notifyEvent(requestEvent);\n },\n );\n }\n } else {\n this.notifyEvent(requestEvent);\n }\n\n return response;\n } catch (error) {\n const endTime = Date.now();\n requestEvent.duration = endTime - startTime;\n\n // Capture error information\n const typedError = error as Error;\n requestEvent.error = {\n name: typedError.name || 'UnknownError',\n message: typedError.message || 'An unknown error occurred',\n };\n\n this.notifyEvent(requestEvent);\n throw error;\n }\n };\n\n this.fetchObserver = () => {\n globalScope.fetch = originalFetch;\n };\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,OAAO,EAUR,MAAM,2BAA2B,CAAC;AAInC,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EACL,aAAa,EACb,yBAAyB,EACzB,kCAAkC,EAInC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,gBAAgB,
|
|
1
|
+
{"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,10 +7,10 @@ var analytics_core_1 = require("@amplitude/analytics-core");
|
|
|
7
7
|
var rrweb_types_1 = require("@amplitude/rrweb-types");
|
|
8
8
|
var joined_config_1 = require("./config/joined-config");
|
|
9
9
|
var constants_1 = require("./constants");
|
|
10
|
+
var helpers_1 = require("./helpers");
|
|
10
11
|
var event_compressor_1 = require("./events/event-compressor");
|
|
11
12
|
var events_manager_1 = require("./events/events-manager");
|
|
12
13
|
var multi_manager_1 = require("./events/multi-manager");
|
|
13
|
-
var helpers_1 = require("./helpers");
|
|
14
14
|
var click_1 = require("./hooks/click");
|
|
15
15
|
var scroll_1 = require("./hooks/scroll");
|
|
16
16
|
var identifiers_1 = require("./identifiers");
|
|
@@ -629,14 +629,14 @@ var SessionReplay = /** @class */ (function () {
|
|
|
629
629
|
});
|
|
630
630
|
};
|
|
631
631
|
SessionReplay.prototype.recordEvents = function (shouldLogMetadata) {
|
|
632
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
632
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
633
633
|
if (shouldLogMetadata === void 0) { shouldLogMetadata = true; }
|
|
634
634
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
635
|
-
var config, shouldRecord, sessionId, recordFunction, privacyConfig, interactionConfig, loggingConfig, hooks, ugcFilterRules,
|
|
636
|
-
var
|
|
635
|
+
var config, shouldRecord, sessionId, recordFunction, networkLoggingConfig, trackUrl, ignoredUrls, privacyConfig, interactionConfig, loggingConfig, hooks, ugcFilterRules, _j, _k, error_5;
|
|
636
|
+
var _l;
|
|
637
637
|
var _this = this;
|
|
638
|
-
return tslib_1.__generator(this, function (
|
|
639
|
-
switch (
|
|
638
|
+
return tslib_1.__generator(this, function (_m) {
|
|
639
|
+
switch (_m.label) {
|
|
640
640
|
case 0:
|
|
641
641
|
config = this.config;
|
|
642
642
|
shouldRecord = this.getShouldRecord();
|
|
@@ -647,40 +647,45 @@ var SessionReplay = /** @class */ (function () {
|
|
|
647
647
|
this.stopRecordingEvents();
|
|
648
648
|
return [4 /*yield*/, this.getRecordFunction()];
|
|
649
649
|
case 1:
|
|
650
|
-
recordFunction =
|
|
650
|
+
recordFunction = _m.sent();
|
|
651
651
|
// May be undefined if cannot import rrweb-record
|
|
652
652
|
if (!recordFunction) {
|
|
653
653
|
return [2 /*return*/];
|
|
654
654
|
}
|
|
655
655
|
return [4 /*yield*/, this.initializeNetworkObservers()];
|
|
656
656
|
case 2:
|
|
657
|
-
|
|
658
|
-
(_b =
|
|
657
|
+
_m.sent();
|
|
658
|
+
networkLoggingConfig = (_b = config.loggingConfig) === null || _b === void 0 ? void 0 : _b.network;
|
|
659
|
+
trackUrl = (0, helpers_1.getServerUrl)(config.serverZone, config.trackServerUrl);
|
|
660
|
+
ignoredUrls = [constants_1.SESSION_REPLAY_SERVER_URL, constants_1.SESSION_REPLAY_EU_URL, constants_1.SESSION_REPLAY_STAGING_URL, trackUrl];
|
|
661
|
+
(_c = this.networkObservers) === null || _c === void 0 ? void 0 : _c.start(function (event) {
|
|
662
|
+
if (ignoredUrls.some(function (url) { return event.url.startsWith(url); }))
|
|
663
|
+
return;
|
|
659
664
|
void _this.addCustomRRWebEvent(constants_1.CustomRRwebEvent.FETCH_REQUEST, event);
|
|
660
|
-
});
|
|
665
|
+
}, networkLoggingConfig);
|
|
661
666
|
privacyConfig = config.privacyConfig, interactionConfig = config.interactionConfig, loggingConfig = config.loggingConfig;
|
|
662
667
|
hooks = (interactionConfig === null || interactionConfig === void 0 ? void 0 : interactionConfig.enabled)
|
|
663
668
|
? {
|
|
664
669
|
mouseInteraction: this.eventsManager &&
|
|
665
|
-
((
|
|
670
|
+
((_d = this.clickHandler) === null || _d === void 0 ? void 0 : _d.createHook({
|
|
666
671
|
eventsManager: this.eventsManager,
|
|
667
672
|
sessionId: sessionId,
|
|
668
673
|
deviceIdFn: this.getDeviceId.bind(this),
|
|
669
674
|
mirror: recordFunction.mirror,
|
|
670
|
-
ugcFilterRules: (
|
|
671
|
-
performanceOptions: (
|
|
675
|
+
ugcFilterRules: (_e = interactionConfig.ugcFilterRules) !== null && _e !== void 0 ? _e : [],
|
|
676
|
+
performanceOptions: (_f = config.performanceConfig) === null || _f === void 0 ? void 0 : _f.interaction,
|
|
672
677
|
})),
|
|
673
678
|
scroll: this.scrollHook,
|
|
674
679
|
}
|
|
675
680
|
: {};
|
|
676
681
|
ugcFilterRules = (interactionConfig === null || interactionConfig === void 0 ? void 0 : interactionConfig.enabled) && interactionConfig.ugcFilterRules ? interactionConfig.ugcFilterRules : [];
|
|
677
682
|
this.loggerProvider.log("Session Replay capture beginning for ".concat(sessionId, "."));
|
|
678
|
-
|
|
683
|
+
_m.label = 3;
|
|
679
684
|
case 3:
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
685
|
+
_m.trys.push([3, 5, , 6]);
|
|
686
|
+
_j = this;
|
|
687
|
+
_k = recordFunction;
|
|
688
|
+
_l = {
|
|
684
689
|
emit: function (event) {
|
|
685
690
|
if (_this.shouldOptOut()) {
|
|
686
691
|
_this.loggerProvider.log("Opting session ".concat(sessionId, " out of recording due to optOut config."));
|
|
@@ -708,8 +713,8 @@ var SessionReplay = /** @class */ (function () {
|
|
|
708
713
|
maskTextSelector: this.getMaskTextSelectors(),
|
|
709
714
|
recordCanvas: false,
|
|
710
715
|
slimDOMOptions: {
|
|
711
|
-
script: (
|
|
712
|
-
comment: (
|
|
716
|
+
script: (_g = config.omitElementTags) === null || _g === void 0 ? void 0 : _g.script,
|
|
717
|
+
comment: (_h = config.omitElementTags) === null || _h === void 0 ? void 0 : _h.comment,
|
|
713
718
|
},
|
|
714
719
|
errorHandler: function (error) {
|
|
715
720
|
var typedError = error;
|
|
@@ -730,15 +735,15 @@ var SessionReplay = /** @class */ (function () {
|
|
|
730
735
|
};
|
|
731
736
|
return [4 /*yield*/, this.getRecordingPlugins(loggingConfig)];
|
|
732
737
|
case 4:
|
|
733
|
-
|
|
734
|
-
|
|
738
|
+
_j.recordCancelCallback = _k.apply(void 0, [(_l.plugins = _m.sent(),
|
|
739
|
+
_l)]);
|
|
735
740
|
void this.addCustomRRWebEvent(constants_1.CustomRRwebEvent.DEBUG_INFO);
|
|
736
741
|
if (shouldLogMetadata) {
|
|
737
742
|
void this.addCustomRRWebEvent(constants_1.CustomRRwebEvent.METADATA, this.metadata);
|
|
738
743
|
}
|
|
739
744
|
return [3 /*break*/, 6];
|
|
740
745
|
case 5:
|
|
741
|
-
error_5 =
|
|
746
|
+
error_5 = _m.sent();
|
|
742
747
|
this.loggerProvider.warn('Failed to initialize session replay:', error_5);
|
|
743
748
|
return [3 /*break*/, 6];
|
|
744
749
|
case 6: return [2 /*return*/];
|