@edraj/sauron-browser 1.0.0

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.
@@ -0,0 +1,493 @@
1
+ /**
2
+ * Sauron wire-contract types.
3
+ *
4
+ * These interfaces mirror the LOCKED envelope shape that the Rust ingest
5
+ * gateway and the Flutter SDK also emit/consume. Field names, nullability and
6
+ * ordering are load-bearing — do not "clean them up".
7
+ */
8
+ /** Severity level. Matches the backend enum exactly. */
9
+ type Level = 'debug' | 'info' | 'warning' | 'error' | 'fatal';
10
+ /** Discriminant for an envelope item. */
11
+ type ItemType = 'error' | 'event' | 'identify' | 'breadcrumb_batch' | 'transaction';
12
+ /** Category of a performance transaction. Matches the backend enum exactly. */
13
+ type TransactionOp = 'navigation' | 'http' | 'resource' | 'screen_load' | 'custom';
14
+ /** A single normalized stack frame (raw, never symbolicated client-side). */
15
+ interface Frame {
16
+ function: string | null;
17
+ filename: string | null;
18
+ lineno: number | null;
19
+ colno: number | null;
20
+ in_app: boolean;
21
+ }
22
+ /** How an exception reached the SDK. */
23
+ interface Mechanism {
24
+ type: string;
25
+ handled: boolean;
26
+ }
27
+ /** The exception payload of an error item. */
28
+ interface ExceptionValue {
29
+ type: string | null;
30
+ value: string | null;
31
+ mechanism: Mechanism;
32
+ stacktrace: Frame[];
33
+ }
34
+ /** A breadcrumb — a short trail-of-events entry. `data`/`message` may be null. */
35
+ interface Breadcrumb {
36
+ type: string;
37
+ category: string;
38
+ message: string | null;
39
+ level: Level;
40
+ timestamp: string;
41
+ data: Record<string, unknown> | null;
42
+ }
43
+ /** An error item (uncaught error, rejection, or manual capture). */
44
+ interface ErrorItem {
45
+ type: 'error';
46
+ /**
47
+ * Stable id the SDK mints for this report so callers can correlate it. Wire
48
+ * field `event_id`. Optional — the backend defaults one when omitted.
49
+ */
50
+ event_id?: string;
51
+ timestamp: string;
52
+ level: Level;
53
+ exception: ExceptionValue;
54
+ /** Optional human-readable summary alongside the exception. */
55
+ message?: string;
56
+ breadcrumbs: Breadcrumb[];
57
+ fingerprint: string[] | null;
58
+ /**
59
+ * Free-form indexed tags lifted from the current scope. Optional — omitted
60
+ * when the scope carries none (the backend defaults to `{}`).
61
+ */
62
+ tags?: Record<string, unknown>;
63
+ /**
64
+ * Dev-owned structured context blocks (e.g. `{ order: { id: 7 } }`). DISTINCT
65
+ * from the machine-owned `context` on the envelope — never overwrites it.
66
+ * Optional — omitted when empty (the backend defaults to `{}`).
67
+ */
68
+ contexts?: Record<string, unknown>;
69
+ /** Freeform JSON bag. Optional — omitted when empty (backend defaults `{}`). */
70
+ extra?: Record<string, unknown>;
71
+ /**
72
+ * Per-item user override (falls back to the envelope-context user). Optional
73
+ * — omitted when no identity is set on the scope.
74
+ */
75
+ user?: UserContext | null;
76
+ session_id?: string | null;
77
+ screen?: string | null;
78
+ }
79
+ /** A product-analytics event (PostHog-style `track`). */
80
+ interface EventItem {
81
+ type: 'event';
82
+ name: string;
83
+ distinct_id: string | null;
84
+ session_id?: string | null;
85
+ screen?: string | null;
86
+ timestamp: string;
87
+ properties: Record<string, unknown>;
88
+ /** Scope+call tags lifted onto the event. Optional — omitted when empty. */
89
+ tags?: Record<string, unknown>;
90
+ /** Scope+call named context blocks. Optional — omitted when empty. */
91
+ contexts?: Record<string, unknown>;
92
+ /** Scope+call freeform JSON. Optional — omitted when empty. */
93
+ extra?: Record<string, unknown>;
94
+ }
95
+ /**
96
+ * A performance transaction (navigation timing, an instrumented `fetch`, a
97
+ * screen load, ...). `duration_ms` is the wall-clock span; the `http_*`/`url`
98
+ * fields carry request metadata for `http` ops and are `null` otherwise.
99
+ */
100
+ interface TransactionItem {
101
+ type: 'transaction';
102
+ name: string;
103
+ op: TransactionOp;
104
+ duration_ms: number;
105
+ status?: string | null;
106
+ http_method?: string | null;
107
+ http_status?: number | null;
108
+ url?: string | null;
109
+ distinct_id?: string | null;
110
+ session_id?: string | null;
111
+ timestamp: string;
112
+ }
113
+ /** An identity association (PostHog-style `identify`). */
114
+ interface IdentifyItem {
115
+ type: 'identify';
116
+ distinct_id: string | null;
117
+ anonymous_id: string | null;
118
+ traits: Record<string, unknown>;
119
+ }
120
+ /** A standalone batch of breadcrumbs (used for periodic session trails). */
121
+ interface BreadcrumbBatchItem {
122
+ type: 'breadcrumb_batch';
123
+ breadcrumbs: Breadcrumb[];
124
+ }
125
+ /** Any item that can appear in an envelope's `items` array. */
126
+ type EnvelopeItem = ErrorItem | EventItem | IdentifyItem | BreadcrumbBatchItem | TransactionItem;
127
+ interface DeviceContext {
128
+ /** Durable, persisted device identity (localStorage `sauron.device_id`). */
129
+ device_id: string;
130
+ family: string | null;
131
+ model: string | null;
132
+ arch: string | null;
133
+ }
134
+ interface OsContext {
135
+ name: string | null;
136
+ version: string | null;
137
+ }
138
+ interface AppContext {
139
+ version: string | null;
140
+ build: string | null;
141
+ }
142
+ interface RuntimeContext {
143
+ name: string | null;
144
+ version: string | null;
145
+ }
146
+ interface UserContext {
147
+ id: string | null;
148
+ email: string | null;
149
+ traits: Record<string, unknown>;
150
+ }
151
+ interface Context {
152
+ device: DeviceContext;
153
+ os: OsContext;
154
+ app: AppContext;
155
+ runtime: RuntimeContext;
156
+ user: UserContext;
157
+ }
158
+ interface SdkInfo {
159
+ name: string;
160
+ version: string;
161
+ }
162
+ interface EnvelopeHeader {
163
+ dsn: string;
164
+ sdk: SdkInfo;
165
+ sent_at: string;
166
+ environment: string;
167
+ release: string | null;
168
+ }
169
+ /** The complete, serializable envelope posted to the ingest gateway. */
170
+ interface Envelope {
171
+ header: EnvelopeHeader;
172
+ context: Context;
173
+ items: EnvelopeItem[];
174
+ }
175
+ /** Loose hint bag passed through to `beforeSend` / `beforeBreadcrumb`. */
176
+ type Hint = Record<string, unknown> & {
177
+ originalException?: unknown;
178
+ event?: unknown;
179
+ /** Per-call metadata overrides, merged over the current scope before send. */
180
+ tags?: Record<string, string>;
181
+ contexts?: Record<string, Record<string, unknown>>;
182
+ extra?: Record<string, unknown>;
183
+ };
184
+ /** Per-call metadata overrides accepted by captureException/captureMessage/track. */
185
+ interface CaptureOptions {
186
+ tags?: Record<string, string>;
187
+ contexts?: Record<string, Record<string, unknown>>;
188
+ extra?: Record<string, unknown>;
189
+ }
190
+ /** Options accepted by `track` — {@link CaptureOptions} plus a screen override. */
191
+ interface TrackOptions extends CaptureOptions {
192
+ screen?: string;
193
+ }
194
+ /** Value accepted by `setUser` — normalized into a `UserContext`. */
195
+ type UserInput = (Partial<UserContext> & {
196
+ id?: string | null;
197
+ email?: string | null;
198
+ }) | null;
199
+ type BeforeSend = (item: EnvelopeItem, hint?: Hint) => EnvelopeItem | null;
200
+ type BeforeBreadcrumb = (breadcrumb: Breadcrumb, hint?: Hint) => Breadcrumb | null;
201
+ /** Transport tuning knobs. */
202
+ interface TransportOptions {
203
+ /** How often the pending batch is flushed, in ms. Default 5000. */
204
+ flushIntervalMs?: number;
205
+ /** Max items per envelope before an eager flush. Default 30. */
206
+ maxBatch?: number;
207
+ /** Cap on the offline localStorage queue, in bytes. Default 1 MiB. */
208
+ maxQueueBytes?: number;
209
+ }
210
+ /** Options accepted by `Sauron.init`. */
211
+ interface InitOptions {
212
+ /** `https://<public_key>@<host>/<project_id>` */
213
+ dsn: string;
214
+ environment?: string;
215
+ release?: string;
216
+ /** Error sample rate in [0, 1]. Default 1 (send everything). */
217
+ sampleRate?: number;
218
+ /** Ring-buffer size for breadcrumbs. Default 50. */
219
+ maxBreadcrumbs?: number;
220
+ beforeSend?: BeforeSend;
221
+ beforeBreadcrumb?: BeforeBreadcrumb;
222
+ transport?: TransportOptions;
223
+ /**
224
+ * Auto-capture performance transactions (navigation, fetch, SPA routes) by
225
+ * patching `fetch`/History. Opt-in — default `false`. Manual
226
+ * `trackTransaction()` works regardless.
227
+ */
228
+ performance?: boolean;
229
+ /** Seed the initial screen name. */
230
+ screen?: string;
231
+ /**
232
+ * Auto-track the current screen from History navigations (reuses the SPA
233
+ * route hook). Opt-in — default `false`. `setScreen()` works regardless.
234
+ */
235
+ screenTracking?: boolean;
236
+ /** Default tags seeded into the global scope (string→string). */
237
+ tags?: Record<string, string>;
238
+ /** Default named context blocks seeded into the global scope. */
239
+ contexts?: Record<string, Record<string, unknown>>;
240
+ /** Default freeform extra seeded into the global scope. */
241
+ extra?: Record<string, unknown>;
242
+ debug?: boolean;
243
+ }
244
+ /** Fully-resolved options with all defaults applied. */
245
+ interface ResolvedOptions {
246
+ dsn: string;
247
+ environment: string;
248
+ release: string | null;
249
+ sampleRate: number;
250
+ maxBreadcrumbs: number;
251
+ beforeSend?: BeforeSend;
252
+ beforeBreadcrumb?: BeforeBreadcrumb;
253
+ transport: Required<TransportOptions>;
254
+ performance: boolean;
255
+ screen?: string;
256
+ screenTracking: boolean;
257
+ tags: Record<string, string>;
258
+ contexts: Record<string, Record<string, unknown>>;
259
+ extra: Record<string, unknown>;
260
+ debug: boolean;
261
+ }
262
+
263
+ /** Partial breadcrumb — missing fields are filled with sensible defaults. */
264
+ interface BreadcrumbInput {
265
+ type?: string;
266
+ category?: string;
267
+ message?: string | null;
268
+ level?: Level;
269
+ timestamp?: string;
270
+ data?: Record<string, unknown> | null;
271
+ }
272
+
273
+ /** Loose (camelCase) input accepted by {@link trackTransaction}. */
274
+ interface TransactionInput {
275
+ name: string;
276
+ op?: string;
277
+ durationMs: number;
278
+ status?: string | null;
279
+ httpMethod?: string | null;
280
+ httpStatus?: number | null;
281
+ url?: string | null;
282
+ }
283
+
284
+ /**
285
+ * DSN parsing.
286
+ *
287
+ * A DSN looks like `https://<public_key>@<host>/<project_id>`. The public key
288
+ * is a non-secret, write-only credential — it is safe to ship in client code.
289
+ */
290
+ interface Dsn {
291
+ /** The raw DSN string (embedded verbatim into the envelope header). */
292
+ raw: string;
293
+ publicKey: string;
294
+ /** `host:port` — used for the infinite-loop denylist. */
295
+ host: string;
296
+ /** Hostname without port. */
297
+ hostname: string;
298
+ /** `https` or `http` (no trailing colon). */
299
+ protocol: string;
300
+ projectId: string;
301
+ /** `POST` target for the primary transport. */
302
+ envelopeUrl: string;
303
+ /** `POST` target for the `sendBeacon` fallback (key in query string). */
304
+ beaconUrl: string;
305
+ }
306
+ declare class DsnError extends Error {
307
+ constructor(message: string);
308
+ }
309
+ /** Parse and validate a DSN, deriving the transport URLs. */
310
+ declare function parseDsn(dsn: string): Dsn;
311
+
312
+ /**
313
+ * Mutable per-client state: the current user, a ring buffer of breadcrumbs and
314
+ * free-form tags. The breadcrumb buffer is capped at `maxBreadcrumbs`; the
315
+ * oldest entries fall off the front (FIFO).
316
+ */
317
+ declare class Scope {
318
+ private user;
319
+ private breadcrumbs;
320
+ private maxBreadcrumbs;
321
+ readonly tags: Record<string, string>;
322
+ readonly contexts: Record<string, unknown>;
323
+ readonly extra: Record<string, unknown>;
324
+ constructor(maxBreadcrumbs?: number);
325
+ setMaxBreadcrumbs(max: number): void;
326
+ setUser(user: UserInput): void;
327
+ /** The user context for an envelope. Never null — defaults to an empty user. */
328
+ getUser(): UserContext;
329
+ /** True when an identifiable user has been set. */
330
+ hasUser(): boolean;
331
+ setTag(key: string, value: string): void;
332
+ /** Merge a batch of tags into the scope (last-write-wins per key). */
333
+ setTags(tags: Record<string, string>): void;
334
+ /** Set (replace) a named context block on the scope. */
335
+ setContext(name: string, block: Record<string, unknown>): void;
336
+ /** Set a single freeform extra value on the scope. */
337
+ setExtra(key: string, value: unknown): void;
338
+ addBreadcrumb(breadcrumb: Breadcrumb): void;
339
+ /** A defensive copy of the current breadcrumb trail. */
340
+ getBreadcrumbs(): Breadcrumb[];
341
+ clearBreadcrumbs(): void;
342
+ private trim;
343
+ }
344
+
345
+ /**
346
+ * The Sauron client singleton. Owns the resolved options, the scope
347
+ * (user + breadcrumbs), the transport, and the installed integrations.
348
+ */
349
+ declare class SauronClient {
350
+ readonly options: ResolvedOptions;
351
+ readonly dsn: Dsn;
352
+ private readonly scope;
353
+ private readonly transport;
354
+ private readonly logger;
355
+ private readonly nativeFetch?;
356
+ private enabled;
357
+ private installed;
358
+ private anonymousId;
359
+ private beaconCleanup;
360
+ constructor(options: ResolvedOptions);
361
+ /** Install global handlers + auto-instrumentation and start the transport. */
362
+ install(): void;
363
+ getScope(): Scope;
364
+ isEnabled(): boolean;
365
+ /** The current distinct id: the user id when identified, else an anon id. */
366
+ getDistinctId(): string | null;
367
+ /** The anonymous id, or null if one was never needed. */
368
+ getAnonymousId(): string | null;
369
+ private ensureAnonymousId;
370
+ /** Stamp a fresh envelope (new `sent_at`, current context) around `items`. */
371
+ makeEnvelope(items: EnvelopeItem[]): Envelope;
372
+ /** Add a breadcrumb, running it through `beforeBreadcrumb` first. */
373
+ addBreadcrumb(breadcrumb: Breadcrumb, hint?: Hint): void;
374
+ /**
375
+ * Reconcile an error item to the shared wire shape by filling the optional
376
+ * `event_id`/`message`/`tags`/`user` fields from the current scope and hint.
377
+ * Each field is left untouched when the item already sets it, and omitted
378
+ * entirely when there is nothing to attach (the backend defaults it) — only
379
+ * `event_id` is always minted so callers can correlate the report.
380
+ */
381
+ private enrichErrorItem;
382
+ /**
383
+ * Run an item through sampling (errors only) and `beforeSend`, then hand it to
384
+ * the transport. Returns silently when dropped.
385
+ */
386
+ captureItem(item: EnvelopeItem, hint?: Hint): void;
387
+ /** Flush pending events. Resolves false if `timeoutMs` elapses first. */
388
+ flush(timeoutMs?: number): Promise<boolean>;
389
+ /** Disable the client (called on 401/403). Stops accepting/sending events. */
390
+ disable(): void;
391
+ /** Restore all patched globals and stop timers/listeners. */
392
+ teardown(): void;
393
+ /** Flush then tear down. Resolves to the flush result. */
394
+ close(timeoutMs?: number): Promise<boolean>;
395
+ }
396
+ /** The active client, or null before `init`. */
397
+ declare function getClient(): SauronClient | null;
398
+
399
+ /**
400
+ * Assemble a canonical envelope. This is intentionally trivial — the shape is
401
+ * the contract, so keep it a pure, side-effect-free constructor that mirrors
402
+ * the golden JSON exactly (header, context, items, in that order).
403
+ */
404
+ declare function buildEnvelope(header: EnvelopeHeader, context: Context, items: EnvelopeItem[]): Envelope;
405
+
406
+ /**
407
+ * Heuristic for whether a frame belongs to first-party ("in app") code:
408
+ * same-origin URLs and bare/relative paths are in-app; cross-origin URLs
409
+ * (typically CDN or third-party scripts) and internal frames are not.
410
+ */
411
+ declare function isInAppFrame(filename: string | null): boolean;
412
+ /**
413
+ * Parse a raw `Error.stack` string into normalized frames (crash frame last).
414
+ * Non-frame lines (the `Error: message` header, `[native code]`, etc.) are
415
+ * skipped.
416
+ */
417
+ declare function parseStackString(stack: string | undefined | null): Frame[];
418
+ /** Parse an `Error`-like object's `.stack`. */
419
+ declare function parseError(err: unknown): Frame[];
420
+
421
+ /** Small dependency-free helpers shared across the SDK. */
422
+ /** SDK identity, embedded in every envelope header. */
423
+ declare const SDK_NAME = "sauron.javascript";
424
+ declare const SDK_VERSION = "1.0.0";
425
+
426
+ /**
427
+ * `@edraj/sauron-browser` — public API surface.
428
+ *
429
+ * Error reporting + product analytics for the browser. Import the named
430
+ * functions, or the `Sauron` facade / default export.
431
+ *
432
+ * ```ts
433
+ * import { Sauron } from '@edraj/sauron-browser';
434
+ * Sauron.init({ dsn: 'https://pk_test@localhost:8081/1', release: 'web@1.4.2' });
435
+ * Sauron.track('checkout_completed', { cart_value: 42.5 });
436
+ * ```
437
+ */
438
+
439
+ /** Initialize the SDK. See {@link InitOptions}. */
440
+ declare function init(options: InitOptions): SauronClient;
441
+ /** Capture an exception (or any thrown value). */
442
+ declare function captureException(err: unknown, hint?: Hint): void;
443
+ /** Capture a plain message at the given `level` (default `info`). */
444
+ declare function captureMessage(message: string, level?: Level, hint?: Hint): void;
445
+ /** Record a product-analytics event, optionally with per-call tags/contexts/extra. */
446
+ declare function track(name: string, properties?: Record<string, unknown>, options?: TrackOptions): void;
447
+ /** Associate the session with a known user. */
448
+ declare function identify(id: string, traits?: Record<string, unknown>): void;
449
+ /** Record a performance transaction (navigation, http, screen load, ...). */
450
+ declare function trackTransaction(input: TransactionInput): void;
451
+ /** Set the current screen (emits a `$screen` view on change). */
452
+ declare function setScreen(name: string): void;
453
+ /** The current screen name, or null. */
454
+ declare function getScreen(): string | null;
455
+ /** Record a breadcrumb. */
456
+ declare function addBreadcrumb(breadcrumb: BreadcrumbInput, hint?: Hint): void;
457
+ /** Set (or clear, with `null`) the current user. */
458
+ declare function setUser(user: UserInput): void;
459
+ /** Set a single scope tag (lifted onto later errors/events). */
460
+ declare function setTag(key: string, value: string): void;
461
+ /** Merge a batch of scope tags (last-write-wins per key). */
462
+ declare function setTags(tags: Record<string, string>): void;
463
+ /** Set (replace) a named scope context block. */
464
+ declare function setContext(name: string, block: Record<string, unknown>): void;
465
+ /** Set a single freeform scope extra value. */
466
+ declare function setExtra(key: string, value: unknown): void;
467
+ /** Flush pending events. Resolves `false` if `timeoutMs` elapses first. */
468
+ declare function flush(timeoutMs?: number): Promise<boolean>;
469
+ /** Flush and tear down the SDK, restoring all patched globals. */
470
+ declare function close(timeoutMs?: number): Promise<boolean>;
471
+
472
+ /** Grouped facade + default export. */
473
+ declare const Sauron: {
474
+ init: typeof init;
475
+ captureException: typeof captureException;
476
+ captureMessage: typeof captureMessage;
477
+ track: typeof track;
478
+ trackTransaction: typeof trackTransaction;
479
+ identify: typeof identify;
480
+ addBreadcrumb: typeof addBreadcrumb;
481
+ setUser: typeof setUser;
482
+ setTag: typeof setTag;
483
+ setTags: typeof setTags;
484
+ setContext: typeof setContext;
485
+ setExtra: typeof setExtra;
486
+ setScreen: typeof setScreen;
487
+ getScreen: typeof getScreen;
488
+ flush: typeof flush;
489
+ close: typeof close;
490
+ getClient: typeof getClient;
491
+ };
492
+
493
+ export { type AppContext, type BeforeBreadcrumb, type BeforeSend, type Breadcrumb, type BreadcrumbBatchItem, type BreadcrumbInput, type CaptureOptions, type Context, type DeviceContext, type Dsn, DsnError, type Envelope, type EnvelopeHeader, type EnvelopeItem, type ErrorItem, type EventItem, type ExceptionValue, type Frame, type Hint, type IdentifyItem, type InitOptions, type ItemType, type Level, type Mechanism, type OsContext, type ResolvedOptions, type RuntimeContext, SDK_NAME, SDK_VERSION, Sauron, SauronClient, type SdkInfo, type TrackOptions, type TransactionInput, type TransactionItem, type TransactionOp, type TransportOptions, type UserContext, type UserInput, addBreadcrumb, buildEnvelope, captureException, captureMessage, close, Sauron as default, flush, getClient, getScreen, identify, init, isInAppFrame, parseDsn, parseError, parseStackString, setContext, setExtra, setScreen, setTag, setTags, setUser, track, trackTransaction };