@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.
- package/AGENTS.md +51 -0
- package/CHANGELOG.md +58 -3
- package/INTEGRATION_PROMPT.md +13 -1
- package/README.md +73 -0
- package/dist/analytics/index.d.mts +17 -6
- package/dist/analytics/index.d.ts +17 -6
- package/dist/analytics/index.js +130 -35
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +126 -36
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/{currentSession-DdnUq2HQ.d.ts → currentSession-D6RiVtc8.d.ts} +88 -30
- package/dist/{currentSession-C0_odnIW.d.mts → currentSession-DsSDHqor.d.mts} +88 -30
- package/dist/index.d.mts +236 -36
- package/dist/index.d.ts +236 -36
- package/dist/index.js +281 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +269 -21
- 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 +49 -6
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +49 -6
- 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 +73 -8
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +73 -8
- package/dist/reviews/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 +1 -0
- package/package.json +1 -1
- package/src/activation/useWireActivation.ts +12 -1
- package/src/activation/wireActivation.ts +36 -24
- package/src/analytics/analyticsFacade.ts +41 -20
- 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 +38 -0
- package/src/analytics/screenTracking.ts +6 -1
- package/src/analytics/useAnalytics.ts +22 -1
- package/src/context/deviceId.ts +109 -0
- package/src/context/userContext.ts +18 -0
- package/src/index.ts +45 -1
- package/src/questionnaire/runtime.ts +12 -2
- package/src/questionnaire/useQuestionnaireGate.ts +9 -7
- 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 +92 -1
- package/src/reviews/transport.ts +21 -2
- package/src/reviews/useReviewGate.ts +12 -7
- package/src/session-analytics/lifecycle.ts +9 -2
- package/src/session-analytics/reportSessionStart.ts +15 -4
- package/src/session-analytics/useLifecycleEvents.ts +28 -2
package/AGENTS.md
CHANGED
|
@@ -50,6 +50,8 @@ There is no `analytics` subpath: the app-event / analytics API is exported from
|
|
|
50
50
|
- `themeFromBrand({ primary })`: derive a full theme from one brand color.
|
|
51
51
|
- `mergeTheme`, `defaultOnboardingTheme`, `OnboardingThemeProvider`, `useOnboardingTheme`.
|
|
52
52
|
- `defaultIllustrations`: dependency-free fallback art; spread your own over it.
|
|
53
|
+
- `createRevenueCatBridge({ analytics, entitlementId })`: the RevenueCat purchase funnel (see "RevenueCat" below).
|
|
54
|
+
- `activationJoinContext(deviceKey)`: builds the `userContext` value that joins an onboarding session to the app's later events. Read the RevenueCat section before you use it.
|
|
53
55
|
- Types: `OnboardingTheme`, `OnboardingResult`, `OnboardingEvent`, `WireOnboardingConfig`, `WireOnboardingProps`, `StepValidator`, `OnboardingCopy`, `IllustrationRegistry`.
|
|
54
56
|
|
|
55
57
|
## `<WireOnboarding>` props
|
|
@@ -131,6 +133,55 @@ write your own fetch.
|
|
|
131
133
|
`createWireActivation(config)`. This is the kit-owned replacement for hand-rolling
|
|
132
134
|
session-id + await-POST + revalidate.
|
|
133
135
|
|
|
136
|
+
## RevenueCat (the purchase funnel)
|
|
137
|
+
|
|
138
|
+
The app sells subscriptions through `react-native-purchases`? Wire the paywall to the same event
|
|
139
|
+
stream as onboarding. The kit does NOT depend on `react-native-purchases` (it is a native module and
|
|
140
|
+
the kit never forces a rebuild); the adapter types the RevenueCat objects structurally, so you pass
|
|
141
|
+
the real ones you already have.
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import { createAnalytics, createRevenueCatBridge, activationJoinContext } from "@wireai/activation";
|
|
145
|
+
|
|
146
|
+
const analytics = createAnalytics({ serverUrl, apiKey, storage, appId, userContext: { deviceKey } });
|
|
147
|
+
const revenuecat = createRevenueCatBridge({ analytics, entitlementId: "pro" });
|
|
148
|
+
|
|
149
|
+
revenuecat.paywallShown(offering, { source: variant });
|
|
150
|
+
revenuecat.checkoutStarted(pkg, { source: variant });
|
|
151
|
+
const entitled = revenuecat.purchaseCompleted(customerInfo, pkg, { source: variant });
|
|
152
|
+
revenuecat.purchaseFailed(error, pkg, { source: variant });
|
|
153
|
+
revenuecat.purchasesRestored(customerInfo);
|
|
154
|
+
revenuecat.syncPlanTier(customerInfo); // at launch, writes plan_tier with no event
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Canonical names (they are also the `question_key` strings a firing trigger matches on):
|
|
158
|
+
`wire_paywall_shown`, `wire_checkout_started`, `wire_purchase_completed`, `wire_purchase_failed`,
|
|
159
|
+
`wire_purchase_restored`.
|
|
160
|
+
|
|
161
|
+
**The join key is `user_context.device_key`, never `session_id`.** An onboarding session id is the
|
|
162
|
+
A2A `contextId` and an app-event session id is the per-open id; the two live in separate spaces on
|
|
163
|
+
purpose, so intersecting them returns nothing. Put the SAME device key on both sides: the analytics
|
|
164
|
+
and activation surfaces auto-mint and stamp it on every event, and onboarding gets it from
|
|
165
|
+
`<WireOnboarding userContext={activationJoinContext(deviceKey)} ... />`. Never hand-write
|
|
166
|
+
`userContext={{ deviceKey }}`, because the server's device lookup reads `device_key` and a
|
|
167
|
+
misspelled bucket produces an empty funnel instead of an error.
|
|
168
|
+
|
|
169
|
+
**If the host owns no device id, read the kit's.** Do not leave the onboarding side blank.
|
|
170
|
+
`resolveAutoDeviceKey({ appId, storage })` returns the exact id `createAnalytics` /
|
|
171
|
+
`createWireActivation` auto-mint and persist for this install, so both halves of the join agree:
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import { resolveAutoDeviceKey, activationJoinContext } from "@wireai/activation";
|
|
175
|
+
|
|
176
|
+
const deviceKey = resolveAutoDeviceKey({ appId, storage }); // the SAME id the analytics side stamps
|
|
177
|
+
<WireOnboarding config={config} userContext={activationJoinContext(deviceKey)} ... />
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Always pass `storage`. Without it the id is per-LAUNCH, not per-install, and a per-launch key makes
|
|
181
|
+
every open look like a new device, which breaks `min_sessions` and A/B arm stickiness as surely as
|
|
182
|
+
no key breaks the join. The same rule governs the lifecycle wiring: `useLifecycleEvents` falls back
|
|
183
|
+
to this id for `app.session_started` / `app.first_open` ONLY when `config.storage` is present.
|
|
184
|
+
|
|
134
185
|
## Gotchas (do not miss)
|
|
135
186
|
|
|
136
187
|
- **EAS / cloud builds:** install via **git URL or registry**, never a local `file:` path *outside the app repo* (EAS won't resolve it).
|
package/CHANGELOG.md
CHANGED
|
@@ -3,10 +3,65 @@
|
|
|
3
3
|
All notable changes to `@wireai/activation` (formerly `wireai-onboarding`).
|
|
4
4
|
Historical entries below the rename keep the old package name on purpose.
|
|
5
5
|
|
|
6
|
-
## [
|
|
6
|
+
## [0.12.0] — 2026-07-27
|
|
7
|
+
|
|
8
|
+
The release Myelino 2.2.0 pins. Ships the RevenueCat path (#52), the identity/counting audit,
|
|
9
|
+
the session-id fallback contract, and the transport/config fixes behind a silently dead lifecycle
|
|
10
|
+
stream (a consumer's `first_open` read 6 all-time while the emitting code was deployed).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **RevenueCat path (`createRevenueCatBridge`).** The purchase funnel is now a first-class, documented
|
|
15
|
+
path instead of glue every consumer writes itself. New ROOT exports: `createRevenueCatBridge`,
|
|
16
|
+
`WIRE_PURCHASE_EVENTS` (`wire_paywall_shown`, `wire_checkout_started`, `wire_purchase_completed`,
|
|
17
|
+
`wire_purchase_failed`, `wire_purchase_restored`), the pure mappers (`activeEntitlement`,
|
|
18
|
+
`resolvePlanTier`, `describePackage`, `describeEntitlement`, `isUserCancelled`, `describeFailure`),
|
|
19
|
+
`PLAN_TIER_CONTEXT_KEY`, and the structural RevenueCat types. The bridge reports through the
|
|
20
|
+
`createAnalytics` or `createWireActivation` instance the host already holds (a compile-time check in
|
|
21
|
+
`revenueCatBridge.ts` keeps both assignable), writes `plan_tier` (paid / trial / free) to the bound
|
|
22
|
+
user context, drops the store's localized error `message` and keeps only the stable RevenueCat
|
|
23
|
+
`code`, and reports a store-confirmed purchase that granted no entitlement as
|
|
24
|
+
`wire_purchase_failed` with `reason: "not_entitled"` rather than swallowing it. **No new dependency:
|
|
25
|
+
`react-native-purchases` is a native module and is never imported**, so the RevenueCat objects are
|
|
26
|
+
typed structurally and the host passes its real ones.
|
|
27
|
+
- **`activationJoinContext(deviceKey)`.** The `userContext` value that joins an onboarding session to
|
|
28
|
+
the app's later events. The join key is `user_context.device_key` and NEVER `session_id`: an
|
|
29
|
+
onboarding session id is the A2A `contextId`, an app-event session id is the per-open id, and
|
|
30
|
+
intersecting the two id spaces returns zero rows every time. This helper exists so the wire spelling
|
|
31
|
+
(`device_key`) is decided in one place; a hand-written `userContext={{ deviceKey }}` builds a bucket
|
|
32
|
+
the server's device lookup does not read, which produces a silently empty funnel rather than an
|
|
33
|
+
error. `src/revenuecat/joinKey.test.ts` locks both sides against each other.
|
|
34
|
+
- **One shared auto `device_key` per install.** `createAnalytics` and `createWireActivation` each
|
|
35
|
+
minted their OWN id and raced a storage read, so a host building both (the documented wiring)
|
|
36
|
+
stamped two different `device_key`s on one install — splitting the key that `min_sessions`, A/B arm
|
|
37
|
+
stickiness and the purchase-to-onboarding join all read. Now ONE id lives in a `globalThis` slot
|
|
38
|
+
keyed by `Symbol.for(...)` (the `currentSession` pattern). `useLifecycleEvents` now stamps the
|
|
39
|
+
shared key on lifecycle events too (storage-gated, so a per-launch id never corrupts the counter),
|
|
40
|
+
and `resolveAutoDeviceKey` is exported from the root and `./analytics` barrels.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
7
43
|
|
|
8
|
-
|
|
9
|
-
|
|
44
|
+
- **Every event carries a `session_id`, or the server eats it.** `POST /v1/events` validates per
|
|
45
|
+
event inside a try/except that counts the failure as "skipped" and returns HTTP 200 anyway, so an
|
|
46
|
+
event without a session id was accepted by the wire and discarded. `ensureCurrentSessionId()`
|
|
47
|
+
returns the registered per-open id when there is one and otherwise mints one, REGISTERS it so later
|
|
48
|
+
events join the same session, and warns once per process in `__DEV__` (mount `useLifecycleEvents`
|
|
49
|
+
at the app root). Closes the `reportAppEvent(target, "screen", { deviceKey })` hole and
|
|
50
|
+
`wire.track()`'s refusal to send without a registered open.
|
|
51
|
+
- **`config.sessionId` on `createAnalytics` now warns in dev.** Passing it pins EVERY event from the
|
|
52
|
+
instance to that one frozen id and opts out of the live per-open session — the exact footgun that
|
|
53
|
+
flatlined a consumer's lifecycle metrics. The JSDoc and README now state the freeze semantics
|
|
54
|
+
honestly; omitting it is the correct default.
|
|
55
|
+
- **The persistent transport paths read the `/v1/events` ACK body.** A 200 was never a receipt: the
|
|
56
|
+
server reports discarded events only as `skipped` in the body. The event queue and
|
|
57
|
+
`reportSessionStart` now warn in dev with the skipped count. Log-only — retry/dequeue semantics
|
|
58
|
+
unchanged.
|
|
59
|
+
- **`user_context.app_version` falls back to `detectAppVersion()`** when the host passes nothing, so
|
|
60
|
+
version attribution on the facade path no longer depends on the host forwarding its own version.
|
|
61
|
+
|
|
62
|
+
## [0.11.0] — 2026-07-21
|
|
63
|
+
|
|
64
|
+
Architecture-audit fixes (#50/#51). One BEHAVIOR CHANGE, called out below.
|
|
10
65
|
|
|
11
66
|
### Changed (BEHAVIOR) — review gate no longer fires on the first session
|
|
12
67
|
|
package/INTEGRATION_PROMPT.md
CHANGED
|
@@ -53,7 +53,19 @@ STEPS (do them in order, stop and ask if a convention is ambiguous):
|
|
|
53
53
|
`useQuestionnaireGate` the same way), and at the action site call `await track("journal_done")`.
|
|
54
54
|
Do NOT hand-roll the session id or the POST; `wire.track` owns both and revalidates the gate on
|
|
55
55
|
a successful 2xx.
|
|
56
|
-
9.
|
|
56
|
+
9. RevenueCat (only if my app sells subscriptions through `react-native-purchases`): wire my paywall
|
|
57
|
+
to the same event stream with `createRevenueCatBridge({ analytics, entitlementId: "<my
|
|
58
|
+
entitlement, e.g. pro>" })` from `@wireai/activation`. Do NOT add or import
|
|
59
|
+
`react-native-purchases` in the kit path; the adapter types the RevenueCat objects structurally,
|
|
60
|
+
so pass my real `PurchasesOffering` / `PurchasesPackage` / `CustomerInfo` straight in. Replace my
|
|
61
|
+
hand-rolled paywall analytics calls with `paywallShown` / `checkoutStarted` / `purchaseCompleted`
|
|
62
|
+
/ `purchaseFailed` / `purchasesRestored`, and call `syncPlanTier(customerInfo)` once at launch.
|
|
63
|
+
THE JOIN KEY: pass the SAME device key to the analytics instance AND to onboarding via
|
|
64
|
+
`<WireOnboarding userContext={activationJoinContext(deviceKey)} />`, because purchases join to
|
|
65
|
+
onboarding on `user_context.device_key` and never on `session_id` (those are separate id spaces
|
|
66
|
+
and intersecting them returns zero rows). If my app has no single stable device key yet, say so
|
|
67
|
+
instead of inventing one.
|
|
68
|
+
10. Verify: type-check (and lint the changed files); test the flag-off + backend-error paths.
|
|
57
69
|
|
|
58
70
|
Report back: the files you changed (path:line), the typecheck result, and anything you couldn't
|
|
59
71
|
infer about my conventions.
|
package/README.md
CHANGED
|
@@ -370,6 +370,24 @@ await clearUserContext({ storage, appId }); // the wire/activation path (then re
|
|
|
370
370
|
|
|
371
371
|
**3. The auto `device_key` is a per-install identifier.** When you supply no `deviceKey`, the analytics and activation surfaces auto-mint a stable per-install `device_key`, persist it via `storage` (`wireai:analytics:deviceKey:<appId>`), and stamp it on every event so the server's review/questionnaire gating and A/B stickiness work out of the box. It carries no hardware id, no IDFA/GAID, and cannot be joined across apps (the privacy category of a first-party cookie), but it IS a persistent per-install id. If you adopt these surfaces, declare it in your App Privacy / Data Safety accordingly. Supply your own `deviceKey` to override it.
|
|
372
372
|
|
|
373
|
+
### Do not pass `sessionId` to `createAnalytics`
|
|
374
|
+
|
|
375
|
+
`CreateAnalyticsConfig.sessionId` is an opt-out knob, not a default. Set it and **every** event that
|
|
376
|
+
instance sends (including `identify`) is pinned to that one frozen id, and the instance stops
|
|
377
|
+
following the live per-open session the kit registers from `app.session_started` (see
|
|
378
|
+
[Session mapping](#session-mapping-know-when-a-user-opens-the-app-again)). Lifecycle analytics then
|
|
379
|
+
collapse onto a single device-scoped id: one "first open" for the life of the install, however many
|
|
380
|
+
times the user comes back. The kit warns about it in dev builds.
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
const analytics = createAnalytics({ serverUrl, apiKey, storage, appId }); // ✅ follows each app-open
|
|
384
|
+
const analytics = createAnalytics({ serverUrl, apiKey, sessionId: myId }); // ⚠️ frozen for good
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Omit it. Pass one only if your host runs its own session lifecycle and owns the id the server should
|
|
388
|
+
correlate on. This is a different field from the per-open `sessionId` that `useWireActivation` and
|
|
389
|
+
`reportAppEvent` READ (that one is the live id, and reading it is always fine).
|
|
390
|
+
|
|
373
391
|
## Session mapping (know when a user opens the app again)
|
|
374
392
|
|
|
375
393
|
Onboarding runs once, on the first launch. Session mapping is the other half: every time the
|
|
@@ -965,6 +983,61 @@ If you are outside React (a service, a saga), `createWireActivation(config)` is
|
|
|
965
983
|
factory. It returns `{ track, sessionId, subscribeRevalidation, getRevalidationVersion }`: the
|
|
966
984
|
same `track`, plus the raw pub/sub the hook wraps for you.
|
|
967
985
|
|
|
986
|
+
## RevenueCat (the purchase funnel)
|
|
987
|
+
|
|
988
|
+
If you sell subscriptions with [RevenueCat](https://www.revenuecat.com/), the paywall is the other end of the funnel onboarding starts. `createRevenueCatBridge` wires the two together: one constructor, then your existing paywall call sites.
|
|
989
|
+
|
|
990
|
+
The kit does **not** depend on `react-native-purchases`, and it never will. That package is a native module, and the kit's whole promise is that it never puts a native rebuild in your way. So the adapter types the RevenueCat objects structurally instead, which means you hand it the real `CustomerInfo` / `PurchasesPackage` / `PurchasesOffering` you already have. Nothing new gets installed.
|
|
991
|
+
|
|
992
|
+
```tsx
|
|
993
|
+
import { createAnalytics, createRevenueCatBridge } from "@wireai/activation";
|
|
994
|
+
|
|
995
|
+
const analytics = createAnalytics({ serverUrl, apiKey, storage, appId, userContext: { deviceKey } });
|
|
996
|
+
const revenuecat = createRevenueCatBridge({ analytics, entitlementId: "pro" });
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
Then five call sites, all fire-and-forget, none of which can throw at your paywall:
|
|
1000
|
+
|
|
1001
|
+
```tsx
|
|
1002
|
+
revenuecat.paywallShown(offering, { source: variant }); // offering_id + packages_count
|
|
1003
|
+
revenuecat.checkoutStarted(pkg, { source: variant }); // package, product, price, currency
|
|
1004
|
+
|
|
1005
|
+
const { customerInfo } = await Purchases.purchasePackage(pkg);
|
|
1006
|
+
if (revenuecat.purchaseCompleted(customerInfo, pkg, { source: variant })) navigateOn();
|
|
1007
|
+
|
|
1008
|
+
revenuecat.purchaseFailed(error, pkg, { source: variant }); // reason: "cancelled" | "error"
|
|
1009
|
+
revenuecat.purchasesRestored(await Purchases.restorePurchases());
|
|
1010
|
+
revenuecat.syncPlanTier(await Purchases.getCustomerInfo()); // at launch, no event
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
**The names are canonical**, the same way `WIRE_ONBOARDING_EVENTS` standardizes the onboarding funnel: `wire_paywall_shown`, `wire_checkout_started`, `wire_purchase_completed`, `wire_purchase_failed`, `wire_purchase_restored`. They are `app_event` `question_key` values on the wire, so they are also the exact strings a review or questionnaire firing trigger matches on.
|
|
1014
|
+
|
|
1015
|
+
**What the bridge decides for you.** The entitlement check lives in one place instead of being copy-pasted at the purchase site and the restore site. `plan_tier` (`paid` / `trial` / `free`) is written to the bound user context on every entitlement change, so the whole funnel can be sliced paid versus free. The store's error `message` is dropped and only the stable RevenueCat `code` rides the event, because that message is localized, unbounded, and occasionally names the account it failed for. And a purchase the store confirmed that granted **no** entitlement fires `wire_purchase_failed` with `reason: "not_entitled"` rather than returning quietly, which is the usual shape of a broken product-to-entitlement mapping.
|
|
1016
|
+
|
|
1017
|
+
### The join key: `device_key`, never `session_id`
|
|
1018
|
+
|
|
1019
|
+
A purchase event is worth nothing unless you can join it to the same user's onboarding. Get this wrong and you do not get a wrong number, you get a permanent zero, which is far harder to notice.
|
|
1020
|
+
|
|
1021
|
+
`session_id` is not that key. The onboarding session id is the A2A `contextId`, the app-event session id gets minted per app-open, and the kit keeps those two id spaces apart on purpose. Intersect them and you get no rows. Ever.
|
|
1022
|
+
|
|
1023
|
+
The key is **`user_context.device_key`**, and you have to put it on both sides:
|
|
1024
|
+
|
|
1025
|
+
```tsx
|
|
1026
|
+
import { activationJoinContext } from "@wireai/activation";
|
|
1027
|
+
|
|
1028
|
+
// Purchase side: createAnalytics / createWireActivation auto-mint and persist a device_key
|
|
1029
|
+
// and stamp it on every event. Supply your own to override it.
|
|
1030
|
+
const analytics = createAnalytics({ serverUrl, apiKey, storage, appId, userContext: { deviceKey } });
|
|
1031
|
+
|
|
1032
|
+
// Onboarding side: forward the SAME key. The kit passes userContext verbatim into the A2A
|
|
1033
|
+
// session-start metadata, and the server records it on the session's session_started event.
|
|
1034
|
+
<WireOnboarding config={config} userContext={activationJoinContext(deviceKey)} onComplete={persist} />
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
`activationJoinContext(deviceKey)` exists because the wire key is `device_key` and the prop-facing name is `deviceKey`. Hand-writing `userContext={{ deviceKey }}` produces a bucket the server's device lookup does not read, and you get the silent-zero funnel instead of an error. This is the one place that spelling is decided.
|
|
1038
|
+
|
|
1039
|
+
Skip the onboarding half and the purchase events are still valid on their own, they just cannot be attributed back to an onboarding.
|
|
1040
|
+
|
|
968
1041
|
## Feature controls (per-module kill switches)
|
|
969
1042
|
|
|
970
1043
|
Every activation surface has a switch you can flip from the dashboard "in case of something":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { R as ReportAppEventOptions, r as reportAppEvent } from '../transport-
|
|
2
|
-
import { W as WireUserContext, E as EventQueueOptions } from '../currentSession-
|
|
3
|
-
export { A as AnalyticsEvent, C as ClearUserContextOptions,
|
|
1
|
+
export { R as ReportAppEventOptions, r as reportAppEvent } from '../transport-CF_eHwzC.mjs';
|
|
2
|
+
import { W as WireUserContext, E as EventQueueOptions } from '../currentSession-DsSDHqor.mjs';
|
|
3
|
+
export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, C as ClearUserContextOptions, b as ClientEvent, c as ClientEventTarget, d as ClientEventType, e as ContextEnvelope, f as ContextEnvelopeInput, D as DeviceKeyStorage, g as EnvelopeSource, h as EventQueue, R as ResolveAutoDeviceKeyOptions, i as WIRE_ONBOARDING_EVENTS, j as WireOnboardingEventName, k as analyticsUserIdStorageKey, l as buildContextEnvelope, m as clearPiiFromContext, n as clearUserContext, o as createEventQueue, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, s as looksLikeEmail, t as makeSessionId, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, B as resolveAutoDeviceKey, F as setCurrentSessionId, G as toAnalyticsEvent } from '../currentSession-DsSDHqor.mjs';
|
|
4
4
|
import '../types-CNUqMK0D.mjs';
|
|
5
5
|
import '../types-BKfpdZzX.mjs';
|
|
6
6
|
import '../types-BcmagF6K.mjs';
|
|
@@ -33,7 +33,12 @@ interface ScreenTrackerOptions {
|
|
|
33
33
|
serverUrl: string;
|
|
34
34
|
apiKey: string;
|
|
35
35
|
};
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* The onboarding/session id to correlate screen views with, when known. Omitting it no longer
|
|
38
|
+
* means the view goes out WITHOUT a `session_id` (the server requires one and drops the event
|
|
39
|
+
* behind an HTTP 200 — that is why screen tracking silently produced nothing for a host that
|
|
40
|
+
* never mounted the lifecycle hook). `reportAppEvent` falls back to the current per-open id.
|
|
41
|
+
*/
|
|
37
42
|
sessionId?: string;
|
|
38
43
|
/** A stable, non-PII device id — groups a device's sessions server-side. */
|
|
39
44
|
deviceKey?: string;
|
|
@@ -103,8 +108,14 @@ type CreateAnalyticsConfig = {
|
|
|
103
108
|
/** Tenant API key; sent as `Authorization: Bearer`. */
|
|
104
109
|
apiKey: string;
|
|
105
110
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
111
|
+
* ⚠️ OPT-OUT KNOB, not a default. Supplying a `sessionId` FREEZES the correlation id: every event
|
|
112
|
+
* this instance ever sends (including `identify`) is pinned to that one id, and the instance stops
|
|
113
|
+
* following the LIVE per-open session the server registered via `app.session_started`. Lifecycle
|
|
114
|
+
* analytics then collapse onto a single device-scoped session — one "first open", forever.
|
|
115
|
+
*
|
|
116
|
+
* OMIT IT — that is the correct default. Without it the kit reuses the live per-open session, and
|
|
117
|
+
* falls back to a stable per-instance id only until an open has been registered. Pass one ONLY if
|
|
118
|
+
* your host runs its own session lifecycle and owns the id the server should correlate on.
|
|
108
119
|
*/
|
|
109
120
|
sessionId?: string;
|
|
110
121
|
/** Tenant/app id used to namespace the queue's default storage key (`wireai:evtq:<appId>`). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { R as ReportAppEventOptions, r as reportAppEvent } from '../transport-
|
|
2
|
-
import { W as WireUserContext, E as EventQueueOptions } from '../currentSession-
|
|
3
|
-
export { A as AnalyticsEvent, C as ClearUserContextOptions,
|
|
1
|
+
export { R as ReportAppEventOptions, r as reportAppEvent } from '../transport-DsRe4epC.js';
|
|
2
|
+
import { W as WireUserContext, E as EventQueueOptions } from '../currentSession-D6RiVtc8.js';
|
|
3
|
+
export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, C as ClearUserContextOptions, b as ClientEvent, c as ClientEventTarget, d as ClientEventType, e as ContextEnvelope, f as ContextEnvelopeInput, D as DeviceKeyStorage, g as EnvelopeSource, h as EventQueue, R as ResolveAutoDeviceKeyOptions, i as WIRE_ONBOARDING_EVENTS, j as WireOnboardingEventName, k as analyticsUserIdStorageKey, l as buildContextEnvelope, m as clearPiiFromContext, n as clearUserContext, o as createEventQueue, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, s as looksLikeEmail, t as makeSessionId, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, B as resolveAutoDeviceKey, F as setCurrentSessionId, G as toAnalyticsEvent } from '../currentSession-D6RiVtc8.js';
|
|
4
4
|
import '../types-Buj9Lw9t.js';
|
|
5
5
|
import '../types-BKfpdZzX.js';
|
|
6
6
|
import '../types-BcmagF6K.js';
|
|
@@ -33,7 +33,12 @@ interface ScreenTrackerOptions {
|
|
|
33
33
|
serverUrl: string;
|
|
34
34
|
apiKey: string;
|
|
35
35
|
};
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* The onboarding/session id to correlate screen views with, when known. Omitting it no longer
|
|
38
|
+
* means the view goes out WITHOUT a `session_id` (the server requires one and drops the event
|
|
39
|
+
* behind an HTTP 200 — that is why screen tracking silently produced nothing for a host that
|
|
40
|
+
* never mounted the lifecycle hook). `reportAppEvent` falls back to the current per-open id.
|
|
41
|
+
*/
|
|
37
42
|
sessionId?: string;
|
|
38
43
|
/** A stable, non-PII device id — groups a device's sessions server-side. */
|
|
39
44
|
deviceKey?: string;
|
|
@@ -103,8 +108,14 @@ type CreateAnalyticsConfig = {
|
|
|
103
108
|
/** Tenant API key; sent as `Authorization: Bearer`. */
|
|
104
109
|
apiKey: string;
|
|
105
110
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
111
|
+
* ⚠️ OPT-OUT KNOB, not a default. Supplying a `sessionId` FREEZES the correlation id: every event
|
|
112
|
+
* this instance ever sends (including `identify`) is pinned to that one id, and the instance stops
|
|
113
|
+
* following the LIVE per-open session the server registered via `app.session_started`. Lifecycle
|
|
114
|
+
* analytics then collapse onto a single device-scoped session — one "first open", forever.
|
|
115
|
+
*
|
|
116
|
+
* OMIT IT — that is the correct default. Without it the kit reuses the live per-open session, and
|
|
117
|
+
* falls back to a stable per-instance id only until an open has been registered. Pass one ONLY if
|
|
118
|
+
* your host runs its own session lifecycle and owns the id the server should correlate on.
|
|
108
119
|
*/
|
|
109
120
|
sessionId?: string;
|
|
110
121
|
/** Tenant/app id used to namespace the queue's default storage key (`wireai:evtq:<appId>`). */
|
package/dist/analytics/index.js
CHANGED
|
@@ -413,6 +413,23 @@ var buildEventsRequest = (target, events) => {
|
|
|
413
413
|
return null;
|
|
414
414
|
}
|
|
415
415
|
};
|
|
416
|
+
var warnOnSkippedEvents = (res) => {
|
|
417
|
+
try {
|
|
418
|
+
const json = res == null ? void 0 : res.json;
|
|
419
|
+
if (typeof json !== "function") return;
|
|
420
|
+
void Promise.resolve(json.call(res)).then((body) => {
|
|
421
|
+
const skipped = body == null ? void 0 : body.skipped;
|
|
422
|
+
if (typeof skipped !== "number" || skipped <= 0) return;
|
|
423
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
424
|
+
console.warn(
|
|
425
|
+
`[wireai] the server ACCEPTED the /v1/events POST but DISCARDED ${skipped} event(s) (skipped in the response body) \u2014 they are gone, not retried. The usual cause is an event with a missing or empty session_id.`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}).catch(() => {
|
|
429
|
+
});
|
|
430
|
+
} catch {
|
|
431
|
+
}
|
|
432
|
+
};
|
|
416
433
|
var reportClientEvents = (target, events) => {
|
|
417
434
|
try {
|
|
418
435
|
const req = buildEventsRequest(target, events);
|
|
@@ -435,15 +452,54 @@ var reportClientEventsAwait = async (target, events) => {
|
|
|
435
452
|
};
|
|
436
453
|
var reportClientEventAwait = (target, event) => reportClientEventsAwait(target, [event]);
|
|
437
454
|
|
|
455
|
+
// src/analytics/currentSession.ts
|
|
456
|
+
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
457
|
+
"@wireai/activation:currentSessionId"
|
|
458
|
+
);
|
|
459
|
+
var globalSlot = globalThis;
|
|
460
|
+
var setCurrentSessionId = (id) => {
|
|
461
|
+
if (typeof id === "string" && id.length > 0) {
|
|
462
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
466
|
+
var resetCurrentSessionId = () => {
|
|
467
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = void 0;
|
|
468
|
+
};
|
|
469
|
+
var warnInDev = (message) => {
|
|
470
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
471
|
+
console.warn(message);
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
474
|
+
return false;
|
|
475
|
+
};
|
|
476
|
+
var MINT_WARNING = "[wireai] No app-open session was registered, so a session id was minted for this event (the server drops an event that has no session_id, and still answers 200). Mount useLifecycleEvents at your app root so events correlate to a real app-open.";
|
|
477
|
+
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
478
|
+
"@wireai/activation:currentSessionIdMintWarned"
|
|
479
|
+
);
|
|
480
|
+
var warnSlot = globalThis;
|
|
481
|
+
var ensureCurrentSessionId = () => {
|
|
482
|
+
const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
483
|
+
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
484
|
+
const minted = makeSessionId();
|
|
485
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = minted;
|
|
486
|
+
if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
|
|
487
|
+
warnSlot[MINT_WARNED_SLOT] = true;
|
|
488
|
+
}
|
|
489
|
+
return minted;
|
|
490
|
+
};
|
|
491
|
+
|
|
438
492
|
// src/reviews/transport.ts
|
|
439
493
|
var reportAppEvent = (target, name2, options = {}) => {
|
|
494
|
+
var _a2;
|
|
440
495
|
if (!(target == null ? void 0 : target.serverUrl) || !name2) return;
|
|
441
496
|
try {
|
|
497
|
+
const supplied = (_a2 = options.sessionId) != null ? _a2 : "";
|
|
442
498
|
const event = {
|
|
443
499
|
event_type: "app_event",
|
|
444
|
-
question_key: name2
|
|
500
|
+
question_key: name2,
|
|
501
|
+
session_id: supplied.trim().length > 0 ? supplied : ensureCurrentSessionId()
|
|
445
502
|
};
|
|
446
|
-
if (options.sessionId) event.session_id = options.sessionId;
|
|
447
503
|
if (options.deviceKey) event.user_context = { device_key: options.deviceKey };
|
|
448
504
|
if (options.meta && Object.keys(options.meta).length > 0) {
|
|
449
505
|
event.meta = JSON.stringify(options.meta);
|
|
@@ -879,6 +935,7 @@ var createEventQueue = (options) => {
|
|
|
879
935
|
const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
|
|
880
936
|
try {
|
|
881
937
|
const res = await fetch(req.url, { ...req.init, signal: controller == null ? void 0 : controller.signal });
|
|
938
|
+
warnOnSkippedEvents(res);
|
|
882
939
|
return !!(res && res.ok);
|
|
883
940
|
} catch {
|
|
884
941
|
return false;
|
|
@@ -957,21 +1014,6 @@ var createEventQueue = (options) => {
|
|
|
957
1014
|
return { enqueue, flush, notifyOnline, size };
|
|
958
1015
|
};
|
|
959
1016
|
|
|
960
|
-
// src/analytics/currentSession.ts
|
|
961
|
-
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
962
|
-
"@wireai/activation:currentSessionId"
|
|
963
|
-
);
|
|
964
|
-
var globalSlot = globalThis;
|
|
965
|
-
var setCurrentSessionId = (id) => {
|
|
966
|
-
if (typeof id === "string" && id.length > 0) {
|
|
967
|
-
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
968
|
-
}
|
|
969
|
-
};
|
|
970
|
-
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
971
|
-
var resetCurrentSessionId = () => {
|
|
972
|
-
globalSlot[CURRENT_SESSION_ID_SLOT] = void 0;
|
|
973
|
-
};
|
|
974
|
-
|
|
975
1017
|
// src/identity/userIdentity.ts
|
|
976
1018
|
var USER_ID_MAX_LENGTH = 128;
|
|
977
1019
|
var sanitizeUserId = (raw) => {
|
|
@@ -1068,9 +1110,50 @@ var mintDeviceId = () => {
|
|
|
1068
1110
|
const time = Date.now().toString(36);
|
|
1069
1111
|
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
1070
1112
|
};
|
|
1113
|
+
var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
|
|
1114
|
+
var deviceKeyGlobal = globalThis;
|
|
1115
|
+
var autoDeviceKeyRegistry = () => {
|
|
1116
|
+
const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
|
|
1117
|
+
if (existing) return existing;
|
|
1118
|
+
const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
|
|
1119
|
+
deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
|
|
1120
|
+
return created;
|
|
1121
|
+
};
|
|
1122
|
+
var resolveAutoDeviceKey = (opts = {}) => {
|
|
1123
|
+
var _a2, _b;
|
|
1124
|
+
const registry = autoDeviceKeyRegistry();
|
|
1125
|
+
const appId = (_a2 = opts.appId) != null ? _a2 : "default";
|
|
1126
|
+
let id = registry.keys.get(appId);
|
|
1127
|
+
if (!id) {
|
|
1128
|
+
id = mintDeviceId();
|
|
1129
|
+
registry.keys.set(appId, id);
|
|
1130
|
+
}
|
|
1131
|
+
const storage = opts.storage;
|
|
1132
|
+
if (storage && !registry.hydrating.has(appId)) {
|
|
1133
|
+
registry.hydrating.add(appId);
|
|
1134
|
+
const slot = deviceIdStorageKey(appId);
|
|
1135
|
+
const minted = id;
|
|
1136
|
+
try {
|
|
1137
|
+
void Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
1138
|
+
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
1139
|
+
if (persisted) registry.keys.set(appId, persisted);
|
|
1140
|
+
else void Promise.resolve(storage.setItem(slot, minted)).catch(() => {
|
|
1141
|
+
});
|
|
1142
|
+
}).catch(() => {
|
|
1143
|
+
});
|
|
1144
|
+
} catch {
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
1148
|
+
};
|
|
1149
|
+
var resetAutoDeviceKeys = () => {
|
|
1150
|
+
const registry = autoDeviceKeyRegistry();
|
|
1151
|
+
registry.keys.clear();
|
|
1152
|
+
registry.hydrating.clear();
|
|
1153
|
+
};
|
|
1071
1154
|
|
|
1072
1155
|
// src/analytics/analyticsFacade.ts
|
|
1073
|
-
var
|
|
1156
|
+
var warnInDev2 = (message) => {
|
|
1074
1157
|
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1075
1158
|
console.warn(message);
|
|
1076
1159
|
}
|
|
@@ -1082,20 +1165,20 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1082
1165
|
var _a3, _b2;
|
|
1083
1166
|
return (_b2 = (_a3 = config.sessionId) != null ? _a3 : getCurrentSessionId()) != null ? _b2 : instanceSessionId;
|
|
1084
1167
|
};
|
|
1168
|
+
if (config.sessionId) {
|
|
1169
|
+
warnInDev2(
|
|
1170
|
+
"[wireai] createAnalytics({ sessionId }) PINS every event from this instance to that one frozen id and opts out of the live per-open session (app.session_started) \u2014 lifecycle analytics collapse onto a single device-scoped id. Remove it unless your host runs its own session lifecycle."
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
const detectedAppVersion = detectAppVersion();
|
|
1085
1174
|
let userContext = { ...(_b = config.userContext) != null ? _b : {} };
|
|
1086
1175
|
const hostDeviceKeyAtInit = typeof ((_c = config.userContext) == null ? void 0 : _c.deviceKey) === "string" && config.userContext.deviceKey.trim() ? config.userContext.deviceKey.trim() : void 0;
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if (persisted) autoDeviceKey = persisted;
|
|
1094
|
-
else void storage.setItem(deviceKey, autoDeviceKey).catch(() => {
|
|
1095
|
-
});
|
|
1096
|
-
}).catch(() => {
|
|
1097
|
-
});
|
|
1098
|
-
}
|
|
1176
|
+
const autoDeviceKeyOptions = {
|
|
1177
|
+
appId: config.appId,
|
|
1178
|
+
// A host-supplied deviceKey opts out of minting AND persisting (unchanged contract).
|
|
1179
|
+
storage: hostDeviceKeyAtInit ? void 0 : config.storage
|
|
1180
|
+
};
|
|
1181
|
+
if (!hostDeviceKeyAtInit) resolveAutoDeviceKey(autoDeviceKeyOptions);
|
|
1099
1182
|
const envelope = () => {
|
|
1100
1183
|
var _a3;
|
|
1101
1184
|
return buildContextEnvelope({
|
|
@@ -1121,20 +1204,21 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1121
1204
|
});
|
|
1122
1205
|
}
|
|
1123
1206
|
const applyContext = (event) => {
|
|
1124
|
-
var _a3;
|
|
1207
|
+
var _a3, _b2;
|
|
1125
1208
|
const hostDeviceKey = typeof userContext.deviceKey === "string" && userContext.deviceKey.trim() ? userContext.deviceKey : void 0;
|
|
1126
1209
|
const resolved = resolveUserContext(
|
|
1127
|
-
|
|
1128
|
-
{
|
|
1210
|
+
// `??` is lazy on purpose: a host-supplied key must never even touch the auto registry.
|
|
1211
|
+
{ ...userContext, deviceKey: hostDeviceKey != null ? hostDeviceKey : resolveAutoDeviceKey(autoDeviceKeyOptions) },
|
|
1212
|
+
{ autoAppVersion: (_a3 = config.appVersion) != null ? _a3 : detectedAppVersion }
|
|
1129
1213
|
);
|
|
1130
1214
|
if (resolved.userContext) {
|
|
1131
|
-
event.user_context = { ...resolved.userContext, ...(
|
|
1215
|
+
event.user_context = { ...resolved.userContext, ...(_b2 = event.user_context) != null ? _b2 : {} };
|
|
1132
1216
|
}
|
|
1133
1217
|
if (boundUserId && !event.user_id) event.user_id = boundUserId;
|
|
1134
1218
|
};
|
|
1135
1219
|
const guardUserId = (clean) => {
|
|
1136
1220
|
if (config.allowEmailAsUserId || !looksLikeEmail(clean)) return clean;
|
|
1137
|
-
|
|
1221
|
+
warnInDev2(
|
|
1138
1222
|
"[wireai] identify() was called with an email-shaped id. A raw email must NOT be the opaque user_id (PII leak) \u2014 pass it as userContext.userEmail instead. Binding was skipped. Set allowEmailAsUserId:true on createAnalytics if your user id genuinely is an email."
|
|
1139
1223
|
);
|
|
1140
1224
|
return void 0;
|
|
@@ -1217,6 +1301,7 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1217
1301
|
};
|
|
1218
1302
|
};
|
|
1219
1303
|
var useAnalytics = (config, options = {}) => {
|
|
1304
|
+
var _a2;
|
|
1220
1305
|
const ref = react.useRef(void 0);
|
|
1221
1306
|
const prevKeys = react.useRef("");
|
|
1222
1307
|
const currentKeys = `${config.serverUrl}|${config.apiKey}|${config.appId}`;
|
|
@@ -1224,9 +1309,15 @@ var useAnalytics = (config, options = {}) => {
|
|
|
1224
1309
|
prevKeys.current = currentKeys;
|
|
1225
1310
|
ref.current = createAnalytics(config, options);
|
|
1226
1311
|
}
|
|
1312
|
+
const hostDeviceKey = typeof ((_a2 = config.userContext) == null ? void 0 : _a2.deviceKey) === "string" && config.userContext.deviceKey.trim() ? config.userContext.deviceKey.trim() : void 0;
|
|
1313
|
+
react.useEffect(() => {
|
|
1314
|
+
var _a3;
|
|
1315
|
+
if (hostDeviceKey) (_a3 = ref.current) == null ? void 0 : _a3.setUserContext({ deviceKey: hostDeviceKey });
|
|
1316
|
+
}, [hostDeviceKey]);
|
|
1227
1317
|
return ref.current;
|
|
1228
1318
|
};
|
|
1229
1319
|
|
|
1320
|
+
exports.AUTO_DEVICE_ID_PREFIX = AUTO_DEVICE_ID_PREFIX;
|
|
1230
1321
|
exports.WIRE_ONBOARDING_EVENTS = WIRE_ONBOARDING_EVENTS;
|
|
1231
1322
|
exports.analyticsUserIdStorageKey = analyticsUserIdStorageKey;
|
|
1232
1323
|
exports.buildContextEnvelope = buildContextEnvelope;
|
|
@@ -1235,6 +1326,8 @@ exports.clearUserContext = clearUserContext;
|
|
|
1235
1326
|
exports.createAnalytics = createAnalytics;
|
|
1236
1327
|
exports.createEventQueue = createEventQueue;
|
|
1237
1328
|
exports.createScreenTracker = createScreenTracker;
|
|
1329
|
+
exports.deviceIdStorageKey = deviceIdStorageKey;
|
|
1330
|
+
exports.ensureCurrentSessionId = ensureCurrentSessionId;
|
|
1238
1331
|
exports.getActiveRouteName = getActiveRouteName;
|
|
1239
1332
|
exports.getCurrentSessionId = getCurrentSessionId;
|
|
1240
1333
|
exports.looksLikeEmail = looksLikeEmail;
|
|
@@ -1244,7 +1337,9 @@ exports.reportClientEvent = reportClientEvent;
|
|
|
1244
1337
|
exports.reportClientEventAwait = reportClientEventAwait;
|
|
1245
1338
|
exports.reportClientEvents = reportClientEvents;
|
|
1246
1339
|
exports.reportClientEventsAwait = reportClientEventsAwait;
|
|
1340
|
+
exports.resetAutoDeviceKeys = resetAutoDeviceKeys;
|
|
1247
1341
|
exports.resetCurrentSessionId = resetCurrentSessionId;
|
|
1342
|
+
exports.resolveAutoDeviceKey = resolveAutoDeviceKey;
|
|
1248
1343
|
exports.screenTrackingHandler = screenTrackingHandler;
|
|
1249
1344
|
exports.setCurrentSessionId = setCurrentSessionId;
|
|
1250
1345
|
exports.toAnalyticsEvent = toAnalyticsEvent;
|