@wireai/activation 0.11.0 → 0.12.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.
Files changed (58) hide show
  1. package/AGENTS.md +51 -0
  2. package/CHANGELOG.md +58 -3
  3. package/INTEGRATION_PROMPT.md +13 -1
  4. package/README.md +73 -0
  5. package/dist/analytics/index.d.mts +17 -6
  6. package/dist/analytics/index.d.ts +17 -6
  7. package/dist/analytics/index.js +130 -35
  8. package/dist/analytics/index.js.map +1 -1
  9. package/dist/analytics/index.mjs +126 -36
  10. package/dist/analytics/index.mjs.map +1 -1
  11. package/dist/{currentSession-DdnUq2HQ.d.ts → currentSession-D6RiVtc8.d.ts} +88 -30
  12. package/dist/{currentSession-C0_odnIW.d.mts → currentSession-DsSDHqor.d.mts} +88 -30
  13. package/dist/index.d.mts +236 -36
  14. package/dist/index.d.ts +236 -36
  15. package/dist/index.js +281 -20
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +269 -21
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/questionnaire/index.d.mts +1 -1
  20. package/dist/questionnaire/index.d.ts +1 -1
  21. package/dist/questionnaire/index.js +49 -6
  22. package/dist/questionnaire/index.js.map +1 -1
  23. package/dist/questionnaire/index.mjs +49 -6
  24. package/dist/questionnaire/index.mjs.map +1 -1
  25. package/dist/reviews/index.d.mts +2 -2
  26. package/dist/reviews/index.d.ts +2 -2
  27. package/dist/reviews/index.js +73 -8
  28. package/dist/reviews/index.js.map +1 -1
  29. package/dist/reviews/index.mjs +73 -8
  30. package/dist/reviews/index.mjs.map +1 -1
  31. package/dist/{transport-BGW9uXZJ.d.mts → transport-CF_eHwzC.d.mts} +15 -1
  32. package/dist/{transport-jUJd5kxu.d.ts → transport-DsRe4epC.d.ts} +15 -1
  33. package/llms.txt +1 -0
  34. package/package.json +1 -1
  35. package/src/activation/useWireActivation.ts +12 -1
  36. package/src/activation/wireActivation.ts +36 -24
  37. package/src/analytics/analyticsFacade.ts +41 -20
  38. package/src/analytics/currentSession.ts +83 -0
  39. package/src/analytics/eventQueue.ts +9 -1
  40. package/src/analytics/index.ts +20 -1
  41. package/src/analytics/reportClientEvent.ts +38 -0
  42. package/src/analytics/screenTracking.ts +6 -1
  43. package/src/analytics/useAnalytics.ts +22 -1
  44. package/src/context/deviceId.ts +109 -0
  45. package/src/context/userContext.ts +18 -0
  46. package/src/index.ts +45 -1
  47. package/src/questionnaire/runtime.ts +12 -2
  48. package/src/questionnaire/useQuestionnaireGate.ts +9 -7
  49. package/src/revenuecat/index.ts +55 -0
  50. package/src/revenuecat/purchaseEvents.ts +167 -0
  51. package/src/revenuecat/revenueCatBridge.ts +221 -0
  52. package/src/revenuecat/types.ts +95 -0
  53. package/src/reviews/runtime.ts +92 -1
  54. package/src/reviews/transport.ts +21 -2
  55. package/src/reviews/useReviewGate.ts +12 -7
  56. package/src/session-analytics/lifecycle.ts +9 -2
  57. package/src/session-analytics/reportSessionStart.ts +15 -4
  58. package/src/session-analytics/useLifecycleEvents.ts +28 -2
@@ -722,6 +722,21 @@ interface ClearUserContextOptions {
722
722
  * person, and stays stable across users of the same install.
723
723
  */
724
724
  declare const clearUserContext: (opts?: ClearUserContextOptions) => Promise<void>;
725
+ /**
726
+ * The `userContext` value to hand `<WireOnboarding userContext={...} />` so an onboarding session
727
+ * and the app's later events (purchases, actions, screens) share ONE join key.
728
+ *
729
+ * WHY it exists as a named function instead of an inline object literal: the wire key is
730
+ * `device_key`, the prop-facing name is `deviceKey`, and the analytics surfaces auto-mint the value
731
+ * for you. A host that hand-writes `userContext={{ deviceKey }}` produces a bucket the server's
732
+ * device lookup does not read, and the resulting funnel is silently EMPTY rather than wrong. This is
733
+ * the one place that spelling is decided.
734
+ *
735
+ * Pass the SAME `deviceKey` you gave `createAnalytics` / `createWireActivation`. `session_id` is not
736
+ * a join key across those two families: an onboarding session id is the A2A `contextId` and an
737
+ * app-event session id is the per-open id, so intersecting them returns nothing.
738
+ */
739
+ declare const activationJoinContext: (deviceKey: string) => Record<string, string | number | boolean>;
725
740
  /** Options for {@link resolveUserContext}. */
726
741
  interface ResolveUserContextOptions {
727
742
  /**
@@ -792,40 +807,65 @@ type IdentifyOnboardingOptions = {
792
807
  declare const identifyOnboarding: (opts: IdentifyOnboardingOptions) => Promise<boolean>;
793
808
 
794
809
  /**
795
- * currentSession — a tiny registry of the CURRENT per-open `session_id`.
796
- *
797
- * WHY it exists (kills the phantom-session): the per-open emitters (`reportSessionStart` and the
798
- * `useSessionStart` / `useLifecycleEvents` hooks) mint a fresh `session_id` for each app-open and
799
- * post `app.session_started` with it so the SERVER knows that id. But other client paths
800
- * (`identify`, host `app_event`s through the analytics façade) used to reference a DIFFERENT id
801
- * (a frozen per-instance id), which the server had never seen, so it back-filled a synthetic
802
- * `session_started` — inflating session counts (the "phantom-session" bug).
810
+ * deviceIdmint a stable, NON-PII, per-install device id the kit owns when the host supplies
811
+ * none. This is the headline of "device fully automatic": the analytics façade auto-mints ONE id,
812
+ * persists it via the host's `storage` abstraction, and reuses it on every subsequent open — so
813
+ * `user_context.device_key` is ALWAYS present and the server's review/questionnaire gating +
814
+ * A/B stickiness (both key on `device_key`) work out of the box, with zero host wiring.
803
815
  *
804
- * This registry is the single seam that lets those paths reuse the LIVE per-open session id the
805
- * server already ingested. `reportSessionStart` writes the current id here on every open; the façade
806
- * reads it so `identify`/app-events correlate to the real session instead of minting a phantom.
816
+ * WHY it is NOT PII and adds NO dependency (the kit's hard rules):
817
+ * The id is a random token generated from `Date.now()` + `Math.random()` it carries NO hardware
818
+ * identifier, NO IDFA/GAID, NO fingerprint. It is a first-party per-install correlation key, the
819
+ * same privacy category as a first-party cookie: it groups a single install's sessions and cannot
820
+ * identify a person or be joined across apps. There is NO `uuid` (or any) dependency — a
821
+ * time+random scheme is sufficient because the id is minted ONCE and then persisted, so global
822
+ * uniqueness across the fleet is not required (a per-install collision is astronomically unlikely
823
+ * and inconsequential — worst case two installs share a bucket).
807
824
  *
808
- * ── WHY A globalThis SLOT, NOT A PLAIN MODULE VARIABLE ────────────────────────────────────────
809
- * This module is exported from TWO package entry points — the main `.` bundle (`src/index.ts`) and
810
- * the `./analytics` subpath (`src/analytics/index.ts`). Under `dist` resolution (node `import`/
811
- * `require`, which is how tests, SSR and some tooling load the kit) tsup inlines a SEPARATE copy of
812
- * this module into each bundle, so a plain `let` would give the SETTER (reached via `.` →
813
- * `reportSessionStart`) and the READER (reached via `./analytics` façade / `userIdentity`) TWO
814
- * different variables: the reader would see `undefined` even after an open set the id, and gating
815
- * would fire under a null session id. On-device this was masked only because Metro's `react-native`
816
- * export condition resolves both subpaths back to this one `src/` file (a single instance) — a
817
- * bundler accident, not a guarantee.
825
+ * A host that wants its OWN device id still wins: pass `WireUserContext.deviceKey` and the kit uses
826
+ * that verbatim and never mints/persists an auto id.
827
+ */
828
+ /** Prefix so an auto-minted id is visibly the kit's (distinguishable from a host-supplied `deviceKey`). */
829
+ declare const AUTO_DEVICE_ID_PREFIX = "wdev_";
830
+ /** The storage key the façade persists the auto-minted id under (namespaced per `appId`). */
831
+ declare const deviceIdStorageKey: (appId?: string) => string;
832
+ /**
833
+ * Mint a fresh per-install device id. Dependency-free (`Date.now()` + `Math.random()`), never
834
+ * throws, and returns a NEW value on every call — the façade mints ONCE and persists, so this is
835
+ * called at most once per install (then the persisted value is reused). Two random chunks plus the
836
+ * timestamp keep the token wide enough that a per-install collision is not a practical concern.
837
+ */
838
+ declare const mintDeviceId: () => string;
839
+ /** The persistence subset {@link resolveAutoDeviceKey} needs (a strict subset of `WireOnboardingStorage`). */
840
+ type DeviceKeyStorage = {
841
+ getItem(key: string): Promise<string | null>;
842
+ setItem(key: string, value: string): Promise<void>;
843
+ };
844
+ /** Options for {@link resolveAutoDeviceKey}. Omitting `storage` gives a PROCESS-scoped id, not a
845
+ * per-install one — see the caller notes: a caller with no persistence must decide whether a
846
+ * per-launch id is better or worse than no id for its metric. */
847
+ interface ResolveAutoDeviceKeyOptions {
848
+ /** Tenant/app id — namespaces both the registry entry and the storage slot. */
849
+ appId?: string;
850
+ /** Host persistence. Present → the id survives launches. Absent → process-scoped only. */
851
+ storage?: DeviceKeyStorage;
852
+ }
853
+ /**
854
+ * The ONE auto-minted `device_key` for an install, shared by every kit surface.
818
855
  *
819
- * The bundler-agnostic fix: keep the ONE live value in a well-known `globalThis` slot keyed by a
820
- * `Symbol.for(...)`. `Symbol.for` uses the runtime-global symbol registry, so every inlined copy of
821
- * this module resolves the SAME symbol and reads/writes the SAME slot one identity no matter how
822
- * many times the module is duplicated across bundles. `globalThis` is present and identical in
823
- * Hermes/React Native, Node and SSR (we never touch `window`), so this is safe on every host.
856
+ * SYNCHRONOUS by contract (a fire-and-forget event path cannot await): returns the current live id
857
+ * immediately, minting one on first call. When `storage` is supplied it also kicks off a SINGLE
858
+ * hydration per `appId` that adopts the persisted id (or persists the freshly minted one). Callers
859
+ * should call this per EVENT rather than caching the return value, so an event built after hydration
860
+ * carries the persisted id.
824
861
  *
825
- * PROCESS-LOCAL, NOT PERSISTED: the slot lives on the runtime global, so it tracks the CURRENT
826
- * process's open and a fresh open overwrites it. There is no cross-launch state.
827
- * `resetCurrentSessionId` clears the slot so a unit test starts from a clean registry.
862
+ * A host-supplied `deviceKey` always wins callers must short-circuit before reaching this.
863
+ * Never throws: a missing, hung, or rejecting storage adapter degrades to the in-memory id.
828
864
  */
865
+ declare const resolveAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => string;
866
+ /** Test-only: forget every auto id + hydration flag so a unit test starts from a clean registry. */
867
+ declare const resetAutoDeviceKeys: () => void;
868
+
829
869
  /**
830
870
  * Record the current per-open `session_id`. Called by `reportSessionStart` when it emits an
831
871
  * app-open. A blank / non-string id is ignored (the previous id stays current). Idempotent.
@@ -835,5 +875,23 @@ declare const setCurrentSessionId: (id: string | undefined) => void;
835
875
  declare const getCurrentSessionId: () => string | undefined;
836
876
  /** Test-only: forget the current session id so a unit test starts from a clean registry. */
837
877
  declare const resetCurrentSessionId: () => void;
878
+ /**
879
+ * The current per-open `session_id`, MINTING and registering one when no app-open has been
880
+ * registered yet. Always returns a non-empty string. Idempotent (a second call returns the same id)
881
+ * and never throws.
882
+ *
883
+ * Use this on every path that puts a `session_id` on the wire. The server REQUIRES a non-empty
884
+ * `session_id` and drops the event otherwise while still answering 200 (see the module header), so
885
+ * "no id yet" must never mean "send it without one".
886
+ *
887
+ * BACKWARD-COMPATIBLE BY CONSTRUCTION: when `reportSessionStart` / `useLifecycleEvents` has already
888
+ * registered the real per-open id, this is `getCurrentSessionId()` and nothing changes. It only ever
889
+ * mints in the case that used to produce a silently discarded event.
890
+ *
891
+ * A mint means the host never registered an app-open, so the minted id is one the server has not
892
+ * seen a `session_started` for — the events land, but the session is thinner than a real open.
893
+ * Hence the one-time dev warning naming the fix (mount `useLifecycleEvents` at the app root).
894
+ */
895
+ declare const ensureCurrentSessionId: () => string;
838
896
 
839
- export { type AnalyticsEvent as A, type OnboardingEvent as B, type ClearUserContextOptions as C, type OnboardingCopy as D, type EventQueueOptions as E, type DeviceContext as F, type DeviceFormFactor as G, EXTRA_KEY_PREFIX as H, type IdentifyOnboardingOptions as I, type OnboardingProgress as J, type ResolveUserContextOptions as K, type ResolvedUserContext as L, collectDeviceContext as M, hashEmailFnv1a as N, type OnboardingResult as O, identifyOnboarding as P, isWireScalar as Q, RESERVED_USER_CONTEXT_KEYS as R, type StepValidator as S, namespaceExtra as T, USER_ID_MAX_LENGTH as U, resolveUserContext as V, type WireUserContext as W, sanitizeUserId as X, type ClientEvent as a, type ClientEventTarget as b, type ClientEventType as c, type ContextEnvelope as d, type ContextEnvelopeInput as e, type EnvelopeSource as f, type EventQueue as g, WIRE_ONBOARDING_EVENTS as h, type WireOnboardingEventName as i, analyticsUserIdStorageKey as j, buildContextEnvelope as k, clearPiiFromContext as l, clearUserContext as m, createEventQueue as n, getCurrentSessionId as o, looksLikeEmail as p, makeSessionId as q, reportClientEvent as r, reportClientEventAwait as s, reportClientEvents as t, reportClientEventsAwait as u, resetCurrentSessionId as v, setCurrentSessionId as w, toAnalyticsEvent as x, type WireOnboardingProps as y, type WireOnboardingConfig as z };
897
+ export { identifyOnboarding as $, AUTO_DEVICE_ID_PREFIX as A, resolveAutoDeviceKey as B, type ClearUserContextOptions as C, type DeviceKeyStorage as D, type EventQueueOptions as E, setCurrentSessionId as F, toAnalyticsEvent as G, type WireOnboardingProps as H, type WireOnboardingConfig as I, type OnboardingEvent as J, type OnboardingCopy as K, type DeviceContext as L, type DeviceFormFactor as M, EXTRA_KEY_PREFIX as N, type OnboardingResult as O, type IdentifyOnboardingOptions as P, type OnboardingProgress as Q, type ResolveAutoDeviceKeyOptions as R, type StepValidator as S, RESERVED_USER_CONTEXT_KEYS as T, type ResolveUserContextOptions as U, type ResolvedUserContext as V, type WireUserContext as W, USER_ID_MAX_LENGTH as X, activationJoinContext as Y, collectDeviceContext as Z, hashEmailFnv1a as _, type AnalyticsEvent as a, isWireScalar as a0, mintDeviceId as a1, namespaceExtra as a2, resolveUserContext as a3, sanitizeUserId as a4, type ClientEvent as b, type ClientEventTarget as c, type ClientEventType as d, type ContextEnvelope as e, type ContextEnvelopeInput as f, type EnvelopeSource as g, type EventQueue as h, WIRE_ONBOARDING_EVENTS as i, type WireOnboardingEventName as j, analyticsUserIdStorageKey as k, buildContextEnvelope as l, clearPiiFromContext as m, clearUserContext as n, createEventQueue as o, deviceIdStorageKey as p, ensureCurrentSessionId as q, getCurrentSessionId as r, looksLikeEmail as s, makeSessionId as t, reportClientEvent as u, reportClientEventAwait as v, reportClientEvents as w, reportClientEventsAwait as x, resetAutoDeviceKeys as y, resetCurrentSessionId as z };
@@ -722,6 +722,21 @@ interface ClearUserContextOptions {
722
722
  * person, and stays stable across users of the same install.
723
723
  */
724
724
  declare const clearUserContext: (opts?: ClearUserContextOptions) => Promise<void>;
725
+ /**
726
+ * The `userContext` value to hand `<WireOnboarding userContext={...} />` so an onboarding session
727
+ * and the app's later events (purchases, actions, screens) share ONE join key.
728
+ *
729
+ * WHY it exists as a named function instead of an inline object literal: the wire key is
730
+ * `device_key`, the prop-facing name is `deviceKey`, and the analytics surfaces auto-mint the value
731
+ * for you. A host that hand-writes `userContext={{ deviceKey }}` produces a bucket the server's
732
+ * device lookup does not read, and the resulting funnel is silently EMPTY rather than wrong. This is
733
+ * the one place that spelling is decided.
734
+ *
735
+ * Pass the SAME `deviceKey` you gave `createAnalytics` / `createWireActivation`. `session_id` is not
736
+ * a join key across those two families: an onboarding session id is the A2A `contextId` and an
737
+ * app-event session id is the per-open id, so intersecting them returns nothing.
738
+ */
739
+ declare const activationJoinContext: (deviceKey: string) => Record<string, string | number | boolean>;
725
740
  /** Options for {@link resolveUserContext}. */
726
741
  interface ResolveUserContextOptions {
727
742
  /**
@@ -792,40 +807,65 @@ type IdentifyOnboardingOptions = {
792
807
  declare const identifyOnboarding: (opts: IdentifyOnboardingOptions) => Promise<boolean>;
793
808
 
794
809
  /**
795
- * currentSession — a tiny registry of the CURRENT per-open `session_id`.
796
- *
797
- * WHY it exists (kills the phantom-session): the per-open emitters (`reportSessionStart` and the
798
- * `useSessionStart` / `useLifecycleEvents` hooks) mint a fresh `session_id` for each app-open and
799
- * post `app.session_started` with it so the SERVER knows that id. But other client paths
800
- * (`identify`, host `app_event`s through the analytics façade) used to reference a DIFFERENT id
801
- * (a frozen per-instance id), which the server had never seen, so it back-filled a synthetic
802
- * `session_started` — inflating session counts (the "phantom-session" bug).
810
+ * deviceIdmint a stable, NON-PII, per-install device id the kit owns when the host supplies
811
+ * none. This is the headline of "device fully automatic": the analytics façade auto-mints ONE id,
812
+ * persists it via the host's `storage` abstraction, and reuses it on every subsequent open — so
813
+ * `user_context.device_key` is ALWAYS present and the server's review/questionnaire gating +
814
+ * A/B stickiness (both key on `device_key`) work out of the box, with zero host wiring.
803
815
  *
804
- * This registry is the single seam that lets those paths reuse the LIVE per-open session id the
805
- * server already ingested. `reportSessionStart` writes the current id here on every open; the façade
806
- * reads it so `identify`/app-events correlate to the real session instead of minting a phantom.
816
+ * WHY it is NOT PII and adds NO dependency (the kit's hard rules):
817
+ * The id is a random token generated from `Date.now()` + `Math.random()` it carries NO hardware
818
+ * identifier, NO IDFA/GAID, NO fingerprint. It is a first-party per-install correlation key, the
819
+ * same privacy category as a first-party cookie: it groups a single install's sessions and cannot
820
+ * identify a person or be joined across apps. There is NO `uuid` (or any) dependency — a
821
+ * time+random scheme is sufficient because the id is minted ONCE and then persisted, so global
822
+ * uniqueness across the fleet is not required (a per-install collision is astronomically unlikely
823
+ * and inconsequential — worst case two installs share a bucket).
807
824
  *
808
- * ── WHY A globalThis SLOT, NOT A PLAIN MODULE VARIABLE ────────────────────────────────────────
809
- * This module is exported from TWO package entry points — the main `.` bundle (`src/index.ts`) and
810
- * the `./analytics` subpath (`src/analytics/index.ts`). Under `dist` resolution (node `import`/
811
- * `require`, which is how tests, SSR and some tooling load the kit) tsup inlines a SEPARATE copy of
812
- * this module into each bundle, so a plain `let` would give the SETTER (reached via `.` →
813
- * `reportSessionStart`) and the READER (reached via `./analytics` façade / `userIdentity`) TWO
814
- * different variables: the reader would see `undefined` even after an open set the id, and gating
815
- * would fire under a null session id. On-device this was masked only because Metro's `react-native`
816
- * export condition resolves both subpaths back to this one `src/` file (a single instance) — a
817
- * bundler accident, not a guarantee.
825
+ * A host that wants its OWN device id still wins: pass `WireUserContext.deviceKey` and the kit uses
826
+ * that verbatim and never mints/persists an auto id.
827
+ */
828
+ /** Prefix so an auto-minted id is visibly the kit's (distinguishable from a host-supplied `deviceKey`). */
829
+ declare const AUTO_DEVICE_ID_PREFIX = "wdev_";
830
+ /** The storage key the façade persists the auto-minted id under (namespaced per `appId`). */
831
+ declare const deviceIdStorageKey: (appId?: string) => string;
832
+ /**
833
+ * Mint a fresh per-install device id. Dependency-free (`Date.now()` + `Math.random()`), never
834
+ * throws, and returns a NEW value on every call — the façade mints ONCE and persists, so this is
835
+ * called at most once per install (then the persisted value is reused). Two random chunks plus the
836
+ * timestamp keep the token wide enough that a per-install collision is not a practical concern.
837
+ */
838
+ declare const mintDeviceId: () => string;
839
+ /** The persistence subset {@link resolveAutoDeviceKey} needs (a strict subset of `WireOnboardingStorage`). */
840
+ type DeviceKeyStorage = {
841
+ getItem(key: string): Promise<string | null>;
842
+ setItem(key: string, value: string): Promise<void>;
843
+ };
844
+ /** Options for {@link resolveAutoDeviceKey}. Omitting `storage` gives a PROCESS-scoped id, not a
845
+ * per-install one — see the caller notes: a caller with no persistence must decide whether a
846
+ * per-launch id is better or worse than no id for its metric. */
847
+ interface ResolveAutoDeviceKeyOptions {
848
+ /** Tenant/app id — namespaces both the registry entry and the storage slot. */
849
+ appId?: string;
850
+ /** Host persistence. Present → the id survives launches. Absent → process-scoped only. */
851
+ storage?: DeviceKeyStorage;
852
+ }
853
+ /**
854
+ * The ONE auto-minted `device_key` for an install, shared by every kit surface.
818
855
  *
819
- * The bundler-agnostic fix: keep the ONE live value in a well-known `globalThis` slot keyed by a
820
- * `Symbol.for(...)`. `Symbol.for` uses the runtime-global symbol registry, so every inlined copy of
821
- * this module resolves the SAME symbol and reads/writes the SAME slot one identity no matter how
822
- * many times the module is duplicated across bundles. `globalThis` is present and identical in
823
- * Hermes/React Native, Node and SSR (we never touch `window`), so this is safe on every host.
856
+ * SYNCHRONOUS by contract (a fire-and-forget event path cannot await): returns the current live id
857
+ * immediately, minting one on first call. When `storage` is supplied it also kicks off a SINGLE
858
+ * hydration per `appId` that adopts the persisted id (or persists the freshly minted one). Callers
859
+ * should call this per EVENT rather than caching the return value, so an event built after hydration
860
+ * carries the persisted id.
824
861
  *
825
- * PROCESS-LOCAL, NOT PERSISTED: the slot lives on the runtime global, so it tracks the CURRENT
826
- * process's open and a fresh open overwrites it. There is no cross-launch state.
827
- * `resetCurrentSessionId` clears the slot so a unit test starts from a clean registry.
862
+ * A host-supplied `deviceKey` always wins callers must short-circuit before reaching this.
863
+ * Never throws: a missing, hung, or rejecting storage adapter degrades to the in-memory id.
828
864
  */
865
+ declare const resolveAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => string;
866
+ /** Test-only: forget every auto id + hydration flag so a unit test starts from a clean registry. */
867
+ declare const resetAutoDeviceKeys: () => void;
868
+
829
869
  /**
830
870
  * Record the current per-open `session_id`. Called by `reportSessionStart` when it emits an
831
871
  * app-open. A blank / non-string id is ignored (the previous id stays current). Idempotent.
@@ -835,5 +875,23 @@ declare const setCurrentSessionId: (id: string | undefined) => void;
835
875
  declare const getCurrentSessionId: () => string | undefined;
836
876
  /** Test-only: forget the current session id so a unit test starts from a clean registry. */
837
877
  declare const resetCurrentSessionId: () => void;
878
+ /**
879
+ * The current per-open `session_id`, MINTING and registering one when no app-open has been
880
+ * registered yet. Always returns a non-empty string. Idempotent (a second call returns the same id)
881
+ * and never throws.
882
+ *
883
+ * Use this on every path that puts a `session_id` on the wire. The server REQUIRES a non-empty
884
+ * `session_id` and drops the event otherwise while still answering 200 (see the module header), so
885
+ * "no id yet" must never mean "send it without one".
886
+ *
887
+ * BACKWARD-COMPATIBLE BY CONSTRUCTION: when `reportSessionStart` / `useLifecycleEvents` has already
888
+ * registered the real per-open id, this is `getCurrentSessionId()` and nothing changes. It only ever
889
+ * mints in the case that used to produce a silently discarded event.
890
+ *
891
+ * A mint means the host never registered an app-open, so the minted id is one the server has not
892
+ * seen a `session_started` for — the events land, but the session is thinner than a real open.
893
+ * Hence the one-time dev warning naming the fix (mount `useLifecycleEvents` at the app root).
894
+ */
895
+ declare const ensureCurrentSessionId: () => string;
838
896
 
839
- export { type AnalyticsEvent as A, type OnboardingEvent as B, type ClearUserContextOptions as C, type OnboardingCopy as D, type EventQueueOptions as E, type DeviceContext as F, type DeviceFormFactor as G, EXTRA_KEY_PREFIX as H, type IdentifyOnboardingOptions as I, type OnboardingProgress as J, type ResolveUserContextOptions as K, type ResolvedUserContext as L, collectDeviceContext as M, hashEmailFnv1a as N, type OnboardingResult as O, identifyOnboarding as P, isWireScalar as Q, RESERVED_USER_CONTEXT_KEYS as R, type StepValidator as S, namespaceExtra as T, USER_ID_MAX_LENGTH as U, resolveUserContext as V, type WireUserContext as W, sanitizeUserId as X, type ClientEvent as a, type ClientEventTarget as b, type ClientEventType as c, type ContextEnvelope as d, type ContextEnvelopeInput as e, type EnvelopeSource as f, type EventQueue as g, WIRE_ONBOARDING_EVENTS as h, type WireOnboardingEventName as i, analyticsUserIdStorageKey as j, buildContextEnvelope as k, clearPiiFromContext as l, clearUserContext as m, createEventQueue as n, getCurrentSessionId as o, looksLikeEmail as p, makeSessionId as q, reportClientEvent as r, reportClientEventAwait as s, reportClientEvents as t, reportClientEventsAwait as u, resetCurrentSessionId as v, setCurrentSessionId as w, toAnalyticsEvent as x, type WireOnboardingProps as y, type WireOnboardingConfig as z };
897
+ export { identifyOnboarding as $, AUTO_DEVICE_ID_PREFIX as A, resolveAutoDeviceKey as B, type ClearUserContextOptions as C, type DeviceKeyStorage as D, type EventQueueOptions as E, setCurrentSessionId as F, toAnalyticsEvent as G, type WireOnboardingProps as H, type WireOnboardingConfig as I, type OnboardingEvent as J, type OnboardingCopy as K, type DeviceContext as L, type DeviceFormFactor as M, EXTRA_KEY_PREFIX as N, type OnboardingResult as O, type IdentifyOnboardingOptions as P, type OnboardingProgress as Q, type ResolveAutoDeviceKeyOptions as R, type StepValidator as S, RESERVED_USER_CONTEXT_KEYS as T, type ResolveUserContextOptions as U, type ResolvedUserContext as V, type WireUserContext as W, USER_ID_MAX_LENGTH as X, activationJoinContext as Y, collectDeviceContext as Z, hashEmailFnv1a as _, type AnalyticsEvent as a, isWireScalar as a0, mintDeviceId as a1, namespaceExtra as a2, resolveUserContext as a3, sanitizeUserId as a4, type ClientEvent as b, type ClientEventTarget as c, type ClientEventType as d, type ContextEnvelope as e, type ContextEnvelopeInput as f, type EnvelopeSource as g, type EventQueue as h, WIRE_ONBOARDING_EVENTS as i, type WireOnboardingEventName as j, analyticsUserIdStorageKey as k, buildContextEnvelope as l, clearPiiFromContext as m, clearUserContext as n, createEventQueue as o, deviceIdStorageKey as p, ensureCurrentSessionId as q, getCurrentSessionId as r, looksLikeEmail as s, makeSessionId as t, reportClientEvent as u, reportClientEventAwait as v, reportClientEvents as w, reportClientEventsAwait as x, resetAutoDeviceKeys as y, resetCurrentSessionId as z };