@corbado/observe 0.9.1 → 0.10.0-next.84-492957c

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
@@ -3,8 +3,8 @@ interface SdkInfo {
3
3
  version: string;
4
4
  }
5
5
  type EventType = "predefined" | "custom" | "error" | "identify";
6
- type AuthMethodType = "identifier-email" | "passkey-login-known-identifier" | "passkey-login-no-identifier" | "passkey-login-cui" | "passkey-login-immediate" | "passkey-enrollment" | "password-login-known-identifier" | "password-login-with-identifier" | "password-enrollment" | "email-otp-login" | "email-otp-enrollment" | "email-link-login" | "email-link-enrollment" | "social-google" | "social-apple" | "social-facebook" | "social-other" | "reset-flow";
7
- type SubflowType = "passkey-enrollment" | "passkey-login" | "email-otp" | "email-link" | "social-login" | "sms-otp" | "provide-identifier" | "provide-data" | "password-login" | "password-enrollment" | "totp" | "app-confirmation";
6
+ type AuthMethodType = "identifier-email" | "passkey-login-known-identifier" | "passkey-login-no-identifier" | "passkey-login-cui" | "passkey-login-immediate" | "passkey-enrollment" | "password-login" | "password-login-known-identifier" | "password-login-with-identifier" | "password-enrollment" | "email-otp" | "email-otp-login" | "email-otp-enrollment" | "email-link" | "email-link-login" | "email-link-enrollment" | "sms-otp" | "sms-otp-login" | "sms-otp-enrollment" | "social-google" | "social-apple" | "social-facebook" | "social-other" | "reset-flow" | "provide-data";
7
+ type SubflowType = "passkey-enrollment" | "passkey-login" | "email-otp" | "email-link" | "social-login" | "sms-otp" | "provide-identifier" | "provide-data" | "password-login" | "password-enrollment" | "totp" | "app-confirmation" | "captcha";
8
8
  type FlowType = "login" | "signup" | "recovery" | "enrollment" | (string & {});
9
9
  type SocialLoginProviderType = "google" | "apple" | "facebook" | "github" | "microsoft" | "other";
10
10
  declare enum AuthEventName {
@@ -175,9 +175,31 @@ interface TelemetryEntry {
175
175
  /** Capture time, unix ms. */
176
176
  ts: number;
177
177
  }
178
+ /**
179
+ * Why a delivery attempt happened. Each value maps 1:1 to one flush trigger, so server-side
180
+ * analyses can tell which triggers actually carry the traffic and whether the trigger switches and
181
+ * opt-in flush features of the reliability config earn their keep.
182
+ *
183
+ * - `timer`: the flush interval elapsed (the ordinary cadence).
184
+ * - `backoff`: a retry whose backoff delay elapsed — a resend, not new work.
185
+ * - `recovery`: events recovered from the durable outbox at boot (previous load never delivered them).
186
+ * - `flow-finished`: a configured priority flow type completed (`flushOnFlowTypeFinished`).
187
+ * - `telemetry`: a telemetry entry escalated to an immediate flush (`flushOnTelemetry`).
188
+ * - `visibility-change`: the document became hidden (`tvc`).
189
+ * - `pagehide`: the page is going away (`tph`).
190
+ * - `low-event-teardown`: a low-event tracker's unload teardown handed over its batched lows (`tlf`).
191
+ * - `destroy`: the tracker was destroyed (`td`).
192
+ * - `manual`: the host app called `flushKeepalive()` itself.
193
+ */
194
+ type FlushReason = "timer" | "backoff" | "recovery" | "flow-finished" | "telemetry" | "visibility-change" | "pagehide" | "low-event-teardown" | "destroy" | "manual";
178
195
  interface EventBatchMeta {
179
196
  sent: number;
180
197
  transport: "fetch" | "beacon";
198
+ /**
199
+ * What triggered the flush this batch was sent in. All batches of one flush (a backlog larger
200
+ * than `batchSize` is drained in several requests) carry the same reason.
201
+ */
202
+ flushReason?: FlushReason;
181
203
  /** Number of prior failed delivery attempts for this batch; omitted on the first attempt. */
182
204
  retryCount?: number;
183
205
  /**
@@ -219,6 +241,11 @@ interface DeviceInfoDataWeb {
219
241
  javaScriptHighEntropy?: JavaScriptHighEntropy;
220
242
  privateMode?: boolean;
221
243
  webdriver?: boolean;
244
+ /**
245
+ * `navigator.maxTouchPoints` — maximum simultaneous touch contact points. Reported raw so the
246
+ * backend can separate iPadOS (non-zero, but sends a macOS user agent) from real macOS devices (0).
247
+ */
248
+ maxTouchPoints?: number;
222
249
  }
223
250
  type DeviceInfoCollectionError = {
224
251
  field: keyof DeviceInfoDataWeb;
@@ -281,7 +308,14 @@ interface SdkReliabilityConfig {
281
308
  * relevant when `sessionContinuity` is enabled.
282
309
  */
283
310
  sessionInactivityMs: number;
284
- /** Maximum time to wait for the cross-tab sequence Web Lock before allocating without it. */
311
+ /**
312
+ * Maximum time to wait for the cross-tab sequence Web Lock before allocating without it.
313
+ *
314
+ * `0` disables the lock: the counter is then allocated straight away, without ever requesting a
315
+ * lock. That gives up cross-tab atomicity — two tabs sharing a continuity session can allocate the
316
+ * same seq, which is what the unlocked fallback already does on every timeout — in exchange for
317
+ * removing a lock round-trip per tracked event.
318
+ */
285
319
  seqLockTimeoutMs: number;
286
320
  /**
287
321
  * Flush-trigger switch: flush pending events when the document becomes hidden
@@ -311,13 +345,31 @@ interface SdkReliabilityConfig {
311
345
  */
312
346
  flushOnTelemetry: boolean;
313
347
  /**
314
- * Event names that trigger an immediate flush when enqueued (e.g. `flow_finished`,
315
- * `conversion`), so high-value events go out via a normal fetch while the page is still alive
316
- * instead of relying on unload delivery. Empty by default (no priority events). The whole
317
- * pending queue is sent, and the immediate flush coexists with the interval timer without
318
- * double-sending (the in-flight drain absorbs it).
348
+ * Flow types (e.g. `login`, `enrollment`) whose completion triggers an immediate flush when
349
+ * enqueued, so the highest-value events go out via a normal fetch while the page is still alive
350
+ * instead of relying on unload delivery. Both completion events count — `flow_finished` and
351
+ * `flow_auto_finished` — matched on their `flowName`; no other event name ever flushes early.
352
+ * Empty by default (no priority flows). The whole pending queue is sent, and the immediate flush
353
+ * coexists with the interval timer without double-sending (the in-flight drain absorbs it).
354
+ */
355
+ flushOnFlowTypeFinished: FlowType[];
356
+ /**
357
+ * Per-field timeout for device info collection, in milliseconds. A field whose browser API does
358
+ * not answer within this budget is recorded as a `TimeoutError` in `collectionErrors` and left
359
+ * undefined; the remaining fields are unaffected. Defaults to 1000ms.
360
+ *
361
+ * Read at collection time, so a config that arrives mid-load applies from the next device info
362
+ * refresh onward. The very first collection of a load booting on the built-in defaults (nothing
363
+ * cached) always runs on the default.
364
+ */
365
+ deviceInfoCollectorTimeoutMs: number;
366
+ /**
367
+ * When true (and `telemetry` is enabled), every device info collection emits an `info` telemetry
368
+ * entry with the total duration and the per-field durations in milliseconds. Defaults to false:
369
+ * this is a diagnostic stream to switch on while investigating slow collection, not something to
370
+ * run permanently.
319
371
  */
320
- flushOnEventNames: string[];
372
+ deviceInfoTiming: boolean;
321
373
  /** Retry configuration for failed event flushes. */
322
374
  retry: SdkRetryConfig;
323
375
  }
@@ -449,10 +501,20 @@ type PasskeyLoginCeremonyStart = {
449
501
  type PasskeyLoginCeremonyFinished = {
450
502
  assertionResponse: string;
451
503
  };
504
+ type PasskeyLoginPostResponseStart = {
505
+ /**
506
+ * Fallback carrier for the WebAuthn assertion, same JSON shape the ceremony step reports. Set it
507
+ * only when the ceremony itself could not be observed — e.g. an extension holds
508
+ * `navigator.credentials` in a form the integration declines to patch — and the submitted
509
+ * credential is the only remaining copy. Leave it unset whenever a ceremony step carried the
510
+ * assertion, so consumers never see two sources for one login.
511
+ */
512
+ assertionResponse?: string;
513
+ };
452
514
  declare class PasskeyLoginOperationFull extends OperationFull {
453
515
  readonly getOptions: StepHelper<PasskeyLoginGetOptionsStart, PasskeyLoginGetOptionsFinished>;
454
516
  readonly ceremony: StepHelper<PasskeyLoginCeremonyStart, PasskeyLoginCeremonyFinished>;
455
- readonly postResponse: StepHelper;
517
+ readonly postResponse: StepHelper<PasskeyLoginPostResponseStart>;
456
518
  private lowEventTracker;
457
519
  constructor(tracker: CorbadoTracker);
458
520
  /**
@@ -470,21 +532,27 @@ type EmailOTPOperationSendOTPStart = {
470
532
  type EmailOTPOperationPostResponseStart = {
471
533
  explicitSpecType?: EmailOTPOperationSpecType;
472
534
  };
535
+ type EmailOTPOperationConfig = {
536
+ explicitSpecType?: EmailOTPOperationSpecType;
537
+ };
473
538
  declare class EmailOtpOperationFull extends OperationFull {
474
539
  readonly send: StepHelper<EmailOTPOperationSendOTPStart>;
475
540
  readonly postResponse: StepHelper<EmailOTPOperationPostResponseStart>;
476
541
  readonly resend: StepHelper;
477
- constructor(tracker: CorbadoTracker);
542
+ constructor(tracker: CorbadoTracker, config?: EmailOTPOperationConfig);
478
543
  }
479
544
 
480
545
  type SmsOTPOperationSpecType = "sms-otp-login" | "sms-otp-enrollment";
481
546
  type SmsOTPOperationPostResponseStart = {
482
547
  explicitSpecType?: SmsOTPOperationSpecType;
483
548
  };
549
+ type SmsOTPOperationConfig = {
550
+ explicitSpecType?: SmsOTPOperationSpecType;
551
+ };
484
552
  declare class SmsOtpOperationFull extends OperationFull {
485
553
  readonly postResponse: StepHelper<SmsOTPOperationPostResponseStart>;
486
554
  readonly resend: StepHelper;
487
- constructor(tracker: CorbadoTracker);
555
+ constructor(tracker: CorbadoTracker, config?: SmsOTPOperationConfig);
488
556
  }
489
557
 
490
558
  type EmailLinkOperationSpecType = "email-link-login" | "email-link-enrollment";
@@ -517,12 +585,26 @@ type PasskeyEnrollmentCeremonyStart = {
517
585
  type PasskeyEnrollmentCeremonyFinished = {
518
586
  attestationResponse: string;
519
587
  };
588
+ type PasskeyEnrollmentPostResponseStart = {
589
+ /**
590
+ * Fallback carrier for the WebAuthn attestation, mirroring the passkey-login contract. Set it only
591
+ * when the ceremony itself could not be observed — e.g. an extension holds `navigator.credentials`
592
+ * in a form the integration declines to patch — and the submitted credential is the only remaining
593
+ * copy. Leave it unset whenever a ceremony step carried the attestation, so consumers never see two
594
+ * sources for one enrollment. A credential recovered from the submission may carry more members
595
+ * than the ceremony reports; consumers must tolerate unknown keys.
596
+ */
597
+ attestationResponse?: string;
598
+ };
599
+ type PasskeyEnrollmentOperationConfig = {
600
+ explicitSpecType?: PasskeyOperationEnrollmentExplicitSpecType;
601
+ };
520
602
  declare class PasskeyEnrollmentOperationFull extends OperationFull {
521
603
  readonly getOptions: StepHelper<PasskeyEnrollmentGetOptionsStart, PasskeyEnrollmentGetOptionsFinished>;
522
604
  readonly ceremony: StepHelper<PasskeyEnrollmentCeremonyStart, PasskeyEnrollmentCeremonyFinished>;
523
- readonly postResponse: StepHelper;
605
+ readonly postResponse: StepHelper<PasskeyEnrollmentPostResponseStart>;
524
606
  private lowEventTracker;
525
- constructor(tracker: CorbadoTracker);
607
+ constructor(tracker: CorbadoTracker, config?: PasskeyEnrollmentOperationConfig);
526
608
  destroy(): void;
527
609
  }
528
610
 
@@ -609,7 +691,7 @@ type PasskeyLoginCUITypedError = {
609
691
  code: PasskeyLoginCUITypedErrorCode;
610
692
  };
611
693
  type PasskeyLoginCUITypedErrorCode = "cancel_detected";
612
- type ProvideIdentifierSpecType = "email";
694
+ type ProvideIdentifierSpecType = "email" | "phone";
613
695
  type ProvideIdentifierPostResponseStart = {
614
696
  explicitSpecType?: ProvideIdentifierSpecType;
615
697
  };
@@ -624,6 +706,10 @@ type PasskeyLoginCUIGetOptionsFinished = {
624
706
  explicitSpecType?: ProvideIdentifierSpecType;
625
707
  assertionOptions: string;
626
708
  };
709
+ type OperationFullProvideIdentifierWithCUIConfig = {
710
+ inputHtmlField?: HTMLInputElement;
711
+ explicitSpecType?: ProvideIdentifierSpecType;
712
+ };
627
713
  declare class OperationFullProvideIdentifierWithCUI {
628
714
  private tracker;
629
715
  private lowEventTracker?;
@@ -635,7 +721,7 @@ declare class OperationFullProvideIdentifierWithCUI {
635
721
  trigger: (data: SubflowTrigger, options?: StepOptions) => void;
636
722
  getOptions: StepHelper<PasskeyLoginCUIGetOptionsStart, PasskeyLoginCUIGetOptionsFinished>;
637
723
  ceremony: StepHelper<CUICeremonyStart, PasskeyLoginCeremonyFinished, PasskeyLoginCUITypedError>;
638
- postResponse: StepHelper;
724
+ postResponse: StepHelper<PasskeyLoginPostResponseStart>;
639
725
  };
640
726
  readonly provideIdentifier: {
641
727
  /**
@@ -646,7 +732,7 @@ declare class OperationFullProvideIdentifierWithCUI {
646
732
  clientValidation: StepHelper;
647
733
  postResponse: StepHelper<ProvideIdentifierPostResponseStart>;
648
734
  };
649
- constructor(tracker: CorbadoTracker, inputHtmlField?: HTMLInputElement);
735
+ constructor(tracker: CorbadoTracker, config?: OperationFullProvideIdentifierWithCUIConfig);
650
736
  private createStep;
651
737
  destroy(): void;
652
738
  }
@@ -683,6 +769,29 @@ declare class AppConfirmationOperationFull extends OperationFull {
683
769
  constructor(tracker: CorbadoTracker, explicitSpecType?: AppConfirmationOperationSpecType);
684
770
  }
685
771
 
772
+ /**
773
+ * Which captcha widget the integration observed. The tier is decided server-side per request, so it
774
+ * is a property of the rendered challenge, not of the integration:
775
+ * - `visible`: a widget presenting UI to the user. Can require interaction.
776
+ * - `invisible`: a widget that never presents UI and always solves itself.
777
+ */
778
+ type CaptchaOperationSpecType = "visible" | "invisible";
779
+ type CaptchaOperationConfig = {
780
+ explicitSpecType?: CaptchaOperationSpecType;
781
+ };
782
+ declare class CaptchaOperationFull extends OperationFull {
783
+ /**
784
+ * Challenge shown (`start`) until a captcha token exists (`finished`) or the challenge failed.
785
+ *
786
+ * The ceremony carries no actor: a widget that self-solves and one awaiting a click are the same
787
+ * observation from the page, and nothing in a vendor's frame reports which of the two happened.
788
+ */
789
+ readonly ceremony: StepHelper;
790
+ /** Submission of the captcha token and the server's verdict on it. */
791
+ readonly postResponse: StepHelper;
792
+ constructor(tracker: CorbadoTracker, config?: CaptchaOperationConfig);
793
+ }
794
+
686
795
  type ProvideDataOperationSpecType = "signup" | "login" | "recovery" | "enrollment";
687
796
  type ProvideDataOperationStart = {
688
797
  /** Stable name of the data field collected by this subflow, when known. */
@@ -715,7 +824,12 @@ interface TrackerOptions {
715
824
  logger?: Logger;
716
825
  /** Minimum time between attaching cached device info to events. */
717
826
  deviceInfoDebounceTime?: number;
718
- /** Per-field collection timeout. Defaults to 200ms. */
827
+ /**
828
+ * @deprecated The device info collection timeout is server-controlled via the SDK reliability
829
+ * config (`deviceInfoCollectorTimeoutMs`). This option only applies as long as no server config
830
+ * has been received (i.e. before the first config response is cached); once a server config is
831
+ * cached it wins. It will be removed in a future release.
832
+ */
719
833
  deviceInfoCollectorTimeout?: number;
720
834
  defaultTags?: Record<string, string>;
721
835
  /**
@@ -731,6 +845,8 @@ interface TrackerOptions {
731
845
  * It will be removed in a future release.
732
846
  */
733
847
  flushInterval?: number;
848
+ /** Generates new Observe session ids; returned values must be UUID-compatible. Existing stored sessions are still reused. */
849
+ sessionIdGenerator?: () => string;
734
850
  }
735
851
  declare class CorbadoTracker {
736
852
  private options;
@@ -749,6 +865,12 @@ declare class CorbadoTracker {
749
865
  private config;
750
866
  private deviceInfoManager;
751
867
  private seq;
868
+ /**
869
+ * When the continuity record's `lastActiveAt` was last persisted. Throttles that write inside the
870
+ * seq critical section (see {@link SESSION_ACTIVITY_WRITE_INTERVAL_MS}). 0 = not written yet on
871
+ * this load, so the first allocation always persists.
872
+ */
873
+ private lastActivityWriteAt;
752
874
  /** Persistent experiment assignments (experiment key → variant), attached to every produced event. */
753
875
  private experiments;
754
876
  /** Self-instrumentation state: re-entrancy guard, readiness, per-load dedupe, and pre-queue buffer. */
@@ -805,24 +927,42 @@ declare class CorbadoTracker {
805
927
  */
806
928
  private resolveExperiments;
807
929
  private isTrackingBlocked;
930
+ private createSessionId;
808
931
  private getSessionId;
809
932
  /**
810
933
  * Resolve the session id from localStorage so it survives reloads and is shared across tabs of the
811
934
  * same browser. Rotates after `config.sessionInactivityMs` of inactivity (server-controlled,
812
935
  * boot-snapshot like the rest of the config), resetting the seq counter.
936
+ *
937
+ * This is the ONLY place an inactivity rotation happens. Rotating here is safe because the load
938
+ * that triggers it also re-announces its flow (an integration emits `flow_started` on page load),
939
+ * so the fresh session id gets a classifiable flow. Mid-page there is no such re-announcement:
940
+ * rotating between two tracked events would leave the started flow unfinished in the old session
941
+ * and the remaining events without a flow start in the new one — one gap counted as a drop-off,
942
+ * one completion counted as nothing. `nextSeq` therefore never rotates on inactivity.
813
943
  */
814
944
  private getContinuitySessionId;
945
+ /**
946
+ * Persist the continuity record and remember when, so the throttled refresh in the seq critical
947
+ * section measures from the last write that actually happened — whoever made it.
948
+ */
949
+ private persistContinuitySession;
815
950
  /**
816
951
  * Return the next sequence number. With session continuity the counter is persisted so ordering
817
952
  * survives reloads/redirects; the read-increment-write runs under a cross-tab Web Lock
818
953
  * ({@link SEQ_LOCK_NAME}) so two tabs sharing the session cannot allocate the same seq (where Web
819
954
  * Locks are unavailable or exceed the configured timeout it degrades to the historical best-effort merge).
820
- * Lock grants are FIFO, so seq order matches `track()` call order within a tab. Every tracked event also bumps the
821
- * continuity session's `lastActiveAt`, so the inactivity window measures real user inactivity
822
- * instead of time-since-page-load (a long-lived active tab must not rotate the session on the next
823
- * reload). An empty store while this load already has a session id means persistence is failing
824
- * (or was cleared); the in-memory id is reused then — never re-minted — so a broken store degrades
825
- * to one session per page load instead of one session per event.
955
+ * Lock grants are FIFO, so seq order matches `track()` call order within a tab. Tracking also keeps the
956
+ * continuity session's `lastActiveAt` fresh (throttled, see {@link SESSION_ACTIVITY_WRITE_INTERVAL_MS}),
957
+ * so the window the NEXT boot evaluates measures real user
958
+ * inactivity instead of time-since-page-load (a long-lived active tab must not rotate the session
959
+ * on the next reload). It never rotates the session itself: the inactivity window is evaluated at
960
+ * boot only ({@link getContinuitySessionId} documents why). Consequence: while a page is never
961
+ * reloaded — foreground idle, bfcache restore, long-lived SPA tab — a session can outlive
962
+ * `sessionInactivityMs`. That inflates flow durations but keeps flow counts correct, which is the
963
+ * better half of the trade. An empty store while this load already has a session id means
964
+ * persistence is failing (or was cleared); the in-memory id is reused then — never re-minted — so
965
+ * a broken store degrades to one session per page load instead of one session per event.
826
966
  */
827
967
  private nextSeq;
828
968
  private resolveTrackingSourcePath;
@@ -1028,9 +1168,12 @@ declare class CorbadoTracker {
1028
1168
  * Synchronously flush any pending events and low events via beacon. Use during page-unload
1029
1169
  * style teardown where async flushing is unreliable.
1030
1170
  *
1171
+ * @param reason What triggered the flush; recorded as `meta.flushReason` on the batch. Defaults
1172
+ * to a direct call by the host app.
1173
+ *
1031
1174
  * @internal
1032
1175
  */
1033
- flushKeepalive(): void;
1176
+ flushKeepalive(reason?: FlushReason): void;
1034
1177
  /**
1035
1178
  * Keepalive flush requested by a low-event tracker's `pagehide` teardown. Gated by the
1036
1179
  * server-controlled trigger switch `tlf` (boot-snapshot, default true = historical behavior):
@@ -1040,16 +1183,17 @@ declare class CorbadoTracker {
1040
1183
  */
1041
1184
  lowEventFlushKeepalive(): void;
1042
1185
  passkeyLoginFullOperation(): PasskeyLoginOperationFull;
1043
- passkeyEnrollmentFullOperation(): PasskeyEnrollmentOperationFull;
1186
+ passkeyEnrollmentFullOperation(config?: PasskeyEnrollmentOperationConfig): PasskeyEnrollmentOperationFull;
1044
1187
  passwordLoginFullOperation(autoTrackConfig?: PasswordLoginAutoTrackConfig): PasswordLoginOperationFull;
1045
1188
  passwordEnrollmentFullOperation(autoTrackConfig?: PasswordEnrollmentAutoTrackConfig): PasswordEnrollmentOperationFull;
1046
1189
  emailLinkOperationFull(): EmailLinkOperationFull;
1047
- emailOtpOperationFull(): EmailOtpOperationFull;
1048
- smsOtpOperationFull(): SmsOtpOperationFull;
1049
- provideIdentifierOperationFull(inputHtmlField?: HTMLInputElement): OperationFullProvideIdentifierWithCUI;
1190
+ emailOtpOperationFull(config?: EmailOTPOperationConfig): EmailOtpOperationFull;
1191
+ smsOtpOperationFull(config?: SmsOTPOperationConfig): SmsOtpOperationFull;
1192
+ provideIdentifierOperationFull(config?: OperationFullProvideIdentifierWithCUIConfig): OperationFullProvideIdentifierWithCUI;
1050
1193
  provideDataOperationFull(config?: ProvideDataOperationConfig): ProvideDataOperationFull;
1051
1194
  socialLoginOperationFull(): SocialLoginOperationFull;
1052
1195
  appConfirmationOperationFull(explicitSpecType?: AppConfirmationOperationSpecType): AppConfirmationOperationFull;
1196
+ captchaOperationFull(config?: CaptchaOperationConfig): CaptchaOperationFull;
1053
1197
  /**
1054
1198
  * Flush any pending events and release all resources (timers, event listeners).
1055
1199
  *
@@ -1206,6 +1350,9 @@ interface QueueOptions {
1206
1350
  *
1207
1351
  * Flushing is timer-driven only (the flush interval); `batchSize` caps the events per request, and a
1208
1352
  * flush drains all due work in sequential capped batches, stopping at the first failed send.
1353
+ *
1354
+ * Every flush carries a {@link FlushReason} that names its trigger; it is stamped onto the batch as
1355
+ * `meta.flushReason` so the server can see which triggers actually deliver.
1209
1356
  */
1210
1357
  declare class RequestQueue {
1211
1358
  private readonly logger;
@@ -1243,8 +1390,11 @@ declare class RequestQueue {
1243
1390
  * config (see {@link handleConfigResponse}).
1244
1391
  */
1245
1392
  private config;
1246
- /** Event names that trigger an immediate flush at enqueue time (config.flushOnEventNames). */
1247
- private priorityNames;
1393
+ /**
1394
+ * Flow types whose completion triggers an immediate flush at enqueue time
1395
+ * (config.flushOnFlowTypeFinished).
1396
+ */
1397
+ private priorityFlowTypes;
1248
1398
  private store?;
1249
1399
  /**
1250
1400
  * Once the server has answered the config request with a 2xx (200 = fresh config cached for the
@@ -1279,20 +1429,35 @@ declare class RequestQueue {
1279
1429
  */
1280
1430
  enqueueTelemetry(entry: TelemetryEntry): void;
1281
1431
  private scheduleAfterEnqueue;
1432
+ /**
1433
+ * Whether this event completes one of the configured priority flow types. Only the two flow
1434
+ * completion events qualify — `flow_finished` and `flow_auto_finished`, both carrying the
1435
+ * finished flow in `data.flowName`. Custom events never match, even when named like one of them.
1436
+ */
1437
+ private isPriorityEvent;
1438
+ /**
1439
+ * Arm the flush timer. The `reason` travels with the timer into the flush it triggers; an
1440
+ * already-armed timer keeps the reason it was armed with (the flush it fires is that flush, and
1441
+ * it takes whatever accumulated in the meantime with it).
1442
+ */
1282
1443
  private scheduleFlush;
1283
1444
  private clearTimer;
1284
1445
  /**
1446
+ * @param reason What triggered this flush; stamped onto every batch it sends as
1447
+ * `meta.flushReason`. Defaults to the ordinary interval cadence.
1448
+ *
1285
1449
  * Error situations:
1286
1450
  * - Queue is empty: return without calling transport.send
1287
1451
  * - A flush is already in progress: return without calling transport.send again
1288
1452
  * - transport.send rejects or throws, or any other error in the try block: log "Unexpected error in queue flush"
1289
1453
  */
1290
- flush(): Promise<void>;
1454
+ flush(reason?: FlushReason): Promise<void>;
1291
1455
  /**
1292
- * Stamps delivery metadata onto the batch: the config version this load runs under (omitted on
1293
- * built-in defaults) and the number of prior failed delivery attempts (the max across its entries,
1294
- * only when at least one entry is a retry). `entry.attempts` is incremented on each failed flush,
1295
- * so it equals the retry count at send time. The transport later merges in `sent`/`transport`.
1456
+ * Stamps delivery metadata onto the batch: the trigger that caused this flush, the config version
1457
+ * this load runs under (omitted on built-in defaults) and the number of prior failed delivery
1458
+ * attempts (the max across its entries, only when at least one entry is a retry). `entry.attempts`
1459
+ * is incremented on each failed flush, so it equals the retry count at send time. The transport
1460
+ * later merges in `sent`/`transport`.
1296
1461
  */
1297
1462
  private attachBatchMeta;
1298
1463
  private getDuePending;
@@ -1311,7 +1476,7 @@ declare class RequestQueue {
1311
1476
  /**
1312
1477
  * Switch this load from the built-in defaults to the first server config (defaults boot only).
1313
1478
  * Most fields are read at use time, so replacing the reference is enough; only the derived state
1314
- * (priority-name set, durable outbox store) needs rebuilding.
1479
+ * (priority flow type set, durable outbox store) needs rebuilding.
1315
1480
  */
1316
1481
  private applyConfigLive;
1317
1482
  /** Returns true when the batch was acknowledged with a 2xx (safe to keep draining). */
@@ -1339,9 +1504,13 @@ declare class RequestQueue {
1339
1504
  * with the host app). With `config.durableOutbox` enabled, entries are NOT removed (they are
1340
1505
  * resent on the next load and deduped server-side); otherwise the sent chunk is removed
1341
1506
  * best-effort and a remainder keeps the next lifecycle signal flush-worthy.
1342
- * Deduplicated: a call with nothing newly enqueued since the last sync flush is a no-op.
1507
+ * Deduplicated: a call with nothing newly enqueued since the last sync flush is a no-op — so of
1508
+ * several unload signals in one navigation only the first one that finds something to send is
1509
+ * recorded as the flush reason.
1510
+ *
1511
+ * @param reason What triggered the unload flush; defaults to a direct call by the host app.
1343
1512
  */
1344
- flushKeepalive(): void;
1513
+ flushKeepalive(reason?: FlushReason): void;
1345
1514
  private flushSync;
1346
1515
  /**
1347
1516
  * Error situations:
@@ -1361,6 +1530,12 @@ declare const CONFIG_REQUEST_HEADER = "X-Corbado-Observe-Config";
1361
1530
  /** Built-in flush interval used until the server provides one. Matches the historical default. */
1362
1531
  declare const DEFAULT_FLUSH_INTERVAL_MS = 500;
1363
1532
  declare const DEFAULT_SEQ_LOCK_TIMEOUT_MS = 50;
1533
+ /**
1534
+ * Built-in per-field device info collection timeout. Also the timeout of the very first collection
1535
+ * of a load that booted without a cached config, since collection starts before the first server
1536
+ * response can arrive — generous enough that a slow-but-working browser API still answers.
1537
+ */
1538
+ declare const DEFAULT_DEVICE_INFO_COLLECTOR_TIMEOUT_MS = 1000;
1364
1539
  /**
1365
1540
  * Clamping bounds for server-provided config values. They protect the client from a dangerous
1366
1541
  * server-side config mistake (e.g. a 0ms flush interval would busy-loop); the server clamps with
@@ -1384,10 +1559,14 @@ declare const CONFIG_BOUNDS: {
1384
1559
  readonly max: 86400000;
1385
1560
  };
1386
1561
  readonly seqLockTimeoutMs: {
1387
- readonly min: 1;
1562
+ readonly min: 0;
1388
1563
  readonly max: 250;
1389
1564
  };
1390
- readonly flushOnEventNames: {
1565
+ readonly deviceInfoCollectorTimeoutMs: {
1566
+ readonly min: 10;
1567
+ readonly max: 10000;
1568
+ };
1569
+ readonly flushOnFlowTypeFinished: {
1391
1570
  readonly maxEntries: 20;
1392
1571
  readonly maxNameLength: 100;
1393
1572
  };
@@ -1493,4 +1672,4 @@ declare function logInfo(message: string): void;
1493
1672
  declare function logError(message: string): void;
1494
1673
  declare function destroy(): Promise<void>;
1495
1674
 
1496
- export { type AppConfirmationOperationCeremonyTypedError, AppConfirmationOperationFull, type AppConfirmationOperationRetryReason, type AppConfirmationOperationSpecType, type AppConfirmationOperationStart, type AuthDecisionFinished, type AuthDecisionStarted, AuthEventName, type AuthMethodDecisionFinished, type AuthMethodDecisionStarted, type AuthMethodType, type BaseEvent, CEREMONY_LOW_EVENT_TAIL_MS, 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_SEQ_LOCK_TIMEOUT_MS, 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, type PasswordLoginCUICeremonyStart, type PasswordLoginCUIGetOptionsFinished, type PasswordLoginCUIGetOptionsStart, type PasswordLoginCUISpecType, type PasswordLoginCUITypedError, PasswordLoginOperationFull, type PasswordLoginTypedError, type PredefinedEvent, type ProvideDataOperationConfig, ProvideDataOperationFull, type ProvideDataOperationPostResponseStart, type ProvideDataOperationSpecType, type ProvideDataOperationStart, 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 SmsOTPOperationPostResponseStart, type SmsOTPOperationSpecType, SmsOtpOperationFull, type SocialLoginExchangeCodeFinished, type SocialLoginExchangeCodeStart, type SocialLoginGetRedirectUrlFinished, type SocialLoginGetRedirectUrlStart, SocialLoginOperationFull, type SocialLoginProviderType, type SocialLoginSpecType, type StepHelper, type StepOptions, type StorageEngine, type SubflowTrigger, type SubflowType, type TelemetryEntry, type TelemetryLevel, type TelemetrySink, type TrackerOptions, type Transport, type TransportMakeRequestResponse, type TransportOptions, type UserReference, cacheConfig, clearExperiment, clearExperiments, createLogger, destroy, getExperiments, getTracker, init, loadCachedConfig, logError, logInfo, parseReliabilityConfig, resetSession, setExperiment, setExperiments, telemetry };
1675
+ export { type AppConfirmationOperationCeremonyTypedError, AppConfirmationOperationFull, type AppConfirmationOperationRetryReason, type AppConfirmationOperationSpecType, type AppConfirmationOperationStart, type AuthDecisionFinished, type AuthDecisionStarted, AuthEventName, type AuthMethodDecisionFinished, type AuthMethodDecisionStarted, type AuthMethodType, type BaseEvent, CEREMONY_LOW_EVENT_TAIL_MS, CONFIG_BOUNDS, CONFIG_REQUEST_HEADER, CONFIG_STORAGE_KEY, type CaptchaOperationConfig, CaptchaOperationFull, type CaptchaOperationSpecType, type ClientCapabilities, type ClientEnvHandleMeta, type ClientEnvHandleMetaSource, type Conversion, CookieStorage, CorbadoTracker, type CreateLoggerOptions, type CustomEvent, DEFAULT_DEVICE_INFO_COLLECTOR_TIMEOUT_MS, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_ENTRIES, DEFAULT_MAX_SERIALIZED_LENGTH, DEFAULT_RELIABILITY_CONFIG, DEFAULT_SEQ_LOCK_TIMEOUT_MS, DEFAULT_SESSION_INACTIVITY_MS, type DeviceInfo, type DeviceInfoCollectionError, type DeviceInfoDataWeb, type DeviceType, EmailLinkOperationFull, type EmailLinkOperationPostResponseStart, type EmailLinkOperationSendStart, type EmailLinkOperationSpecType, type EmailOTPOperationConfig, 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 FlushReason, type JavaScriptHighEntropy, LocalStorage, type Logger, type LowEvent, type MultiFlowReset, type MultiFlowStarted, type NormalizedError, OUTBOX_LOCK_NAME, OUTBOX_STORAGE_KEY, OperationFull, OperationFullProvideIdentifierWithCUI, type OperationFullProvideIdentifierWithCUIConfig, type OutboxEntry, type PasskeyEnrollmentCeremonyFinished, type PasskeyEnrollmentCeremonyStart, type PasskeyEnrollmentGetOptionsFinished, type PasskeyEnrollmentGetOptionsStart, type PasskeyEnrollmentOperationConfig, PasskeyEnrollmentOperationFull, type PasskeyEnrollmentPostResponseStart, type PasskeyLoginCUIGetOptionsFinished, type PasskeyLoginCUIGetOptionsStart, type PasskeyLoginCUISpecType, type PasskeyLoginCeremonyFinished, type PasskeyLoginCeremonyStart, type PasskeyLoginClientError, type PasskeyLoginFinish, type PasskeyLoginGetOptionsFinished, type PasskeyLoginGetOptionsStart, PasskeyLoginOperationFull, type PasskeyLoginPostResponseStart, type PasskeyLoginStartable, type PasskeyLoginSubmitted, type PasskeyOperationEnrollmentExplicitSpecType, type PasskeyOperationLoginExplicitSpecType, type PasswordEnrollmentAutoTrackConfig, PasswordEnrollmentOperationFull, type PasswordEnrollmentTypedError, type PasswordLoginAutoTrackConfig, type PasswordLoginCUICeremonyStart, type PasswordLoginCUIGetOptionsFinished, type PasswordLoginCUIGetOptionsStart, type PasswordLoginCUISpecType, type PasswordLoginCUITypedError, PasswordLoginOperationFull, type PasswordLoginTypedError, type PredefinedEvent, type ProvideDataOperationConfig, ProvideDataOperationFull, type ProvideDataOperationPostResponseStart, type ProvideDataOperationSpecType, type ProvideDataOperationStart, 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 SmsOTPOperationConfig, type SmsOTPOperationPostResponseStart, type SmsOTPOperationSpecType, SmsOtpOperationFull, type SocialLoginExchangeCodeFinished, type SocialLoginExchangeCodeStart, type SocialLoginGetRedirectUrlFinished, type SocialLoginGetRedirectUrlStart, SocialLoginOperationFull, type SocialLoginProviderType, type SocialLoginSpecType, type StepHelper, type StepOptions, type StorageEngine, type SubflowTrigger, type SubflowType, type TelemetryEntry, type TelemetryLevel, type TelemetrySink, type TrackerOptions, type Transport, type TransportMakeRequestResponse, type TransportOptions, type UserReference, cacheConfig, clearExperiment, clearExperiments, createLogger, destroy, getExperiments, getTracker, init, loadCachedConfig, logError, logInfo, parseReliabilityConfig, resetSession, setExperiment, setExperiments, telemetry };