@wireai/activation 0.11.0 → 0.12.1
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/AGENTS.md +62 -8
- package/CHANGELOG.md +188 -3
- package/INTEGRATION_PROMPT.md +25 -2
- package/README.md +112 -1
- package/dist/analytics/index.d.mts +18 -6
- package/dist/analytics/index.d.ts +18 -6
- package/dist/analytics/index.js +151 -41
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +147 -42
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/coachmarks/index.d.mts +16 -0
- package/dist/coachmarks/index.d.ts +16 -0
- package/dist/coachmarks/index.js +19 -13
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs +19 -13
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/{currentSession-C0_odnIW.d.mts → currentSession-BlCeDP0f.d.mts} +145 -33
- package/dist/{currentSession-DdnUq2HQ.d.ts → currentSession-BxEB37xt.d.ts} +145 -33
- package/dist/index.d.mts +243 -53
- package/dist/index.d.ts +243 -53
- package/dist/index.js +531 -156
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +517 -157
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.d.mts +1 -1
- package/dist/questionnaire/index.d.ts +1 -1
- package/dist/questionnaire/index.js +79 -14
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +79 -14
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.d.mts +2 -2
- package/dist/reviews/index.d.ts +2 -2
- package/dist/reviews/index.js +103 -16
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +103 -16
- package/dist/reviews/index.mjs.map +1 -1
- package/dist/showcase/index.js +15 -6
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/index.mjs +15 -6
- package/dist/showcase/index.mjs.map +1 -1
- package/dist/{transport-BGW9uXZJ.d.mts → transport-CF_eHwzC.d.mts} +15 -1
- package/dist/{transport-jUJd5kxu.d.ts → transport-DsRe4epC.d.ts} +15 -1
- package/llms.txt +3 -0
- package/package.json +1 -1
- package/src/WireOnboarding.tsx +140 -5
- package/src/activation/useWireActivation.ts +12 -1
- package/src/activation/wireActivation.ts +44 -25
- package/src/analytics/analyticsFacade.ts +54 -29
- package/src/analytics/currentSession.ts +83 -0
- package/src/analytics/eventQueue.ts +9 -1
- package/src/analytics/index.ts +20 -1
- package/src/analytics/reportClientEvent.ts +42 -0
- package/src/analytics/screenTracking.ts +6 -1
- package/src/analytics/useAnalytics.ts +22 -1
- package/src/coachmarks/runtime.ts +53 -17
- package/src/config/wireConfigFromEnv.ts +46 -2
- package/src/context/deviceId.ts +173 -0
- package/src/context/userContext.ts +18 -0
- package/src/index.ts +54 -2
- package/src/questionnaire/runtime.ts +13 -2
- package/src/questionnaire/useQuestionnaireGate.ts +11 -4
- package/src/revenuecat/index.ts +55 -0
- package/src/revenuecat/purchaseEvents.ts +167 -0
- package/src/revenuecat/revenueCatBridge.ts +221 -0
- package/src/revenuecat/types.ts +95 -0
- package/src/reviews/runtime.ts +153 -1
- package/src/reviews/transport.ts +21 -2
- package/src/reviews/useReviewGate.ts +14 -4
- package/src/session-analytics/lifecycle.ts +9 -2
- package/src/session-analytics/reportSessionStart.ts +15 -4
- package/src/session-analytics/useLifecycleEvents.ts +83 -27
- package/src/session-analytics/useSessionStart.ts +37 -1
- package/src/types.ts +41 -4
|
@@ -349,16 +349,53 @@ type WireOnboardingProps = {
|
|
|
349
349
|
/** Lifecycle hook for host-side analytics (started / per-turn / error). */
|
|
350
350
|
onEvent?: (event: OnboardingEvent) => void;
|
|
351
351
|
/**
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
352
|
+
* **THIS IS WHERE THE JOIN KEY GOES.** `user_context.device_key` is the ONLY thing that joins an
|
|
353
|
+
* onboarding session to everything the app reports later (analytics, purchases, gate decisions).
|
|
354
|
+
* Build the value with the helper so the wire spelling is decided in one place:
|
|
355
|
+
*
|
|
356
|
+
* ```tsx
|
|
357
|
+
* <WireOnboarding userContext={activationJoinContext(deviceKey)} ... />
|
|
358
|
+
* // no device id of your own? read the kit's:
|
|
359
|
+
* <WireOnboarding userContext={activationJoinContext(resolveAutoDeviceKey({ appId, storage }))} ... />
|
|
360
|
+
* ```
|
|
361
|
+
*
|
|
362
|
+
* SINCE 0.12.2, leaving it out no longer silently empties the funnel: when you pass `storage` and
|
|
363
|
+
* this prop carries no `device_key`, the kit injects its OWN per-install key — the same one the
|
|
364
|
+
* analytics surfaces mint and persist — so the default wiring joins. Anything you DO pass wins
|
|
365
|
+
* verbatim and is never touched. See `autoJoinKey` for the opt-out and the two cases where the kit
|
|
366
|
+
* still cannot fill the gap (no `storage`, or you opted out), which keep warning in dev.
|
|
367
|
+
*
|
|
368
|
+
* Never hand-write `userContext={{ deviceKey }}`: the server's device lookup reads `device_key`,
|
|
369
|
+
* so a misspelled bucket produces a silently empty funnel rather than an error. And never join on
|
|
370
|
+
* `session_id` — an onboarding session id is the A2A `contextId` while an app-event session id is
|
|
371
|
+
* the per-open id, so intersecting those two id spaces returns zero rows every time.
|
|
372
|
+
*
|
|
373
|
+
* SECOND JOB, segmentation: anything else non-PII the app already knows — signup method, referral
|
|
374
|
+
* source, plan tier, a HASHED user id. Same host-injection philosophy as `storage`: the kit
|
|
375
|
+
* collects nothing here; the host passes what it wants. Forwarded to the backend on the session
|
|
376
|
+
* metadata AND on client events so analytics can segment the funnel.
|
|
356
377
|
*
|
|
357
378
|
* MUST NOT contain PII such as raw emails, names, or phone numbers — pass a hash if you need
|
|
358
379
|
* a user key. Values are limited to primitives (`string | number | boolean`); the server caps
|
|
359
380
|
* key count / size and drops deep nesting. Old servers ignore it (backward compatible).
|
|
360
381
|
*/
|
|
361
382
|
userContext?: Record<string, string | number | boolean>;
|
|
383
|
+
/**
|
|
384
|
+
* OPT OUT of the automatic join key. Default `true`.
|
|
385
|
+
*
|
|
386
|
+
* By default (0.12.2+), an onboarding session that was given no `userContext.device_key` gets the
|
|
387
|
+
* kit's own per-install key injected — the SAME id `createAnalytics` / `createWireActivation` mint
|
|
388
|
+
* and persist — so the `activated` funnel joins without the host wiring anything. Pass
|
|
389
|
+
* `autoJoinKey={false}` if you genuinely want an UNLINKED onboarding session; that restores the
|
|
390
|
+
* pre-0.12.2 behavior exactly (nothing injected) and the dev warning fires again.
|
|
391
|
+
*
|
|
392
|
+
* Two things this flag does NOT do. It never overrides a `device_key` you passed — a host-supplied
|
|
393
|
+
* key always wins, whatever this is set to. And it cannot conjure a key without `storage`: with no
|
|
394
|
+
* persistence the kit's id is minted fresh every launch, and a per-launch key corrupts
|
|
395
|
+
* `min_sessions` instead of merely leaving the join empty, so the kit declines to inject and warns
|
|
396
|
+
* in dev instead.
|
|
397
|
+
*/
|
|
398
|
+
autoJoinKey?: boolean;
|
|
362
399
|
/**
|
|
363
400
|
* The host's own user id, so onboarding sessions can be reconciled to real users later
|
|
364
401
|
* (console sessions ↔ your user table / GA4 users). First-class alongside `userContext`.
|
|
@@ -722,6 +759,21 @@ interface ClearUserContextOptions {
|
|
|
722
759
|
* person, and stays stable across users of the same install.
|
|
723
760
|
*/
|
|
724
761
|
declare const clearUserContext: (opts?: ClearUserContextOptions) => Promise<void>;
|
|
762
|
+
/**
|
|
763
|
+
* The `userContext` value to hand `<WireOnboarding userContext={...} />` so an onboarding session
|
|
764
|
+
* and the app's later events (purchases, actions, screens) share ONE join key.
|
|
765
|
+
*
|
|
766
|
+
* WHY it exists as a named function instead of an inline object literal: the wire key is
|
|
767
|
+
* `device_key`, the prop-facing name is `deviceKey`, and the analytics surfaces auto-mint the value
|
|
768
|
+
* for you. A host that hand-writes `userContext={{ deviceKey }}` produces a bucket the server's
|
|
769
|
+
* device lookup does not read, and the resulting funnel is silently EMPTY rather than wrong. This is
|
|
770
|
+
* the one place that spelling is decided.
|
|
771
|
+
*
|
|
772
|
+
* Pass the SAME `deviceKey` you gave `createAnalytics` / `createWireActivation`. `session_id` is not
|
|
773
|
+
* a join key across those two families: an onboarding session id is the A2A `contextId` and an
|
|
774
|
+
* app-event session id is the per-open id, so intersecting them returns nothing.
|
|
775
|
+
*/
|
|
776
|
+
declare const activationJoinContext: (deviceKey: string) => Record<string, string | number | boolean>;
|
|
725
777
|
/** Options for {@link resolveUserContext}. */
|
|
726
778
|
interface ResolveUserContextOptions {
|
|
727
779
|
/**
|
|
@@ -792,40 +844,82 @@ type IdentifyOnboardingOptions = {
|
|
|
792
844
|
declare const identifyOnboarding: (opts: IdentifyOnboardingOptions) => Promise<boolean>;
|
|
793
845
|
|
|
794
846
|
/**
|
|
795
|
-
*
|
|
847
|
+
* deviceId — mint a stable, NON-PII, per-install device id the kit owns when the host supplies
|
|
848
|
+
* none. This is the headline of "device fully automatic": the analytics façade auto-mints ONE id,
|
|
849
|
+
* persists it via the host's `storage` abstraction, and reuses it on every subsequent open — so
|
|
850
|
+
* `user_context.device_key` is ALWAYS present and the server's review/questionnaire gating +
|
|
851
|
+
* A/B stickiness (both key on `device_key`) work out of the box, with zero host wiring.
|
|
852
|
+
*
|
|
853
|
+
* WHY it is NOT PII and adds NO dependency (the kit's hard rules):
|
|
854
|
+
* The id is a random token generated from `Date.now()` + `Math.random()` — it carries NO hardware
|
|
855
|
+
* identifier, NO IDFA/GAID, NO fingerprint. It is a first-party per-install correlation key, the
|
|
856
|
+
* same privacy category as a first-party cookie: it groups a single install's sessions and cannot
|
|
857
|
+
* identify a person or be joined across apps. There is NO `uuid` (or any) dependency — a
|
|
858
|
+
* time+random scheme is sufficient because the id is minted ONCE and then persisted, so global
|
|
859
|
+
* uniqueness across the fleet is not required (a per-install collision is astronomically unlikely
|
|
860
|
+
* and inconsequential — worst case two installs share a bucket).
|
|
796
861
|
*
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
862
|
+
* A host that wants its OWN device id still wins: pass `WireUserContext.deviceKey` and the kit uses
|
|
863
|
+
* that verbatim and never mints/persists an auto id.
|
|
864
|
+
*/
|
|
865
|
+
/** Prefix so an auto-minted id is visibly the kit's (distinguishable from a host-supplied `deviceKey`). */
|
|
866
|
+
declare const AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
867
|
+
/** The storage key the façade persists the auto-minted id under (namespaced per `appId`). */
|
|
868
|
+
declare const deviceIdStorageKey: (appId?: string) => string;
|
|
869
|
+
/**
|
|
870
|
+
* Mint a fresh per-install device id. Dependency-free (`Date.now()` + `Math.random()`), never
|
|
871
|
+
* throws, and returns a NEW value on every call — the façade mints ONCE and persists, so this is
|
|
872
|
+
* called at most once per install (then the persisted value is reused). Two random chunks plus the
|
|
873
|
+
* timestamp keep the token wide enough that a per-install collision is not a practical concern.
|
|
874
|
+
*/
|
|
875
|
+
declare const mintDeviceId: () => string;
|
|
876
|
+
/** The persistence subset {@link resolveAutoDeviceKey} needs (a strict subset of `WireOnboardingStorage`). */
|
|
877
|
+
type DeviceKeyStorage = {
|
|
878
|
+
getItem(key: string): Promise<string | null>;
|
|
879
|
+
setItem(key: string, value: string): Promise<void>;
|
|
880
|
+
};
|
|
881
|
+
/** Options for {@link resolveAutoDeviceKey}. Omitting `storage` gives a PROCESS-scoped id, not a
|
|
882
|
+
* per-install one — see the caller notes: a caller with no persistence must decide whether a
|
|
883
|
+
* per-launch id is better or worse than no id for its metric. */
|
|
884
|
+
interface ResolveAutoDeviceKeyOptions {
|
|
885
|
+
/** Tenant/app id — namespaces both the registry entry and the storage slot. */
|
|
886
|
+
appId?: string;
|
|
887
|
+
/** Host persistence. Present → the id survives launches. Absent → process-scoped only. */
|
|
888
|
+
storage?: DeviceKeyStorage;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* The ONE auto-minted `device_key` for an install, shared by every kit surface.
|
|
803
892
|
*
|
|
804
|
-
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
893
|
+
* SYNCHRONOUS by contract (a fire-and-forget event path cannot await): returns the current live id
|
|
894
|
+
* immediately, minting one on first call. When `storage` is supplied it also kicks off a SINGLE
|
|
895
|
+
* hydration per `appId` that adopts the persisted id (or persists the freshly minted one). Callers
|
|
896
|
+
* should call this per EVENT rather than caching the return value, so an event built after hydration
|
|
897
|
+
* carries the persisted id.
|
|
807
898
|
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
*
|
|
816
|
-
* export condition resolves both subpaths back to this one `src/` file (a single instance) — a
|
|
817
|
-
* bundler accident, not a guarantee.
|
|
899
|
+
* A host-supplied `deviceKey` always wins — callers must short-circuit before reaching this.
|
|
900
|
+
* Never throws: a missing, hung, or rejecting storage adapter degrades to the in-memory id.
|
|
901
|
+
*/
|
|
902
|
+
declare const resolveAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => string;
|
|
903
|
+
/**
|
|
904
|
+
* The AWAITABLE sibling of {@link resolveAutoDeviceKey}: resolve to the auto `device_key` AFTER the
|
|
905
|
+
* persisted id has been read back (or written, on a first run), so the caller stamps the id this
|
|
906
|
+
* install will keep rather than the one that was minted a millisecond ago.
|
|
818
907
|
*
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
*
|
|
823
|
-
*
|
|
908
|
+
* WHY IT EXISTS: `resolveAutoDeviceKey` is synchronous by contract, so a caller firing at mount got
|
|
909
|
+
* the freshly minted id and the persisted one landed milliseconds later. For most events that is
|
|
910
|
+
* noise. For `app.session_started` it is the whole metric: the server computes `min_sessions` by
|
|
911
|
+
* counting distinct opens grouped by `device_key`, so a per-launch key there makes the counter
|
|
912
|
+
* structurally incapable of exceeding 1, and it splits `first_open` off from every event that
|
|
913
|
+
* follows it. Only a caller that can afford one storage read should use this; the fire-and-forget
|
|
914
|
+
* event paths must stay on the sync function.
|
|
824
915
|
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
* `resetCurrentSessionId` clears the slot so a unit test starts from a clean registry.
|
|
916
|
+
* Never throws or rejects: a missing, hung, or rejecting adapter resolves to the in-memory id, and
|
|
917
|
+
* with no `storage` it resolves immediately (there is nothing to hydrate from).
|
|
828
918
|
*/
|
|
919
|
+
declare const hydrateAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => Promise<string>;
|
|
920
|
+
/** Test-only: forget every auto id + hydration flag so a unit test starts from a clean registry. */
|
|
921
|
+
declare const resetAutoDeviceKeys: () => void;
|
|
922
|
+
|
|
829
923
|
/**
|
|
830
924
|
* Record the current per-open `session_id`. Called by `reportSessionStart` when it emits an
|
|
831
925
|
* app-open. A blank / non-string id is ignored (the previous id stays current). Idempotent.
|
|
@@ -835,5 +929,23 @@ declare const setCurrentSessionId: (id: string | undefined) => void;
|
|
|
835
929
|
declare const getCurrentSessionId: () => string | undefined;
|
|
836
930
|
/** Test-only: forget the current session id so a unit test starts from a clean registry. */
|
|
837
931
|
declare const resetCurrentSessionId: () => void;
|
|
932
|
+
/**
|
|
933
|
+
* The current per-open `session_id`, MINTING and registering one when no app-open has been
|
|
934
|
+
* registered yet. Always returns a non-empty string. Idempotent (a second call returns the same id)
|
|
935
|
+
* and never throws.
|
|
936
|
+
*
|
|
937
|
+
* Use this on every path that puts a `session_id` on the wire. The server REQUIRES a non-empty
|
|
938
|
+
* `session_id` and drops the event otherwise while still answering 200 (see the module header), so
|
|
939
|
+
* "no id yet" must never mean "send it without one".
|
|
940
|
+
*
|
|
941
|
+
* BACKWARD-COMPATIBLE BY CONSTRUCTION: when `reportSessionStart` / `useLifecycleEvents` has already
|
|
942
|
+
* registered the real per-open id, this is `getCurrentSessionId()` and nothing changes. It only ever
|
|
943
|
+
* mints in the case that used to produce a silently discarded event.
|
|
944
|
+
*
|
|
945
|
+
* A mint means the host never registered an app-open, so the minted id is one the server has not
|
|
946
|
+
* seen a `session_started` for — the events land, but the session is thinner than a real open.
|
|
947
|
+
* Hence the one-time dev warning naming the fix (mount `useLifecycleEvents` at the app root).
|
|
948
|
+
*/
|
|
949
|
+
declare const ensureCurrentSessionId: () => string;
|
|
838
950
|
|
|
839
|
-
export {
|
|
951
|
+
export { hydrateAutoDeviceKey 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, identifyOnboarding as a0, isWireScalar as a1, mintDeviceId as a2, namespaceExtra as a3, resolveUserContext as a4, sanitizeUserId as a5, 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 };
|
|
@@ -349,16 +349,53 @@ type WireOnboardingProps = {
|
|
|
349
349
|
/** Lifecycle hook for host-side analytics (started / per-turn / error). */
|
|
350
350
|
onEvent?: (event: OnboardingEvent) => void;
|
|
351
351
|
/**
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
352
|
+
* **THIS IS WHERE THE JOIN KEY GOES.** `user_context.device_key` is the ONLY thing that joins an
|
|
353
|
+
* onboarding session to everything the app reports later (analytics, purchases, gate decisions).
|
|
354
|
+
* Build the value with the helper so the wire spelling is decided in one place:
|
|
355
|
+
*
|
|
356
|
+
* ```tsx
|
|
357
|
+
* <WireOnboarding userContext={activationJoinContext(deviceKey)} ... />
|
|
358
|
+
* // no device id of your own? read the kit's:
|
|
359
|
+
* <WireOnboarding userContext={activationJoinContext(resolveAutoDeviceKey({ appId, storage }))} ... />
|
|
360
|
+
* ```
|
|
361
|
+
*
|
|
362
|
+
* SINCE 0.12.2, leaving it out no longer silently empties the funnel: when you pass `storage` and
|
|
363
|
+
* this prop carries no `device_key`, the kit injects its OWN per-install key — the same one the
|
|
364
|
+
* analytics surfaces mint and persist — so the default wiring joins. Anything you DO pass wins
|
|
365
|
+
* verbatim and is never touched. See `autoJoinKey` for the opt-out and the two cases where the kit
|
|
366
|
+
* still cannot fill the gap (no `storage`, or you opted out), which keep warning in dev.
|
|
367
|
+
*
|
|
368
|
+
* Never hand-write `userContext={{ deviceKey }}`: the server's device lookup reads `device_key`,
|
|
369
|
+
* so a misspelled bucket produces a silently empty funnel rather than an error. And never join on
|
|
370
|
+
* `session_id` — an onboarding session id is the A2A `contextId` while an app-event session id is
|
|
371
|
+
* the per-open id, so intersecting those two id spaces returns zero rows every time.
|
|
372
|
+
*
|
|
373
|
+
* SECOND JOB, segmentation: anything else non-PII the app already knows — signup method, referral
|
|
374
|
+
* source, plan tier, a HASHED user id. Same host-injection philosophy as `storage`: the kit
|
|
375
|
+
* collects nothing here; the host passes what it wants. Forwarded to the backend on the session
|
|
376
|
+
* metadata AND on client events so analytics can segment the funnel.
|
|
356
377
|
*
|
|
357
378
|
* MUST NOT contain PII such as raw emails, names, or phone numbers — pass a hash if you need
|
|
358
379
|
* a user key. Values are limited to primitives (`string | number | boolean`); the server caps
|
|
359
380
|
* key count / size and drops deep nesting. Old servers ignore it (backward compatible).
|
|
360
381
|
*/
|
|
361
382
|
userContext?: Record<string, string | number | boolean>;
|
|
383
|
+
/**
|
|
384
|
+
* OPT OUT of the automatic join key. Default `true`.
|
|
385
|
+
*
|
|
386
|
+
* By default (0.12.2+), an onboarding session that was given no `userContext.device_key` gets the
|
|
387
|
+
* kit's own per-install key injected — the SAME id `createAnalytics` / `createWireActivation` mint
|
|
388
|
+
* and persist — so the `activated` funnel joins without the host wiring anything. Pass
|
|
389
|
+
* `autoJoinKey={false}` if you genuinely want an UNLINKED onboarding session; that restores the
|
|
390
|
+
* pre-0.12.2 behavior exactly (nothing injected) and the dev warning fires again.
|
|
391
|
+
*
|
|
392
|
+
* Two things this flag does NOT do. It never overrides a `device_key` you passed — a host-supplied
|
|
393
|
+
* key always wins, whatever this is set to. And it cannot conjure a key without `storage`: with no
|
|
394
|
+
* persistence the kit's id is minted fresh every launch, and a per-launch key corrupts
|
|
395
|
+
* `min_sessions` instead of merely leaving the join empty, so the kit declines to inject and warns
|
|
396
|
+
* in dev instead.
|
|
397
|
+
*/
|
|
398
|
+
autoJoinKey?: boolean;
|
|
362
399
|
/**
|
|
363
400
|
* The host's own user id, so onboarding sessions can be reconciled to real users later
|
|
364
401
|
* (console sessions ↔ your user table / GA4 users). First-class alongside `userContext`.
|
|
@@ -722,6 +759,21 @@ interface ClearUserContextOptions {
|
|
|
722
759
|
* person, and stays stable across users of the same install.
|
|
723
760
|
*/
|
|
724
761
|
declare const clearUserContext: (opts?: ClearUserContextOptions) => Promise<void>;
|
|
762
|
+
/**
|
|
763
|
+
* The `userContext` value to hand `<WireOnboarding userContext={...} />` so an onboarding session
|
|
764
|
+
* and the app's later events (purchases, actions, screens) share ONE join key.
|
|
765
|
+
*
|
|
766
|
+
* WHY it exists as a named function instead of an inline object literal: the wire key is
|
|
767
|
+
* `device_key`, the prop-facing name is `deviceKey`, and the analytics surfaces auto-mint the value
|
|
768
|
+
* for you. A host that hand-writes `userContext={{ deviceKey }}` produces a bucket the server's
|
|
769
|
+
* device lookup does not read, and the resulting funnel is silently EMPTY rather than wrong. This is
|
|
770
|
+
* the one place that spelling is decided.
|
|
771
|
+
*
|
|
772
|
+
* Pass the SAME `deviceKey` you gave `createAnalytics` / `createWireActivation`. `session_id` is not
|
|
773
|
+
* a join key across those two families: an onboarding session id is the A2A `contextId` and an
|
|
774
|
+
* app-event session id is the per-open id, so intersecting them returns nothing.
|
|
775
|
+
*/
|
|
776
|
+
declare const activationJoinContext: (deviceKey: string) => Record<string, string | number | boolean>;
|
|
725
777
|
/** Options for {@link resolveUserContext}. */
|
|
726
778
|
interface ResolveUserContextOptions {
|
|
727
779
|
/**
|
|
@@ -792,40 +844,82 @@ type IdentifyOnboardingOptions = {
|
|
|
792
844
|
declare const identifyOnboarding: (opts: IdentifyOnboardingOptions) => Promise<boolean>;
|
|
793
845
|
|
|
794
846
|
/**
|
|
795
|
-
*
|
|
847
|
+
* deviceId — mint a stable, NON-PII, per-install device id the kit owns when the host supplies
|
|
848
|
+
* none. This is the headline of "device fully automatic": the analytics façade auto-mints ONE id,
|
|
849
|
+
* persists it via the host's `storage` abstraction, and reuses it on every subsequent open — so
|
|
850
|
+
* `user_context.device_key` is ALWAYS present and the server's review/questionnaire gating +
|
|
851
|
+
* A/B stickiness (both key on `device_key`) work out of the box, with zero host wiring.
|
|
852
|
+
*
|
|
853
|
+
* WHY it is NOT PII and adds NO dependency (the kit's hard rules):
|
|
854
|
+
* The id is a random token generated from `Date.now()` + `Math.random()` — it carries NO hardware
|
|
855
|
+
* identifier, NO IDFA/GAID, NO fingerprint. It is a first-party per-install correlation key, the
|
|
856
|
+
* same privacy category as a first-party cookie: it groups a single install's sessions and cannot
|
|
857
|
+
* identify a person or be joined across apps. There is NO `uuid` (or any) dependency — a
|
|
858
|
+
* time+random scheme is sufficient because the id is minted ONCE and then persisted, so global
|
|
859
|
+
* uniqueness across the fleet is not required (a per-install collision is astronomically unlikely
|
|
860
|
+
* and inconsequential — worst case two installs share a bucket).
|
|
796
861
|
*
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
862
|
+
* A host that wants its OWN device id still wins: pass `WireUserContext.deviceKey` and the kit uses
|
|
863
|
+
* that verbatim and never mints/persists an auto id.
|
|
864
|
+
*/
|
|
865
|
+
/** Prefix so an auto-minted id is visibly the kit's (distinguishable from a host-supplied `deviceKey`). */
|
|
866
|
+
declare const AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
867
|
+
/** The storage key the façade persists the auto-minted id under (namespaced per `appId`). */
|
|
868
|
+
declare const deviceIdStorageKey: (appId?: string) => string;
|
|
869
|
+
/**
|
|
870
|
+
* Mint a fresh per-install device id. Dependency-free (`Date.now()` + `Math.random()`), never
|
|
871
|
+
* throws, and returns a NEW value on every call — the façade mints ONCE and persists, so this is
|
|
872
|
+
* called at most once per install (then the persisted value is reused). Two random chunks plus the
|
|
873
|
+
* timestamp keep the token wide enough that a per-install collision is not a practical concern.
|
|
874
|
+
*/
|
|
875
|
+
declare const mintDeviceId: () => string;
|
|
876
|
+
/** The persistence subset {@link resolveAutoDeviceKey} needs (a strict subset of `WireOnboardingStorage`). */
|
|
877
|
+
type DeviceKeyStorage = {
|
|
878
|
+
getItem(key: string): Promise<string | null>;
|
|
879
|
+
setItem(key: string, value: string): Promise<void>;
|
|
880
|
+
};
|
|
881
|
+
/** Options for {@link resolveAutoDeviceKey}. Omitting `storage` gives a PROCESS-scoped id, not a
|
|
882
|
+
* per-install one — see the caller notes: a caller with no persistence must decide whether a
|
|
883
|
+
* per-launch id is better or worse than no id for its metric. */
|
|
884
|
+
interface ResolveAutoDeviceKeyOptions {
|
|
885
|
+
/** Tenant/app id — namespaces both the registry entry and the storage slot. */
|
|
886
|
+
appId?: string;
|
|
887
|
+
/** Host persistence. Present → the id survives launches. Absent → process-scoped only. */
|
|
888
|
+
storage?: DeviceKeyStorage;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* The ONE auto-minted `device_key` for an install, shared by every kit surface.
|
|
803
892
|
*
|
|
804
|
-
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
893
|
+
* SYNCHRONOUS by contract (a fire-and-forget event path cannot await): returns the current live id
|
|
894
|
+
* immediately, minting one on first call. When `storage` is supplied it also kicks off a SINGLE
|
|
895
|
+
* hydration per `appId` that adopts the persisted id (or persists the freshly minted one). Callers
|
|
896
|
+
* should call this per EVENT rather than caching the return value, so an event built after hydration
|
|
897
|
+
* carries the persisted id.
|
|
807
898
|
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
*
|
|
816
|
-
* export condition resolves both subpaths back to this one `src/` file (a single instance) — a
|
|
817
|
-
* bundler accident, not a guarantee.
|
|
899
|
+
* A host-supplied `deviceKey` always wins — callers must short-circuit before reaching this.
|
|
900
|
+
* Never throws: a missing, hung, or rejecting storage adapter degrades to the in-memory id.
|
|
901
|
+
*/
|
|
902
|
+
declare const resolveAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => string;
|
|
903
|
+
/**
|
|
904
|
+
* The AWAITABLE sibling of {@link resolveAutoDeviceKey}: resolve to the auto `device_key` AFTER the
|
|
905
|
+
* persisted id has been read back (or written, on a first run), so the caller stamps the id this
|
|
906
|
+
* install will keep rather than the one that was minted a millisecond ago.
|
|
818
907
|
*
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
*
|
|
823
|
-
*
|
|
908
|
+
* WHY IT EXISTS: `resolveAutoDeviceKey` is synchronous by contract, so a caller firing at mount got
|
|
909
|
+
* the freshly minted id and the persisted one landed milliseconds later. For most events that is
|
|
910
|
+
* noise. For `app.session_started` it is the whole metric: the server computes `min_sessions` by
|
|
911
|
+
* counting distinct opens grouped by `device_key`, so a per-launch key there makes the counter
|
|
912
|
+
* structurally incapable of exceeding 1, and it splits `first_open` off from every event that
|
|
913
|
+
* follows it. Only a caller that can afford one storage read should use this; the fire-and-forget
|
|
914
|
+
* event paths must stay on the sync function.
|
|
824
915
|
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
* `resetCurrentSessionId` clears the slot so a unit test starts from a clean registry.
|
|
916
|
+
* Never throws or rejects: a missing, hung, or rejecting adapter resolves to the in-memory id, and
|
|
917
|
+
* with no `storage` it resolves immediately (there is nothing to hydrate from).
|
|
828
918
|
*/
|
|
919
|
+
declare const hydrateAutoDeviceKey: (opts?: ResolveAutoDeviceKeyOptions) => Promise<string>;
|
|
920
|
+
/** Test-only: forget every auto id + hydration flag so a unit test starts from a clean registry. */
|
|
921
|
+
declare const resetAutoDeviceKeys: () => void;
|
|
922
|
+
|
|
829
923
|
/**
|
|
830
924
|
* Record the current per-open `session_id`. Called by `reportSessionStart` when it emits an
|
|
831
925
|
* app-open. A blank / non-string id is ignored (the previous id stays current). Idempotent.
|
|
@@ -835,5 +929,23 @@ declare const setCurrentSessionId: (id: string | undefined) => void;
|
|
|
835
929
|
declare const getCurrentSessionId: () => string | undefined;
|
|
836
930
|
/** Test-only: forget the current session id so a unit test starts from a clean registry. */
|
|
837
931
|
declare const resetCurrentSessionId: () => void;
|
|
932
|
+
/**
|
|
933
|
+
* The current per-open `session_id`, MINTING and registering one when no app-open has been
|
|
934
|
+
* registered yet. Always returns a non-empty string. Idempotent (a second call returns the same id)
|
|
935
|
+
* and never throws.
|
|
936
|
+
*
|
|
937
|
+
* Use this on every path that puts a `session_id` on the wire. The server REQUIRES a non-empty
|
|
938
|
+
* `session_id` and drops the event otherwise while still answering 200 (see the module header), so
|
|
939
|
+
* "no id yet" must never mean "send it without one".
|
|
940
|
+
*
|
|
941
|
+
* BACKWARD-COMPATIBLE BY CONSTRUCTION: when `reportSessionStart` / `useLifecycleEvents` has already
|
|
942
|
+
* registered the real per-open id, this is `getCurrentSessionId()` and nothing changes. It only ever
|
|
943
|
+
* mints in the case that used to produce a silently discarded event.
|
|
944
|
+
*
|
|
945
|
+
* A mint means the host never registered an app-open, so the minted id is one the server has not
|
|
946
|
+
* seen a `session_started` for — the events land, but the session is thinner than a real open.
|
|
947
|
+
* Hence the one-time dev warning naming the fix (mount `useLifecycleEvents` at the app root).
|
|
948
|
+
*/
|
|
949
|
+
declare const ensureCurrentSessionId: () => string;
|
|
838
950
|
|
|
839
|
-
export {
|
|
951
|
+
export { hydrateAutoDeviceKey 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, identifyOnboarding as a0, isWireScalar as a1, mintDeviceId as a2, namespaceExtra as a3, resolveUserContext as a4, sanitizeUserId as a5, 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 };
|