@newtalaria/browser 0.1.1 → 0.1.5

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,9 +29,15 @@ 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, upload ~60s before + ~15s after (0–1). Default 1.
32
+ // On error, promote the ~60s ring buffer (0–1). Default 1.
33
33
  replaysOnErrorSampleRate: 1,
34
+ // Post-error upload window. Default 15000 (cheap clip). Use 0 to continue
35
+ // until the 5-minute max duration (Sentry-like, more expensive).
36
+ replaysErrorAfterMs: 15_000,
34
37
  maskAllInputs: true, // default
38
+ // Embed same-origin CSS into the snapshot (needed for auth-gated UIs like CMS).
39
+ // Default false — player re-fetches public stylesheet hrefs instead.
40
+ // inlineStylesheet: true,
35
41
  });
36
42
 
37
43
  try {
@@ -49,6 +55,53 @@ await Talaria.close();
49
55
 
50
56
  `Talaria.init` also installs `window.onerror` / `unhandledrejection` handlers unless you pass `disableDefaultIntegrations: true`.
51
57
 
58
+ Optional init `tags` are merged into every captured event (per-call tags win on key conflict).
59
+
60
+ ## Script tag (IIFE)
61
+
62
+ For hosts without a bundler (e.g. Silverstripe `Requirements`):
63
+
64
+ ```html
65
+ <script src="/path/to/talaria.browser.iife.js"></script>
66
+ <script>
67
+ Talaria.init({
68
+ dsn: 'https://api.example.com',
69
+ apiKey: 'tal_live_…',
70
+ environment: 'production',
71
+ replaysSessionSampleRate: 0,
72
+ replaysOnErrorSampleRate: 0,
73
+ });
74
+ </script>
75
+ ```
76
+
77
+ Build output: `dist/talaria.browser.iife.js` (also produced by `npm run build` / `npm run build:iife`).
78
+
79
+ ## Recommended sampling (cost)
80
+
81
+ You pay for **uploaded + retained** bytes, not for local buffering. Prefer error clips in production; keep full-session sampling low.
82
+
83
+ | Traffic | `replaysSessionSampleRate` | `replaysOnErrorSampleRate` | `replaysErrorAfterMs` |
84
+ | --- | --- | --- | --- |
85
+ | High (100k+/day) | `0.01` | `1.0` | `15000` (default clip) |
86
+ | Medium (10k–100k/day) | `0.1` | `1.0` | `15000` |
87
+ | Low (under 10k/day) | `0.25` | `1.0` | `15000` |
88
+ | Marketing / docs site | `0` | `1.0` | `15000` |
89
+ | Rich post-error context | `0` | `1.0` | `0` (continue to 5 min cap) |
90
+
91
+ **Defaults (`session=0`, `onError=1`, `errorAfterMs=15000`)** are the cheapest useful profile: quiet traffic costs nothing; each sampled error keeps ~60s before + ~15s after.
92
+
93
+ ## Replay sampling behavior
94
+
95
+ | Mode | Behavior |
96
+ | --- | --- |
97
+ | Session sample hit | `replays/start` immediately; segments upload every ~5s or ~100KB until unload or **5 min** max |
98
+ | Session sample miss | Record into a ~60s ring buffer; nothing uploaded until an error sample hits |
99
+ | Error sample + `replaysErrorAfterMs > 0` | Upload buffer (≤480KiB gzip pack target / 512KiB server cap) + trailing window, hard caps **12 segments** or **2MiB** compressed, attach `replayId` only if segments landed, `finish`, return to buffer mode |
100
+ | Error sample + `replaysErrorAfterMs = 0` | Upload buffer then continue like session mode until **5 min** / unload / size caps |
101
+ | Server limit (replay segments / total size / duration) | Stop uploading that replay; no retries |
102
+ | Oversized single rrweb event | Dropped with a console warning (cannot fit under segment cap) |
103
+ | `pagehide` / `close` | Flush pending segments with `fetch` `keepalive`, then `replays/finish`; `close` fully resets so `init()` works again (React Strict Mode) |
104
+
52
105
  ## Serverpod URL pattern
53
106
 
54
107
  Talaria uses **Serverpod RPC**, not REST resource URLs:
@@ -91,16 +144,6 @@ decode('<base64>', 'base64')
91
144
 
92
145
  Segment payloads are a **gzip-compressed JSON array** of rrweb events.
93
146
 
94
- ## Replay sampling
95
-
96
- | Mode | Behavior |
97
- | --- | --- |
98
- | Session sample hit | `replays/start` immediately; segments upload every ~5s or ~100KB until unload |
99
- | Session sample miss | Record into a ~60s ring buffer; nothing uploaded until an error sample hits |
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 |
102
- | `pagehide` / `close` | Flush pending segments with `fetch` `keepalive`, then `replays/finish` |
103
-
104
147
  Custom rrweb events:
105
148
 
106
149
  - `talaria-console` — console level + args (truncated)
@@ -108,6 +151,12 @@ Custom rrweb events:
108
151
 
109
152
  Privacy defaults: `maskAllInputs: true`, password fields masked, `[data-talaria-mask]` blocked.
110
153
 
154
+ ### Auth-gated CSS (CMS / admin)
155
+
156
+ By default `inlineStylesheet` is `false`: linked stylesheets stay as `href`s and are re-fetched when you watch the replay. That works for **public** CSS, but fails for login-protected admin CSS (player has no session cookies).
157
+
158
+ Set `inlineStylesheet: true` so same-origin stylesheet rules are embedded while the user is logged in. Cross-origin sheets without CORS still cannot be inlined (browser `cssRules` restriction).
159
+
111
160
  ## Public API
112
161
 
113
162
  | API | Description |
@@ -122,3 +171,31 @@ Privacy defaults: `maskAllInputs: true`, password fields masked, `[data-talaria-
122
171
  ## Example
123
172
 
124
173
  See [`../../examples/sdk-spa`](../../examples/sdk-spa) for a minimal page wired to `localhost:8080`.
174
+
175
+ ## Local `file:` install (marketing / monorepo)
176
+
177
+ `dist/` is gitignored. After editing SDK `src/`, rebuild before the consumer picks up changes:
178
+
179
+ ```bash
180
+ cd new_talaria_js/packages/browser
181
+ npm run build
182
+ # or: npm install in the consumer — `prepare` runs build for file: installs
183
+ ```
184
+
185
+ Then restart the Next app with a clean cache:
186
+
187
+ ```bash
188
+ cd ../new_talaria_marketing # sibling repo
189
+ rm -rf .next
190
+ npm run dev
191
+ ```
192
+
193
+ **Serverpod must be restarted** after changing `ReplayLimits` (compressed cap is 512KiB). A live process still on 256KiB will 400 every near-max segment.
194
+
195
+ ### Verify error-clip ingest
196
+
197
+ 1. Restart Serverpod (512KiB live).
198
+ 2. Rebuild the browser package; restart marketing with clean `.next`.
199
+ 3. Browse `/docs/**` ~30s, throw one test exception.
200
+ 4. Expect: a few `ingestSegment` 200s, one `finish`, **zero** compressed-size 400 spam, dashboard replay plays.
201
+ 5. Throw again later: a new bounded clip is allowed (buffer mode reset).
package/dist/client.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import type { CaptureContext, SeverityLevel, TalariaInitOptions } from './types.js';
2
+ export { computeErrorClipDeadlineMs, fitCompressedPrefix, isErrorClipBudgetExhausted, planOversizedRetry, } from './replay/fit_segment.js';
3
+ export { MAX_SEGMENTS_ERROR_CLIP, MAX_ERROR_CLIP_COMPRESSED_BYTES, TARGET_COMPRESSED_SEGMENT_BYTES, MAX_COMPRESSED_SEGMENT_BYTES, } from './replay/segment_buffer.js';
2
4
  export declare class TalariaClient {
3
5
  private options;
4
6
  private transport;
@@ -12,11 +14,22 @@ export declare class TalariaClient {
12
14
  private startedOnServer;
13
15
  private finishedOnServer;
14
16
  private segmentIndex;
17
+ private uploadedCompressedBytes;
18
+ /** Wall clock when continuous upload began (session or error promote). */
19
+ private uploadStartedAtMs;
20
+ /** Absolute deadline for error-clip mode (not extended by later errors). */
21
+ private errorClipDeadlineMs;
15
22
  private flushTimer;
16
23
  private errorClipTimer;
24
+ private maxDurationTimer;
17
25
  private uploadChain;
18
26
  private closed;
19
27
  private teardowns;
28
+ /**
29
+ * Replay id that just finished with segments — kept briefly so capture can
30
+ * attach it to the error event after budget/timer finish resets buffer mode.
31
+ */
32
+ private linkableReplayId;
20
33
  init(options: TalariaInitOptions): void;
21
34
  getReplayId(): string | null;
22
35
  captureException(error: unknown, context?: CaptureContext): Promise<void>;
@@ -26,20 +39,49 @@ export declare class TalariaClient {
26
39
  keepalive?: boolean;
27
40
  finish?: boolean;
28
41
  }): Promise<void>;
42
+ /**
43
+ * Stop recording, flush while still open, then fully reset so `init()` can
44
+ * run again (React Strict Mode remount).
45
+ */
29
46
  close(): Promise<void>;
30
47
  private onRrwebEvent;
31
48
  private capture;
49
+ private markUploadStarted;
50
+ private isPastMaxDuration;
51
+ private isPastErrorClipDeadline;
52
+ private isErrorClipMode;
53
+ private scheduleMaxDurationStop;
54
+ private clearMaxDurationTimer;
55
+ private runStopForMaxDuration;
56
+ /**
57
+ * Schedule end of the cheap error clip against a fixed absolute deadline.
58
+ * Subsequent errors re-arm the timer but never push the wall later.
59
+ */
32
60
  private scheduleErrorClipEnd;
33
61
  private clearErrorClipTimer;
34
62
  /** Flush trailing post-error window, finish replay, return to ring buffer. */
35
63
  private endErrorClip;
64
+ private runEndErrorClip;
36
65
  /** After an error clip (or terminal limit), buffer locally until the next error. */
37
66
  private resetToBufferMode;
67
+ /** Emit a checkout FullSnapshot into the ring buffer (sync via rrweb emit). */
68
+ private checkoutFullSnapshot;
69
+ /**
70
+ * Ensure segment 0 begins at Meta+FullSnapshot. Orphan increments alone
71
+ * produce a blank rrweb player.
72
+ */
73
+ private prepareBufferForNewReplay;
38
74
  private stopUploadingAfterLimit;
39
75
  private installGlobalHandlers;
40
76
  private enqueueUpload;
41
77
  private ensureStarted;
42
78
  private uploadPendingSegments;
79
+ private maxSegmentsAllowed;
80
+ /**
81
+ * Pull the largest event prefix that gzips under the target compressed size.
82
+ * Drops a single event that can never fit.
83
+ */
84
+ private takeFittedSegment;
43
85
  private finishOnServer;
44
86
  }
45
87
  //# sourceMappingURL=client.d.ts.map
@@ -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;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"}
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;AAoCpB,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;AAyEpC,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,uBAAuB,CAAK;IACpC,0EAA0E;IAC1E,OAAO,CAAC,iBAAiB,CAAuB;IAChD,4EAA4E;IAC5E,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,gBAAgB,CAA8C;IACtE,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAkB;IACnC;;;OAGG;IACH,OAAO,CAAC,gBAAgB,CAAuB;IAE/C,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAiEvC,WAAW,IAAI,MAAM,GAAG,IAAI;IAKtB,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;IA0CjB;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmD5B,OAAO,CAAC,YAAY;YAwBN,OAAO;IA+ErB,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,uBAAuB;IAK/B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,uBAAuB;IAiB/B,OAAO,CAAC,qBAAqB;YAOf,qBAAqB;IAsBnC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,mBAAmB;IAO3B,8EAA8E;YAChE,YAAY;YAgBZ,eAAe;IAW7B,oFAAoF;IACpF,OAAO,CAAC,iBAAiB;IAiBzB,+EAA+E;IAC/E,OAAO,CAAC,oBAAoB;IAI5B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAYjC,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,qBAAqB;IA6B7B,OAAO,CAAC,aAAa;YAKP,aAAa;YAkBb,qBAAqB;IA8HnC,OAAO,CAAC,kBAAkB;IAQ1B;;;OAGG;YACW,iBAAiB;YA2BjB,cAAc;CAuB7B"}
package/dist/iife.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=iife.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iife.d.ts","sourceRoot":"","sources":["../src/iife.ts"],"names":[],"mappings":""}
package/dist/index.d.ts CHANGED
@@ -9,8 +9,9 @@ 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).
13
12
  * replaysOnErrorSampleRate: 1,
13
+ * // Default 15s post-error clip; set 0 to continue until 5 min cap.
14
+ * replaysErrorAfterMs: 15_000,
14
15
  * });
15
16
  *
16
17
  * try {
@@ -28,7 +29,9 @@ export declare const Talaria: {
28
29
  flush(): Promise<void>;
29
30
  close(): Promise<void>;
30
31
  };
31
- export type { CaptureContext, Environment, SeverityLevel, TalariaInitOptions, } from './types.js';
32
32
  export { TalariaClient } from './client.js';
33
+ export { computeErrorClipDeadlineMs, fitCompressedPrefix, isErrorClipBudgetExhausted, planOversizedRetry, } from './replay/fit_segment.js';
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
+ export type { CaptureContext, Environment, SeverityLevel, TalariaInitOptions, } from './types.js';
33
36
  export default Talaria;
34
37
  //# sourceMappingURL=index.d.ts.map
@@ -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;;;;;;;;;;;;;;;;;;;;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"}
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,aAAa,EACb,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,eAAe,OAAO,CAAC"}