@corbado/observe 0.2.0 → 0.3.0-next.34-a2ae3ef

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/dist/index.d.mts CHANGED
@@ -23,9 +23,15 @@ declare enum AuthEventName {
23
23
  SubflowTrigger = "subflow_trigger",
24
24
  SubflowError = "subflow_error",
25
25
  SubflowStarted = "subflow_started",
26
- SubflowFinished = "subflow_finished"
26
+ SubflowFinished = "subflow_finished",
27
+ Conversion = "conversion"
27
28
  }
28
29
  interface BaseEvent {
30
+ /**
31
+ * Client-generated idempotency id (uuidv7). Stamped once at capture and persisted with the event so
32
+ * resends (retry / durable outbox / unload beacon) reuse the same value and the server can dedupe.
33
+ */
34
+ id: string;
29
35
  timestamp: number;
30
36
  seq: number;
31
37
  type: EventType;
@@ -33,6 +39,7 @@ interface BaseEvent {
33
39
  contexts?: Record<string, unknown>;
34
40
  tags?: Record<string, string>;
35
41
  deviceInfo?: DeviceInfo;
42
+ meta?: EventMeta;
36
43
  }
37
44
  type UserReference = {
38
45
  userId?: string;
@@ -59,6 +66,16 @@ type MultiFlowStarted = {
59
66
  type FlowDecided = {
60
67
  flowName: FlowType;
61
68
  };
69
+ /**
70
+ * A business/marketing conversion that is not part of an authentication flow,
71
+ * e.g. a completed purchase, a started subscription, or a newsletter signup.
72
+ */
73
+ type Conversion = {
74
+ /** Name of the conversion, e.g. `"purchase"` or `"add-to-cart"`. */
75
+ name: string;
76
+ /** Where the conversion happened, e.g. `"checkout"` or `"product-page"`. */
77
+ touchpoint?: string;
78
+ } & UserReference;
62
79
  type FlowFinished = {
63
80
  flowName: FlowType;
64
81
  explicitOutcome?: "skipped" | "invisible";
@@ -136,6 +153,18 @@ interface LowEvent {
136
153
  }
137
154
  interface EventBatchMeta {
138
155
  sent: number;
156
+ transport: "fetch" | "beacon";
157
+ /** Number of prior failed delivery attempts for this batch; omitted on the first attempt. */
158
+ retryCount?: number;
159
+ /**
160
+ * Version of the SDK reliability config this batch was captured under; omitted when running on
161
+ * built-in defaults. Lets server-side analyses segment by the config actually in effect (config
162
+ * changes propagate with page-load lag, so calendar-date cohorts are mixed).
163
+ */
164
+ configVersion?: string;
165
+ }
166
+ interface EventMeta {
167
+ trackingSourcePath: string;
139
168
  }
140
169
  interface EventBatch {
141
170
  sessionId: string;
@@ -149,7 +178,13 @@ interface DeviceInfo {
149
178
  type: DeviceType;
150
179
  clientEnvHandle: string;
151
180
  clientEnvHandleMeta: ClientEnvHandleMeta;
181
+ /**
182
+ * Per-tab id (uuidv7) stored in `sessionStorage`. Distinguishes two tabs that share one
183
+ * `localStorage`-backed session when session continuity is enabled.
184
+ */
185
+ tabId?: string;
152
186
  data: DeviceInfoDataWeb;
187
+ collectionErrors?: DeviceInfoCollectionError[];
153
188
  }
154
189
  interface DeviceInfoDataWeb {
155
190
  bluetoothAvailable?: boolean;
@@ -160,6 +195,11 @@ interface DeviceInfoDataWeb {
160
195
  privateMode?: boolean;
161
196
  webdriver?: boolean;
162
197
  }
198
+ type DeviceInfoCollectionError = {
199
+ field: keyof DeviceInfoDataWeb;
200
+ source: string;
201
+ error: NormalizedError;
202
+ };
163
203
  type ClientCapabilities = Record<string, string>;
164
204
  type JavaScriptHighEntropy = {
165
205
  platform?: string;
@@ -171,6 +211,70 @@ type ClientEnvHandleMeta = {
171
211
  source: ClientEnvHandleMetaSource;
172
212
  };
173
213
  type ClientEnvHandleMetaSource = "localstorage" | "cookie" | "native";
214
+ /**
215
+ * Client-side retry configuration (exponential backoff) for failed event flushes.
216
+ * Mirrors the server `observeEventCreateResRetry` schema.
217
+ */
218
+ interface SdkRetryConfig {
219
+ /** Maximum number of send attempts, including the first one. `1` disables retries. */
220
+ maxAttempts: number;
221
+ /** Base backoff delay between attempts, in milliseconds. */
222
+ baseDelayMs: number;
223
+ /** Maximum backoff delay between attempts, in milliseconds. */
224
+ maxDelayMs: number;
225
+ }
226
+ /**
227
+ * SDK reliability configuration returned by the events endpoint (opt-in via the
228
+ * `X-Corbado-Observe-Config` header). Mirrors the server `observeEventCreateRes` schema.
229
+ * All delivery-reliability features default OFF until the server returns config.
230
+ *
231
+ * @remarks
232
+ * Boot-snapshot model: the config resolved at construction (from the localStorage cache) is the
233
+ * config for the entire page load. A config received from the server during the load is only
234
+ * cached for the NEXT load, never applied live — this keeps session identity, seq source and
235
+ * outbox mode coherent within one load.
236
+ */
237
+ interface SdkReliabilityConfig {
238
+ /**
239
+ * Content-derived version (hash) assigned by the server. Echoed in the config request header so
240
+ * the server can answer 204 when nothing changed, and stamped into every batch's
241
+ * `meta.configVersion`. Empty string for the built-in defaults (nothing cached yet).
242
+ */
243
+ version: string;
244
+ /** How often the SDK flushes its event queue, in milliseconds. */
245
+ flushIntervalMs: number;
246
+ /** Persist queued events to a durable outbox so they survive reloads, redirects and context switches. */
247
+ durableOutbox: boolean;
248
+ /** Use keepalive/sendBeacon transport for flushes triggered during page unload. */
249
+ beaconKeepalive: boolean;
250
+ /** Maintain session continuity across reloads/tabs by persisting the session id in localStorage. */
251
+ sessionContinuity: boolean;
252
+ /**
253
+ * Inactivity threshold for continuity sessions, in milliseconds. When more time than this has
254
+ * passed since the last tracked activity, the session id is rotated on the next load. Only
255
+ * relevant when `sessionContinuity` is enabled.
256
+ */
257
+ sessionInactivityMs: number;
258
+ /**
259
+ * Flush-trigger switch: flush pending events when the document becomes hidden
260
+ * (`visibilitychange`). Defaults to true — a missing value can never disable delivery.
261
+ */
262
+ tvc: boolean;
263
+ /** Flush-trigger switch: flush pending events on `pagehide` (navigation/close). Defaults to true. */
264
+ tph: boolean;
265
+ /** Flush-trigger switch: flush pending events when the integrator calls `destroy()`. Defaults to true. */
266
+ td: boolean;
267
+ /**
268
+ * Event names that trigger an immediate flush when enqueued (e.g. `flow_finished`,
269
+ * `conversion`), so high-value events go out via a normal fetch while the page is still alive
270
+ * instead of relying on unload delivery. Empty by default (no priority events). The whole
271
+ * pending queue is sent, and the immediate flush coexists with the interval timer without
272
+ * double-sending (the in-flight drain absorbs it).
273
+ */
274
+ flushOnEventNames: string[];
275
+ /** Retry configuration for failed event flushes. */
276
+ retry: SdkRetryConfig;
277
+ }
174
278
 
175
279
  type StepHelper<TStart = {}, TFinished = {}, TTypedError = never> = {
176
280
  start: (data: TStart, options?: StepOptions) => void;
@@ -373,18 +477,16 @@ interface TrackerOptions {
373
477
  deviceInfoDebounceTime?: number;
374
478
  defaultTags?: Record<string, string>;
375
479
  applicationId?: string;
480
+ /**
481
+ * @deprecated The flush interval is server-controlled via the SDK reliability config
482
+ * (`flushIntervalMs`). This option only applies as long as no server config has been received
483
+ * (i.e. before the first config response is cached); once a server config is cached it wins.
484
+ * It will be removed in a future release.
485
+ */
376
486
  flushInterval?: number;
487
+ /** Generates new Observe session ids; returned values must be UUID-compatible. Existing stored sessions are still reused. */
488
+ sessionIdGenerator?: () => string;
377
489
  }
378
- /**
379
- * @remarks
380
- * **Constructor**
381
- * - If `apiBaseUrl` is non-empty and missing `http://` or `https://`: logs via `logger.critical` (does not throw).
382
- * - If `apiBaseUrl` is non-empty and ends with `/`: logs via `logger.critical` (does not throw).
383
- * - Session bootstrap (`getSessionId`): on failure logs `Failed to get session id` and continues with an empty session id (does not throw).
384
- *
385
- * **Typed tracking helpers** (`flowStarted`, `flowDecided`, and the other public methods that call `track`)
386
- * - Error handling for queued events is defined on {@link CorbadoTracker.track}.
387
- */
388
490
  declare class CorbadoTracker {
389
491
  private options;
390
492
  private logger;
@@ -392,19 +494,48 @@ declare class CorbadoTracker {
392
494
  private sessionId;
393
495
  private storage;
394
496
  private sessionStorage;
497
+ /** Dedicated localStorage engine for reliability state (config cache, outbox, continuity session, seq). */
498
+ private persistentStorage;
499
+ /** Boot snapshot of the reliability config; immutable for the lifetime of this page load. */
500
+ private readonly config;
395
501
  private deviceInfoCollector;
396
502
  private deviceInfoDebounceTime;
397
503
  private deviceInfoTransmittedLastTime;
398
504
  private seq;
399
505
  constructor(options: TrackerOptions);
506
+ /**
507
+ * Cache fresh server config as last-known. Boot-snapshot model: the config is NOT applied to this
508
+ * load — `this.config` stays the construction-time snapshot — it takes effect on the next page
509
+ * load via {@link loadCachedConfig}.
510
+ */
511
+ private handleConfig;
400
512
  /**
401
513
  * Returns the logger used by this tracker.
402
514
  */
403
515
  getLogger(): Logger;
404
516
  private applicationTag;
405
517
  private isTrackingBlocked;
518
+ private createSessionId;
406
519
  private getSessionId;
520
+ /**
521
+ * Resolve the session id from localStorage so it survives reloads and is shared across tabs of the
522
+ * same browser. Rotates after `config.sessionInactivityMs` of inactivity (server-controlled,
523
+ * boot-snapshot like the rest of the config), resetting the seq counter.
524
+ */
525
+ private getContinuitySessionId;
526
+ /**
527
+ * Return the next sequence number. With session continuity the counter is persisted so ordering
528
+ * survives reloads/redirects; the read-increment-write runs under a cross-tab Web Lock
529
+ * ({@link SEQ_LOCK_NAME}) so two tabs sharing the session cannot allocate the same seq (where Web
530
+ * Locks are unavailable it degrades to the historical best-effort merge). Lock grants are FIFO, so
531
+ * seq order matches `track()` call order within a tab. Every tracked event also bumps the
532
+ * continuity session's `lastActiveAt`, so the inactivity window measures real user inactivity
533
+ * instead of time-since-page-load (a long-lived active tab must not rotate the session on the next
534
+ * reload).
535
+ */
536
+ private nextSeq;
407
537
  private updateDeviceDebounced;
538
+ private resolveTrackingSourcePath;
408
539
  trackSubflowStarted(subflowType: SubflowType, data: Record<string, any>, options?: StepOptions): void;
409
540
  trackSubflowTrigger(subflowType: SubflowType, data: Record<string, any>, options?: StepOptions): void;
410
541
  trackSubflowStepStarted(subflowType: SubflowType, stepName: string, data: Record<string, any>, options?: StepOptions, ignoreAsInteraction?: boolean): void;
@@ -457,6 +588,29 @@ declare class CorbadoTracker {
457
588
  * ```
458
589
  */
459
590
  flowDecided(data: FlowDecided, tags?: Record<string, string>): void;
591
+ /**
592
+ * Track a business/marketing conversion that is not part of an authentication flow.
593
+ *
594
+ * @remarks
595
+ * Use this for outcomes you care about beyond auth, such as a completed purchase,
596
+ * a started subscription, or a newsletter signup. Provide a stable `name` for the
597
+ * conversion and (optionally) the `touchpoint` where it happened so it can be
598
+ * segmented in the same way as flows. If available, include `userId` and/or
599
+ * `identifier` to link the conversion to a user.
600
+ *
601
+ * @param data - Conversion name, optional touchpoint, and optional user reference.
602
+ * @param tags - Optional key-value tags for filtering and segmentation.
603
+ *
604
+ * @example
605
+ * ```typescript
606
+ * tracker.conversion({
607
+ * name: "purchase",
608
+ * touchpoint: "checkout",
609
+ * userId: "usr_123",
610
+ * });
611
+ * ```
612
+ */
613
+ conversion(data: Conversion, tags?: Record<string, string>): void;
460
614
  /**
461
615
  * Track when a flow successfully completes.
462
616
  *
@@ -571,10 +725,14 @@ declare class CorbadoTracker {
571
725
  * After calling destroy, the tracker must not be used. Operation objects
572
726
  * returned by factory methods (e.g. `passwordLoginFullOperation`) that hold
573
727
  * DOM references must be destroyed separately by the caller.
728
+ * The final flush is gated by the server-controlled trigger switch `td` (default true);
729
+ * resources are released either way.
574
730
  */
575
731
  destroy(): Promise<void>;
576
732
  /** @internal */
577
733
  resetSession(): string;
734
+ private resetContinuitySession;
735
+ private resetStorageSession;
578
736
  }
579
737
 
580
738
  declare const SDK_NAME = "@corbado/observe";
@@ -583,9 +741,11 @@ declare const SDK_VERSION: string;
583
741
  interface TransportMakeRequestResponse {
584
742
  statusCode?: number;
585
743
  headers?: Record<string, string | null>;
744
+ /** Parsed SDK reliability config, present only when the server returns it (HTTP 200). */
745
+ config?: SdkReliabilityConfig;
586
746
  }
587
747
  interface Transport {
588
- send(batch: EventBatch): Promise<TransportMakeRequestResponse>;
748
+ send(batch: EventBatch, extraHeaders?: Record<string, string>): Promise<TransportMakeRequestResponse>;
589
749
  sendBeacon?(batch: EventBatch): boolean;
590
750
  flush(timeout?: number): Promise<boolean>;
591
751
  }
@@ -595,58 +755,6 @@ interface TransportOptions {
595
755
  headers?: Record<string, string>;
596
756
  }
597
757
 
598
- interface QueueOptions {
599
- batchSize?: number;
600
- flushInterval?: number;
601
- }
602
- /**
603
- * @remarks When `window` is defined, `visibilitychange` (when the document becomes hidden) and `pagehide` trigger a synchronous beacon flush (`flushSync`). Any thrown error in that path is logged as "Unexpected error in queue flushSync".
604
- */
605
- declare class RequestQueue {
606
- private readonly logger;
607
- private transport;
608
- private sessionId;
609
- private readonly sdk;
610
- private options;
611
- private queue;
612
- private lowsQueue;
613
- private timer;
614
- private isFlushing;
615
- private onVisibilityChange;
616
- private onPageHide;
617
- constructor(logger: Logger, transport: Transport, sessionId: string, sdk: SdkInfo, options?: QueueOptions);
618
- setSessionId(sessionId: string): void;
619
- /**
620
- * Error situations:
621
- * - Any error while enqueueing or scheduling flush (including a synchronous throw from `flush`): log "Failed to enqueue event"
622
- */
623
- enqueue(event: Event): void;
624
- /**
625
- * Error situations:
626
- * - Any error while enqueueing or scheduling flush (including a synchronous throw from `flush`): log "Failed to enqueue low event"
627
- */
628
- enqueueLow(low: LowEvent): void;
629
- /**
630
- * Error situations:
631
- * - Queue is empty: return without calling transport.send
632
- * - A flush is already in progress: return without calling transport.send again
633
- * - transport.send rejects or throws, or any other error in the try block: log "Unexpected error in queue flush"
634
- */
635
- flush(): Promise<void>;
636
- private setupLifecycleHooks;
637
- /**
638
- * Synchronously flush both events and lows via `sendBeacon` if the transport supports it.
639
- * Safe to call multiple times: a no-op when both queues are empty.
640
- */
641
- flushKeepalive(): void;
642
- private flushSync;
643
- /**
644
- * Error situations:
645
- * - Timer clearance or event listener removal throws: log "Failed to destroy queue lifecycle hooks"
646
- */
647
- destroy(): void;
648
- }
649
-
650
758
  interface StorageEngine {
651
759
  getItem<T>(key: string): T | null;
652
760
  setItem<T>(key: string, value: T): void;
@@ -727,9 +835,282 @@ declare class SessionStorage implements StorageEngine {
727
835
  removeItem(key: string): void;
728
836
  }
729
837
 
838
+ interface QueueOptions {
839
+ /**
840
+ * Maximum number of events per request (payload cap). A backlog larger than this is drained in
841
+ * sequential capped batches within one flush. Reaching this size does NOT trigger an early
842
+ * flush — delivery cadence is controlled solely by the flush interval.
843
+ */
844
+ batchSize?: number;
845
+ /**
846
+ * Explicit override of the flush timer interval; otherwise `config.flushIntervalMs` is used.
847
+ * Internal/test use only — the tracker does not pass this; the interval is server-controlled.
848
+ */
849
+ flushInterval?: number;
850
+ debug?: boolean;
851
+ /**
852
+ * Reliability config snapshot for this page load; defaults to all-features-OFF. Boot-snapshot
853
+ * model: the queue never changes its behavior mid-load. A fresh config returned by the server is
854
+ * only handed to {@link QueueOptions.onConfigReceived} for caching and applies on the next load.
855
+ */
856
+ config?: SdkReliabilityConfig;
857
+ /** Called when the server returns a fresh config, so the client can cache it for the next load. */
858
+ onConfigReceived?: (config: SdkReliabilityConfig) => void;
859
+ /** Whether to send the opt-in config header on the first flush of this load. Defaults to true. */
860
+ requestConfig?: boolean;
861
+ }
862
+ /**
863
+ * @remarks When `window` is defined, `visibilitychange` (when the document becomes hidden) and `pagehide`
864
+ * trigger a synchronous unload flush (`flushSync`). Any thrown error in that path is logged as
865
+ * "Unexpected error in queue flushSync". Sync flushes are deduplicated: once everything pending has
866
+ * been handed to a sync transport, further lifecycle signals are no-ops until something new is
867
+ * enqueued — otherwise a single navigation would resend the same batch several times
868
+ * (visibilitychange + pagehide + per-input flushKeepalive), since entries are intentionally kept
869
+ * under `durableOutbox`.
870
+ *
871
+ * Pending events are held as {@link OutboxEntry} records. When `config.durableOutbox` is enabled they are
872
+ * additionally persisted to a localStorage-backed {@link EventStore} and recovered on the next load.
873
+ * Delivery removes an entry only on a 2xx response; retryable failures back off (honoring `Retry-After`)
874
+ * up to `config.retry.maxAttempts`. Low events are best-effort and never persisted.
875
+ *
876
+ * Flushing is timer-driven only (the flush interval); `batchSize` caps the events per request, and a
877
+ * flush drains all due work in sequential capped batches, stopping at the first failed send.
878
+ */
879
+ declare class RequestQueue {
880
+ private readonly logger;
881
+ private transport;
882
+ private sessionId;
883
+ private readonly sdk;
884
+ private readonly storage?;
885
+ private pending;
886
+ private lowsQueue;
887
+ private timer;
888
+ private isFlushing;
889
+ /**
890
+ * True when something was enqueued (or recovered) since the last sync flush. visibilitychange,
891
+ * pagehide and per-input flushKeepalive calls all funnel into flushSync; this flag makes the
892
+ * duplicate calls no-ops. The async flush path ignores it.
893
+ */
894
+ private dirtySinceSyncFlush;
895
+ private onVisibilityChange;
896
+ private onPageHide;
897
+ private readonly batchSize;
898
+ private readonly flushIntervalOverride?;
899
+ private readonly debug;
900
+ private readonly requestConfig;
901
+ private readonly onConfigReceived?;
902
+ private readonly config;
903
+ /** Event names that trigger an immediate flush at enqueue time (config.flushOnEventNames). */
904
+ private readonly priorityNames;
905
+ private store?;
906
+ /**
907
+ * Once the server has answered the config request with a 2xx (200 = fresh config cached for the
908
+ * next load, 204 = cached version still current) we stop sending the header for this load. A
909
+ * transport-level failure or error status keeps the request pending for the next flush.
910
+ */
911
+ private configResolved;
912
+ constructor(logger: Logger, transport: Transport, sessionId: string, sdk: SdkInfo, options?: QueueOptions, storage?: StorageEngine | undefined);
913
+ setSessionId(sessionId: string): void;
914
+ private flushInterval;
915
+ /**
916
+ * Error situations:
917
+ * - Any error while enqueueing or scheduling the flush timer: log "Failed to enqueue event"
918
+ */
919
+ enqueue(event: Event): void;
920
+ /**
921
+ * Error situations:
922
+ * - Any error while enqueueing or scheduling the flush timer: log "Failed to enqueue low event"
923
+ */
924
+ enqueueLow(low: LowEvent): void;
925
+ private scheduleAfterEnqueue;
926
+ private scheduleFlush;
927
+ private clearTimer;
928
+ /**
929
+ * Error situations:
930
+ * - Queue is empty: return without calling transport.send
931
+ * - A flush is already in progress: return without calling transport.send again
932
+ * - transport.send rejects or throws, or any other error in the try block: log "Unexpected error in queue flush"
933
+ */
934
+ flush(): Promise<void>;
935
+ /**
936
+ * Stamps delivery metadata onto the batch: the config version this load runs under (omitted on
937
+ * built-in defaults) and the number of prior failed delivery attempts (the max across its entries,
938
+ * only when at least one entry is a retry). `entry.attempts` is incremented on each failed flush,
939
+ * so it equals the retry count at send time. The transport later merges in `sent`/`transport`.
940
+ */
941
+ private attachBatchMeta;
942
+ private getDuePending;
943
+ private takeLows;
944
+ private shouldRequestConfig;
945
+ /**
946
+ * Handle the server's answer to the config request. Boot-snapshot model: a fresh config is ONLY
947
+ * handed to `onConfigReceived` for caching — it is never applied to this load. The behavior the
948
+ * queue was constructed with stays in effect until the page unloads.
949
+ */
950
+ private handleConfigResponse;
951
+ /** Returns true when the batch was acknowledged with a 2xx (safe to keep draining). */
952
+ private handleSendResult;
953
+ private isRetryable;
954
+ /**
955
+ * Exponential backoff with equal jitter. The exponential ceiling is `baseDelayMs * 2^(attempt-1)`
956
+ * (capped at `maxDelayMs`); the actual delay is half that ceiling plus a random portion of the other
957
+ * half. The jitter spreads retries from many clients to avoid synchronized retry storms and needs no
958
+ * extra config. A `Retry-After` header, when present, acts as a lower bound.
959
+ */
960
+ private backoffForAttempt;
961
+ private parseRetryAfter;
962
+ private removeEntries;
963
+ private rescheduleIfPending;
964
+ private setupLifecycleHooks;
965
+ private writeLifecycleDebugOutput;
966
+ /**
967
+ * Synchronously flush pending events (and best-effort lows) during page unload.
968
+ *
969
+ * @remarks
970
+ * When `config.beaconKeepalive` is on, uses `sendBeacon` (falling back to fetch keepalive when
971
+ * the UA refuses the beacon); otherwise fires a fire-and-forget `fetch` keepalive request. The
972
+ * send is capped at one `batchSize` chunk per call (unload transports share an in-flight budget
973
+ * with the host app). With `config.durableOutbox` enabled, entries are NOT removed (they are
974
+ * resent on the next load and deduped server-side); otherwise the sent chunk is removed
975
+ * best-effort and a remainder keeps the next lifecycle signal flush-worthy.
976
+ * Deduplicated: a call with nothing newly enqueued since the last sync flush is a no-op.
977
+ */
978
+ flushKeepalive(): void;
979
+ private flushSync;
980
+ /**
981
+ * Error situations:
982
+ * - Timer clearance or event listener removal throws: log "Failed to destroy queue lifecycle hooks"
983
+ */
984
+ destroy(): void;
985
+ }
986
+
987
+ /** localStorage key under which the last-known server config is cached. */
988
+ declare const CONFIG_STORAGE_KEY = "cbo_sdk_config";
989
+ /**
990
+ * Opt-in request header that asks the events endpoint to return the SDK reliability config. Its value
991
+ * is the config version the SDK is currently running, or "1" when none is cached yet. The server
992
+ * answers 204 when that version is still current and 200 + config body when it is stale.
993
+ */
994
+ declare const CONFIG_REQUEST_HEADER = "X-Corbado-Observe-Config";
995
+ /** Built-in flush interval used until the server provides one. Matches the historical default. */
996
+ declare const DEFAULT_FLUSH_INTERVAL_MS = 500;
997
+ /**
998
+ * Clamping bounds for server-provided config values. They protect the client from a dangerous
999
+ * server-side config mistake (e.g. a 0ms flush interval would busy-loop); the server clamps with
1000
+ * the same bounds as the first line of defense.
1001
+ */
1002
+ declare const CONFIG_BOUNDS: {
1003
+ readonly flushIntervalMs: {
1004
+ readonly min: 100;
1005
+ readonly max: 60000;
1006
+ };
1007
+ readonly retryMaxAttempts: {
1008
+ readonly min: 1;
1009
+ readonly max: 10;
1010
+ };
1011
+ readonly retryDelayMs: {
1012
+ readonly min: 50;
1013
+ readonly max: 120000;
1014
+ };
1015
+ readonly sessionInactivityMs: {
1016
+ readonly min: 60000;
1017
+ readonly max: 86400000;
1018
+ };
1019
+ readonly flushOnEventNames: {
1020
+ readonly maxEntries: 20;
1021
+ readonly maxNameLength: 100;
1022
+ };
1023
+ };
1024
+ /** Built-in continuity-session inactivity threshold (30 minutes); matches the historical default. */
1025
+ declare const DEFAULT_SESSION_INACTIVITY_MS: number;
1026
+ /**
1027
+ * Default config: every delivery-reliability feature is OFF. The SDK behaves like a plain async
1028
+ * fetch-keepalive flusher until the server returns config (which is then cached as last-known and
1029
+ * applied on the NEXT page load — boot-snapshot model). The empty version means "nothing cached";
1030
+ * the config request header sends "1" in that case.
1031
+ */
1032
+ declare const DEFAULT_RELIABILITY_CONFIG: SdkReliabilityConfig;
1033
+ /**
1034
+ * Coerce an arbitrary value (cached blob or parsed API body) into a {@link SdkReliabilityConfig}.
1035
+ * Returns `null` if the input is not an object or carries no `version` (a config without identity
1036
+ * cannot participate in the version handshake and must not be trusted). Missing fields fall back to
1037
+ * the OFF defaults so a partial/older payload can never silently enable a feature; the
1038
+ * flush-trigger switches (`tvc`/`tph`/`td`) invert this and default ON, so a partial payload can
1039
+ * never silently disable delivery. Numeric values are clamped to {@link CONFIG_BOUNDS} so a bad
1040
+ * server value cannot harm the client.
1041
+ */
1042
+ declare function parseReliabilityConfig(raw: unknown): SdkReliabilityConfig | null;
1043
+ /** Read the cached config from storage, or `null` if absent/invalid. */
1044
+ declare function loadCachedConfig(storage: StorageEngine): SdkReliabilityConfig | null;
1045
+ /** Persist the config as last-known so the next page load starts with these features enabled. */
1046
+ declare function cacheConfig(storage: StorageEngine, config: SdkReliabilityConfig): void;
1047
+
1048
+ /** A persisted, not-yet-acknowledged event awaiting delivery. */
1049
+ interface OutboxEntry {
1050
+ /** Event idempotency id; primary key of the entry. */
1051
+ id: string;
1052
+ /** Session id snapshot taken at enqueue time (so session rotation never relabels old entries). */
1053
+ sessionId: string;
1054
+ /** Earliest timestamp (ms) at which this entry may be sent again (backoff). `0` = due now. */
1055
+ flushAfter: number;
1056
+ /** Number of completed send attempts so far. */
1057
+ attempts: number;
1058
+ /** The event payload. */
1059
+ event: Event;
1060
+ }
1061
+ /** localStorage key under which the durable outbox is persisted. */
1062
+ declare const OUTBOX_STORAGE_KEY = "cbo_outbox";
1063
+ /** Web Lock name guarding outbox mutations across tabs. */
1064
+ declare const OUTBOX_LOCK_NAME = "cbo_outbox";
1065
+ /** Default maximum number of entries kept; oldest are evicted past this. */
1066
+ declare const DEFAULT_MAX_ENTRIES = 500;
1067
+ /**
1068
+ * Default budget for the outbox's serialized size, in UTF-16 code units (the unit localStorage
1069
+ * quotas are measured in; ~1 MB on disk). The entry-count cap alone does not bound bytes —
1070
+ * `event.data` is arbitrary customer payload — and localStorage's ~5M-unit quota is shared with
1071
+ * the HOST app: an unbounded outbox could make the host's own writes throw QuotaExceededError.
1072
+ */
1073
+ declare const DEFAULT_MAX_SERIALIZED_LENGTH: number;
1074
+ /**
1075
+ * Durable, localStorage-backed outbox of pending events, mirrored in memory.
1076
+ *
1077
+ * @remarks
1078
+ * Every mutation is a read-merge-write against storage, executed under a cross-tab Web Lock
1079
+ * ({@link OUTBOX_LOCK_NAME}) so concurrent tabs sharing the same localStorage cannot interleave
1080
+ * between the read and the write (which would silently drop the other tab's entries). Where the
1081
+ * Web Locks API is unavailable the mutation runs unlocked, matching the historical best-effort
1082
+ * behavior. Mutations never reject; failures are logged.
1083
+ * The store is size-capped; overflow evicts the oldest entries and logs the count.
1084
+ */
1085
+ declare class EventStore {
1086
+ private readonly storage;
1087
+ private readonly logger;
1088
+ private readonly maxEntries;
1089
+ private readonly maxSerializedLength;
1090
+ private entries;
1091
+ /** Malformed entries are dropped on every read; report only once per instance to avoid log spam. */
1092
+ private reportedMalformed;
1093
+ constructor(storage: StorageEngine, logger: Logger, maxEntries?: number, maxSerializedLength?: number);
1094
+ private read;
1095
+ private mutate;
1096
+ /** Evict oldest entries until the serialized outbox fits {@link maxSerializedLength}. */
1097
+ private enforceSizeBudget;
1098
+ /** Append an entry. No-op if an entry with the same id already exists. */
1099
+ add(entry: OutboxEntry): Promise<void>;
1100
+ /** Re-read from storage and return all entries (memory mirror refreshed). */
1101
+ all(): OutboxEntry[];
1102
+ /** Entries whose `flushAfter` has elapsed, in insertion order. */
1103
+ getDue(now: number): OutboxEntry[];
1104
+ /** Remove entries by id (acknowledged or permanently dropped). */
1105
+ remove(ids: string[]): Promise<void>;
1106
+ /** Replace existing entries (matched by id) with updated copies, e.g. after bumping backoff. */
1107
+ update(updated: OutboxEntry[]): Promise<void>;
1108
+ size(): number;
1109
+ }
1110
+
730
1111
  declare function init(options: TrackerOptions): CorbadoTracker;
731
1112
  declare function getTracker(): CorbadoTracker | undefined;
732
1113
  declare function resetSession(): string | undefined;
733
1114
  declare function destroy(): Promise<void>;
734
1115
 
735
- export { type AuthDecisionFinished, type AuthDecisionStarted, AuthEventName, type AuthMethodDecisionFinished, type AuthMethodDecisionStarted, type AuthMethodType, type BaseEvent, type ClientCapabilities, type ClientEnvHandleMeta, type ClientEnvHandleMetaSource, CookieStorage, CorbadoTracker, type CreateLoggerOptions, type CustomEvent, type DeviceInfo, type DeviceInfoDataWeb, type DeviceType, EmailLinkOperationFull, type EmailLinkOperationPostResponseStart, type EmailLinkOperationSendStart, type EmailLinkOperationSpecType, type EmailOTPOperationPostResponseStart, type EmailOTPOperationSendOTPStart, type EmailOTPOperationSpecType, EmailOtpOperationFull, type Event, type EventBatch, type EventBatchMeta, type EventType, type FlowAutoFinished, type FlowDecided, type FlowFinished, type FlowReset, type FlowStarted, type FlowType, type JavaScriptHighEntropy, LocalStorage, type Logger, type LowEvent, type MultiFlowReset, type MultiFlowStarted, type NormalizedError, OperationFull, OperationFullProvideIdentifierWithCUI, type PasskeyEnrollmentCeremonyFinished, type PasskeyEnrollmentCeremonyStart, type PasskeyEnrollmentGetOptionsFinished, type PasskeyEnrollmentGetOptionsStart, PasskeyEnrollmentOperationFull, type PasskeyLoginCUIGetOptionsFinished, type PasskeyLoginCUIGetOptionsStart, type PasskeyLoginCUISpecType, type PasskeyLoginCeremonyFinished, type PasskeyLoginCeremonyStart, type PasskeyLoginClientError, type PasskeyLoginFinish, type PasskeyLoginGetOptionsFinished, type PasskeyLoginGetOptionsStart, PasskeyLoginOperationFull, type PasskeyLoginStartable, type PasskeyLoginSubmitted, type PasskeyOperationEnrollmentExplicitSpecType, type PasskeyOperationLoginExplicitSpecType, type PasswordEnrollmentAutoTrackConfig, PasswordEnrollmentOperationFull, type PasswordEnrollmentTypedError, type PasswordLoginAutoTrackConfig, PasswordLoginOperationFull, type PasswordLoginTypedError, type PredefinedEvent, type ProvideIdentifierError, type ProvideIdentifierFinish, type ProvideIdentifierPostResponseStart, type ProvideIdentifierSpecType, type ProvideIdentifierStart, type QueueOptions, RequestQueue, SDK_NAME, SDK_VERSION, type SdkInfo, SessionStorage, type SocialLoginExchangeCodeFinished, type SocialLoginExchangeCodeStart, type SocialLoginGetRedirectUrlFinished, type SocialLoginGetRedirectUrlStart, SocialLoginOperationFull, type SocialLoginProviderType, type SocialLoginSpecType, type StepHelper, type StepOptions, type StorageEngine, type SubflowTrigger, type SubflowType, type TrackerOptions, type Transport, type TransportMakeRequestResponse, type TransportOptions, type UserReference, createLogger, destroy, getTracker, init, resetSession };
1116
+ export { type AuthDecisionFinished, type AuthDecisionStarted, AuthEventName, type AuthMethodDecisionFinished, type AuthMethodDecisionStarted, type AuthMethodType, type BaseEvent, CONFIG_BOUNDS, CONFIG_REQUEST_HEADER, CONFIG_STORAGE_KEY, type ClientCapabilities, type ClientEnvHandleMeta, type ClientEnvHandleMetaSource, type Conversion, CookieStorage, CorbadoTracker, type CreateLoggerOptions, type CustomEvent, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_ENTRIES, DEFAULT_MAX_SERIALIZED_LENGTH, DEFAULT_RELIABILITY_CONFIG, DEFAULT_SESSION_INACTIVITY_MS, type DeviceInfo, type DeviceInfoCollectionError, type DeviceInfoDataWeb, type DeviceType, EmailLinkOperationFull, type EmailLinkOperationPostResponseStart, type EmailLinkOperationSendStart, type EmailLinkOperationSpecType, type EmailOTPOperationPostResponseStart, type EmailOTPOperationSendOTPStart, type EmailOTPOperationSpecType, EmailOtpOperationFull, type Event, type EventBatch, type EventBatchMeta, type EventMeta, EventStore, type EventType, type FlowAutoFinished, type FlowDecided, type FlowFinished, type FlowReset, type FlowStarted, type FlowType, type JavaScriptHighEntropy, LocalStorage, type Logger, type LowEvent, type MultiFlowReset, type MultiFlowStarted, type NormalizedError, OUTBOX_LOCK_NAME, OUTBOX_STORAGE_KEY, OperationFull, OperationFullProvideIdentifierWithCUI, type OutboxEntry, type PasskeyEnrollmentCeremonyFinished, type PasskeyEnrollmentCeremonyStart, type PasskeyEnrollmentGetOptionsFinished, type PasskeyEnrollmentGetOptionsStart, PasskeyEnrollmentOperationFull, type PasskeyLoginCUIGetOptionsFinished, type PasskeyLoginCUIGetOptionsStart, type PasskeyLoginCUISpecType, type PasskeyLoginCeremonyFinished, type PasskeyLoginCeremonyStart, type PasskeyLoginClientError, type PasskeyLoginFinish, type PasskeyLoginGetOptionsFinished, type PasskeyLoginGetOptionsStart, PasskeyLoginOperationFull, type PasskeyLoginStartable, type PasskeyLoginSubmitted, type PasskeyOperationEnrollmentExplicitSpecType, type PasskeyOperationLoginExplicitSpecType, type PasswordEnrollmentAutoTrackConfig, PasswordEnrollmentOperationFull, type PasswordEnrollmentTypedError, type PasswordLoginAutoTrackConfig, PasswordLoginOperationFull, type PasswordLoginTypedError, type PredefinedEvent, type ProvideIdentifierError, type ProvideIdentifierFinish, type ProvideIdentifierPostResponseStart, type ProvideIdentifierSpecType, type ProvideIdentifierStart, type QueueOptions, RequestQueue, SDK_NAME, SDK_VERSION, type SdkInfo, type SdkReliabilityConfig, type SdkRetryConfig, SessionStorage, type SocialLoginExchangeCodeFinished, type SocialLoginExchangeCodeStart, type SocialLoginGetRedirectUrlFinished, type SocialLoginGetRedirectUrlStart, SocialLoginOperationFull, type SocialLoginProviderType, type SocialLoginSpecType, type StepHelper, type StepOptions, type StorageEngine, type SubflowTrigger, type SubflowType, type TrackerOptions, type Transport, type TransportMakeRequestResponse, type TransportOptions, type UserReference, cacheConfig, createLogger, destroy, getTracker, init, loadCachedConfig, parseReliabilityConfig, resetSession };