@corbado/observe 0.2.0 → 0.3.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.
- package/dist/cdn/script.global.js +1 -1
- package/dist/index.d.mts +441 -65
- package/dist/index.d.ts +441 -65
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
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,14 @@ 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;
|
|
377
487
|
}
|
|
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
488
|
declare class CorbadoTracker {
|
|
389
489
|
private options;
|
|
390
490
|
private logger;
|
|
@@ -392,11 +492,21 @@ declare class CorbadoTracker {
|
|
|
392
492
|
private sessionId;
|
|
393
493
|
private storage;
|
|
394
494
|
private sessionStorage;
|
|
495
|
+
/** Dedicated localStorage engine for reliability state (config cache, outbox, continuity session, seq). */
|
|
496
|
+
private persistentStorage;
|
|
497
|
+
/** Boot snapshot of the reliability config; immutable for the lifetime of this page load. */
|
|
498
|
+
private readonly config;
|
|
395
499
|
private deviceInfoCollector;
|
|
396
500
|
private deviceInfoDebounceTime;
|
|
397
501
|
private deviceInfoTransmittedLastTime;
|
|
398
502
|
private seq;
|
|
399
503
|
constructor(options: TrackerOptions);
|
|
504
|
+
/**
|
|
505
|
+
* Cache fresh server config as last-known. Boot-snapshot model: the config is NOT applied to this
|
|
506
|
+
* load — `this.config` stays the construction-time snapshot — it takes effect on the next page
|
|
507
|
+
* load via {@link loadCachedConfig}.
|
|
508
|
+
*/
|
|
509
|
+
private handleConfig;
|
|
400
510
|
/**
|
|
401
511
|
* Returns the logger used by this tracker.
|
|
402
512
|
*/
|
|
@@ -404,7 +514,25 @@ declare class CorbadoTracker {
|
|
|
404
514
|
private applicationTag;
|
|
405
515
|
private isTrackingBlocked;
|
|
406
516
|
private getSessionId;
|
|
517
|
+
/**
|
|
518
|
+
* Resolve the session id from localStorage so it survives reloads and is shared across tabs of the
|
|
519
|
+
* same browser. Rotates after `config.sessionInactivityMs` of inactivity (server-controlled,
|
|
520
|
+
* boot-snapshot like the rest of the config), resetting the seq counter.
|
|
521
|
+
*/
|
|
522
|
+
private getContinuitySessionId;
|
|
523
|
+
/**
|
|
524
|
+
* Return the next sequence number. With session continuity the counter is persisted so ordering
|
|
525
|
+
* survives reloads/redirects; the read-increment-write runs under a cross-tab Web Lock
|
|
526
|
+
* ({@link SEQ_LOCK_NAME}) so two tabs sharing the session cannot allocate the same seq (where Web
|
|
527
|
+
* Locks are unavailable it degrades to the historical best-effort merge). Lock grants are FIFO, so
|
|
528
|
+
* seq order matches `track()` call order within a tab. Every tracked event also bumps the
|
|
529
|
+
* continuity session's `lastActiveAt`, so the inactivity window measures real user inactivity
|
|
530
|
+
* instead of time-since-page-load (a long-lived active tab must not rotate the session on the next
|
|
531
|
+
* reload).
|
|
532
|
+
*/
|
|
533
|
+
private nextSeq;
|
|
407
534
|
private updateDeviceDebounced;
|
|
535
|
+
private resolveTrackingSourcePath;
|
|
408
536
|
trackSubflowStarted(subflowType: SubflowType, data: Record<string, any>, options?: StepOptions): void;
|
|
409
537
|
trackSubflowTrigger(subflowType: SubflowType, data: Record<string, any>, options?: StepOptions): void;
|
|
410
538
|
trackSubflowStepStarted(subflowType: SubflowType, stepName: string, data: Record<string, any>, options?: StepOptions, ignoreAsInteraction?: boolean): void;
|
|
@@ -457,6 +585,29 @@ declare class CorbadoTracker {
|
|
|
457
585
|
* ```
|
|
458
586
|
*/
|
|
459
587
|
flowDecided(data: FlowDecided, tags?: Record<string, string>): void;
|
|
588
|
+
/**
|
|
589
|
+
* Track a business/marketing conversion that is not part of an authentication flow.
|
|
590
|
+
*
|
|
591
|
+
* @remarks
|
|
592
|
+
* Use this for outcomes you care about beyond auth, such as a completed purchase,
|
|
593
|
+
* a started subscription, or a newsletter signup. Provide a stable `name` for the
|
|
594
|
+
* conversion and (optionally) the `touchpoint` where it happened so it can be
|
|
595
|
+
* segmented in the same way as flows. If available, include `userId` and/or
|
|
596
|
+
* `identifier` to link the conversion to a user.
|
|
597
|
+
*
|
|
598
|
+
* @param data - Conversion name, optional touchpoint, and optional user reference.
|
|
599
|
+
* @param tags - Optional key-value tags for filtering and segmentation.
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```typescript
|
|
603
|
+
* tracker.conversion({
|
|
604
|
+
* name: "purchase",
|
|
605
|
+
* touchpoint: "checkout",
|
|
606
|
+
* userId: "usr_123",
|
|
607
|
+
* });
|
|
608
|
+
* ```
|
|
609
|
+
*/
|
|
610
|
+
conversion(data: Conversion, tags?: Record<string, string>): void;
|
|
460
611
|
/**
|
|
461
612
|
* Track when a flow successfully completes.
|
|
462
613
|
*
|
|
@@ -571,6 +722,8 @@ declare class CorbadoTracker {
|
|
|
571
722
|
* After calling destroy, the tracker must not be used. Operation objects
|
|
572
723
|
* returned by factory methods (e.g. `passwordLoginFullOperation`) that hold
|
|
573
724
|
* DOM references must be destroyed separately by the caller.
|
|
725
|
+
* The final flush is gated by the server-controlled trigger switch `td` (default true);
|
|
726
|
+
* resources are released either way.
|
|
574
727
|
*/
|
|
575
728
|
destroy(): Promise<void>;
|
|
576
729
|
/** @internal */
|
|
@@ -583,9 +736,11 @@ declare const SDK_VERSION: string;
|
|
|
583
736
|
interface TransportMakeRequestResponse {
|
|
584
737
|
statusCode?: number;
|
|
585
738
|
headers?: Record<string, string | null>;
|
|
739
|
+
/** Parsed SDK reliability config, present only when the server returns it (HTTP 200). */
|
|
740
|
+
config?: SdkReliabilityConfig;
|
|
586
741
|
}
|
|
587
742
|
interface Transport {
|
|
588
|
-
send(batch: EventBatch): Promise<TransportMakeRequestResponse>;
|
|
743
|
+
send(batch: EventBatch, extraHeaders?: Record<string, string>): Promise<TransportMakeRequestResponse>;
|
|
589
744
|
sendBeacon?(batch: EventBatch): boolean;
|
|
590
745
|
flush(timeout?: number): Promise<boolean>;
|
|
591
746
|
}
|
|
@@ -595,58 +750,6 @@ interface TransportOptions {
|
|
|
595
750
|
headers?: Record<string, string>;
|
|
596
751
|
}
|
|
597
752
|
|
|
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
753
|
interface StorageEngine {
|
|
651
754
|
getItem<T>(key: string): T | null;
|
|
652
755
|
setItem<T>(key: string, value: T): void;
|
|
@@ -727,9 +830,282 @@ declare class SessionStorage implements StorageEngine {
|
|
|
727
830
|
removeItem(key: string): void;
|
|
728
831
|
}
|
|
729
832
|
|
|
833
|
+
interface QueueOptions {
|
|
834
|
+
/**
|
|
835
|
+
* Maximum number of events per request (payload cap). A backlog larger than this is drained in
|
|
836
|
+
* sequential capped batches within one flush. Reaching this size does NOT trigger an early
|
|
837
|
+
* flush — delivery cadence is controlled solely by the flush interval.
|
|
838
|
+
*/
|
|
839
|
+
batchSize?: number;
|
|
840
|
+
/**
|
|
841
|
+
* Explicit override of the flush timer interval; otherwise `config.flushIntervalMs` is used.
|
|
842
|
+
* Internal/test use only — the tracker does not pass this; the interval is server-controlled.
|
|
843
|
+
*/
|
|
844
|
+
flushInterval?: number;
|
|
845
|
+
debug?: boolean;
|
|
846
|
+
/**
|
|
847
|
+
* Reliability config snapshot for this page load; defaults to all-features-OFF. Boot-snapshot
|
|
848
|
+
* model: the queue never changes its behavior mid-load. A fresh config returned by the server is
|
|
849
|
+
* only handed to {@link QueueOptions.onConfigReceived} for caching and applies on the next load.
|
|
850
|
+
*/
|
|
851
|
+
config?: SdkReliabilityConfig;
|
|
852
|
+
/** Called when the server returns a fresh config, so the client can cache it for the next load. */
|
|
853
|
+
onConfigReceived?: (config: SdkReliabilityConfig) => void;
|
|
854
|
+
/** Whether to send the opt-in config header on the first flush of this load. Defaults to true. */
|
|
855
|
+
requestConfig?: boolean;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* @remarks When `window` is defined, `visibilitychange` (when the document becomes hidden) and `pagehide`
|
|
859
|
+
* trigger a synchronous unload flush (`flushSync`). Any thrown error in that path is logged as
|
|
860
|
+
* "Unexpected error in queue flushSync". Sync flushes are deduplicated: once everything pending has
|
|
861
|
+
* been handed to a sync transport, further lifecycle signals are no-ops until something new is
|
|
862
|
+
* enqueued — otherwise a single navigation would resend the same batch several times
|
|
863
|
+
* (visibilitychange + pagehide + per-input flushKeepalive), since entries are intentionally kept
|
|
864
|
+
* under `durableOutbox`.
|
|
865
|
+
*
|
|
866
|
+
* Pending events are held as {@link OutboxEntry} records. When `config.durableOutbox` is enabled they are
|
|
867
|
+
* additionally persisted to a localStorage-backed {@link EventStore} and recovered on the next load.
|
|
868
|
+
* Delivery removes an entry only on a 2xx response; retryable failures back off (honoring `Retry-After`)
|
|
869
|
+
* up to `config.retry.maxAttempts`. Low events are best-effort and never persisted.
|
|
870
|
+
*
|
|
871
|
+
* Flushing is timer-driven only (the flush interval); `batchSize` caps the events per request, and a
|
|
872
|
+
* flush drains all due work in sequential capped batches, stopping at the first failed send.
|
|
873
|
+
*/
|
|
874
|
+
declare class RequestQueue {
|
|
875
|
+
private readonly logger;
|
|
876
|
+
private transport;
|
|
877
|
+
private sessionId;
|
|
878
|
+
private readonly sdk;
|
|
879
|
+
private readonly storage?;
|
|
880
|
+
private pending;
|
|
881
|
+
private lowsQueue;
|
|
882
|
+
private timer;
|
|
883
|
+
private isFlushing;
|
|
884
|
+
/**
|
|
885
|
+
* True when something was enqueued (or recovered) since the last sync flush. visibilitychange,
|
|
886
|
+
* pagehide and per-input flushKeepalive calls all funnel into flushSync; this flag makes the
|
|
887
|
+
* duplicate calls no-ops. The async flush path ignores it.
|
|
888
|
+
*/
|
|
889
|
+
private dirtySinceSyncFlush;
|
|
890
|
+
private onVisibilityChange;
|
|
891
|
+
private onPageHide;
|
|
892
|
+
private readonly batchSize;
|
|
893
|
+
private readonly flushIntervalOverride?;
|
|
894
|
+
private readonly debug;
|
|
895
|
+
private readonly requestConfig;
|
|
896
|
+
private readonly onConfigReceived?;
|
|
897
|
+
private readonly config;
|
|
898
|
+
/** Event names that trigger an immediate flush at enqueue time (config.flushOnEventNames). */
|
|
899
|
+
private readonly priorityNames;
|
|
900
|
+
private store?;
|
|
901
|
+
/**
|
|
902
|
+
* Once the server has answered the config request with a 2xx (200 = fresh config cached for the
|
|
903
|
+
* next load, 204 = cached version still current) we stop sending the header for this load. A
|
|
904
|
+
* transport-level failure or error status keeps the request pending for the next flush.
|
|
905
|
+
*/
|
|
906
|
+
private configResolved;
|
|
907
|
+
constructor(logger: Logger, transport: Transport, sessionId: string, sdk: SdkInfo, options?: QueueOptions, storage?: StorageEngine | undefined);
|
|
908
|
+
setSessionId(sessionId: string): void;
|
|
909
|
+
private flushInterval;
|
|
910
|
+
/**
|
|
911
|
+
* Error situations:
|
|
912
|
+
* - Any error while enqueueing or scheduling the flush timer: log "Failed to enqueue event"
|
|
913
|
+
*/
|
|
914
|
+
enqueue(event: Event): void;
|
|
915
|
+
/**
|
|
916
|
+
* Error situations:
|
|
917
|
+
* - Any error while enqueueing or scheduling the flush timer: log "Failed to enqueue low event"
|
|
918
|
+
*/
|
|
919
|
+
enqueueLow(low: LowEvent): void;
|
|
920
|
+
private scheduleAfterEnqueue;
|
|
921
|
+
private scheduleFlush;
|
|
922
|
+
private clearTimer;
|
|
923
|
+
/**
|
|
924
|
+
* Error situations:
|
|
925
|
+
* - Queue is empty: return without calling transport.send
|
|
926
|
+
* - A flush is already in progress: return without calling transport.send again
|
|
927
|
+
* - transport.send rejects or throws, or any other error in the try block: log "Unexpected error in queue flush"
|
|
928
|
+
*/
|
|
929
|
+
flush(): Promise<void>;
|
|
930
|
+
/**
|
|
931
|
+
* Stamps delivery metadata onto the batch: the config version this load runs under (omitted on
|
|
932
|
+
* built-in defaults) and the number of prior failed delivery attempts (the max across its entries,
|
|
933
|
+
* only when at least one entry is a retry). `entry.attempts` is incremented on each failed flush,
|
|
934
|
+
* so it equals the retry count at send time. The transport later merges in `sent`/`transport`.
|
|
935
|
+
*/
|
|
936
|
+
private attachBatchMeta;
|
|
937
|
+
private getDuePending;
|
|
938
|
+
private takeLows;
|
|
939
|
+
private shouldRequestConfig;
|
|
940
|
+
/**
|
|
941
|
+
* Handle the server's answer to the config request. Boot-snapshot model: a fresh config is ONLY
|
|
942
|
+
* handed to `onConfigReceived` for caching — it is never applied to this load. The behavior the
|
|
943
|
+
* queue was constructed with stays in effect until the page unloads.
|
|
944
|
+
*/
|
|
945
|
+
private handleConfigResponse;
|
|
946
|
+
/** Returns true when the batch was acknowledged with a 2xx (safe to keep draining). */
|
|
947
|
+
private handleSendResult;
|
|
948
|
+
private isRetryable;
|
|
949
|
+
/**
|
|
950
|
+
* Exponential backoff with equal jitter. The exponential ceiling is `baseDelayMs * 2^(attempt-1)`
|
|
951
|
+
* (capped at `maxDelayMs`); the actual delay is half that ceiling plus a random portion of the other
|
|
952
|
+
* half. The jitter spreads retries from many clients to avoid synchronized retry storms and needs no
|
|
953
|
+
* extra config. A `Retry-After` header, when present, acts as a lower bound.
|
|
954
|
+
*/
|
|
955
|
+
private backoffForAttempt;
|
|
956
|
+
private parseRetryAfter;
|
|
957
|
+
private removeEntries;
|
|
958
|
+
private rescheduleIfPending;
|
|
959
|
+
private setupLifecycleHooks;
|
|
960
|
+
private writeLifecycleDebugOutput;
|
|
961
|
+
/**
|
|
962
|
+
* Synchronously flush pending events (and best-effort lows) during page unload.
|
|
963
|
+
*
|
|
964
|
+
* @remarks
|
|
965
|
+
* When `config.beaconKeepalive` is on, uses `sendBeacon` (falling back to fetch keepalive when
|
|
966
|
+
* the UA refuses the beacon); otherwise fires a fire-and-forget `fetch` keepalive request. The
|
|
967
|
+
* send is capped at one `batchSize` chunk per call (unload transports share an in-flight budget
|
|
968
|
+
* with the host app). With `config.durableOutbox` enabled, entries are NOT removed (they are
|
|
969
|
+
* resent on the next load and deduped server-side); otherwise the sent chunk is removed
|
|
970
|
+
* best-effort and a remainder keeps the next lifecycle signal flush-worthy.
|
|
971
|
+
* Deduplicated: a call with nothing newly enqueued since the last sync flush is a no-op.
|
|
972
|
+
*/
|
|
973
|
+
flushKeepalive(): void;
|
|
974
|
+
private flushSync;
|
|
975
|
+
/**
|
|
976
|
+
* Error situations:
|
|
977
|
+
* - Timer clearance or event listener removal throws: log "Failed to destroy queue lifecycle hooks"
|
|
978
|
+
*/
|
|
979
|
+
destroy(): void;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/** localStorage key under which the last-known server config is cached. */
|
|
983
|
+
declare const CONFIG_STORAGE_KEY = "cbo_sdk_config";
|
|
984
|
+
/**
|
|
985
|
+
* Opt-in request header that asks the events endpoint to return the SDK reliability config. Its value
|
|
986
|
+
* is the config version the SDK is currently running, or "1" when none is cached yet. The server
|
|
987
|
+
* answers 204 when that version is still current and 200 + config body when it is stale.
|
|
988
|
+
*/
|
|
989
|
+
declare const CONFIG_REQUEST_HEADER = "X-Corbado-Observe-Config";
|
|
990
|
+
/** Built-in flush interval used until the server provides one. Matches the historical default. */
|
|
991
|
+
declare const DEFAULT_FLUSH_INTERVAL_MS = 500;
|
|
992
|
+
/**
|
|
993
|
+
* Clamping bounds for server-provided config values. They protect the client from a dangerous
|
|
994
|
+
* server-side config mistake (e.g. a 0ms flush interval would busy-loop); the server clamps with
|
|
995
|
+
* the same bounds as the first line of defense.
|
|
996
|
+
*/
|
|
997
|
+
declare const CONFIG_BOUNDS: {
|
|
998
|
+
readonly flushIntervalMs: {
|
|
999
|
+
readonly min: 100;
|
|
1000
|
+
readonly max: 60000;
|
|
1001
|
+
};
|
|
1002
|
+
readonly retryMaxAttempts: {
|
|
1003
|
+
readonly min: 1;
|
|
1004
|
+
readonly max: 10;
|
|
1005
|
+
};
|
|
1006
|
+
readonly retryDelayMs: {
|
|
1007
|
+
readonly min: 50;
|
|
1008
|
+
readonly max: 120000;
|
|
1009
|
+
};
|
|
1010
|
+
readonly sessionInactivityMs: {
|
|
1011
|
+
readonly min: 60000;
|
|
1012
|
+
readonly max: 86400000;
|
|
1013
|
+
};
|
|
1014
|
+
readonly flushOnEventNames: {
|
|
1015
|
+
readonly maxEntries: 20;
|
|
1016
|
+
readonly maxNameLength: 100;
|
|
1017
|
+
};
|
|
1018
|
+
};
|
|
1019
|
+
/** Built-in continuity-session inactivity threshold (30 minutes); matches the historical default. */
|
|
1020
|
+
declare const DEFAULT_SESSION_INACTIVITY_MS: number;
|
|
1021
|
+
/**
|
|
1022
|
+
* Default config: every delivery-reliability feature is OFF. The SDK behaves like a plain async
|
|
1023
|
+
* fetch-keepalive flusher until the server returns config (which is then cached as last-known and
|
|
1024
|
+
* applied on the NEXT page load — boot-snapshot model). The empty version means "nothing cached";
|
|
1025
|
+
* the config request header sends "1" in that case.
|
|
1026
|
+
*/
|
|
1027
|
+
declare const DEFAULT_RELIABILITY_CONFIG: SdkReliabilityConfig;
|
|
1028
|
+
/**
|
|
1029
|
+
* Coerce an arbitrary value (cached blob or parsed API body) into a {@link SdkReliabilityConfig}.
|
|
1030
|
+
* Returns `null` if the input is not an object or carries no `version` (a config without identity
|
|
1031
|
+
* cannot participate in the version handshake and must not be trusted). Missing fields fall back to
|
|
1032
|
+
* the OFF defaults so a partial/older payload can never silently enable a feature; the
|
|
1033
|
+
* flush-trigger switches (`tvc`/`tph`/`td`) invert this and default ON, so a partial payload can
|
|
1034
|
+
* never silently disable delivery. Numeric values are clamped to {@link CONFIG_BOUNDS} so a bad
|
|
1035
|
+
* server value cannot harm the client.
|
|
1036
|
+
*/
|
|
1037
|
+
declare function parseReliabilityConfig(raw: unknown): SdkReliabilityConfig | null;
|
|
1038
|
+
/** Read the cached config from storage, or `null` if absent/invalid. */
|
|
1039
|
+
declare function loadCachedConfig(storage: StorageEngine): SdkReliabilityConfig | null;
|
|
1040
|
+
/** Persist the config as last-known so the next page load starts with these features enabled. */
|
|
1041
|
+
declare function cacheConfig(storage: StorageEngine, config: SdkReliabilityConfig): void;
|
|
1042
|
+
|
|
1043
|
+
/** A persisted, not-yet-acknowledged event awaiting delivery. */
|
|
1044
|
+
interface OutboxEntry {
|
|
1045
|
+
/** Event idempotency id; primary key of the entry. */
|
|
1046
|
+
id: string;
|
|
1047
|
+
/** Session id snapshot taken at enqueue time (so session rotation never relabels old entries). */
|
|
1048
|
+
sessionId: string;
|
|
1049
|
+
/** Earliest timestamp (ms) at which this entry may be sent again (backoff). `0` = due now. */
|
|
1050
|
+
flushAfter: number;
|
|
1051
|
+
/** Number of completed send attempts so far. */
|
|
1052
|
+
attempts: number;
|
|
1053
|
+
/** The event payload. */
|
|
1054
|
+
event: Event;
|
|
1055
|
+
}
|
|
1056
|
+
/** localStorage key under which the durable outbox is persisted. */
|
|
1057
|
+
declare const OUTBOX_STORAGE_KEY = "cbo_outbox";
|
|
1058
|
+
/** Web Lock name guarding outbox mutations across tabs. */
|
|
1059
|
+
declare const OUTBOX_LOCK_NAME = "cbo_outbox";
|
|
1060
|
+
/** Default maximum number of entries kept; oldest are evicted past this. */
|
|
1061
|
+
declare const DEFAULT_MAX_ENTRIES = 500;
|
|
1062
|
+
/**
|
|
1063
|
+
* Default budget for the outbox's serialized size, in UTF-16 code units (the unit localStorage
|
|
1064
|
+
* quotas are measured in; ~1 MB on disk). The entry-count cap alone does not bound bytes —
|
|
1065
|
+
* `event.data` is arbitrary customer payload — and localStorage's ~5M-unit quota is shared with
|
|
1066
|
+
* the HOST app: an unbounded outbox could make the host's own writes throw QuotaExceededError.
|
|
1067
|
+
*/
|
|
1068
|
+
declare const DEFAULT_MAX_SERIALIZED_LENGTH: number;
|
|
1069
|
+
/**
|
|
1070
|
+
* Durable, localStorage-backed outbox of pending events, mirrored in memory.
|
|
1071
|
+
*
|
|
1072
|
+
* @remarks
|
|
1073
|
+
* Every mutation is a read-merge-write against storage, executed under a cross-tab Web Lock
|
|
1074
|
+
* ({@link OUTBOX_LOCK_NAME}) so concurrent tabs sharing the same localStorage cannot interleave
|
|
1075
|
+
* between the read and the write (which would silently drop the other tab's entries). Where the
|
|
1076
|
+
* Web Locks API is unavailable the mutation runs unlocked, matching the historical best-effort
|
|
1077
|
+
* behavior. Mutations never reject; failures are logged.
|
|
1078
|
+
* The store is size-capped; overflow evicts the oldest entries and logs the count.
|
|
1079
|
+
*/
|
|
1080
|
+
declare class EventStore {
|
|
1081
|
+
private readonly storage;
|
|
1082
|
+
private readonly logger;
|
|
1083
|
+
private readonly maxEntries;
|
|
1084
|
+
private readonly maxSerializedLength;
|
|
1085
|
+
private entries;
|
|
1086
|
+
/** Malformed entries are dropped on every read; report only once per instance to avoid log spam. */
|
|
1087
|
+
private reportedMalformed;
|
|
1088
|
+
constructor(storage: StorageEngine, logger: Logger, maxEntries?: number, maxSerializedLength?: number);
|
|
1089
|
+
private read;
|
|
1090
|
+
private mutate;
|
|
1091
|
+
/** Evict oldest entries until the serialized outbox fits {@link maxSerializedLength}. */
|
|
1092
|
+
private enforceSizeBudget;
|
|
1093
|
+
/** Append an entry. No-op if an entry with the same id already exists. */
|
|
1094
|
+
add(entry: OutboxEntry): Promise<void>;
|
|
1095
|
+
/** Re-read from storage and return all entries (memory mirror refreshed). */
|
|
1096
|
+
all(): OutboxEntry[];
|
|
1097
|
+
/** Entries whose `flushAfter` has elapsed, in insertion order. */
|
|
1098
|
+
getDue(now: number): OutboxEntry[];
|
|
1099
|
+
/** Remove entries by id (acknowledged or permanently dropped). */
|
|
1100
|
+
remove(ids: string[]): Promise<void>;
|
|
1101
|
+
/** Replace existing entries (matched by id) with updated copies, e.g. after bumping backoff. */
|
|
1102
|
+
update(updated: OutboxEntry[]): Promise<void>;
|
|
1103
|
+
size(): number;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
730
1106
|
declare function init(options: TrackerOptions): CorbadoTracker;
|
|
731
1107
|
declare function getTracker(): CorbadoTracker | undefined;
|
|
732
1108
|
declare function resetSession(): string | undefined;
|
|
733
1109
|
declare function destroy(): Promise<void>;
|
|
734
1110
|
|
|
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 };
|
|
1111
|
+
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 };
|