@newtalaria/browser 0.1.8 → 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.
@@ -23,11 +23,19 @@ export declare function planOversizedRetry(events: RrwebEvent[]): {
23
23
  left: RrwebEvent[];
24
24
  right: RrwebEvent[];
25
25
  };
26
+ /** Leading Meta+FullSnapshot (2) or FullSnapshot alone (1); else 0. */
27
+ export declare function paintBaseEventCount(events: RrwebEvent[]): number;
26
28
  /**
27
29
  * Binary-search the largest prefix that gzips under the pack target.
28
30
  * Remainder is returned for re-queue; null means every event was dropped.
31
+ *
32
+ * When `atomicMinCount` is set (segment-0 paint base), never return a prefix
33
+ * shorter than that count unless the atomic unit itself exceeds `hardCap`
34
+ * (then null — caller should abort as oversized, not ship Meta-only).
29
35
  */
30
- export declare function fitCompressedPrefix(events: RrwebEvent[], compress: CompressEvents, targetBytes?: number, hardCap?: number): Promise<{
36
+ export declare function fitCompressedPrefix(events: RrwebEvent[], compress: CompressEvents, targetBytes?: number, hardCap?: number, opts?: {
37
+ atomicMinCount?: number;
38
+ }): Promise<{
31
39
  events: RrwebEvent[];
32
40
  gzip: Uint8Array;
33
41
  remainder: RrwebEvent[];
@@ -1 +1 @@
1
- {"version":3,"file":"fit_segment.d.ts","sourceRoot":"","sources":["../../src/replay/fit_segment.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3E;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,iBAAiB,EAAE,MAAM,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,SAA4B,GAClC,MAAM,CAER;AAED,2EAA2E;AAC3E,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAOV;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAAE,GACnB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CAUpF;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,EAAE,EACpB,QAAQ,EAAE,cAAc,EACxB,WAAW,SAAkC,EAC7C,OAAO,SAA+B,GACrC,OAAO,CAAC;IACT,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,UAAU,EAAE,CAAC;CACzB,GAAG,IAAI,CAAC,CAkCR"}
1
+ {"version":3,"file":"fit_segment.d.ts","sourceRoot":"","sources":["../../src/replay/fit_segment.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3E;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,iBAAiB,EAAE,MAAM,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,SAA4B,GAClC,MAAM,CAER;AAED,2EAA2E;AAC3E,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAOV;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAAE,GACnB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CAUpF;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAYhE;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,EAAE,EACpB,QAAQ,EAAE,cAAc,EACxB,WAAW,SAAkC,EAC7C,OAAO,SAA+B,EACtC,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,OAAO,CAAC;IACT,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,UAAU,EAAE,CAAC;CACzB,GAAG,IAAI,CAAC,CA2ER"}
@@ -0,0 +1,19 @@
1
+ import { type RrwebEvent } from './segment_buffer.js';
2
+ /** Size diagnostics when segment 0 cannot ship a paint base. */
3
+ export type PaintBaseSizeDetails = {
4
+ segmentEventTypes: number[];
5
+ /** Estimated JSON bytes of the Meta event, if present. */
6
+ metaEstimatedBytes?: number;
7
+ /** Estimated JSON bytes of the FullSnapshot, if present. */
8
+ fullSnapshotEstimatedBytes?: number;
9
+ /** Actual gzip size of FullSnapshot alone, when measured. */
10
+ fullSnapshotCompressedBytes?: number;
11
+ /** Soft estimated take window (not the server uncompressed hard cap). */
12
+ softTakeEstimatedBytes: number;
13
+ targetCompressedSegmentBytes: number;
14
+ maxCompressedSegmentBytes: number;
15
+ } & Record<string, unknown>;
16
+ export declare function paintBaseSizeDetails(events: RrwebEvent[], opts?: {
17
+ fullSnapshotCompressedBytes?: number;
18
+ }): PaintBaseSizeDetails;
19
+ //# sourceMappingURL=paint_base_size.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paint_base_size.d.ts","sourceRoot":"","sources":["../../src/replay/paint_base_size.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAE7B,gEAAgE;AAChE,MAAM,MAAM,oBAAoB,GAAG;IACjC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,6DAA6D;IAC7D,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,yEAAyE;IACzE,sBAAsB,EAAE,MAAM,CAAC;IAC/B,4BAA4B,EAAE,MAAM,CAAC;IACrC,yBAAyB,EAAE,MAAM,CAAC;CACnC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE5B,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,UAAU,EAAE,EACpB,IAAI,CAAC,EAAE;IAAE,2BAA2B,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C,oBAAoB,CAsBtB"}
@@ -5,8 +5,9 @@ export interface RrwebEvent {
5
5
  timestamp: number;
6
6
  }
7
7
  /**
8
- * Soft uncompressed take size before gzip fitting.
9
- * Keep in sync with ReplayLimits.maxUncompressedSegmentBytes intent.
8
+ * Soft estimated-JSON take size before gzip fitting.
9
+ * Not a hard server limit — packing uses MAX_COMPRESSED_SEGMENT_BYTES.
10
+ * Paint base (Meta+FullSnapshot) is taken atomically and may exceed this.
10
11
  */
11
12
  export declare const SEGMENT_SIZE_BYTES = 800000;
12
13
  /**
@@ -53,6 +54,8 @@ export declare class SegmentBuffer {
53
54
  get estimatedBytes(): number;
54
55
  push(event: RrwebEvent): void;
55
56
  hasFullSnapshot(): boolean;
57
+ /** First FullSnapshot still queued (does not remove it). */
58
+ peekFullSnapshot(): RrwebEvent | undefined;
56
59
  /**
57
60
  * Drop leading events until Meta+FullSnapshot (or FullSnapshot alone).
58
61
  * Returns false if no FullSnapshot is present (replay would paint blank).
@@ -64,12 +67,23 @@ export declare class SegmentBuffer {
64
67
  */
65
68
  trimRing(now?: number, maxBytes?: number): void;
66
69
  shouldFlushBySize(): boolean;
70
+ /**
71
+ * How many leading events form the rrweb paint base (Meta+FullSnapshot or
72
+ * FullSnapshot alone). Returns 0 if the buffer does not start with a paint base.
73
+ */
74
+ paintBaseLength(): number;
67
75
  /**
68
76
  * Take a prefix of events whose estimated JSON size is ~maxBytes.
69
77
  * Always returns at least one event when the buffer is non-empty (caller
70
78
  * must handle a single oversized event).
79
+ *
80
+ * When `keepPaintBaseTogether` is true (segment 0), Meta immediately followed
81
+ * by FullSnapshot is taken as one unit even if estimated size exceeds
82
+ * maxBytes — gzip fitting / hard caps decide whether it can ship.
71
83
  */
72
- takeByEstimatedBytes(maxBytes: number): RrwebEvent[];
84
+ takeByEstimatedBytes(maxBytes: number, opts?: {
85
+ keepPaintBaseTogether?: boolean;
86
+ }): RrwebEvent[];
73
87
  takeAll(): RrwebEvent[];
74
88
  /** Re-queue events at the front (failed upload retry / bisect remainder). */
75
89
  prepend(events: RrwebEvent[]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"segment_buffer.d.ts","sourceRoot":"","sources":["../../src/replay/segment_buffer.ts"],"names":[],"mappings":"AAEA,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAC1C;;;GAGG;AACH,eAAO,MAAM,4BAA4B,QAAc,CAAC;AACxD,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,QACF,CAAC;AAC3C;;;GAGG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C;;GAEG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,uEAAuE;AACvE,eAAO,MAAM,+BAA+B,QAAkB,CAAC;AAC/D,8DAA8D;AAC9D,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAC/C,+BAA+B;AAC/B,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC,oEAAoE;AACpE,eAAO,MAAM,cAAc,QAAS,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,qBAAqB,UAAY,CAAC;AAC/C,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAC5C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAgB,CAAC;AAEpD,mCAAmC;AACnC,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,2BAA2B;AAC3B,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,YAAY,CAAK;IAEzB,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAK7B,eAAe,IAAI,OAAO;IAI1B;;;OAGG;IACH,kBAAkB,IAAI,OAAO;IAc7B;;;OAGG;IACH,QAAQ,CAAC,GAAG,SAAa,EAAE,QAAQ,SAAwB,GAAG,IAAI;IAiBlE,iBAAiB,IAAI,OAAO;IAI5B;;;;OAIG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE;IAyBpD,OAAO,IAAI,UAAU,EAAE;IAOvB,6EAA6E;IAC7E,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI;IAMnC,KAAK,IAAI,IAAI;IAKb,SAAS,IAAI;QAAE,SAAS,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI;IAUtD,OAAO,CAAC,cAAc;CAGvB"}
1
+ {"version":3,"file":"segment_buffer.d.ts","sourceRoot":"","sources":["../../src/replay/segment_buffer.ts"],"names":[],"mappings":"AAEA,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAC1C;;;GAGG;AACH,eAAO,MAAM,4BAA4B,QAAc,CAAC;AACxD,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,QACF,CAAC;AAC3C;;;GAGG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C;;GAEG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,uEAAuE;AACvE,eAAO,MAAM,+BAA+B,QAAkB,CAAC;AAC/D,8DAA8D;AAC9D,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAC/C,+BAA+B;AAC/B,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC,oEAAoE;AACpE,eAAO,MAAM,cAAc,QAAS,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,qBAAqB,UAAY,CAAC;AAC/C,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAC5C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAgB,CAAC;AAEpD,mCAAmC;AACnC,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,2BAA2B;AAC3B,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,YAAY,CAAK;IAEzB,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAK7B,eAAe,IAAI,OAAO;IAI1B,4DAA4D;IAC5D,gBAAgB,IAAI,UAAU,GAAG,SAAS;IAI1C;;;OAGG;IACH,kBAAkB,IAAI,OAAO;IAc7B;;;OAGG;IACH,QAAQ,CAAC,GAAG,SAAa,EAAE,QAAQ,SAAwB,GAAG,IAAI;IAiBlE,iBAAiB,IAAI,OAAO;IAI5B;;;OAGG;IACH,eAAe,IAAI,MAAM;IAczB;;;;;;;;OAQG;IACH,oBAAoB,CAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;QAAE,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAAE,GACzC,UAAU,EAAE;IAqCf,OAAO,IAAI,UAAU,EAAE;IAOvB,6EAA6E;IAC7E,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI;IAMnC,KAAK,IAAI,IAAI;IAKb,SAAS,IAAI;QAAE,SAAS,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI;IAUtD,OAAO,CAAC,cAAc;CAGvB"}
@@ -163,6 +163,10 @@
163
163
  hasFullSnapshot() {
164
164
  return this.events.some((e) => e.type === RRWEB_FULL_SNAPSHOT);
165
165
  }
166
+ /** First FullSnapshot still queued (does not remove it). */
167
+ peekFullSnapshot() {
168
+ return this.events.find((e) => e.type === RRWEB_FULL_SNAPSHOT);
169
+ }
166
170
  /**
167
171
  * Drop leading events until Meta+FullSnapshot (or FullSnapshot alone).
168
172
  * Returns false if no FullSnapshot is present (replay would paint blank).
@@ -202,15 +206,44 @@
202
206
  shouldFlushBySize() {
203
207
  return this.pendingBytes >= SEGMENT_SIZE_BYTES;
204
208
  }
209
+ /**
210
+ * How many leading events form the rrweb paint base (Meta+FullSnapshot or
211
+ * FullSnapshot alone). Returns 0 if the buffer does not start with a paint base.
212
+ */
213
+ paintBaseLength() {
214
+ if (this.events.length === 0) return 0;
215
+ if (this.events[0].type === RRWEB_META && this.events[1]?.type === RRWEB_FULL_SNAPSHOT) {
216
+ return 2;
217
+ }
218
+ if (this.events[0].type === RRWEB_FULL_SNAPSHOT) {
219
+ return 1;
220
+ }
221
+ return 0;
222
+ }
205
223
  /**
206
224
  * Take a prefix of events whose estimated JSON size is ~maxBytes.
207
225
  * Always returns at least one event when the buffer is non-empty (caller
208
226
  * must handle a single oversized event).
227
+ *
228
+ * When `keepPaintBaseTogether` is true (segment 0), Meta immediately followed
229
+ * by FullSnapshot is taken as one unit even if estimated size exceeds
230
+ * maxBytes — gzip fitting / hard caps decide whether it can ship.
209
231
  */
210
- takeByEstimatedBytes(maxBytes) {
232
+ takeByEstimatedBytes(maxBytes, opts) {
211
233
  if (this.events.length === 0) return [];
212
234
  const batch = [];
213
235
  let batchBytes = 0;
236
+ if (opts?.keepPaintBaseTogether) {
237
+ const paintLen = this.paintBaseLength();
238
+ if (paintLen > 0) {
239
+ for (let i = 0; i < paintLen; i++) {
240
+ const ev = this.events.shift();
241
+ batch.push(ev);
242
+ batchBytes += estimateJsonBytes(ev);
243
+ }
244
+ this.recomputeBytes();
245
+ }
246
+ }
214
247
  while (this.events.length > 0) {
215
248
  const next = this.events[0];
216
249
  const nextBytes = estimateJsonBytes(next);
@@ -335,8 +368,53 @@
335
368
  right: events.slice(mid)
336
369
  };
337
370
  }
338
- async function fitCompressedPrefix(events, compress, targetBytes = TARGET_COMPRESSED_SEGMENT_BYTES, hardCap = MAX_COMPRESSED_SEGMENT_BYTES) {
371
+ function paintBaseEventCount(events) {
372
+ if (events.length === 0) return 0;
373
+ if (events[0].type === RRWEB_META && events[1]?.type === RRWEB_FULL_SNAPSHOT) {
374
+ return 2;
375
+ }
376
+ if (events[0].type === RRWEB_FULL_SNAPSHOT) {
377
+ return 1;
378
+ }
379
+ return 0;
380
+ }
381
+ async function fitCompressedPrefix(events, compress, targetBytes = TARGET_COMPRESSED_SEGMENT_BYTES, hardCap = MAX_COMPRESSED_SEGMENT_BYTES, opts) {
339
382
  if (events.length === 0) return null;
383
+ const atomicMin = opts?.atomicMinCount ?? 0;
384
+ if (atomicMin > 0) {
385
+ if (events.length < atomicMin) return null;
386
+ const paint = events.slice(0, atomicMin);
387
+ const paintGzip = await compress(paint);
388
+ if (paintGzip.length > hardCap) {
389
+ return null;
390
+ }
391
+ let lo = atomicMin;
392
+ let hi = events.length;
393
+ let best = {
394
+ events: paint,
395
+ gzip: paintGzip
396
+ };
397
+ while (lo <= hi) {
398
+ const mid = Math.floor((lo + hi) / 2);
399
+ if (mid < atomicMin) {
400
+ lo = atomicMin;
401
+ continue;
402
+ }
403
+ const prefix = events.slice(0, mid);
404
+ const gzip = await compress(prefix);
405
+ if (gzip.length <= targetBytes && gzip.length <= hardCap) {
406
+ best = { events: prefix, gzip };
407
+ lo = mid + 1;
408
+ } else {
409
+ hi = mid - 1;
410
+ }
411
+ }
412
+ return {
413
+ events: best.events,
414
+ gzip: best.gzip,
415
+ remainder: events.slice(best.events.length)
416
+ };
417
+ }
340
418
  let remaining = events;
341
419
  while (remaining.length > 0) {
342
420
  let lo = 1;
@@ -392,6 +470,28 @@
392
470
  };
393
471
  }
394
472
 
473
+ // src/replay/paint_base_size.ts
474
+ function paintBaseSizeDetails(events, opts) {
475
+ const meta = events.find((e) => e.type === RRWEB_META);
476
+ const full = events.find((e) => e.type === RRWEB_FULL_SNAPSHOT);
477
+ const details = {
478
+ segmentEventTypes: events.map((e) => e.type),
479
+ softTakeEstimatedBytes: SEGMENT_SIZE_BYTES,
480
+ targetCompressedSegmentBytes: TARGET_COMPRESSED_SEGMENT_BYTES,
481
+ maxCompressedSegmentBytes: MAX_COMPRESSED_SEGMENT_BYTES
482
+ };
483
+ if (meta) {
484
+ details.metaEstimatedBytes = estimateJsonBytes(meta);
485
+ }
486
+ if (full) {
487
+ details.fullSnapshotEstimatedBytes = estimateJsonBytes(full);
488
+ }
489
+ if (opts?.fullSnapshotCompressedBytes !== void 0) {
490
+ details.fullSnapshotCompressedBytes = opts.fullSnapshotCompressedBytes;
491
+ }
492
+ return details;
493
+ }
494
+
395
495
  // ../../node_modules/rrweb/dist/rrweb.js
396
496
  var __defProp = Object.defineProperty;
397
497
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -13977,7 +14077,11 @@
13977
14077
  if (!this.options || !this.transport || !this.replayId) return;
13978
14078
  if (!this.startedOnServer || this.finishedOnServer) return;
13979
14079
  if (!this.prepareBufferForNewReplay()) {
13980
- await this.abortUnusableClip("no_full_snapshot");
14080
+ const leftover = this.buffer.peekFullSnapshot();
14081
+ await this.abortUnusableClip(
14082
+ "no_full_snapshot",
14083
+ paintBaseSizeDetails(leftover ? [leftover] : [])
14084
+ );
13981
14085
  return;
13982
14086
  }
13983
14087
  while (this.buffer.length > 0) {
@@ -14025,9 +14129,10 @@
14025
14129
  }
14026
14130
  const { events, gzip } = fitted;
14027
14131
  if (this.segmentIndex === 0 && !events.some((e) => e.type === RRWEB_FULL_SNAPSHOT)) {
14028
- await this.abortUnusableClip("no_full_snapshot", {
14029
- segmentEventTypes: events.map((e) => e.type)
14030
- });
14132
+ await this.abortUnusableClip(
14133
+ "no_full_snapshot",
14134
+ paintBaseSizeDetails(events)
14135
+ );
14031
14136
  return;
14032
14137
  }
14033
14138
  if (this.isErrorClipMode() && this.uploadedCompressedBytes + gzip.length > MAX_ERROR_CLIP_COMPRESSED_BYTES && this.segmentIndex > 0) {
@@ -14071,6 +14176,14 @@
14071
14176
  break;
14072
14177
  }
14073
14178
  if (isOversizedSegmentError(error)) {
14179
+ if (this.segmentIndex === 0 && paintBaseEventCount(events) > 0) {
14180
+ await this.abortUnusableClip("oversized_full_snapshot", {
14181
+ source: "server_reject_paint_base",
14182
+ ...paintBaseSizeDetails(events),
14183
+ rejectedCompressedBytes: gzip.length
14184
+ });
14185
+ return;
14186
+ }
14074
14187
  const plan = planOversizedRetry(events);
14075
14188
  if (plan.action === "drop") {
14076
14189
  const droppedType = events[0]?.type;
@@ -14078,10 +14191,15 @@
14078
14191
  "@newtalaria/browser: dropping rrweb event rejected as oversized",
14079
14192
  { type: droppedType }
14080
14193
  );
14081
- if (this.segmentIndex === 0 && droppedType === RRWEB_FULL_SNAPSHOT) {
14082
- await this.abortUnusableClip("oversized_full_snapshot", {
14083
- source: "server_reject"
14084
- });
14194
+ if (droppedType === RRWEB_FULL_SNAPSHOT) {
14195
+ await this.abortUnusableClip(
14196
+ "oversized_full_snapshot",
14197
+ {
14198
+ source: "server_reject",
14199
+ ...paintBaseSizeDetails(events),
14200
+ rejectedCompressedBytes: gzip.length
14201
+ }
14202
+ );
14085
14203
  return;
14086
14204
  }
14087
14205
  continue;
@@ -14111,45 +14229,60 @@
14111
14229
  }
14112
14230
  /**
14113
14231
  * Pull the largest event prefix that gzips under the target compressed size.
14114
- * Drops a single event that can never fit — except FullSnapshot on segment 0
14115
- * (abort the clip instead of uploading a blank orphan stream).
14232
+ *
14233
+ * Segment 0 keeps Meta+FullSnapshot (paint base) atomic through take + fit so
14234
+ * a soft estimated-byte window cannot orphan the FullSnapshot.
14116
14235
  */
14117
14236
  async takeFittedSegment() {
14118
- const chunk = this.buffer.takeByEstimatedBytes(SEGMENT_SIZE_BYTES);
14237
+ const isPaintBaseSegment = this.segmentIndex === 0;
14238
+ const chunk = this.buffer.takeByEstimatedBytes(SEGMENT_SIZE_BYTES, {
14239
+ keepPaintBaseTogether: isPaintBaseSegment
14240
+ });
14119
14241
  if (chunk.length === 0) return null;
14242
+ const atomicMin = isPaintBaseSegment ? paintBaseEventCount(chunk) : 0;
14120
14243
  const fitted = await fitCompressedPrefix(
14121
14244
  chunk,
14122
14245
  compressReplayEvents,
14123
14246
  TARGET_COMPRESSED_SEGMENT_BYTES,
14124
- MAX_COMPRESSED_SEGMENT_BYTES
14247
+ MAX_COMPRESSED_SEGMENT_BYTES,
14248
+ atomicMin > 0 ? { atomicMinCount: atomicMin } : void 0
14125
14249
  );
14126
14250
  if (!fitted) {
14127
- const dropped = chunk[0];
14128
- console.warn(
14129
- "@newtalaria/browser: dropping rrweb event that exceeds max segment size",
14130
- { type: dropped?.type }
14131
- );
14132
- if (this.segmentIndex === 0 && dropped?.type === RRWEB_FULL_SNAPSHOT) {
14251
+ if (isPaintBaseSegment && atomicMin > 0) {
14252
+ const paint = chunk.slice(0, atomicMin);
14253
+ let compressedBytes;
14254
+ const full = paint.find((e) => e.type === RRWEB_FULL_SNAPSHOT);
14255
+ if (full) {
14256
+ try {
14257
+ compressedBytes = (await compressReplayEvents([full])).length;
14258
+ } catch {
14259
+ }
14260
+ }
14261
+ if (chunk.length > atomicMin) {
14262
+ this.buffer.prepend(chunk.slice(atomicMin));
14263
+ }
14133
14264
  return {
14134
14265
  events: [],
14135
14266
  gzip: new Uint8Array(),
14136
14267
  abortReason: "oversized_full_snapshot",
14137
- abortDetails: { source: "fit_compressed_prefix" }
14268
+ abortDetails: {
14269
+ source: "paint_base_exceeds_hard_cap",
14270
+ ...paintBaseSizeDetails(paint, {
14271
+ fullSnapshotCompressedBytes: compressedBytes
14272
+ })
14273
+ }
14138
14274
  };
14139
14275
  }
14276
+ const dropped = chunk[0];
14277
+ console.warn(
14278
+ "@newtalaria/browser: dropping rrweb event that exceeds max segment size",
14279
+ { type: dropped?.type }
14280
+ );
14140
14281
  if (chunk.length > 1) {
14141
14282
  this.buffer.prepend(chunk.slice(1));
14142
14283
  }
14143
14284
  return this.takeFittedSegment();
14144
14285
  }
14145
- if (this.segmentIndex === 0 && chunk.some((e) => e.type === RRWEB_FULL_SNAPSHOT) && !fitted.events.some((e) => e.type === RRWEB_FULL_SNAPSHOT)) {
14146
- return {
14147
- events: [],
14148
- gzip: new Uint8Array(),
14149
- abortReason: "oversized_full_snapshot",
14150
- abortDetails: { source: "fit_dropped_full_snapshot" }
14151
- };
14152
- }
14153
14286
  if (fitted.remainder.length > 0) {
14154
14287
  this.buffer.prepend(fitted.remainder);
14155
14288
  }