@newtalaria/browser 0.1.6 → 0.1.7
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 +14 -2
- package/dist/client.d.ts +12 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +174 -16
- package/dist/index.js.map +3 -3
- package/dist/replay/capture_outcome.d.ts +18 -0
- package/dist/replay/capture_outcome.d.ts.map +1 -0
- package/dist/replay/segment_buffer.d.ts +2 -2
- package/dist/replay/segment_buffer.d.ts.map +1 -1
- package/dist/talaria.browser.iife.js +172 -16
- package/dist/talaria.browser.iife.js.map +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -96,12 +96,24 @@ You pay for **uploaded + retained** bytes, not for local buffering. Prefer error
|
|
|
96
96
|
| --- | --- |
|
|
97
97
|
| Session sample hit | `replays/start` immediately; segments upload every ~5s or ~100KB until unload or **5 min** max |
|
|
98
98
|
| Session sample miss | Record into a ~60s ring buffer; nothing uploaded until an error sample hits |
|
|
99
|
-
| Error sample + `replaysErrorAfterMs > 0` | Upload buffer (
|
|
99
|
+
| Error sample + `replaysErrorAfterMs > 0` | Upload buffer (≤~960KiB gzip pack target / **1MiB** server cap) + trailing window, hard caps **12 segments** or **2MiB** compressed, attach `replayId` only if segments landed, `finish`, return to buffer mode |
|
|
100
100
|
| Error sample + `replaysErrorAfterMs = 0` | Upload buffer then continue like session mode until **5 min** / unload / size caps |
|
|
101
101
|
| Server limit (replay segments / total size / duration) | Stop uploading that replay; no retries |
|
|
102
|
-
| Oversized
|
|
102
|
+
| Oversized FullSnapshot on segment 0 | **Abort** the clip (no blank orphan upload); error event gets `replay.capture=failed` + reason |
|
|
103
|
+
| Oversized single non-FS rrweb event | Dropped with a console warning (cannot fit under segment cap) |
|
|
103
104
|
| `pagehide` / `close` | Flush pending segments with `fetch` `keepalive`, then `replays/finish`; `close` fully resets so `init()` works again (React Strict Mode) |
|
|
104
105
|
|
|
106
|
+
### Replay capture outcome tags
|
|
107
|
+
|
|
108
|
+
When an **error/fatal** event attempts an on-error clip (`replaysSessionSampleRate` miss), the SDK may attach:
|
|
109
|
+
|
|
110
|
+
| Tag | Values |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| `replay.capture` | `ok` \| `failed` \| `skipped` |
|
|
113
|
+
| `replay.capture_reason` | `oversized_full_snapshot` \| `no_full_snapshot` \| `upload_failed` \| `not_sampled` \| `buffer_empty` |
|
|
114
|
+
|
|
115
|
+
Failed captures do **not** set `replayId` (avoids linking a blank player). Details are also under `extra.replayCapture`.
|
|
116
|
+
|
|
105
117
|
## Serverpod URL pattern
|
|
106
118
|
|
|
107
119
|
Talaria uses **Serverpod RPC**, not REST resource URLs:
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CaptureContext, SeverityLevel, TalariaInitOptions } from './types.js';
|
|
2
2
|
export { computeErrorClipDeadlineMs, fitCompressedPrefix, isErrorClipBudgetExhausted, planOversizedRetry, } from './replay/fit_segment.js';
|
|
3
3
|
export { MAX_SEGMENTS_ERROR_CLIP, MAX_ERROR_CLIP_COMPRESSED_BYTES, TARGET_COMPRESSED_SEGMENT_BYTES, MAX_COMPRESSED_SEGMENT_BYTES, } from './replay/segment_buffer.js';
|
|
4
|
+
export { REPLAY_CAPTURE_TAG, REPLAY_CAPTURE_REASON_TAG, applyReplayCaptureTags, mergeReplayCaptureExtra, } from './replay/capture_outcome.js';
|
|
5
|
+
export type { ReplayCaptureOutcome, ReplayCaptureReason, ReplayCaptureStatus, } from './replay/capture_outcome.js';
|
|
4
6
|
export declare class TalariaClient {
|
|
5
7
|
private options;
|
|
6
8
|
private transport;
|
|
@@ -30,6 +32,8 @@ export declare class TalariaClient {
|
|
|
30
32
|
* attach it to the error event after budget/timer finish resets buffer mode.
|
|
31
33
|
*/
|
|
32
34
|
private linkableReplayId;
|
|
35
|
+
/** Last error-clip paint/upload failure (cleared on successful segment 0). */
|
|
36
|
+
private lastReplayCaptureFailure;
|
|
33
37
|
init(options: TalariaInitOptions): void;
|
|
34
38
|
getReplayId(): string | null;
|
|
35
39
|
captureException(error: unknown, context?: CaptureContext): Promise<void>;
|
|
@@ -69,8 +73,14 @@ export declare class TalariaClient {
|
|
|
69
73
|
/**
|
|
70
74
|
* Ensure segment 0 begins at Meta+FullSnapshot. Orphan increments alone
|
|
71
75
|
* produce a blank rrweb player.
|
|
76
|
+
* @returns false when no FullSnapshot is available.
|
|
72
77
|
*/
|
|
73
78
|
private prepareBufferForNewReplay;
|
|
79
|
+
/**
|
|
80
|
+
* Stop an error clip that cannot paint (missing/oversized FullSnapshot).
|
|
81
|
+
* Does not set linkableReplayId — the error event should not open a blank player.
|
|
82
|
+
*/
|
|
83
|
+
private abortUnusableClip;
|
|
74
84
|
private stopUploadingAfterLimit;
|
|
75
85
|
private installGlobalHandlers;
|
|
76
86
|
private enqueueUpload;
|
|
@@ -79,7 +89,8 @@ export declare class TalariaClient {
|
|
|
79
89
|
private maxSegmentsAllowed;
|
|
80
90
|
/**
|
|
81
91
|
* Pull the largest event prefix that gzips under the target compressed size.
|
|
82
|
-
* Drops a single event that can never fit
|
|
92
|
+
* Drops a single event that can never fit — except FullSnapshot on segment 0
|
|
93
|
+
* (abort the clip instead of uploading a blank orphan stream).
|
|
83
94
|
*/
|
|
84
95
|
private takeFittedSegment;
|
|
85
96
|
private finishOnServer;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EAEd,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -33,5 +33,7 @@ export { TalariaClient } from './client.js';
|
|
|
33
33
|
export { computeErrorClipDeadlineMs, fitCompressedPrefix, isErrorClipBudgetExhausted, planOversizedRetry, } from './replay/fit_segment.js';
|
|
34
34
|
export { MAX_SEGMENTS_ERROR_CLIP, MAX_ERROR_CLIP_COMPRESSED_BYTES, TARGET_COMPRESSED_SEGMENT_BYTES, MAX_COMPRESSED_SEGMENT_BYTES, } from './replay/segment_buffer.js';
|
|
35
35
|
export type { CaptureContext, Environment, FailedRequestStatusCode, SeverityLevel, TalariaInitOptions, } from './types.js';
|
|
36
|
+
export { REPLAY_CAPTURE_TAG, REPLAY_CAPTURE_REASON_TAG, } from './replay/capture_outcome.js';
|
|
37
|
+
export type { ReplayCaptureOutcome, ReplayCaptureReason, ReplayCaptureStatus, } from './replay/capture_outcome.js';
|
|
36
38
|
export default Talaria;
|
|
37
39
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAIpB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,OAAO;kBACJ,kBAAkB,GAAG,IAAI;4BAIf,OAAO,YAAY,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;4BAK9D,MAAM,UACP,aAAa,YACX,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;mBAID,MAAM,GAAG,IAAI;aAInB,OAAO,CAAC,IAAI,CAAC;aAIb,OAAO,CAAC,IAAI,CAAC;CAGvB,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uBAAuB,EACvB,+BAA+B,EAC/B,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC;AAEpC,YAAY,EACV,cAAc,EACd,WAAW,EACX,uBAAuB,EACvB,aAAa,EACb,kBAAkB,GACnB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAIpB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,OAAO;kBACJ,kBAAkB,GAAG,IAAI;4BAIf,OAAO,YAAY,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;4BAK9D,MAAM,UACP,aAAa,YACX,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;mBAID,MAAM,GAAG,IAAI;aAInB,OAAO,CAAC,IAAI,CAAC;aAIb,OAAO,CAAC,IAAI,CAAC;CAGvB,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uBAAuB,EACvB,+BAA+B,EAC/B,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC;AAEpC,YAAY,EACV,cAAc,EACd,WAAW,EACX,uBAAuB,EACvB,aAAa,EACb,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AAErC,eAAe,OAAO,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -121,9 +121,9 @@ function estimateJsonBytes(value) {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// src/replay/segment_buffer.ts
|
|
124
|
-
var SEGMENT_SIZE_BYTES =
|
|
125
|
-
var MAX_COMPRESSED_SEGMENT_BYTES =
|
|
126
|
-
var TARGET_COMPRESSED_SEGMENT_BYTES = MAX_COMPRESSED_SEGMENT_BYTES -
|
|
124
|
+
var SEGMENT_SIZE_BYTES = 8e5;
|
|
125
|
+
var MAX_COMPRESSED_SEGMENT_BYTES = 1024 * 1024;
|
|
126
|
+
var TARGET_COMPRESSED_SEGMENT_BYTES = MAX_COMPRESSED_SEGMENT_BYTES - 64 * 1024;
|
|
127
127
|
var MAX_SEGMENTS_PER_REPLAY = 200;
|
|
128
128
|
var MAX_SEGMENTS_ERROR_CLIP = 12;
|
|
129
129
|
var MAX_ERROR_CLIP_COMPRESSED_BYTES = 2 * 1024 * 1024;
|
|
@@ -355,6 +355,33 @@ async function fitCompressedPrefix(events, compress, targetBytes = TARGET_COMPRE
|
|
|
355
355
|
return null;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
// src/replay/capture_outcome.ts
|
|
359
|
+
var REPLAY_CAPTURE_TAG = "replay.capture";
|
|
360
|
+
var REPLAY_CAPTURE_REASON_TAG = "replay.capture_reason";
|
|
361
|
+
function applyReplayCaptureTags(tags, outcome) {
|
|
362
|
+
if (!outcome) return tags;
|
|
363
|
+
const next = { ...tags };
|
|
364
|
+
next[REPLAY_CAPTURE_TAG] = outcome.status;
|
|
365
|
+
if (outcome.reason) {
|
|
366
|
+
next[REPLAY_CAPTURE_REASON_TAG] = outcome.reason;
|
|
367
|
+
}
|
|
368
|
+
return next;
|
|
369
|
+
}
|
|
370
|
+
function mergeReplayCaptureExtra(extra, outcome) {
|
|
371
|
+
if (!outcome || outcome.status === "ok") {
|
|
372
|
+
return extra;
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
...extra ?? {},
|
|
376
|
+
replayCapture: {
|
|
377
|
+
attempted: outcome.status === "failed",
|
|
378
|
+
status: outcome.status,
|
|
379
|
+
reason: outcome.reason,
|
|
380
|
+
...outcome.details ?? {}
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
358
385
|
// ../../node_modules/rrweb/dist/rrweb.js
|
|
359
386
|
var __defProp = Object.defineProperty;
|
|
360
387
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -13399,6 +13426,8 @@ var TalariaClient = class {
|
|
|
13399
13426
|
* attach it to the error event after budget/timer finish resets buffer mode.
|
|
13400
13427
|
*/
|
|
13401
13428
|
this.linkableReplayId = null;
|
|
13429
|
+
/** Last error-clip paint/upload failure (cleared on successful segment 0). */
|
|
13430
|
+
this.lastReplayCaptureFailure = null;
|
|
13402
13431
|
}
|
|
13403
13432
|
init(options) {
|
|
13404
13433
|
if (this.options) {
|
|
@@ -13420,6 +13449,7 @@ var TalariaClient = class {
|
|
|
13420
13449
|
this.uploadStartedAtMs = null;
|
|
13421
13450
|
this.errorClipDeadlineMs = null;
|
|
13422
13451
|
this.linkableReplayId = null;
|
|
13452
|
+
this.lastReplayCaptureFailure = null;
|
|
13423
13453
|
this.buffer = new SegmentBuffer();
|
|
13424
13454
|
this.uploadChain = Promise.resolve();
|
|
13425
13455
|
this.sessionSampled = Math.random() < this.options.replaysSessionSampleRate;
|
|
@@ -13581,6 +13611,7 @@ var TalariaClient = class {
|
|
|
13581
13611
|
this.uploadChain = Promise.resolve();
|
|
13582
13612
|
this.closed = false;
|
|
13583
13613
|
this.linkableReplayId = null;
|
|
13614
|
+
this.lastReplayCaptureFailure = null;
|
|
13584
13615
|
}
|
|
13585
13616
|
onRrwebEvent(event) {
|
|
13586
13617
|
if (this.closed || !this.options) return;
|
|
@@ -13606,15 +13637,22 @@ var TalariaClient = class {
|
|
|
13606
13637
|
throw new Error("@newtalaria/browser: call Talaria.init() first");
|
|
13607
13638
|
}
|
|
13608
13639
|
const isErrorLike = args.level === "error" || args.level === "fatal";
|
|
13640
|
+
let errorClipOutcome = null;
|
|
13641
|
+
let attemptedErrorClip = false;
|
|
13609
13642
|
if (isErrorLike && !this.sessionSampled) {
|
|
13610
13643
|
if (!this.uploadEnabled) {
|
|
13611
13644
|
if (Math.random() < this.options.replaysOnErrorSampleRate) {
|
|
13645
|
+
attemptedErrorClip = true;
|
|
13646
|
+
this.lastReplayCaptureFailure = null;
|
|
13612
13647
|
this.uploadEnabled = true;
|
|
13613
13648
|
this.checkoutFullSnapshot();
|
|
13614
13649
|
this.markUploadStarted();
|
|
13615
13650
|
this.scheduleErrorClipEnd();
|
|
13651
|
+
} else {
|
|
13652
|
+
errorClipOutcome = { status: "skipped", reason: "not_sampled" };
|
|
13616
13653
|
}
|
|
13617
13654
|
} else {
|
|
13655
|
+
attemptedErrorClip = true;
|
|
13618
13656
|
this.scheduleErrorClipEnd();
|
|
13619
13657
|
}
|
|
13620
13658
|
}
|
|
@@ -13623,15 +13661,49 @@ var TalariaClient = class {
|
|
|
13623
13661
|
await this.flush({ reason: "capture", keepalive: args.keepalive });
|
|
13624
13662
|
} catch (error) {
|
|
13625
13663
|
console.warn("@newtalaria/browser: replay flush failed", error);
|
|
13626
|
-
|
|
13664
|
+
this.lastReplayCaptureFailure = {
|
|
13665
|
+
reason: "upload_failed",
|
|
13666
|
+
details: {
|
|
13667
|
+
message: error instanceof Error ? error.message : String(error)
|
|
13668
|
+
}
|
|
13669
|
+
};
|
|
13670
|
+
if (!this.sessionSampled && this.segmentIndex === 0) {
|
|
13671
|
+
await this.abortUnusableClip("upload_failed", {
|
|
13672
|
+
message: error instanceof Error ? error.message : String(error)
|
|
13673
|
+
});
|
|
13674
|
+
}
|
|
13627
13675
|
}
|
|
13628
13676
|
}
|
|
13629
|
-
|
|
13630
|
-
|
|
13631
|
-
|
|
13632
|
-
}
|
|
13633
|
-
|
|
13634
|
-
|
|
13677
|
+
let replayId = this.uploadEnabled && this.segmentIndex > 0 ? this.replayId : this.linkableReplayId;
|
|
13678
|
+
if (this.lastReplayCaptureFailure && this.segmentIndex === 0) {
|
|
13679
|
+
replayId = null;
|
|
13680
|
+
}
|
|
13681
|
+
if (isErrorLike && !this.sessionSampled) {
|
|
13682
|
+
if (replayId) {
|
|
13683
|
+
errorClipOutcome = { status: "ok" };
|
|
13684
|
+
this.lastReplayCaptureFailure = null;
|
|
13685
|
+
} else if (errorClipOutcome?.status === "skipped") {
|
|
13686
|
+
} else if (this.lastReplayCaptureFailure) {
|
|
13687
|
+
errorClipOutcome = {
|
|
13688
|
+
status: "failed",
|
|
13689
|
+
reason: this.lastReplayCaptureFailure.reason,
|
|
13690
|
+
details: this.lastReplayCaptureFailure.details
|
|
13691
|
+
};
|
|
13692
|
+
} else if (attemptedErrorClip) {
|
|
13693
|
+
errorClipOutcome = { status: "failed", reason: "buffer_empty" };
|
|
13694
|
+
}
|
|
13695
|
+
}
|
|
13696
|
+
const tags = applyReplayCaptureTags(
|
|
13697
|
+
{
|
|
13698
|
+
...this.options.tags ?? {},
|
|
13699
|
+
...args.context?.tags ?? {}
|
|
13700
|
+
},
|
|
13701
|
+
errorClipOutcome
|
|
13702
|
+
);
|
|
13703
|
+
const extra = mergeReplayCaptureExtra(
|
|
13704
|
+
args.context?.extra,
|
|
13705
|
+
errorClipOutcome
|
|
13706
|
+
);
|
|
13635
13707
|
try {
|
|
13636
13708
|
await ingestEvent(this.transport, {
|
|
13637
13709
|
message: args.message,
|
|
@@ -13643,7 +13715,7 @@ var TalariaClient = class {
|
|
|
13643
13715
|
release: this.options.release,
|
|
13644
13716
|
userId: args.context?.userId ?? this.options.userId,
|
|
13645
13717
|
sessionId: this.sessionId ?? void 0,
|
|
13646
|
-
replayId,
|
|
13718
|
+
replayId: replayId ?? void 0,
|
|
13647
13719
|
url: typeof location !== "undefined" ? location.href : void 0,
|
|
13648
13720
|
tags: Object.keys(tags).length ? tags : void 0,
|
|
13649
13721
|
extraJson: extra ? JSON.stringify(extra) : void 0,
|
|
@@ -13788,9 +13860,10 @@ var TalariaClient = class {
|
|
|
13788
13860
|
/**
|
|
13789
13861
|
* Ensure segment 0 begins at Meta+FullSnapshot. Orphan increments alone
|
|
13790
13862
|
* produce a blank rrweb player.
|
|
13863
|
+
* @returns false when no FullSnapshot is available.
|
|
13791
13864
|
*/
|
|
13792
13865
|
prepareBufferForNewReplay() {
|
|
13793
|
-
if (this.segmentIndex !== 0) return;
|
|
13866
|
+
if (this.segmentIndex !== 0) return true;
|
|
13794
13867
|
if (!this.buffer.hasFullSnapshot()) {
|
|
13795
13868
|
this.checkoutFullSnapshot();
|
|
13796
13869
|
}
|
|
@@ -13798,6 +13871,40 @@ var TalariaClient = class {
|
|
|
13798
13871
|
console.warn(
|
|
13799
13872
|
"@newtalaria/browser: replay has no FullSnapshot; player may show a blank screen"
|
|
13800
13873
|
);
|
|
13874
|
+
return false;
|
|
13875
|
+
}
|
|
13876
|
+
return true;
|
|
13877
|
+
}
|
|
13878
|
+
/**
|
|
13879
|
+
* Stop an error clip that cannot paint (missing/oversized FullSnapshot).
|
|
13880
|
+
* Does not set linkableReplayId — the error event should not open a blank player.
|
|
13881
|
+
*/
|
|
13882
|
+
async abortUnusableClip(reason, details) {
|
|
13883
|
+
this.lastReplayCaptureFailure = { reason, details };
|
|
13884
|
+
console.warn("@newtalaria/browser: aborting unusable replay clip", {
|
|
13885
|
+
reason,
|
|
13886
|
+
...details
|
|
13887
|
+
});
|
|
13888
|
+
this.buffer.clear();
|
|
13889
|
+
this.linkableReplayId = null;
|
|
13890
|
+
if (this.startedOnServer && !this.finishedOnServer && this.transport && this.replayId) {
|
|
13891
|
+
try {
|
|
13892
|
+
await finishReplay(this.transport, {
|
|
13893
|
+
replayId: this.replayId,
|
|
13894
|
+
reason: `capture_failed:${reason}`,
|
|
13895
|
+
keepalive: false
|
|
13896
|
+
});
|
|
13897
|
+
this.finishedOnServer = true;
|
|
13898
|
+
} catch (error) {
|
|
13899
|
+
console.warn("@newtalaria/browser: replays/finish failed after abort", error);
|
|
13900
|
+
}
|
|
13901
|
+
}
|
|
13902
|
+
if (!this.sessionSampled) {
|
|
13903
|
+
this.resetToBufferMode();
|
|
13904
|
+
} else {
|
|
13905
|
+
this.uploadEnabled = false;
|
|
13906
|
+
this.clearErrorClipTimer();
|
|
13907
|
+
this.clearMaxDurationTimer();
|
|
13801
13908
|
}
|
|
13802
13909
|
}
|
|
13803
13910
|
stopUploadingAfterLimit() {
|
|
@@ -13858,7 +13965,10 @@ var TalariaClient = class {
|
|
|
13858
13965
|
async uploadPendingSegments(opts) {
|
|
13859
13966
|
if (!this.options || !this.transport || !this.replayId) return;
|
|
13860
13967
|
if (!this.startedOnServer || this.finishedOnServer) return;
|
|
13861
|
-
this.prepareBufferForNewReplay()
|
|
13968
|
+
if (!this.prepareBufferForNewReplay()) {
|
|
13969
|
+
await this.abortUnusableClip("no_full_snapshot");
|
|
13970
|
+
return;
|
|
13971
|
+
}
|
|
13862
13972
|
while (this.buffer.length > 0) {
|
|
13863
13973
|
if (this.isPastMaxDuration()) {
|
|
13864
13974
|
break;
|
|
@@ -13898,7 +14008,17 @@ var TalariaClient = class {
|
|
|
13898
14008
|
}
|
|
13899
14009
|
const fitted = await this.takeFittedSegment();
|
|
13900
14010
|
if (!fitted) break;
|
|
14011
|
+
if (fitted.abortReason) {
|
|
14012
|
+
await this.abortUnusableClip(fitted.abortReason, fitted.abortDetails);
|
|
14013
|
+
return;
|
|
14014
|
+
}
|
|
13901
14015
|
const { events, gzip } = fitted;
|
|
14016
|
+
if (this.segmentIndex === 0 && !events.some((e) => e.type === RRWEB_FULL_SNAPSHOT)) {
|
|
14017
|
+
await this.abortUnusableClip("no_full_snapshot", {
|
|
14018
|
+
segmentEventTypes: events.map((e) => e.type)
|
|
14019
|
+
});
|
|
14020
|
+
return;
|
|
14021
|
+
}
|
|
13902
14022
|
if (this.isErrorClipMode() && this.uploadedCompressedBytes + gzip.length > MAX_ERROR_CLIP_COMPRESSED_BYTES && this.segmentIndex > 0) {
|
|
13903
14023
|
this.buffer.clear();
|
|
13904
14024
|
await this.finishOnServer({
|
|
@@ -13923,6 +14043,9 @@ var TalariaClient = class {
|
|
|
13923
14043
|
});
|
|
13924
14044
|
this.segmentIndex = index2 + 1;
|
|
13925
14045
|
this.uploadedCompressedBytes += gzip.length;
|
|
14046
|
+
if (index2 === 0) {
|
|
14047
|
+
this.lastReplayCaptureFailure = null;
|
|
14048
|
+
}
|
|
13926
14049
|
} catch (error) {
|
|
13927
14050
|
if (isTerminalReplayLimitError(error)) {
|
|
13928
14051
|
console.warn(
|
|
@@ -13939,10 +14062,17 @@ var TalariaClient = class {
|
|
|
13939
14062
|
if (isOversizedSegmentError(error)) {
|
|
13940
14063
|
const plan = planOversizedRetry(events);
|
|
13941
14064
|
if (plan.action === "drop") {
|
|
14065
|
+
const droppedType = events[0]?.type;
|
|
13942
14066
|
console.warn(
|
|
13943
14067
|
"@newtalaria/browser: dropping rrweb event rejected as oversized",
|
|
13944
|
-
{ type:
|
|
14068
|
+
{ type: droppedType }
|
|
13945
14069
|
);
|
|
14070
|
+
if (this.segmentIndex === 0 && droppedType === RRWEB_FULL_SNAPSHOT) {
|
|
14071
|
+
await this.abortUnusableClip("oversized_full_snapshot", {
|
|
14072
|
+
source: "server_reject"
|
|
14073
|
+
});
|
|
14074
|
+
return;
|
|
14075
|
+
}
|
|
13946
14076
|
continue;
|
|
13947
14077
|
}
|
|
13948
14078
|
this.buffer.prepend(plan.right);
|
|
@@ -13951,6 +14081,11 @@ var TalariaClient = class {
|
|
|
13951
14081
|
}
|
|
13952
14082
|
this.buffer.prepend(events);
|
|
13953
14083
|
console.warn("@newtalaria/browser: replays/ingestSegment failed", error);
|
|
14084
|
+
if (this.segmentIndex === 0) {
|
|
14085
|
+
await this.abortUnusableClip("upload_failed", {
|
|
14086
|
+
message: error instanceof Error ? error.message : String(error)
|
|
14087
|
+
});
|
|
14088
|
+
}
|
|
13954
14089
|
break;
|
|
13955
14090
|
}
|
|
13956
14091
|
if (opts.keepalive) break;
|
|
@@ -13965,7 +14100,8 @@ var TalariaClient = class {
|
|
|
13965
14100
|
}
|
|
13966
14101
|
/**
|
|
13967
14102
|
* Pull the largest event prefix that gzips under the target compressed size.
|
|
13968
|
-
* Drops a single event that can never fit
|
|
14103
|
+
* Drops a single event that can never fit — except FullSnapshot on segment 0
|
|
14104
|
+
* (abort the clip instead of uploading a blank orphan stream).
|
|
13969
14105
|
*/
|
|
13970
14106
|
async takeFittedSegment() {
|
|
13971
14107
|
const chunk = this.buffer.takeByEstimatedBytes(SEGMENT_SIZE_BYTES);
|
|
@@ -13977,12 +14113,32 @@ var TalariaClient = class {
|
|
|
13977
14113
|
MAX_COMPRESSED_SEGMENT_BYTES
|
|
13978
14114
|
);
|
|
13979
14115
|
if (!fitted) {
|
|
14116
|
+
const dropped = chunk[0];
|
|
13980
14117
|
console.warn(
|
|
13981
14118
|
"@newtalaria/browser: dropping rrweb event that exceeds max segment size",
|
|
13982
|
-
{ type:
|
|
14119
|
+
{ type: dropped?.type }
|
|
13983
14120
|
);
|
|
14121
|
+
if (this.segmentIndex === 0 && dropped?.type === RRWEB_FULL_SNAPSHOT) {
|
|
14122
|
+
return {
|
|
14123
|
+
events: [],
|
|
14124
|
+
gzip: new Uint8Array(),
|
|
14125
|
+
abortReason: "oversized_full_snapshot",
|
|
14126
|
+
abortDetails: { source: "fit_compressed_prefix" }
|
|
14127
|
+
};
|
|
14128
|
+
}
|
|
14129
|
+
if (chunk.length > 1) {
|
|
14130
|
+
this.buffer.prepend(chunk.slice(1));
|
|
14131
|
+
}
|
|
13984
14132
|
return this.takeFittedSegment();
|
|
13985
14133
|
}
|
|
14134
|
+
if (this.segmentIndex === 0 && chunk.some((e) => e.type === RRWEB_FULL_SNAPSHOT) && !fitted.events.some((e) => e.type === RRWEB_FULL_SNAPSHOT)) {
|
|
14135
|
+
return {
|
|
14136
|
+
events: [],
|
|
14137
|
+
gzip: new Uint8Array(),
|
|
14138
|
+
abortReason: "oversized_full_snapshot",
|
|
14139
|
+
abortDetails: { source: "fit_dropped_full_snapshot" }
|
|
14140
|
+
};
|
|
14141
|
+
}
|
|
13986
14142
|
if (fitted.remainder.length > 0) {
|
|
13987
14143
|
this.buffer.prepend(fitted.remainder);
|
|
13988
14144
|
}
|
|
@@ -14034,6 +14190,8 @@ export {
|
|
|
14034
14190
|
MAX_COMPRESSED_SEGMENT_BYTES,
|
|
14035
14191
|
MAX_ERROR_CLIP_COMPRESSED_BYTES,
|
|
14036
14192
|
MAX_SEGMENTS_ERROR_CLIP,
|
|
14193
|
+
REPLAY_CAPTURE_REASON_TAG,
|
|
14194
|
+
REPLAY_CAPTURE_TAG,
|
|
14037
14195
|
TARGET_COMPRESSED_SEGMENT_BYTES,
|
|
14038
14196
|
Talaria,
|
|
14039
14197
|
TalariaClient,
|