@amplitude/session-replay-browser 1.42.1 → 1.42.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/config/joined-config.d.ts.map +1 -1
- package/lib/cjs/config/joined-config.js +19 -4
- package/lib/cjs/config/joined-config.js.map +1 -1
- package/lib/cjs/constants.d.ts +5 -0
- package/lib/cjs/constants.d.ts.map +1 -1
- package/lib/cjs/constants.js +10 -1
- package/lib/cjs/constants.js.map +1 -1
- package/lib/cjs/messages.d.ts +1 -0
- package/lib/cjs/messages.d.ts.map +1 -1
- package/lib/cjs/messages.js +2 -1
- package/lib/cjs/messages.js.map +1 -1
- package/lib/cjs/track-destination.d.ts +15 -0
- package/lib/cjs/track-destination.d.ts.map +1 -1
- package/lib/cjs/track-destination.js +133 -20
- package/lib/cjs/track-destination.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/joined-config.d.ts.map +1 -1
- package/lib/esm/config/joined-config.js +19 -4
- package/lib/esm/config/joined-config.js.map +1 -1
- package/lib/esm/constants.d.ts +5 -0
- package/lib/esm/constants.d.ts.map +1 -1
- package/lib/esm/constants.js +9 -0
- package/lib/esm/constants.js.map +1 -1
- package/lib/esm/messages.d.ts +1 -0
- package/lib/esm/messages.d.ts.map +1 -1
- package/lib/esm/messages.js +1 -0
- package/lib/esm/messages.js.map +1 -1
- package/lib/esm/track-destination.d.ts +15 -0
- package/lib/esm/track-destination.d.ts.map +1 -1
- package/lib/esm/track-destination.js +135 -22
- package/lib/esm/track-destination.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/worker-min.js +1 -1
- package/lib/scripts/worker-min.js.gz +0 -0
- package/package.json +1 -1
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { __assign, __awaiter, __generator, __read, __values } from "tslib";
|
|
2
2
|
import { BaseTransport, getGlobalScope, Status } from '@amplitude/analytics-core';
|
|
3
3
|
import { getCurrentUrl, getServerUrl } from './helpers';
|
|
4
|
-
import { MAX_RETRIES_EXCEEDED_MESSAGE, MISSING_API_KEY_MESSAGE, MISSING_DEVICE_ID_MESSAGE, UNEXPECTED_ERROR_MESSAGE, UNEXPECTED_NETWORK_ERROR_MESSAGE, } from './messages';
|
|
4
|
+
import { MAX_RETRIES_EXCEEDED_MESSAGE, MISSING_API_KEY_MESSAGE, MISSING_DEVICE_ID_MESSAGE, SESSION_KILLED_MESSAGE, UNEXPECTED_ERROR_MESSAGE, UNEXPECTED_NETWORK_ERROR_MESSAGE, } from './messages';
|
|
5
5
|
import { VERSION } from './version';
|
|
6
|
-
import { MAX_URL_LENGTH, KB_SIZE, MAX_KEEPALIVE_BYTES, WAF_PAYLOAD_TOO_LARGE_PATTERN } from './constants';
|
|
6
|
+
import { MAX_URL_LENGTH, KB_SIZE, MAX_KEEPALIVE_BYTES, WAF_PAYLOAD_TOO_LARGE_PATTERN, EVENT_SKIPPED_HEADER, EVENT_SKIP_CODE_THROTTLED, EVENT_SKIP_CODE_INVALID_RANGE, EVENT_SKIP_CODE_CAPTURE_DISABLED, THROTTLED_FLUSH_PAUSE_MS, } from './constants';
|
|
7
7
|
import { gzipJson } from './utils/gzip';
|
|
8
|
+
// Bounded so a long-lived SDK instance can't accumulate kill records indefinitely;
|
|
9
|
+
// sessions are time-bounded in practice, this cap is just a defensive ceiling.
|
|
10
|
+
var MAX_KILLED_SESSIONS = 256;
|
|
8
11
|
var SessionReplayTrackDestination = /** @class */ (function () {
|
|
9
12
|
function SessionReplayTrackDestination(_a) {
|
|
10
13
|
var trackServerUrl = _a.trackServerUrl, loggerProvider = _a.loggerProvider, payloadBatcher = _a.payloadBatcher, workerScript = _a.workerScript;
|
|
@@ -15,6 +18,11 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
15
18
|
this.queue = [];
|
|
16
19
|
this.sendIdCounter = 0;
|
|
17
20
|
this.pendingWorkerRequests = new Map();
|
|
21
|
+
// Server back-pressure state, fed by the X-Session-Replay-Event-Skipped header on 200s.
|
|
22
|
+
// The server uses this header (instead of 4xx) to signal a deliberate no-retry drop so SDKs
|
|
23
|
+
// don't retry-storm. We honor it here by slowing or stopping our flush schedule.
|
|
24
|
+
this.flushPauseUntilMs = 0;
|
|
25
|
+
this.killedSessions = new Set();
|
|
18
26
|
this.loggerProvider = loggerProvider;
|
|
19
27
|
this.payloadBatcher = payloadBatcher ? payloadBatcher : function (payload) { return payload; };
|
|
20
28
|
this.trackServerUrl = trackServerUrl;
|
|
@@ -67,6 +75,9 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
67
75
|
else if (msg.type === 'complete') {
|
|
68
76
|
var pending_3 = _this.pendingWorkerRequests.get(msg.id);
|
|
69
77
|
if (pending_3) {
|
|
78
|
+
if (msg.skipCode !== undefined) {
|
|
79
|
+
_this.applyServerDirective(pending_3.context.sessionId, msg.skipCode);
|
|
80
|
+
}
|
|
70
81
|
_this.completeRequest({ context: pending_3.context });
|
|
71
82
|
pending_3.resolve();
|
|
72
83
|
_this.pendingWorkerRequests.delete(msg.id);
|
|
@@ -146,6 +157,15 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
146
157
|
list[_i] = arguments[_i];
|
|
147
158
|
}
|
|
148
159
|
var tryable = list.filter(function (context) {
|
|
160
|
+
if (_this.killedSessions.has(context.sessionId)) {
|
|
161
|
+
// Server has signaled capture_disabled or session_in_invalid_range for this session;
|
|
162
|
+
// drop the batch (and clean up its IDB record via onComplete) instead of POSTing.
|
|
163
|
+
_this.completeRequest({
|
|
164
|
+
context: context,
|
|
165
|
+
err: SESSION_KILLED_MESSAGE,
|
|
166
|
+
});
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
149
169
|
if (context.attempts < (context.flushMaxRetries || 0)) {
|
|
150
170
|
context.attempts += 1;
|
|
151
171
|
return true;
|
|
@@ -165,13 +185,17 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
165
185
|
var _this = this;
|
|
166
186
|
if (this.scheduled)
|
|
167
187
|
return;
|
|
188
|
+
// If the server signaled throttling on a recent 200, defer the next flush until the
|
|
189
|
+
// pause window ends. This lets us keep batching events without retry-storming the server.
|
|
190
|
+
var pauseRemaining = this.flushPauseUntilMs - Date.now();
|
|
191
|
+
var effectiveTimeout = pauseRemaining > timeout ? pauseRemaining : timeout;
|
|
168
192
|
this.scheduled = setTimeout(function () {
|
|
169
193
|
void _this.flush(true).then(function () {
|
|
170
194
|
if (_this.queue.length > 0) {
|
|
171
195
|
_this.schedule(timeout);
|
|
172
196
|
}
|
|
173
197
|
});
|
|
174
|
-
},
|
|
198
|
+
}, effectiveTimeout);
|
|
175
199
|
};
|
|
176
200
|
SessionReplayTrackDestination.prototype.flush = function (useRetry) {
|
|
177
201
|
if (useRetry === void 0) { useRetry = false; }
|
|
@@ -223,6 +247,12 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
223
247
|
return __awaiter(this, void 0, void 0, function () {
|
|
224
248
|
var apiKey, deviceId, payload, worker;
|
|
225
249
|
return __generator(this, function (_a) {
|
|
250
|
+
// A kill directive can arrive between flush() snapshotting the queue and us reaching
|
|
251
|
+
// each context. Re-check before hitting the network so we don't waste POSTs on a
|
|
252
|
+
// session the server has already told us to stop sending for.
|
|
253
|
+
if (this.killedSessions.has(context.sessionId)) {
|
|
254
|
+
return [2 /*return*/, this.completeRequest({ context: context, err: SESSION_KILLED_MESSAGE })];
|
|
255
|
+
}
|
|
226
256
|
apiKey = context.apiKey;
|
|
227
257
|
if (!apiKey) {
|
|
228
258
|
return [2 /*return*/, this.completeRequest({ context: context, err: MISSING_API_KEY_MESSAGE })];
|
|
@@ -281,11 +311,11 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
281
311
|
});
|
|
282
312
|
};
|
|
283
313
|
SessionReplayTrackDestination.prototype.sendOnMainThread = function (apiKey, deviceId, context, payload, useRetry) {
|
|
284
|
-
var _a, _b, _c, _d;
|
|
314
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
285
315
|
return __awaiter(this, void 0, void 0, function () {
|
|
286
|
-
var url, version, sampleRate, urlParams, sessionReplayLibrary, payloadJson, globalScope, gzipped,
|
|
287
|
-
return __generator(this, function (
|
|
288
|
-
switch (
|
|
316
|
+
var url, version, sampleRate, urlParams, sessionReplayLibrary, payloadJson, globalScope, gzipped, _h, payloadSize, options, serverUrl, res, skipCode, responseBody, responseBody, _j, e_3;
|
|
317
|
+
return __generator(this, function (_k) {
|
|
318
|
+
switch (_k.label) {
|
|
289
319
|
case 0:
|
|
290
320
|
url = getCurrentUrl();
|
|
291
321
|
version = VERSION;
|
|
@@ -296,21 +326,21 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
296
326
|
type: "".concat(context.type),
|
|
297
327
|
});
|
|
298
328
|
sessionReplayLibrary = "".concat((_b = (_a = context.version) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : 'standalone', "/").concat((_d = (_c = context.version) === null || _c === void 0 ? void 0 : _c.version) !== null && _d !== void 0 ? _d : version);
|
|
299
|
-
|
|
329
|
+
_k.label = 1;
|
|
300
330
|
case 1:
|
|
301
|
-
|
|
331
|
+
_k.trys.push([1, 13, , 14]);
|
|
302
332
|
payloadJson = JSON.stringify(payload);
|
|
303
333
|
globalScope = getGlobalScope();
|
|
304
334
|
if (!(globalScope && 'CompressionStream' in globalScope)) return [3 /*break*/, 3];
|
|
305
335
|
return [4 /*yield*/, gzipJson(payloadJson, globalScope)];
|
|
306
336
|
case 2:
|
|
307
|
-
|
|
337
|
+
_h = _k.sent();
|
|
308
338
|
return [3 /*break*/, 4];
|
|
309
339
|
case 3:
|
|
310
|
-
|
|
311
|
-
|
|
340
|
+
_h = null;
|
|
341
|
+
_k.label = 4;
|
|
312
342
|
case 4:
|
|
313
|
-
gzipped =
|
|
343
|
+
gzipped = _h;
|
|
314
344
|
payloadSize = gzipped ? gzipped.byteLength : new Blob([payloadJson]).size;
|
|
315
345
|
options = {
|
|
316
346
|
headers: __assign({ 'Content-Type': 'application/json', Accept: '*/*', Authorization: "Bearer ".concat(apiKey), 'X-Client-Version': version, 'X-Client-Library': sessionReplayLibrary, 'X-Client-Url': url.substring(0, MAX_URL_LENGTH), 'X-Client-Sample-Rate': "".concat(sampleRate), 'X-Sampling-Hash-Alg': 'xxhash32' }, (gzipped ? { 'Content-Encoding': 'gzip' } : {})),
|
|
@@ -323,17 +353,21 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
323
353
|
serverUrl = "".concat(getServerUrl(context.serverZone, this.trackServerUrl), "?").concat(urlParams.toString());
|
|
324
354
|
return [4 /*yield*/, fetch(serverUrl, options)];
|
|
325
355
|
case 5:
|
|
326
|
-
res =
|
|
356
|
+
res = _k.sent();
|
|
327
357
|
if (res === null) {
|
|
328
358
|
this.completeRequest({ context: context, err: UNEXPECTED_ERROR_MESSAGE });
|
|
329
359
|
return [2 /*return*/];
|
|
330
360
|
}
|
|
361
|
+
if (res.status >= 200 && res.status < 300) {
|
|
362
|
+
skipCode = (_g = (_f = (_e = res.headers) === null || _e === void 0 ? void 0 : _e.get) === null || _f === void 0 ? void 0 : _f.call(_e, EVENT_SKIPPED_HEADER)) !== null && _g !== void 0 ? _g : null;
|
|
363
|
+
this.applyServerDirective(context.sessionId, skipCode);
|
|
364
|
+
}
|
|
331
365
|
if (!!useRetry) return [3 /*break*/, 6];
|
|
332
366
|
responseBody = '';
|
|
333
367
|
try {
|
|
334
368
|
responseBody = JSON.stringify(res.body, null, 2);
|
|
335
369
|
}
|
|
336
|
-
catch (
|
|
370
|
+
catch (_l) {
|
|
337
371
|
// to avoid crash, but don't care about the error, add comment to avoid empty block lint error
|
|
338
372
|
}
|
|
339
373
|
this.completeRequest({ context: context, success: "".concat(res.status, ": ").concat(responseBody) });
|
|
@@ -341,23 +375,23 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
341
375
|
case 6:
|
|
342
376
|
responseBody = '';
|
|
343
377
|
if (!(res.status === 413)) return [3 /*break*/, 10];
|
|
344
|
-
|
|
378
|
+
_k.label = 7;
|
|
345
379
|
case 7:
|
|
346
|
-
|
|
380
|
+
_k.trys.push([7, 9, , 10]);
|
|
347
381
|
return [4 /*yield*/, res.text()];
|
|
348
382
|
case 8:
|
|
349
|
-
responseBody =
|
|
383
|
+
responseBody = _k.sent();
|
|
350
384
|
return [3 /*break*/, 10];
|
|
351
385
|
case 9:
|
|
352
|
-
|
|
386
|
+
_j = _k.sent();
|
|
353
387
|
return [3 /*break*/, 10];
|
|
354
388
|
case 10: return [4 /*yield*/, this.handleReponse(res.status, context, responseBody)];
|
|
355
389
|
case 11:
|
|
356
|
-
|
|
357
|
-
|
|
390
|
+
_k.sent();
|
|
391
|
+
_k.label = 12;
|
|
358
392
|
case 12: return [3 /*break*/, 14];
|
|
359
393
|
case 13:
|
|
360
|
-
e_3 =
|
|
394
|
+
e_3 = _k.sent();
|
|
361
395
|
this.completeRequest({ context: context, err: e_3 });
|
|
362
396
|
return [3 /*break*/, 14];
|
|
363
397
|
case 14: return [2 /*return*/];
|
|
@@ -472,6 +506,85 @@ var SessionReplayTrackDestination = /** @class */ (function () {
|
|
|
472
506
|
this.loggerProvider.log(success);
|
|
473
507
|
}
|
|
474
508
|
};
|
|
509
|
+
/**
|
|
510
|
+
* Applies the server's back-pressure signal carried on a 200 response.
|
|
511
|
+
*
|
|
512
|
+
* - `EVENT_SKIP_CODE_THROTTLED` (server-side rate limit): pause the flush schedule
|
|
513
|
+
* for `THROTTLED_FLUSH_PAUSE_MS` so we keep batching events instead of retry-storming.
|
|
514
|
+
* - `EVENT_SKIP_CODE_CAPTURE_DISABLED` / `EVENT_SKIP_CODE_INVALID_RANGE`: hard kill
|
|
515
|
+
* switch for this session — drop the queued contexts and stop accepting new ones.
|
|
516
|
+
* New sessions are unaffected.
|
|
517
|
+
* - `null` (clean 200, no header): clear any throttle pause; subsequent flushes resume
|
|
518
|
+
* on the normal cadence.
|
|
519
|
+
*/
|
|
520
|
+
SessionReplayTrackDestination.prototype.applyServerDirective = function (sessionId, skipCode) {
|
|
521
|
+
if (skipCode === null) {
|
|
522
|
+
this.flushPauseUntilMs = 0;
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (skipCode === EVENT_SKIP_CODE_THROTTLED) {
|
|
526
|
+
var wasInPause = this.flushPauseUntilMs > Date.now();
|
|
527
|
+
this.flushPauseUntilMs = Date.now() + THROTTLED_FLUSH_PAUSE_MS;
|
|
528
|
+
// Log only on pause-state transitions — a throttled server may reply to many
|
|
529
|
+
// batches per minute, and one log per batch would flood the console.
|
|
530
|
+
if (!wasInPause) {
|
|
531
|
+
this.loggerProvider.log("Session replay throttled by server; pausing flush schedule for ".concat(THROTTLED_FLUSH_PAUSE_MS / 1000, "s"));
|
|
532
|
+
}
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
if (skipCode === EVENT_SKIP_CODE_CAPTURE_DISABLED || skipCode === EVENT_SKIP_CODE_INVALID_RANGE) {
|
|
536
|
+
this.killSession(sessionId, skipCode);
|
|
537
|
+
}
|
|
538
|
+
// Unknown skip codes are ignored — the server may add new ones, and our default
|
|
539
|
+
// behavior (treat as a normal 200) preserves throughput rather than penalizing the
|
|
540
|
+
// session for a code we don't recognize.
|
|
541
|
+
};
|
|
542
|
+
SessionReplayTrackDestination.prototype.killSession = function (sessionId, skipCode) {
|
|
543
|
+
var e_4, _a, e_5, _b;
|
|
544
|
+
if (this.killedSessions.has(sessionId))
|
|
545
|
+
return;
|
|
546
|
+
this.killedSessions.add(sessionId);
|
|
547
|
+
// Set preserves insertion order, so deleting the first key evicts the oldest entry.
|
|
548
|
+
if (this.killedSessions.size > MAX_KILLED_SESSIONS) {
|
|
549
|
+
try {
|
|
550
|
+
for (var _c = __values(this.killedSessions), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
551
|
+
var oldest = _d.value;
|
|
552
|
+
this.killedSessions.delete(oldest);
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
557
|
+
finally {
|
|
558
|
+
try {
|
|
559
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
560
|
+
}
|
|
561
|
+
finally { if (e_4) throw e_4.error; }
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
this.loggerProvider.log("Session replay capture stopped for session ".concat(sessionId, " by server directive ").concat(skipCode, "; remaining events will be dropped"));
|
|
565
|
+
// Drain any queued contexts for this session so their IDB records get cleaned up
|
|
566
|
+
// via onComplete, instead of sitting in the queue waiting for a flush we'll never make.
|
|
567
|
+
var remaining = [];
|
|
568
|
+
try {
|
|
569
|
+
for (var _e = __values(this.queue), _f = _e.next(); !_f.done; _f = _e.next()) {
|
|
570
|
+
var queued = _f.value;
|
|
571
|
+
if (queued.sessionId === sessionId) {
|
|
572
|
+
this.completeRequest({ context: queued, err: SESSION_KILLED_MESSAGE });
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
remaining.push(queued);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
580
|
+
finally {
|
|
581
|
+
try {
|
|
582
|
+
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
|
583
|
+
}
|
|
584
|
+
finally { if (e_5) throw e_5.error; }
|
|
585
|
+
}
|
|
586
|
+
this.queue = remaining;
|
|
587
|
+
};
|
|
475
588
|
return SessionReplayTrackDestination;
|
|
476
589
|
}());
|
|
477
590
|
export { SessionReplayTrackDestination };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"track-destination.js","sourceRoot":"","sources":["../../src/track-destination.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseTransport, getGlobalScope, ILogger, ServerZone, Status } from '@amplitude/analytics-core';\nimport { getCurrentUrl, getServerUrl } from './helpers';\nimport {\n MAX_RETRIES_EXCEEDED_MESSAGE,\n MISSING_API_KEY_MESSAGE,\n MISSING_DEVICE_ID_MESSAGE,\n UNEXPECTED_ERROR_MESSAGE,\n UNEXPECTED_NETWORK_ERROR_MESSAGE,\n} from './messages';\nimport {\n SessionReplayTrackDestination as AmplitudeSessionReplayTrackDestination,\n SessionReplayDestination,\n SessionReplayDestinationContext,\n} from './typings/session-replay';\nimport { VERSION } from './version';\nimport { MAX_URL_LENGTH, KB_SIZE, MAX_KEEPALIVE_BYTES, WAF_PAYLOAD_TOO_LARGE_PATTERN } from './constants';\nimport { gzipJson } from './utils/gzip';\n\ninterface WorkerCompleteMessage {\n type: 'complete';\n id: string;\n err?: string;\n}\ninterface WorkerLogMessage {\n type: 'log' | 'warn';\n id: string;\n message: string;\n}\ninterface WorkerPayloadTooLargeMessage {\n type: 'payload_too_large';\n id: string;\n isWaf: boolean;\n}\ntype WorkerMessage = WorkerCompleteMessage | WorkerLogMessage | WorkerPayloadTooLargeMessage;\n\nexport type PayloadBatcher = ({ version, events }: { version: number; events: string[] }) => {\n version: number;\n events: unknown[];\n};\n\nexport class SessionReplayTrackDestination implements AmplitudeSessionReplayTrackDestination {\n loggerProvider: ILogger;\n storageKey = '';\n trackServerUrl?: string;\n retryTimeout = 1000;\n private scheduled: ReturnType<typeof setTimeout> | null = null;\n payloadBatcher: PayloadBatcher;\n queue: SessionReplayDestinationContext[] = [];\n private worker?: Worker;\n private sendIdCounter = 0;\n private pendingWorkerRequests = new Map<string, { context: SessionReplayDestinationContext; resolve: () => void }>();\n\n constructor({\n trackServerUrl,\n loggerProvider,\n payloadBatcher,\n workerScript,\n }: {\n trackServerUrl?: string;\n loggerProvider: ILogger;\n payloadBatcher?: PayloadBatcher;\n workerScript?: string;\n }) {\n this.loggerProvider = loggerProvider;\n this.payloadBatcher = payloadBatcher ? payloadBatcher : (payload) => payload;\n this.trackServerUrl = trackServerUrl;\n\n if (workerScript) {\n try {\n const blob = new Blob([workerScript], { type: 'application/javascript' });\n const blobUrl = URL.createObjectURL(blob);\n const worker = new Worker(blobUrl);\n worker.onerror = (e) => {\n e.preventDefault();\n loggerProvider.error(\n `Track destination worker failed, falling back to main-thread sending: ${e.message} (${e.filename}:${e.lineno})`,\n );\n worker.terminate();\n this.worker = undefined;\n // Resolve pending promises so flush() doesn't hang. Do NOT call completeRequest\n // here — the events were never delivered, so onComplete must not fire and the\n // IDB/memory store entries must remain intact for recovery by sendStoredEvents.\n for (const [, pending] of this.pendingWorkerRequests) {\n loggerProvider.warn(`Session replay event send failed due to worker crash: ${e.message}`);\n pending.resolve();\n }\n this.pendingWorkerRequests.clear();\n };\n worker.onmessage = (e: MessageEvent<WorkerMessage>) => {\n const msg = e.data;\n if (msg.type === 'log') {\n loggerProvider.log(msg.message);\n } else if (msg.type === 'warn') {\n loggerProvider.warn(msg.message);\n } else if (msg.type === 'payload_too_large') {\n const pending = this.pendingWorkerRequests.get(msg.id);\n if (pending) {\n this.handlePayloadTooLargeResponse(pending.context, msg.isWaf);\n pending.resolve();\n this.pendingWorkerRequests.delete(msg.id);\n }\n } else if (msg.type === 'complete') {\n const pending = this.pendingWorkerRequests.get(msg.id);\n if (pending) {\n this.completeRequest({ context: pending.context });\n pending.resolve();\n this.pendingWorkerRequests.delete(msg.id);\n }\n }\n };\n this.worker = worker;\n } catch (error) {\n loggerProvider.error('Failed to create track destination worker, falling back to main-thread sending:', error);\n }\n }\n }\n\n sendEventsList(destinationData: SessionReplayDestination) {\n this.addToQueue({\n ...destinationData,\n attempts: 0,\n timeout: 0,\n });\n }\n\n /**\n * Sends events via navigator.sendBeacon on page exit.\n * Beacon payloads are sent as uncompressed JSON because sendBeacon does not support\n * Content-Encoding, and small incremental batches don't benefit much from compression.\n * The full snapshot has already been sent eagerly via fetch, so the beacon only needs\n * to cover the remaining incremental events since the last fetch flush.\n */\n sendBeacon({\n events,\n sessionId,\n deviceId,\n apiKey,\n serverZone,\n }: {\n events: string[];\n sessionId: string | number;\n deviceId: string;\n apiKey: string;\n serverZone?: keyof typeof ServerZone;\n }) {\n const MAX_BEACON_BYTES = 64 * 1024;\n const byteLength = (s: string) => new Blob([s]).size;\n let trimmedEvents = events;\n let payload = JSON.stringify({ version: 2, events: trimmedEvents });\n if (byteLength(payload) > MAX_BEACON_BYTES) {\n // Binary search for the largest prefix that fits within the beacon size limit.\n // Uses Blob.size to get the UTF-8 byte count, which is what sendBeacon measures.\n let lo = 0;\n let hi = trimmedEvents.length;\n while (lo < hi) {\n const mid = Math.floor((lo + hi + 1) / 2);\n if (byteLength(JSON.stringify({ version: 2, events: trimmedEvents.slice(0, mid) })) <= MAX_BEACON_BYTES) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n trimmedEvents = trimmedEvents.slice(0, lo);\n payload = JSON.stringify({ version: 2, events: trimmedEvents });\n this.loggerProvider.warn(\n `sendBeacon payload exceeded 64 KB limit, trimmed from ${events.length} to ${trimmedEvents.length} events`,\n );\n }\n if (trimmedEvents.length === 0) {\n return;\n }\n const urlParams = new URLSearchParams({\n device_id: deviceId,\n session_id: String(sessionId),\n type: 'replay',\n api_key: apiKey,\n });\n const serverUrl = `${getServerUrl(serverZone, this.trackServerUrl)}?${urlParams.toString()}`;\n const globalScope = getGlobalScope();\n try {\n // Wrap in a Blob to set Content-Type: application/json; a plain string would\n // cause the browser to send Content-Type: text/plain, which the server rejects.\n const payloadBlob = new Blob([payload], { type: 'application/json' });\n const sent = globalScope?.navigator?.sendBeacon?.(serverUrl, payloadBlob);\n if (sent === false) {\n this.loggerProvider.warn('sendBeacon failed to queue session replay payload');\n }\n } catch {\n // Best effort — no fallback on page exit.\n }\n }\n\n addToQueue(...list: SessionReplayDestinationContext[]) {\n const tryable = list.filter((context) => {\n if (context.attempts < (context.flushMaxRetries || 0)) {\n context.attempts += 1;\n return true;\n }\n this.completeRequest({\n context,\n err: MAX_RETRIES_EXCEEDED_MESSAGE,\n });\n return false;\n });\n tryable.forEach((context) => {\n this.queue = this.queue.concat(context);\n this.schedule(0);\n });\n }\n\n schedule(timeout: number) {\n if (this.scheduled) return;\n this.scheduled = setTimeout(() => {\n void this.flush(true).then(() => {\n if (this.queue.length > 0) {\n this.schedule(timeout);\n }\n });\n }, timeout);\n }\n\n async flush(useRetry = false) {\n const list = this.queue;\n this.queue = [];\n\n if (this.scheduled) {\n clearTimeout(this.scheduled);\n this.scheduled = null;\n }\n\n for (const context of list) {\n await this.send(context, useRetry);\n }\n }\n\n async send(context: SessionReplayDestinationContext, useRetry = true) {\n const apiKey = context.apiKey;\n if (!apiKey) {\n return this.completeRequest({ context, err: MISSING_API_KEY_MESSAGE });\n }\n const deviceId = context.deviceId;\n if (!deviceId) {\n return this.completeRequest({ context, err: MISSING_DEVICE_ID_MESSAGE });\n }\n\n const payload = this.payloadBatcher({\n version: 1,\n events: context.events,\n });\n\n if (payload.events.length === 0) {\n this.completeRequest({ context });\n return;\n }\n\n const { worker } = this;\n if (worker) {\n return this.sendViaWorker(worker, context, payload, useRetry);\n }\n\n return this.sendOnMainThread(apiKey, deviceId, context, payload, useRetry);\n }\n\n private async sendViaWorker(\n worker: Worker,\n context: SessionReplayDestinationContext,\n payload: { version: number; events: unknown[] },\n useRetry: boolean,\n ): Promise<void> {\n const id = `${++this.sendIdCounter}`;\n return new Promise<void>((resolve) => {\n this.pendingWorkerRequests.set(id, { context, resolve });\n worker.postMessage({\n type: 'send',\n id,\n payload,\n useRetry,\n context: {\n apiKey: context.apiKey,\n deviceId: context.deviceId,\n sessionId: context.sessionId,\n events: context.events,\n eventType: context.type,\n flushMaxRetries: context.flushMaxRetries ?? 0,\n sampleRate: context.sampleRate,\n serverZone: context.serverZone,\n trackServerUrl: this.trackServerUrl,\n version: context.version,\n currentUrl: getCurrentUrl(),\n sdkVersion: VERSION,\n },\n });\n });\n }\n\n private async sendOnMainThread(\n apiKey: string,\n deviceId: string,\n context: SessionReplayDestinationContext,\n payload: { version: number; events: unknown[] },\n useRetry: boolean,\n ): Promise<void> {\n const url = getCurrentUrl();\n const version = VERSION;\n const sampleRate = context.sampleRate;\n const urlParams = new URLSearchParams({\n device_id: deviceId,\n session_id: `${context.sessionId}`,\n type: `${context.type}`,\n });\n const sessionReplayLibrary = `${context.version?.type ?? 'standalone'}/${context.version?.version ?? version}`;\n\n try {\n const payloadJson = JSON.stringify(payload);\n // Only await gzip when CompressionStream is actually available; skipping the\n // await entirely preserves the synchronous fast-path for browsers/environments\n // (e.g. Jest) that don't support it, keeping retry-timing tests unaffected.\n const globalScope = getGlobalScope();\n const gzipped =\n globalScope && 'CompressionStream' in globalScope ? await gzipJson(payloadJson, globalScope) : null;\n const payloadSize = gzipped ? gzipped.byteLength : new Blob([payloadJson]).size;\n const options: RequestInit = {\n headers: {\n 'Content-Type': 'application/json',\n Accept: '*/*',\n Authorization: `Bearer ${apiKey}`,\n 'X-Client-Version': version,\n 'X-Client-Library': sessionReplayLibrary,\n 'X-Client-Url': url.substring(0, MAX_URL_LENGTH), // limit url length to 1000 characters to avoid ELB 400 error\n 'X-Client-Sample-Rate': `${sampleRate}`,\n 'X-Sampling-Hash-Alg': 'xxhash32',\n ...(gzipped ? { 'Content-Encoding': 'gzip' } : {}),\n },\n body: (gzipped ?? payloadJson) as BodyInit,\n method: 'POST',\n // keepalive lets the request survive page navigation, preventing 499 (client-closed) errors.\n // Must stay under the browser's 64 KB keepalive budget; large payloads skip it.\n keepalive: payloadSize <= MAX_KEEPALIVE_BYTES,\n };\n\n const serverUrl = `${getServerUrl(context.serverZone, this.trackServerUrl)}?${urlParams.toString()}`;\n const res = await fetch(serverUrl, options);\n if (res === null) {\n this.completeRequest({ context, err: UNEXPECTED_ERROR_MESSAGE });\n return;\n }\n if (!useRetry) {\n let responseBody = '';\n try {\n responseBody = JSON.stringify(res.body, null, 2);\n } catch {\n // to avoid crash, but don't care about the error, add comment to avoid empty block lint error\n }\n this.completeRequest({ context, success: `${res.status}: ${responseBody}` });\n } else {\n let responseBody = '';\n if (res.status === 413) {\n try {\n responseBody = await res.text();\n } catch {\n // best effort\n }\n }\n await this.handleReponse(res.status, context, responseBody);\n }\n } catch (e) {\n this.completeRequest({ context, err: e as string });\n }\n }\n\n async handleReponse(status: number, context: SessionReplayDestinationContext, responseBody = '') {\n const parsedStatus = new BaseTransport().buildStatus(status);\n switch (parsedStatus) {\n case Status.Success:\n this.handleSuccessResponse(context);\n break;\n case Status.Failed:\n case Status.Timeout: // 408: server timed out waiting for request, data not received\n case Status.RateLimit: // 429: retry with existing backoff rather than silently dropping\n await this.handleOtherResponse(context);\n break;\n case Status.PayloadTooLarge:\n this.handlePayloadTooLargeResponse(context, WAF_PAYLOAD_TOO_LARGE_PATTERN.test(responseBody));\n break;\n default:\n // 499 (client closed connection / upstream dropped) is also retryable\n if (status === 499) {\n await this.handleOtherResponse(context);\n break;\n }\n this.completeRequest({ context, err: UNEXPECTED_NETWORK_ERROR_MESSAGE });\n }\n }\n\n handlePayloadTooLargeResponse(context: SessionReplayDestinationContext, isWaf: boolean): void {\n const source = isWaf ? 'WAF (compressed payload too large)' : 'server (event too large)';\n const totalSizeKB = Math.round(context.events.reduce((sum, e) => sum + e.length, 0) / KB_SIZE);\n\n if (!isWaf) {\n this.completeRequest({\n context,\n err: `Session replay event batch dropped: ${source} rejected payload (${context.events.length} events, ${totalSizeKB} KB) — not retrying non-WAF 413`,\n });\n return;\n }\n\n if (context.events.length === 1) {\n this.completeRequest({\n context,\n err: `Session replay event dropped: single event (${totalSizeKB} KB, 1 event) rejected by ${source} — cannot split further`,\n });\n return;\n }\n\n this.loggerProvider.warn(\n `Session replay event batch rejected by ${source} (${context.events.length} events, ${totalSizeKB} KB total) — splitting and retrying`,\n );\n\n // Clean up the original IDB record, then re-enqueue both halves as new in-memory batches\n void context.onComplete();\n const noop = (): Promise<void> => Promise.resolve();\n const mid = Math.floor(context.events.length / 2);\n this.sendEventsList({ ...context, events: context.events.slice(0, mid), onComplete: noop });\n this.sendEventsList({ ...context, events: context.events.slice(mid), onComplete: noop });\n }\n\n handleSuccessResponse(context: SessionReplayDestinationContext) {\n const sizeOfEventsList = Math.round(new Blob(context.events).size / KB_SIZE);\n this.completeRequest({\n context,\n success: `Session replay event batch tracked successfully for session id ${context.sessionId}, size of events: ${sizeOfEventsList} KB`,\n });\n }\n\n async handleOtherResponse(context: SessionReplayDestinationContext) {\n const delay = Math.random() * context.attempts * this.retryTimeout;\n context.attempts++;\n if (context.attempts > (context.flushMaxRetries || 0)) {\n this.completeRequest({ context, err: MAX_RETRIES_EXCEEDED_MESSAGE });\n return;\n }\n await new Promise<void>((resolve) => setTimeout(resolve, delay));\n await this.send(context, true);\n }\n\n completeRequest({\n context,\n err,\n success,\n }: {\n context: SessionReplayDestinationContext;\n err?: string;\n success?: string;\n }) {\n void context.onComplete();\n if (err) {\n this.loggerProvider.warn(err);\n } else if (success) {\n this.loggerProvider.log(success);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"track-destination.js","sourceRoot":"","sources":["../../src/track-destination.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseTransport, getGlobalScope, ILogger, ServerZone, Status } from '@amplitude/analytics-core';\nimport { getCurrentUrl, getServerUrl } from './helpers';\nimport {\n MAX_RETRIES_EXCEEDED_MESSAGE,\n MISSING_API_KEY_MESSAGE,\n MISSING_DEVICE_ID_MESSAGE,\n SESSION_KILLED_MESSAGE,\n UNEXPECTED_ERROR_MESSAGE,\n UNEXPECTED_NETWORK_ERROR_MESSAGE,\n} from './messages';\nimport {\n SessionReplayTrackDestination as AmplitudeSessionReplayTrackDestination,\n SessionReplayDestination,\n SessionReplayDestinationContext,\n} from './typings/session-replay';\nimport { VERSION } from './version';\nimport {\n MAX_URL_LENGTH,\n KB_SIZE,\n MAX_KEEPALIVE_BYTES,\n WAF_PAYLOAD_TOO_LARGE_PATTERN,\n EVENT_SKIPPED_HEADER,\n EVENT_SKIP_CODE_THROTTLED,\n EVENT_SKIP_CODE_INVALID_RANGE,\n EVENT_SKIP_CODE_CAPTURE_DISABLED,\n THROTTLED_FLUSH_PAUSE_MS,\n} from './constants';\nimport { gzipJson } from './utils/gzip';\n\ninterface WorkerCompleteMessage {\n type: 'complete';\n id: string;\n err?: string;\n // null when the response was a clean 200 (no skip header), undefined when the\n // request did not produce a 200, otherwise the server's skip-code string.\n skipCode?: string | null;\n}\ninterface WorkerLogMessage {\n type: 'log' | 'warn';\n id: string;\n message: string;\n}\ninterface WorkerPayloadTooLargeMessage {\n type: 'payload_too_large';\n id: string;\n isWaf: boolean;\n}\ntype WorkerMessage = WorkerCompleteMessage | WorkerLogMessage | WorkerPayloadTooLargeMessage;\n\nexport type PayloadBatcher = ({ version, events }: { version: number; events: string[] }) => {\n version: number;\n events: unknown[];\n};\n\n// Bounded so a long-lived SDK instance can't accumulate kill records indefinitely;\n// sessions are time-bounded in practice, this cap is just a defensive ceiling.\nconst MAX_KILLED_SESSIONS = 256;\n\nexport class SessionReplayTrackDestination implements AmplitudeSessionReplayTrackDestination {\n loggerProvider: ILogger;\n storageKey = '';\n trackServerUrl?: string;\n retryTimeout = 1000;\n private scheduled: ReturnType<typeof setTimeout> | null = null;\n payloadBatcher: PayloadBatcher;\n queue: SessionReplayDestinationContext[] = [];\n private worker?: Worker;\n private sendIdCounter = 0;\n private pendingWorkerRequests = new Map<string, { context: SessionReplayDestinationContext; resolve: () => void }>();\n // Server back-pressure state, fed by the X-Session-Replay-Event-Skipped header on 200s.\n // The server uses this header (instead of 4xx) to signal a deliberate no-retry drop so SDKs\n // don't retry-storm. We honor it here by slowing or stopping our flush schedule.\n private flushPauseUntilMs = 0;\n private killedSessions = new Set<string | number>();\n\n constructor({\n trackServerUrl,\n loggerProvider,\n payloadBatcher,\n workerScript,\n }: {\n trackServerUrl?: string;\n loggerProvider: ILogger;\n payloadBatcher?: PayloadBatcher;\n workerScript?: string;\n }) {\n this.loggerProvider = loggerProvider;\n this.payloadBatcher = payloadBatcher ? payloadBatcher : (payload) => payload;\n this.trackServerUrl = trackServerUrl;\n\n if (workerScript) {\n try {\n const blob = new Blob([workerScript], { type: 'application/javascript' });\n const blobUrl = URL.createObjectURL(blob);\n const worker = new Worker(blobUrl);\n worker.onerror = (e) => {\n e.preventDefault();\n loggerProvider.error(\n `Track destination worker failed, falling back to main-thread sending: ${e.message} (${e.filename}:${e.lineno})`,\n );\n worker.terminate();\n this.worker = undefined;\n // Resolve pending promises so flush() doesn't hang. Do NOT call completeRequest\n // here — the events were never delivered, so onComplete must not fire and the\n // IDB/memory store entries must remain intact for recovery by sendStoredEvents.\n for (const [, pending] of this.pendingWorkerRequests) {\n loggerProvider.warn(`Session replay event send failed due to worker crash: ${e.message}`);\n pending.resolve();\n }\n this.pendingWorkerRequests.clear();\n };\n worker.onmessage = (e: MessageEvent<WorkerMessage>) => {\n const msg = e.data;\n if (msg.type === 'log') {\n loggerProvider.log(msg.message);\n } else if (msg.type === 'warn') {\n loggerProvider.warn(msg.message);\n } else if (msg.type === 'payload_too_large') {\n const pending = this.pendingWorkerRequests.get(msg.id);\n if (pending) {\n this.handlePayloadTooLargeResponse(pending.context, msg.isWaf);\n pending.resolve();\n this.pendingWorkerRequests.delete(msg.id);\n }\n } else if (msg.type === 'complete') {\n const pending = this.pendingWorkerRequests.get(msg.id);\n if (pending) {\n if (msg.skipCode !== undefined) {\n this.applyServerDirective(pending.context.sessionId, msg.skipCode);\n }\n this.completeRequest({ context: pending.context });\n pending.resolve();\n this.pendingWorkerRequests.delete(msg.id);\n }\n }\n };\n this.worker = worker;\n } catch (error) {\n loggerProvider.error('Failed to create track destination worker, falling back to main-thread sending:', error);\n }\n }\n }\n\n sendEventsList(destinationData: SessionReplayDestination) {\n this.addToQueue({\n ...destinationData,\n attempts: 0,\n timeout: 0,\n });\n }\n\n /**\n * Sends events via navigator.sendBeacon on page exit.\n * Beacon payloads are sent as uncompressed JSON because sendBeacon does not support\n * Content-Encoding, and small incremental batches don't benefit much from compression.\n * The full snapshot has already been sent eagerly via fetch, so the beacon only needs\n * to cover the remaining incremental events since the last fetch flush.\n */\n sendBeacon({\n events,\n sessionId,\n deviceId,\n apiKey,\n serverZone,\n }: {\n events: string[];\n sessionId: string | number;\n deviceId: string;\n apiKey: string;\n serverZone?: keyof typeof ServerZone;\n }) {\n const MAX_BEACON_BYTES = 64 * 1024;\n const byteLength = (s: string) => new Blob([s]).size;\n let trimmedEvents = events;\n let payload = JSON.stringify({ version: 2, events: trimmedEvents });\n if (byteLength(payload) > MAX_BEACON_BYTES) {\n // Binary search for the largest prefix that fits within the beacon size limit.\n // Uses Blob.size to get the UTF-8 byte count, which is what sendBeacon measures.\n let lo = 0;\n let hi = trimmedEvents.length;\n while (lo < hi) {\n const mid = Math.floor((lo + hi + 1) / 2);\n if (byteLength(JSON.stringify({ version: 2, events: trimmedEvents.slice(0, mid) })) <= MAX_BEACON_BYTES) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n trimmedEvents = trimmedEvents.slice(0, lo);\n payload = JSON.stringify({ version: 2, events: trimmedEvents });\n this.loggerProvider.warn(\n `sendBeacon payload exceeded 64 KB limit, trimmed from ${events.length} to ${trimmedEvents.length} events`,\n );\n }\n if (trimmedEvents.length === 0) {\n return;\n }\n const urlParams = new URLSearchParams({\n device_id: deviceId,\n session_id: String(sessionId),\n type: 'replay',\n api_key: apiKey,\n });\n const serverUrl = `${getServerUrl(serverZone, this.trackServerUrl)}?${urlParams.toString()}`;\n const globalScope = getGlobalScope();\n try {\n // Wrap in a Blob to set Content-Type: application/json; a plain string would\n // cause the browser to send Content-Type: text/plain, which the server rejects.\n const payloadBlob = new Blob([payload], { type: 'application/json' });\n const sent = globalScope?.navigator?.sendBeacon?.(serverUrl, payloadBlob);\n if (sent === false) {\n this.loggerProvider.warn('sendBeacon failed to queue session replay payload');\n }\n } catch {\n // Best effort — no fallback on page exit.\n }\n }\n\n addToQueue(...list: SessionReplayDestinationContext[]) {\n const tryable = list.filter((context) => {\n if (this.killedSessions.has(context.sessionId)) {\n // Server has signaled capture_disabled or session_in_invalid_range for this session;\n // drop the batch (and clean up its IDB record via onComplete) instead of POSTing.\n this.completeRequest({\n context,\n err: SESSION_KILLED_MESSAGE,\n });\n return false;\n }\n if (context.attempts < (context.flushMaxRetries || 0)) {\n context.attempts += 1;\n return true;\n }\n this.completeRequest({\n context,\n err: MAX_RETRIES_EXCEEDED_MESSAGE,\n });\n return false;\n });\n tryable.forEach((context) => {\n this.queue = this.queue.concat(context);\n this.schedule(0);\n });\n }\n\n schedule(timeout: number) {\n if (this.scheduled) return;\n // If the server signaled throttling on a recent 200, defer the next flush until the\n // pause window ends. This lets us keep batching events without retry-storming the server.\n const pauseRemaining = this.flushPauseUntilMs - Date.now();\n const effectiveTimeout = pauseRemaining > timeout ? pauseRemaining : timeout;\n this.scheduled = setTimeout(() => {\n void this.flush(true).then(() => {\n if (this.queue.length > 0) {\n this.schedule(timeout);\n }\n });\n }, effectiveTimeout);\n }\n\n async flush(useRetry = false) {\n const list = this.queue;\n this.queue = [];\n\n if (this.scheduled) {\n clearTimeout(this.scheduled);\n this.scheduled = null;\n }\n\n for (const context of list) {\n await this.send(context, useRetry);\n }\n }\n\n async send(context: SessionReplayDestinationContext, useRetry = true) {\n // A kill directive can arrive between flush() snapshotting the queue and us reaching\n // each context. Re-check before hitting the network so we don't waste POSTs on a\n // session the server has already told us to stop sending for.\n if (this.killedSessions.has(context.sessionId)) {\n return this.completeRequest({ context, err: SESSION_KILLED_MESSAGE });\n }\n const apiKey = context.apiKey;\n if (!apiKey) {\n return this.completeRequest({ context, err: MISSING_API_KEY_MESSAGE });\n }\n const deviceId = context.deviceId;\n if (!deviceId) {\n return this.completeRequest({ context, err: MISSING_DEVICE_ID_MESSAGE });\n }\n\n const payload = this.payloadBatcher({\n version: 1,\n events: context.events,\n });\n\n if (payload.events.length === 0) {\n this.completeRequest({ context });\n return;\n }\n\n const { worker } = this;\n if (worker) {\n return this.sendViaWorker(worker, context, payload, useRetry);\n }\n\n return this.sendOnMainThread(apiKey, deviceId, context, payload, useRetry);\n }\n\n private async sendViaWorker(\n worker: Worker,\n context: SessionReplayDestinationContext,\n payload: { version: number; events: unknown[] },\n useRetry: boolean,\n ): Promise<void> {\n const id = `${++this.sendIdCounter}`;\n return new Promise<void>((resolve) => {\n this.pendingWorkerRequests.set(id, { context, resolve });\n worker.postMessage({\n type: 'send',\n id,\n payload,\n useRetry,\n context: {\n apiKey: context.apiKey,\n deviceId: context.deviceId,\n sessionId: context.sessionId,\n events: context.events,\n eventType: context.type,\n flushMaxRetries: context.flushMaxRetries ?? 0,\n sampleRate: context.sampleRate,\n serverZone: context.serverZone,\n trackServerUrl: this.trackServerUrl,\n version: context.version,\n currentUrl: getCurrentUrl(),\n sdkVersion: VERSION,\n },\n });\n });\n }\n\n private async sendOnMainThread(\n apiKey: string,\n deviceId: string,\n context: SessionReplayDestinationContext,\n payload: { version: number; events: unknown[] },\n useRetry: boolean,\n ): Promise<void> {\n const url = getCurrentUrl();\n const version = VERSION;\n const sampleRate = context.sampleRate;\n const urlParams = new URLSearchParams({\n device_id: deviceId,\n session_id: `${context.sessionId}`,\n type: `${context.type}`,\n });\n const sessionReplayLibrary = `${context.version?.type ?? 'standalone'}/${context.version?.version ?? version}`;\n\n try {\n const payloadJson = JSON.stringify(payload);\n // Only await gzip when CompressionStream is actually available; skipping the\n // await entirely preserves the synchronous fast-path for browsers/environments\n // (e.g. Jest) that don't support it, keeping retry-timing tests unaffected.\n const globalScope = getGlobalScope();\n const gzipped =\n globalScope && 'CompressionStream' in globalScope ? await gzipJson(payloadJson, globalScope) : null;\n const payloadSize = gzipped ? gzipped.byteLength : new Blob([payloadJson]).size;\n const options: RequestInit = {\n headers: {\n 'Content-Type': 'application/json',\n Accept: '*/*',\n Authorization: `Bearer ${apiKey}`,\n 'X-Client-Version': version,\n 'X-Client-Library': sessionReplayLibrary,\n 'X-Client-Url': url.substring(0, MAX_URL_LENGTH), // limit url length to 1000 characters to avoid ELB 400 error\n 'X-Client-Sample-Rate': `${sampleRate}`,\n 'X-Sampling-Hash-Alg': 'xxhash32',\n ...(gzipped ? { 'Content-Encoding': 'gzip' } : {}),\n },\n body: (gzipped ?? payloadJson) as BodyInit,\n method: 'POST',\n // keepalive lets the request survive page navigation, preventing 499 (client-closed) errors.\n // Must stay under the browser's 64 KB keepalive budget; large payloads skip it.\n keepalive: payloadSize <= MAX_KEEPALIVE_BYTES,\n };\n\n const serverUrl = `${getServerUrl(context.serverZone, this.trackServerUrl)}?${urlParams.toString()}`;\n const res = await fetch(serverUrl, options);\n if (res === null) {\n this.completeRequest({ context, err: UNEXPECTED_ERROR_MESSAGE });\n return;\n }\n if (res.status >= 200 && res.status < 300) {\n const skipCode = res.headers?.get?.(EVENT_SKIPPED_HEADER) ?? null;\n this.applyServerDirective(context.sessionId, skipCode);\n }\n if (!useRetry) {\n let responseBody = '';\n try {\n responseBody = JSON.stringify(res.body, null, 2);\n } catch {\n // to avoid crash, but don't care about the error, add comment to avoid empty block lint error\n }\n this.completeRequest({ context, success: `${res.status}: ${responseBody}` });\n } else {\n let responseBody = '';\n if (res.status === 413) {\n try {\n responseBody = await res.text();\n } catch {\n // best effort\n }\n }\n await this.handleReponse(res.status, context, responseBody);\n }\n } catch (e) {\n this.completeRequest({ context, err: e as string });\n }\n }\n\n async handleReponse(status: number, context: SessionReplayDestinationContext, responseBody = '') {\n const parsedStatus = new BaseTransport().buildStatus(status);\n switch (parsedStatus) {\n case Status.Success:\n this.handleSuccessResponse(context);\n break;\n case Status.Failed:\n case Status.Timeout: // 408: server timed out waiting for request, data not received\n case Status.RateLimit: // 429: retry with existing backoff rather than silently dropping\n await this.handleOtherResponse(context);\n break;\n case Status.PayloadTooLarge:\n this.handlePayloadTooLargeResponse(context, WAF_PAYLOAD_TOO_LARGE_PATTERN.test(responseBody));\n break;\n default:\n // 499 (client closed connection / upstream dropped) is also retryable\n if (status === 499) {\n await this.handleOtherResponse(context);\n break;\n }\n this.completeRequest({ context, err: UNEXPECTED_NETWORK_ERROR_MESSAGE });\n }\n }\n\n handlePayloadTooLargeResponse(context: SessionReplayDestinationContext, isWaf: boolean): void {\n const source = isWaf ? 'WAF (compressed payload too large)' : 'server (event too large)';\n const totalSizeKB = Math.round(context.events.reduce((sum, e) => sum + e.length, 0) / KB_SIZE);\n\n if (!isWaf) {\n this.completeRequest({\n context,\n err: `Session replay event batch dropped: ${source} rejected payload (${context.events.length} events, ${totalSizeKB} KB) — not retrying non-WAF 413`,\n });\n return;\n }\n\n if (context.events.length === 1) {\n this.completeRequest({\n context,\n err: `Session replay event dropped: single event (${totalSizeKB} KB, 1 event) rejected by ${source} — cannot split further`,\n });\n return;\n }\n\n this.loggerProvider.warn(\n `Session replay event batch rejected by ${source} (${context.events.length} events, ${totalSizeKB} KB total) — splitting and retrying`,\n );\n\n // Clean up the original IDB record, then re-enqueue both halves as new in-memory batches\n void context.onComplete();\n const noop = (): Promise<void> => Promise.resolve();\n const mid = Math.floor(context.events.length / 2);\n this.sendEventsList({ ...context, events: context.events.slice(0, mid), onComplete: noop });\n this.sendEventsList({ ...context, events: context.events.slice(mid), onComplete: noop });\n }\n\n handleSuccessResponse(context: SessionReplayDestinationContext) {\n const sizeOfEventsList = Math.round(new Blob(context.events).size / KB_SIZE);\n this.completeRequest({\n context,\n success: `Session replay event batch tracked successfully for session id ${context.sessionId}, size of events: ${sizeOfEventsList} KB`,\n });\n }\n\n async handleOtherResponse(context: SessionReplayDestinationContext) {\n const delay = Math.random() * context.attempts * this.retryTimeout;\n context.attempts++;\n if (context.attempts > (context.flushMaxRetries || 0)) {\n this.completeRequest({ context, err: MAX_RETRIES_EXCEEDED_MESSAGE });\n return;\n }\n await new Promise<void>((resolve) => setTimeout(resolve, delay));\n await this.send(context, true);\n }\n\n completeRequest({\n context,\n err,\n success,\n }: {\n context: SessionReplayDestinationContext;\n err?: string;\n success?: string;\n }) {\n void context.onComplete();\n if (err) {\n this.loggerProvider.warn(err);\n } else if (success) {\n this.loggerProvider.log(success);\n }\n }\n\n /**\n * Applies the server's back-pressure signal carried on a 200 response.\n *\n * - `EVENT_SKIP_CODE_THROTTLED` (server-side rate limit): pause the flush schedule\n * for `THROTTLED_FLUSH_PAUSE_MS` so we keep batching events instead of retry-storming.\n * - `EVENT_SKIP_CODE_CAPTURE_DISABLED` / `EVENT_SKIP_CODE_INVALID_RANGE`: hard kill\n * switch for this session — drop the queued contexts and stop accepting new ones.\n * New sessions are unaffected.\n * - `null` (clean 200, no header): clear any throttle pause; subsequent flushes resume\n * on the normal cadence.\n */\n private applyServerDirective(sessionId: string | number, skipCode: string | null) {\n if (skipCode === null) {\n this.flushPauseUntilMs = 0;\n return;\n }\n if (skipCode === EVENT_SKIP_CODE_THROTTLED) {\n const wasInPause = this.flushPauseUntilMs > Date.now();\n this.flushPauseUntilMs = Date.now() + THROTTLED_FLUSH_PAUSE_MS;\n // Log only on pause-state transitions — a throttled server may reply to many\n // batches per minute, and one log per batch would flood the console.\n if (!wasInPause) {\n this.loggerProvider.log(\n `Session replay throttled by server; pausing flush schedule for ${THROTTLED_FLUSH_PAUSE_MS / 1000}s`,\n );\n }\n return;\n }\n if (skipCode === EVENT_SKIP_CODE_CAPTURE_DISABLED || skipCode === EVENT_SKIP_CODE_INVALID_RANGE) {\n this.killSession(sessionId, skipCode);\n }\n // Unknown skip codes are ignored — the server may add new ones, and our default\n // behavior (treat as a normal 200) preserves throughput rather than penalizing the\n // session for a code we don't recognize.\n }\n\n private killSession(sessionId: string | number, skipCode: string) {\n if (this.killedSessions.has(sessionId)) return;\n this.killedSessions.add(sessionId);\n // Set preserves insertion order, so deleting the first key evicts the oldest entry.\n if (this.killedSessions.size > MAX_KILLED_SESSIONS) {\n for (const oldest of this.killedSessions) {\n this.killedSessions.delete(oldest);\n break;\n }\n }\n this.loggerProvider.log(\n `Session replay capture stopped for session ${sessionId} by server directive ${skipCode}; remaining events will be dropped`,\n );\n // Drain any queued contexts for this session so their IDB records get cleaned up\n // via onComplete, instead of sitting in the queue waiting for a flush we'll never make.\n const remaining: SessionReplayDestinationContext[] = [];\n for (const queued of this.queue) {\n if (queued.sessionId === sessionId) {\n this.completeRequest({ context: queued, err: SESSION_KILLED_MESSAGE });\n } else {\n remaining.push(queued);\n }\n }\n this.queue = remaining;\n }\n}\n"]}
|
package/lib/esm/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "1.42.
|
|
1
|
+
export declare const VERSION = "1.42.3";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/lib/esm/version.js
CHANGED
package/lib/esm/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,MAAM,CAAC,IAAM,OAAO,GAAG,QAAQ,CAAC","sourcesContent":["// Autogenerated by `pnpm version-file`. DO NOT EDIT\nexport const VERSION = '1.42.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,MAAM,CAAC,IAAM,OAAO,GAAG,QAAQ,CAAC","sourcesContent":["// Autogenerated by `pnpm version-file`. DO NOT EDIT\nexport const VERSION = '1.42.3';\n"]}
|
package/lib/esm/worker/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
var compressionScript = "var WebWorker=function(e){\"use strict\";onmessage=function(e){var s=\"string\"==typeof e.data?JSON.parse(e.data):e.data,t=s.event,a=s.sessionId,n=t.type,r=t.timestamp,i=t.delay,o=t.data,d=null!=i?JSON.stringify({type:n,timestamp:r,delay:i,data:o}):JSON.stringify({type:n,timestamp:r,data:o});postMessage({compressedEvent:d,sessionId:a})};var s=onmessage;return e.compressionOnMessage=s,Object.defineProperty(e,\"__esModule\",{value:!0}),e}({});\n";
|
|
2
|
-
var trackDestinationScript = "var WebWorker=function(e){\"use strict\";var t,r=function(){return r=Object.assign||function(e){for(var t,r=1,n=arguments.length;r<n;r++)for(var s in t=arguments[r])Object.prototype.hasOwnProperty.call(t,s)&&(e[s]=t[s]);return e},r.apply(this,arguments)};function n(e,t,r,n){return new(r||(r=Promise))(function(s,a){function o(e){try{c(n.next(e))}catch(e){a(e)}}function i(e){try{c(n.throw(e))}catch(e){a(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof r?t:new r(function(e){e(t)})).then(o,i)}c((n=n.apply(e,t||[])).next())})}function s(e,t){var r,n,s,a={label:0,sent:function(){if(1&s[0])throw s[1];return s[1]},trys:[],ops:[]},o=Object.create((\"function\"==typeof Iterator?Iterator:Object).prototype);return o.next=i(0),o.throw=i(1),o.return=i(2),\"function\"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function i(i){return function(c){return function(i){if(r)throw new TypeError(\"Generator is already executing.\");for(;o&&(o=0,i[0]&&(a=0)),a;)try{if(r=1,n&&(s=2&i[0]?n.return:i[0]?n.throw||((s=n.return)&&s.call(n),0):n.next)&&!(s=s.call(n,i[1])).done)return s;switch(n=0,s&&(i=[2&i[0],s.value]),i[0]){case 0:case 1:s=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,n=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(s=a.trys,(s=s.length>0&&s[s.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!s||i[1]>s[0]&&i[1]<s[3])){a.label=i[1];break}if(6===i[0]&&a.label<s[1]){a.label=s[1],s=i;break}if(s&&a.label<s[2]){a.label=s[2],a.ops.push(i);break}s[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],n=0}finally{r=s=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,c])}}}\"function\"==typeof SuppressedError&&SuppressedError,function(e){e.US=\"US\",e.EU=\"EU\",e.STAGING=\"STAGING\"}(t||(t={})),t.US;var a,o=/payload.*exceed/i;!function(e){e.GET_SR_PROPS=\"get-sr-props\",e.DEBUG_INFO=\"debug-info\",e.FETCH_REQUEST=\"fetch-request\",e.METADATA=\"metadata\",e.TARGETING_DECISION=\"targeting-decision\"}(a||(a={}));var i=\"Network error occurred, event batch rejected\";function c(e,t){return n(this,void 0,void 0,function(){var r,a,o,i,c,u,l,d,p,f,h,y,
|
|
2
|
+
var trackDestinationScript = "var WebWorker=function(e){\"use strict\";var t,r=function(){return r=Object.assign||function(e){for(var t,r=1,n=arguments.length;r<n;r++)for(var s in t=arguments[r])Object.prototype.hasOwnProperty.call(t,s)&&(e[s]=t[s]);return e},r.apply(this,arguments)};function n(e,t,r,n){return new(r||(r=Promise))(function(s,a){function o(e){try{c(n.next(e))}catch(e){a(e)}}function i(e){try{c(n.throw(e))}catch(e){a(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof r?t:new r(function(e){e(t)})).then(o,i)}c((n=n.apply(e,t||[])).next())})}function s(e,t){var r,n,s,a={label:0,sent:function(){if(1&s[0])throw s[1];return s[1]},trys:[],ops:[]},o=Object.create((\"function\"==typeof Iterator?Iterator:Object).prototype);return o.next=i(0),o.throw=i(1),o.return=i(2),\"function\"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function i(i){return function(c){return function(i){if(r)throw new TypeError(\"Generator is already executing.\");for(;o&&(o=0,i[0]&&(a=0)),a;)try{if(r=1,n&&(s=2&i[0]?n.return:i[0]?n.throw||((s=n.return)&&s.call(n),0):n.next)&&!(s=s.call(n,i[1])).done)return s;switch(n=0,s&&(i=[2&i[0],s.value]),i[0]){case 0:case 1:s=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,n=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(s=a.trys,(s=s.length>0&&s[s.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!s||i[1]>s[0]&&i[1]<s[3])){a.label=i[1];break}if(6===i[0]&&a.label<s[1]){a.label=s[1],s=i;break}if(s&&a.label<s[2]){a.label=s[2],a.ops.push(i);break}s[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],n=0}finally{r=s=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,c])}}}\"function\"==typeof SuppressedError&&SuppressedError,function(e){e.US=\"US\",e.EU=\"EU\",e.STAGING=\"STAGING\"}(t||(t={})),t.US;var a,o=/payload.*exceed/i;!function(e){e.GET_SR_PROPS=\"get-sr-props\",e.DEBUG_INFO=\"debug-info\",e.FETCH_REQUEST=\"fetch-request\",e.METADATA=\"metadata\",e.TARGETING_DECISION=\"targeting-decision\"}(a||(a={}));var i=\"Network error occurred, event batch rejected\";function c(e,t){return n(this,void 0,void 0,function(){var r,a,o,i,c,u,l,d,p,f,h,v,y,g,b=this;return s(this,function(m){switch(m.label){case 0:return m.trys.push([0,4,,5]),r=t.CompressionStream,a=new r(\"gzip\"),o=a.writable.getWriter(),i=a.readable.getReader(),c=[],u=n(b,void 0,void 0,function(){var e,t,r;return s(this,function(n){switch(n.label){case 0:return[4,i.read()];case 1:if(e=n.sent(),t=e.done,r=e.value,t)return[3,3];c.push(r),n.label=2;case 2:return[3,0];case 3:return[2]}})}),[4,o.write((new TextEncoder).encode(e))];case 1:return m.sent(),[4,o.close()];case 2:return m.sent(),[4,u];case 3:m.sent(),l=c.reduce(function(e,t){return e+t.length},0),d=new Uint8Array(l),p=0;try{for(f=function(e){var t=\"function\"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&\"number\"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?\"Object is not iterable.\":\"Symbol.iterator is not defined.\")}(c),h=f.next();!h.done;h=f.next())v=h.value,d.set(v,p),p+=v.length}catch(e){y={error:e}}finally{try{h&&!h.done&&(g=f.return)&&g.call(f)}finally{if(y)throw y.error}}return[2,d];case 4:return m.sent(),[2,null];case 5:return[2]}})})}function u(e,t){var a,u,l,d,p,f,h;return n(this,void 0,void 0,function(){var n,v,y,g,b,m,S,w,T,k,R,x;return s(this,function(s){switch(s.label){case 0:return s.trys.push([0,10,,11]),\"CompressionStream\"in self?[4,c(e,self)]:[3,2];case 1:return v=s.sent(),[3,3];case 2:v=null,s.label=3;case 3:return n=v,y=\"\".concat(null!==(u=null===(a=t.version)||void 0===a?void 0:a.type)&&void 0!==u?u:\"standalone\",\"/\").concat(null!==(d=null===(l=t.version)||void 0===l?void 0:l.version)&&void 0!==d?d:t.sdkVersion),g=new URLSearchParams({device_id:t.deviceId,session_id:\"\".concat(t.sessionId),type:t.eventType}),b=\"\".concat((E=t.serverZone,U=t.trackServerUrl,U||(\"STAGING\"===E?\"https://api-sr.stag2.amplitude.com/sessions/v2/track\":\"EU\"===E?\"https://api-sr.eu.amplitude.com/sessions/v2/track\":\"https://api-sr.amplitude.com/sessions/v2/track\")),\"?\").concat(g.toString()),m=r({\"Content-Type\":\"application/json\",Accept:\"*/*\",Authorization:\"Bearer \".concat(t.apiKey),\"X-Client-Version\":t.sdkVersion,\"X-Client-Library\":y,\"X-Client-Url\":t.currentUrl.substring(0,1e3),\"X-Client-Sample-Rate\":\"\".concat(t.sampleRate),\"X-Sampling-Hash-Alg\":\"xxhash32\"},n?{\"Content-Encoding\":\"gzip\"}:{}),S=n?n.byteLength:new Blob([e]).size,[4,fetch(b,{method:\"POST\",headers:m,body:null!=n?n:e,keepalive:S<=65536})];case 4:if(null===(w=s.sent()))return[2,{shouldRetry:!1,success:!1,message:\"Unexpected error occurred\"}];if(w.status>=200&&w.status<300)return T=Math.round(new Blob(t.events).size/1024),k=null!==(h=null===(f=null===(p=w.headers)||void 0===p?void 0:p.get)||void 0===f?void 0:f.call(p,\"X-Session-Replay-Event-Skipped\"))&&void 0!==h?h:null,[2,{shouldRetry:!1,success:!0,message:\"Session replay event batch tracked successfully for session id \".concat(t.sessionId,\", size of events: \").concat(T,\" KB\"),skipCode:k}];if(413!==w.status)return[3,9];R=\"\",s.label=5;case 5:return s.trys.push([5,7,,8]),[4,w.text()];case 6:return R=s.sent(),[3,8];case 7:return s.sent(),[3,8];case 8:return[2,{shouldRetry:!1,success:!1,message:i,payloadTooLarge:!0,isWaf:o.test(R)}];case 9:return w.status>=500||408===w.status||429===w.status||499===w.status?[2,{shouldRetry:!0,success:!1,message:\"HTTP \".concat(w.status)}]:[2,{shouldRetry:!1,success:!1,message:i}];case 10:return x=s.sent(),[2,{shouldRetry:!1,success:!1,message:String(x)}];case 11:return[2]}var E,U})})}function l(e,t,r,a){var o;return n(this,void 0,void 0,function(){var n,i,c;return s(this,function(s){switch(s.label){case 0:n=1,s.label=1;case 1:return[4,u(t,r)];case 2:return(i=s.sent()).success?(postMessage({type:\"log\",id:e,message:i.message}),postMessage({type:\"complete\",id:e,skipCode:null!==(o=i.skipCode)&&void 0!==o?o:null}),[2]):i.payloadTooLarge&&a?(postMessage({type:\"payload_too_large\",id:e,isWaf:!0===i.isWaf}),[2]):a&&i.shouldRetry&&n<r.flushMaxRetries?[4,new Promise(function(e){return setTimeout(e,Math.random()*n*1e3)})]:[3,4];case 3:return s.sent(),n++,[3,5];case 4:return c=n>=r.flushMaxRetries?\"Session replay event batch rejected due to exceeded retry count\":i.message,postMessage({type:\"warn\",id:e,message:c}),postMessage({type:\"complete\",id:e}),[2];case 5:return[3,1];case 6:return[2]}})})}onmessage=function(e){return n(void 0,void 0,void 0,function(){var t,r,n,a,o,i,c;return s(this,function(s){switch(s.label){case 0:return t=e.data,r=t.type,n=t.id,a=t.payload,o=t.context,i=t.useRetry,\"send\"!==r?[3,2]:(c=JSON.stringify(a),[4,l(n,c,o,i)]);case 1:s.sent(),s.label=2;case 2:return[2]}})})};var d=onmessage;return e.trackDestinationOnMessage=d,Object.defineProperty(e,\"__esModule\",{value:!0}),e}({});\n";
|
|
3
3
|
|
|
4
4
|
export { compressionScript, trackDestinationScript };
|