@newtalaria/browser 0.1.0 → 0.1.1

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 CHANGED
@@ -29,7 +29,7 @@ Talaria.init({
29
29
  release: '1.0.0',
30
30
  // Continuous session upload (0–1). Default 0 = buffer only until an error.
31
31
  replaysSessionSampleRate: 0,
32
- // On error, flush buffer + attach replayId (0–1). Default 1.
32
+ // On error, upload ~60s before + ~15s after (0–1). Default 1.
33
33
  replaysOnErrorSampleRate: 1,
34
34
  maskAllInputs: true, // default
35
35
  });
@@ -95,9 +95,10 @@ Segment payloads are a **gzip-compressed JSON array** of rrweb events.
95
95
 
96
96
  | Mode | Behavior |
97
97
  | --- | --- |
98
- | Session sample hit | `replays/start` immediately; segments upload every ~5s or ~100KB |
98
+ | Session sample hit | `replays/start` immediately; segments upload every ~5s or ~100KB until unload |
99
99
  | Session sample miss | Record into a ~60s ring buffer; nothing uploaded until an error sample hits |
100
- | Error sample hit | Enable upload, flush buffer, send event with `replayId` |
100
+ | Error sample hit | Upload the ring buffer + ~15s after the error, attach `replayId`, then `finish` and return to buffer-only mode |
101
+ | Server limit (segments / size / duration) | Stop uploading that replay; no retries |
101
102
  | `pagehide` / `close` | Flush pending segments with `fetch` `keepalive`, then `replays/finish` |
102
103
 
103
104
  Custom rrweb events:
package/dist/client.d.ts CHANGED
@@ -6,11 +6,14 @@ export declare class TalariaClient {
6
6
  private sessionId;
7
7
  private recorder;
8
8
  private buffer;
9
+ /** Continuous upload for the whole page session (session sample hit). */
10
+ private sessionSampled;
9
11
  private uploadEnabled;
10
12
  private startedOnServer;
11
13
  private finishedOnServer;
12
14
  private segmentIndex;
13
15
  private flushTimer;
16
+ private errorClipTimer;
14
17
  private uploadChain;
15
18
  private closed;
16
19
  private teardowns;
@@ -26,6 +29,13 @@ export declare class TalariaClient {
26
29
  close(): Promise<void>;
27
30
  private onRrwebEvent;
28
31
  private capture;
32
+ private scheduleErrorClipEnd;
33
+ private clearErrorClipTimer;
34
+ /** Flush trailing post-error window, finish replay, return to ring buffer. */
35
+ private endErrorClip;
36
+ /** After an error clip (or terminal limit), buffer locally until the next error. */
37
+ private resetToBufferMode;
38
+ private stopUploadingAfterLimit;
29
39
  private installGlobalHandlers;
30
40
  private enqueueUpload;
31
41
  private ensureStarted;
@@ -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;AA6DpB,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAkB;IAEnC,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IA0DvC,WAAW,IAAI,MAAM,GAAG,IAAI;IAItB,gBAAgB,CACpB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;IAcV,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,aAAsB,EAC7B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;IASV,KAAK,CAAC,IAAI,CAAC,EAAE;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBX,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B,OAAO,CAAC,YAAY;YAcN,OAAO;IA4DrB,OAAO,CAAC,qBAAqB;IA6B7B,OAAO,CAAC,aAAa;YAKP,aAAa;YAiBb,qBAAqB;YAqCrB,cAAc;CAkB7B"}
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;AAyEpB,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,MAAM,CAAuB;IACrC,yEAAyE;IACzE,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAkB;IAEnC,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IA0DvC,WAAW,IAAI,MAAM,GAAG,IAAI;IAItB,gBAAgB,CACpB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;IAcV,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,aAAsB,EAC7B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;IASV,KAAK,CAAC,IAAI,CAAC,EAAE;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BX,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B5B,OAAO,CAAC,YAAY;YAcN,OAAO;IAkErB,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,mBAAmB;IAO3B,8EAA8E;YAChE,YAAY;IAoB1B,oFAAoF;IACpF,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,qBAAqB;IA6B7B,OAAO,CAAC,aAAa;YAKP,aAAa;YAiBb,qBAAqB;YAkDrB,cAAc;CAkB7B"}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ import type { CaptureContext, SeverityLevel, TalariaInitOptions } from './types.
9
9
  * dsn: 'http://localhost:8080',
10
10
  * apiKey: 'tal_live_…',
11
11
  * environment: 'development',
12
+ * // Error clip: ~60s before + ~15s after (not full-session upload).
12
13
  * replaysOnErrorSampleRate: 1,
13
14
  * });
14
15
  *
@@ -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;;;;;;;;;;;;;;;;;;;GAmBG;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,YAAY,EACV,cAAc,EACd,WAAW,EACX,aAAa,EACb,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,eAAe,OAAO,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;;;;;;;;;;;;;;;;;;;;GAoBG;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,YAAY,EACV,cAAc,EACd,WAAW,EACX,aAAa,EACb,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,eAAe,OAAO,CAAC"}
package/dist/index.js CHANGED
@@ -164,6 +164,7 @@ function estimateJsonBytes(value) {
164
164
  var SEGMENT_SIZE_BYTES = 1e5;
165
165
  var SEGMENT_FLUSH_MS = 5e3;
166
166
  var RING_BUFFER_MS = 6e4;
167
+ var ERROR_REPLAY_AFTER_MS = 15e3;
167
168
  var SegmentBuffer = class {
168
169
  constructor() {
169
170
  this.events = [];
@@ -13145,6 +13146,10 @@ function levelToEventType(level) {
13145
13146
  if (level === "debug") return "debug";
13146
13147
  return "info";
13147
13148
  }
13149
+ function isTerminalReplayLimitError(error) {
13150
+ const msg = error instanceof Error ? error.message : String(error);
13151
+ return msg.includes("replay exceeds max segment count") || msg.includes("replay exceeds max total size") || msg.includes("replay exceeds max duration") || msg.includes("Replay has expired");
13152
+ }
13148
13153
  var TalariaClient = class {
13149
13154
  constructor() {
13150
13155
  this.options = null;
@@ -13153,11 +13158,14 @@ var TalariaClient = class {
13153
13158
  this.sessionId = null;
13154
13159
  this.recorder = null;
13155
13160
  this.buffer = new SegmentBuffer();
13161
+ /** Continuous upload for the whole page session (session sample hit). */
13162
+ this.sessionSampled = false;
13156
13163
  this.uploadEnabled = false;
13157
13164
  this.startedOnServer = false;
13158
13165
  this.finishedOnServer = false;
13159
13166
  this.segmentIndex = 0;
13160
13167
  this.flushTimer = null;
13168
+ this.errorClipTimer = null;
13161
13169
  this.uploadChain = Promise.resolve();
13162
13170
  this.closed = false;
13163
13171
  this.teardowns = [];
@@ -13179,8 +13187,8 @@ var TalariaClient = class {
13179
13187
  this.startedOnServer = false;
13180
13188
  this.segmentIndex = 0;
13181
13189
  this.buffer = new SegmentBuffer();
13182
- const sessionSampled = Math.random() < this.options.replaysSessionSampleRate;
13183
- this.uploadEnabled = sessionSampled;
13190
+ this.sessionSampled = Math.random() < this.options.replaysSessionSampleRate;
13191
+ this.uploadEnabled = this.sessionSampled;
13184
13192
  this.recorder = startRecorder({
13185
13193
  maskAllInputs: this.options.maskAllInputs,
13186
13194
  blockSelector: this.options.blockSelector,
@@ -13239,16 +13247,26 @@ var TalariaClient = class {
13239
13247
  return;
13240
13248
  }
13241
13249
  await this.enqueueUpload(async () => {
13250
+ if (!this.uploadEnabled || this.closed) return;
13242
13251
  await this.ensureStarted({ keepalive });
13243
13252
  await this.uploadPendingSegments({ keepalive });
13244
- if (opts?.finish) {
13245
- await this.finishOnServer({ keepalive, reason: opts.reason ?? "pagehide" });
13253
+ if (opts?.finish && this.uploadEnabled) {
13254
+ await this.finishOnServer({
13255
+ keepalive,
13256
+ reason: opts.reason ?? "pagehide"
13257
+ });
13258
+ if (!this.sessionSampled) {
13259
+ this.resetToBufferMode();
13260
+ } else {
13261
+ this.uploadEnabled = false;
13262
+ }
13246
13263
  }
13247
13264
  });
13248
13265
  }
13249
13266
  async close() {
13250
13267
  if (this.closed) return;
13251
13268
  this.closed = true;
13269
+ this.clearErrorClipTimer();
13252
13270
  if (this.flushTimer) {
13253
13271
  clearInterval(this.flushTimer);
13254
13272
  this.flushTimer = null;
@@ -13281,9 +13299,14 @@ var TalariaClient = class {
13281
13299
  throw new Error("@newtalaria/browser: call Talaria.init() first");
13282
13300
  }
13283
13301
  const isErrorLike = args.level === "error" || args.level === "fatal";
13284
- if (isErrorLike && !this.uploadEnabled) {
13285
- if (Math.random() < this.options.replaysOnErrorSampleRate) {
13286
- this.uploadEnabled = true;
13302
+ if (isErrorLike && !this.sessionSampled) {
13303
+ if (!this.uploadEnabled) {
13304
+ if (Math.random() < this.options.replaysOnErrorSampleRate) {
13305
+ this.uploadEnabled = true;
13306
+ this.scheduleErrorClipEnd();
13307
+ }
13308
+ } else {
13309
+ this.scheduleErrorClipEnd();
13287
13310
  }
13288
13311
  }
13289
13312
  const attachReplay = this.uploadEnabled;
@@ -13321,6 +13344,55 @@ var TalariaClient = class {
13321
13344
  throw error;
13322
13345
  }
13323
13346
  }
13347
+ scheduleErrorClipEnd() {
13348
+ if (this.sessionSampled || this.closed) return;
13349
+ this.clearErrorClipTimer();
13350
+ this.errorClipTimer = setTimeout(() => {
13351
+ this.errorClipTimer = null;
13352
+ void this.endErrorClip();
13353
+ }, ERROR_REPLAY_AFTER_MS);
13354
+ }
13355
+ clearErrorClipTimer() {
13356
+ if (this.errorClipTimer) {
13357
+ clearTimeout(this.errorClipTimer);
13358
+ this.errorClipTimer = null;
13359
+ }
13360
+ }
13361
+ /** Flush trailing post-error window, finish replay, return to ring buffer. */
13362
+ async endErrorClip() {
13363
+ if (this.closed || this.sessionSampled || !this.uploadEnabled || !this.options || !this.transport) {
13364
+ return;
13365
+ }
13366
+ await this.enqueueUpload(async () => {
13367
+ if (!this.uploadEnabled || this.sessionSampled) return;
13368
+ await this.ensureStarted({ keepalive: false });
13369
+ await this.uploadPendingSegments({ keepalive: false });
13370
+ await this.finishOnServer({ keepalive: false, reason: "error_clip" });
13371
+ this.resetToBufferMode();
13372
+ });
13373
+ }
13374
+ /** After an error clip (or terminal limit), buffer locally until the next error. */
13375
+ resetToBufferMode() {
13376
+ this.clearErrorClipTimer();
13377
+ this.uploadEnabled = false;
13378
+ this.startedOnServer = false;
13379
+ this.finishedOnServer = false;
13380
+ this.segmentIndex = 0;
13381
+ this.replayId = createId();
13382
+ this.buffer.trimRing();
13383
+ }
13384
+ stopUploadingAfterLimit() {
13385
+ this.clearErrorClipTimer();
13386
+ this.uploadEnabled = false;
13387
+ if (this.sessionSampled) {
13388
+ if (this.flushTimer) {
13389
+ clearInterval(this.flushTimer);
13390
+ this.flushTimer = null;
13391
+ }
13392
+ } else {
13393
+ this.resetToBufferMode();
13394
+ }
13395
+ }
13324
13396
  installGlobalHandlers() {
13325
13397
  if (typeof window === "undefined") return;
13326
13398
  const onError = (event) => {
@@ -13382,6 +13454,18 @@ var TalariaClient = class {
13382
13454
  });
13383
13455
  this.segmentIndex = index2 + 1;
13384
13456
  } catch (error) {
13457
+ if (isTerminalReplayLimitError(error)) {
13458
+ console.warn(
13459
+ "@newtalaria/browser: replay upload stopped (server limit)",
13460
+ error
13461
+ );
13462
+ await this.finishOnServer({
13463
+ keepalive: opts.keepalive,
13464
+ reason: "limit"
13465
+ });
13466
+ this.stopUploadingAfterLimit();
13467
+ break;
13468
+ }
13385
13469
  this.buffer.prepend(events);
13386
13470
  console.warn("@newtalaria/browser: replays/ingestSegment failed", error);
13387
13471
  break;