@newtalaria/browser 0.1.9 → 0.1.10
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 +1 -1
- package/dist/client.d.ts +4 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/index.js +115 -52
- package/dist/index.js.map +2 -2
- package/dist/replay/fit_segment.d.ts +9 -1
- package/dist/replay/fit_segment.d.ts.map +1 -1
- package/dist/replay/paint_base_size.d.ts +2 -1
- package/dist/replay/paint_base_size.d.ts.map +1 -1
- package/dist/replay/segment_buffer.d.ts +15 -3
- package/dist/replay/segment_buffer.d.ts.map +1 -1
- package/dist/talaria.browser.iife.js +115 -52
- package/dist/talaria.browser.iife.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ You pay for **uploaded + retained** bytes, not for local buffering. Prefer error
|
|
|
99
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 FullSnapshot on segment 0 | **Abort** the clip (no blank orphan upload); error event gets `replay.capture=failed` + reason |
|
|
102
|
+
| Oversized FullSnapshot on segment 0 | **Abort** the clip (no blank orphan upload); error event gets `replay.capture=failed` + reason. Meta+FullSnapshot are taken/fitted **atomically** so a soft estimated take window cannot orphan the snapshot. |
|
|
103
103
|
| Oversized single non-FS rrweb event | Dropped with a console warning (cannot fit under segment cap) |
|
|
104
104
|
| `pagehide` / `close` | Flush pending segments with `fetch` `keepalive`, then `replays/finish`; `close` fully resets so `init()` works again (React Strict Mode) |
|
|
105
105
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CaptureContext, SeverityLevel, TalariaInitOptions } from './types.js';
|
|
2
|
-
export { computeErrorClipDeadlineMs, fitCompressedPrefix, isErrorClipBudgetExhausted, planOversizedRetry, } from './replay/fit_segment.js';
|
|
2
|
+
export { computeErrorClipDeadlineMs, fitCompressedPrefix, isErrorClipBudgetExhausted, paintBaseEventCount, 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
4
|
export { REPLAY_CAPTURE_TAG, REPLAY_CAPTURE_REASON_TAG, applyReplayCaptureTags, mergeReplayCaptureExtra, } from './replay/capture_outcome.js';
|
|
5
5
|
export type { ReplayCaptureOutcome, ReplayCaptureReason, ReplayCaptureStatus, } from './replay/capture_outcome.js';
|
|
@@ -89,8 +89,9 @@ export declare class TalariaClient {
|
|
|
89
89
|
private maxSegmentsAllowed;
|
|
90
90
|
/**
|
|
91
91
|
* Pull the largest event prefix that gzips under the target compressed size.
|
|
92
|
-
*
|
|
93
|
-
*
|
|
92
|
+
*
|
|
93
|
+
* Segment 0 keeps Meta+FullSnapshot (paint base) atomic through take + fit so
|
|
94
|
+
* a soft estimated-byte window cannot orphan the FullSnapshot.
|
|
94
95
|
*/
|
|
95
96
|
private takeFittedSegment;
|
|
96
97
|
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.js
CHANGED
|
@@ -204,15 +204,44 @@ var SegmentBuffer = class {
|
|
|
204
204
|
shouldFlushBySize() {
|
|
205
205
|
return this.pendingBytes >= SEGMENT_SIZE_BYTES;
|
|
206
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* How many leading events form the rrweb paint base (Meta+FullSnapshot or
|
|
209
|
+
* FullSnapshot alone). Returns 0 if the buffer does not start with a paint base.
|
|
210
|
+
*/
|
|
211
|
+
paintBaseLength() {
|
|
212
|
+
if (this.events.length === 0) return 0;
|
|
213
|
+
if (this.events[0].type === RRWEB_META && this.events[1]?.type === RRWEB_FULL_SNAPSHOT) {
|
|
214
|
+
return 2;
|
|
215
|
+
}
|
|
216
|
+
if (this.events[0].type === RRWEB_FULL_SNAPSHOT) {
|
|
217
|
+
return 1;
|
|
218
|
+
}
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
207
221
|
/**
|
|
208
222
|
* Take a prefix of events whose estimated JSON size is ~maxBytes.
|
|
209
223
|
* Always returns at least one event when the buffer is non-empty (caller
|
|
210
224
|
* must handle a single oversized event).
|
|
225
|
+
*
|
|
226
|
+
* When `keepPaintBaseTogether` is true (segment 0), Meta immediately followed
|
|
227
|
+
* by FullSnapshot is taken as one unit even if estimated size exceeds
|
|
228
|
+
* maxBytes — gzip fitting / hard caps decide whether it can ship.
|
|
211
229
|
*/
|
|
212
|
-
takeByEstimatedBytes(maxBytes) {
|
|
230
|
+
takeByEstimatedBytes(maxBytes, opts) {
|
|
213
231
|
if (this.events.length === 0) return [];
|
|
214
232
|
const batch = [];
|
|
215
233
|
let batchBytes = 0;
|
|
234
|
+
if (opts?.keepPaintBaseTogether) {
|
|
235
|
+
const paintLen = this.paintBaseLength();
|
|
236
|
+
if (paintLen > 0) {
|
|
237
|
+
for (let i = 0; i < paintLen; i++) {
|
|
238
|
+
const ev = this.events.shift();
|
|
239
|
+
batch.push(ev);
|
|
240
|
+
batchBytes += estimateJsonBytes(ev);
|
|
241
|
+
}
|
|
242
|
+
this.recomputeBytes();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
216
245
|
while (this.events.length > 0) {
|
|
217
246
|
const next = this.events[0];
|
|
218
247
|
const nextBytes = estimateJsonBytes(next);
|
|
@@ -337,8 +366,53 @@ function planOversizedRetry(events) {
|
|
|
337
366
|
right: events.slice(mid)
|
|
338
367
|
};
|
|
339
368
|
}
|
|
340
|
-
|
|
369
|
+
function paintBaseEventCount(events) {
|
|
370
|
+
if (events.length === 0) return 0;
|
|
371
|
+
if (events[0].type === RRWEB_META && events[1]?.type === RRWEB_FULL_SNAPSHOT) {
|
|
372
|
+
return 2;
|
|
373
|
+
}
|
|
374
|
+
if (events[0].type === RRWEB_FULL_SNAPSHOT) {
|
|
375
|
+
return 1;
|
|
376
|
+
}
|
|
377
|
+
return 0;
|
|
378
|
+
}
|
|
379
|
+
async function fitCompressedPrefix(events, compress, targetBytes = TARGET_COMPRESSED_SEGMENT_BYTES, hardCap = MAX_COMPRESSED_SEGMENT_BYTES, opts) {
|
|
341
380
|
if (events.length === 0) return null;
|
|
381
|
+
const atomicMin = opts?.atomicMinCount ?? 0;
|
|
382
|
+
if (atomicMin > 0) {
|
|
383
|
+
if (events.length < atomicMin) return null;
|
|
384
|
+
const paint = events.slice(0, atomicMin);
|
|
385
|
+
const paintGzip = await compress(paint);
|
|
386
|
+
if (paintGzip.length > hardCap) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
let lo = atomicMin;
|
|
390
|
+
let hi = events.length;
|
|
391
|
+
let best = {
|
|
392
|
+
events: paint,
|
|
393
|
+
gzip: paintGzip
|
|
394
|
+
};
|
|
395
|
+
while (lo <= hi) {
|
|
396
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
397
|
+
if (mid < atomicMin) {
|
|
398
|
+
lo = atomicMin;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
const prefix = events.slice(0, mid);
|
|
402
|
+
const gzip = await compress(prefix);
|
|
403
|
+
if (gzip.length <= targetBytes && gzip.length <= hardCap) {
|
|
404
|
+
best = { events: prefix, gzip };
|
|
405
|
+
lo = mid + 1;
|
|
406
|
+
} else {
|
|
407
|
+
hi = mid - 1;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
events: best.events,
|
|
412
|
+
gzip: best.gzip,
|
|
413
|
+
remainder: events.slice(best.events.length)
|
|
414
|
+
};
|
|
415
|
+
}
|
|
342
416
|
let remaining = events;
|
|
343
417
|
while (remaining.length > 0) {
|
|
344
418
|
let lo = 1;
|
|
@@ -400,7 +474,7 @@ function paintBaseSizeDetails(events, opts) {
|
|
|
400
474
|
const full = events.find((e) => e.type === RRWEB_FULL_SNAPSHOT);
|
|
401
475
|
const details = {
|
|
402
476
|
segmentEventTypes: events.map((e) => e.type),
|
|
403
|
-
|
|
477
|
+
softTakeEstimatedBytes: SEGMENT_SIZE_BYTES,
|
|
404
478
|
targetCompressedSegmentBytes: TARGET_COMPRESSED_SEGMENT_BYTES,
|
|
405
479
|
maxCompressedSegmentBytes: MAX_COMPRESSED_SEGMENT_BYTES
|
|
406
480
|
};
|
|
@@ -14053,20 +14127,9 @@ var TalariaClient = class {
|
|
|
14053
14127
|
}
|
|
14054
14128
|
const { events, gzip } = fitted;
|
|
14055
14129
|
if (this.segmentIndex === 0 && !events.some((e) => e.type === RRWEB_FULL_SNAPSHOT)) {
|
|
14056
|
-
const leftoverFs = this.buffer.peekFullSnapshot();
|
|
14057
|
-
let compressedBytes;
|
|
14058
|
-
if (leftoverFs) {
|
|
14059
|
-
try {
|
|
14060
|
-
compressedBytes = (await compressReplayEvents([leftoverFs])).length;
|
|
14061
|
-
} catch {
|
|
14062
|
-
}
|
|
14063
|
-
}
|
|
14064
14130
|
await this.abortUnusableClip(
|
|
14065
14131
|
"no_full_snapshot",
|
|
14066
|
-
paintBaseSizeDetails(
|
|
14067
|
-
leftoverFs ? [...events, leftoverFs] : events,
|
|
14068
|
-
{ fullSnapshotCompressedBytes: compressedBytes }
|
|
14069
|
-
)
|
|
14132
|
+
paintBaseSizeDetails(events)
|
|
14070
14133
|
);
|
|
14071
14134
|
return;
|
|
14072
14135
|
}
|
|
@@ -14111,6 +14174,14 @@ var TalariaClient = class {
|
|
|
14111
14174
|
break;
|
|
14112
14175
|
}
|
|
14113
14176
|
if (isOversizedSegmentError(error)) {
|
|
14177
|
+
if (this.segmentIndex === 0 && paintBaseEventCount(events) > 0) {
|
|
14178
|
+
await this.abortUnusableClip("oversized_full_snapshot", {
|
|
14179
|
+
source: "server_reject_paint_base",
|
|
14180
|
+
...paintBaseSizeDetails(events),
|
|
14181
|
+
rejectedCompressedBytes: gzip.length
|
|
14182
|
+
});
|
|
14183
|
+
return;
|
|
14184
|
+
}
|
|
14114
14185
|
const plan = planOversizedRetry(events);
|
|
14115
14186
|
if (plan.action === "drop") {
|
|
14116
14187
|
const droppedType = events[0]?.type;
|
|
@@ -14118,7 +14189,7 @@ var TalariaClient = class {
|
|
|
14118
14189
|
"@newtalaria/browser: dropping rrweb event rejected as oversized",
|
|
14119
14190
|
{ type: droppedType }
|
|
14120
14191
|
);
|
|
14121
|
-
if (
|
|
14192
|
+
if (droppedType === RRWEB_FULL_SNAPSHOT) {
|
|
14122
14193
|
await this.abortUnusableClip(
|
|
14123
14194
|
"oversized_full_snapshot",
|
|
14124
14195
|
{
|
|
@@ -14156,68 +14227,60 @@ var TalariaClient = class {
|
|
|
14156
14227
|
}
|
|
14157
14228
|
/**
|
|
14158
14229
|
* Pull the largest event prefix that gzips under the target compressed size.
|
|
14159
|
-
*
|
|
14160
|
-
*
|
|
14230
|
+
*
|
|
14231
|
+
* Segment 0 keeps Meta+FullSnapshot (paint base) atomic through take + fit so
|
|
14232
|
+
* a soft estimated-byte window cannot orphan the FullSnapshot.
|
|
14161
14233
|
*/
|
|
14162
14234
|
async takeFittedSegment() {
|
|
14163
|
-
const
|
|
14235
|
+
const isPaintBaseSegment = this.segmentIndex === 0;
|
|
14236
|
+
const chunk = this.buffer.takeByEstimatedBytes(SEGMENT_SIZE_BYTES, {
|
|
14237
|
+
keepPaintBaseTogether: isPaintBaseSegment
|
|
14238
|
+
});
|
|
14164
14239
|
if (chunk.length === 0) return null;
|
|
14240
|
+
const atomicMin = isPaintBaseSegment ? paintBaseEventCount(chunk) : 0;
|
|
14165
14241
|
const fitted = await fitCompressedPrefix(
|
|
14166
14242
|
chunk,
|
|
14167
14243
|
compressReplayEvents,
|
|
14168
14244
|
TARGET_COMPRESSED_SEGMENT_BYTES,
|
|
14169
|
-
MAX_COMPRESSED_SEGMENT_BYTES
|
|
14245
|
+
MAX_COMPRESSED_SEGMENT_BYTES,
|
|
14246
|
+
atomicMin > 0 ? { atomicMinCount: atomicMin } : void 0
|
|
14170
14247
|
);
|
|
14171
14248
|
if (!fitted) {
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
"@newtalaria/browser: dropping rrweb event that exceeds max segment size",
|
|
14175
|
-
{ type: dropped?.type }
|
|
14176
|
-
);
|
|
14177
|
-
if (this.segmentIndex === 0 && dropped?.type === RRWEB_FULL_SNAPSHOT) {
|
|
14249
|
+
if (isPaintBaseSegment && atomicMin > 0) {
|
|
14250
|
+
const paint = chunk.slice(0, atomicMin);
|
|
14178
14251
|
let compressedBytes;
|
|
14179
|
-
|
|
14180
|
-
|
|
14181
|
-
|
|
14252
|
+
const full = paint.find((e) => e.type === RRWEB_FULL_SNAPSHOT);
|
|
14253
|
+
if (full) {
|
|
14254
|
+
try {
|
|
14255
|
+
compressedBytes = (await compressReplayEvents([full])).length;
|
|
14256
|
+
} catch {
|
|
14257
|
+
}
|
|
14258
|
+
}
|
|
14259
|
+
if (chunk.length > atomicMin) {
|
|
14260
|
+
this.buffer.prepend(chunk.slice(atomicMin));
|
|
14182
14261
|
}
|
|
14183
14262
|
return {
|
|
14184
14263
|
events: [],
|
|
14185
14264
|
gzip: new Uint8Array(),
|
|
14186
14265
|
abortReason: "oversized_full_snapshot",
|
|
14187
14266
|
abortDetails: {
|
|
14188
|
-
source: "
|
|
14189
|
-
...paintBaseSizeDetails(
|
|
14267
|
+
source: "paint_base_exceeds_hard_cap",
|
|
14268
|
+
...paintBaseSizeDetails(paint, {
|
|
14190
14269
|
fullSnapshotCompressedBytes: compressedBytes
|
|
14191
14270
|
})
|
|
14192
14271
|
}
|
|
14193
14272
|
};
|
|
14194
14273
|
}
|
|
14274
|
+
const dropped = chunk[0];
|
|
14275
|
+
console.warn(
|
|
14276
|
+
"@newtalaria/browser: dropping rrweb event that exceeds max segment size",
|
|
14277
|
+
{ type: dropped?.type }
|
|
14278
|
+
);
|
|
14195
14279
|
if (chunk.length > 1) {
|
|
14196
14280
|
this.buffer.prepend(chunk.slice(1));
|
|
14197
14281
|
}
|
|
14198
14282
|
return this.takeFittedSegment();
|
|
14199
14283
|
}
|
|
14200
|
-
if (this.segmentIndex === 0 && chunk.some((e) => e.type === RRWEB_FULL_SNAPSHOT) && !fitted.events.some((e) => e.type === RRWEB_FULL_SNAPSHOT)) {
|
|
14201
|
-
const full = chunk.find((e) => e.type === RRWEB_FULL_SNAPSHOT);
|
|
14202
|
-
let compressedBytes;
|
|
14203
|
-
if (full) {
|
|
14204
|
-
try {
|
|
14205
|
-
compressedBytes = (await compressReplayEvents([full])).length;
|
|
14206
|
-
} catch {
|
|
14207
|
-
}
|
|
14208
|
-
}
|
|
14209
|
-
return {
|
|
14210
|
-
events: [],
|
|
14211
|
-
gzip: new Uint8Array(),
|
|
14212
|
-
abortReason: "oversized_full_snapshot",
|
|
14213
|
-
abortDetails: {
|
|
14214
|
-
source: "fit_dropped_full_snapshot",
|
|
14215
|
-
...paintBaseSizeDetails(chunk, {
|
|
14216
|
-
fullSnapshotCompressedBytes: compressedBytes
|
|
14217
|
-
})
|
|
14218
|
-
}
|
|
14219
|
-
};
|
|
14220
|
-
}
|
|
14221
14284
|
if (fitted.remainder.length > 0) {
|
|
14222
14285
|
this.buffer.prepend(fitted.remainder);
|
|
14223
14286
|
}
|