@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.
Files changed (73) hide show
  1. package/AGENTS.md +62 -8
  2. package/CHANGELOG.md +188 -3
  3. package/INTEGRATION_PROMPT.md +25 -2
  4. package/README.md +112 -1
  5. package/dist/analytics/index.d.mts +18 -6
  6. package/dist/analytics/index.d.ts +18 -6
  7. package/dist/analytics/index.js +151 -41
  8. package/dist/analytics/index.js.map +1 -1
  9. package/dist/analytics/index.mjs +147 -42
  10. package/dist/analytics/index.mjs.map +1 -1
  11. package/dist/coachmarks/index.d.mts +16 -0
  12. package/dist/coachmarks/index.d.ts +16 -0
  13. package/dist/coachmarks/index.js +19 -13
  14. package/dist/coachmarks/index.js.map +1 -1
  15. package/dist/coachmarks/index.mjs +19 -13
  16. package/dist/coachmarks/index.mjs.map +1 -1
  17. package/dist/{currentSession-C0_odnIW.d.mts → currentSession-BlCeDP0f.d.mts} +145 -33
  18. package/dist/{currentSession-DdnUq2HQ.d.ts → currentSession-BxEB37xt.d.ts} +145 -33
  19. package/dist/index.d.mts +243 -53
  20. package/dist/index.d.ts +243 -53
  21. package/dist/index.js +531 -156
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +517 -157
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/questionnaire/index.d.mts +1 -1
  26. package/dist/questionnaire/index.d.ts +1 -1
  27. package/dist/questionnaire/index.js +79 -14
  28. package/dist/questionnaire/index.js.map +1 -1
  29. package/dist/questionnaire/index.mjs +79 -14
  30. package/dist/questionnaire/index.mjs.map +1 -1
  31. package/dist/reviews/index.d.mts +2 -2
  32. package/dist/reviews/index.d.ts +2 -2
  33. package/dist/reviews/index.js +103 -16
  34. package/dist/reviews/index.js.map +1 -1
  35. package/dist/reviews/index.mjs +103 -16
  36. package/dist/reviews/index.mjs.map +1 -1
  37. package/dist/showcase/index.js +15 -6
  38. package/dist/showcase/index.js.map +1 -1
  39. package/dist/showcase/index.mjs +15 -6
  40. package/dist/showcase/index.mjs.map +1 -1
  41. package/dist/{transport-BGW9uXZJ.d.mts → transport-CF_eHwzC.d.mts} +15 -1
  42. package/dist/{transport-jUJd5kxu.d.ts → transport-DsRe4epC.d.ts} +15 -1
  43. package/llms.txt +3 -0
  44. package/package.json +1 -1
  45. package/src/WireOnboarding.tsx +140 -5
  46. package/src/activation/useWireActivation.ts +12 -1
  47. package/src/activation/wireActivation.ts +44 -25
  48. package/src/analytics/analyticsFacade.ts +54 -29
  49. package/src/analytics/currentSession.ts +83 -0
  50. package/src/analytics/eventQueue.ts +9 -1
  51. package/src/analytics/index.ts +20 -1
  52. package/src/analytics/reportClientEvent.ts +42 -0
  53. package/src/analytics/screenTracking.ts +6 -1
  54. package/src/analytics/useAnalytics.ts +22 -1
  55. package/src/coachmarks/runtime.ts +53 -17
  56. package/src/config/wireConfigFromEnv.ts +46 -2
  57. package/src/context/deviceId.ts +173 -0
  58. package/src/context/userContext.ts +18 -0
  59. package/src/index.ts +54 -2
  60. package/src/questionnaire/runtime.ts +13 -2
  61. package/src/questionnaire/useQuestionnaireGate.ts +11 -4
  62. package/src/revenuecat/index.ts +55 -0
  63. package/src/revenuecat/purchaseEvents.ts +167 -0
  64. package/src/revenuecat/revenueCatBridge.ts +221 -0
  65. package/src/revenuecat/types.ts +95 -0
  66. package/src/reviews/runtime.ts +153 -1
  67. package/src/reviews/transport.ts +21 -2
  68. package/src/reviews/useReviewGate.ts +14 -4
  69. package/src/session-analytics/lifecycle.ts +9 -2
  70. package/src/session-analytics/reportSessionStart.ts +15 -4
  71. package/src/session-analytics/useLifecycleEvents.ts +83 -27
  72. package/src/session-analytics/useSessionStart.ts +37 -1
  73. package/src/types.ts +41 -4
package/dist/index.js CHANGED
@@ -1518,6 +1518,23 @@ var buildEventsRequest = (target, events) => {
1518
1518
  return null;
1519
1519
  }
1520
1520
  };
1521
+ var warnOnSkippedEvents = (res) => {
1522
+ if (typeof __DEV__ === "undefined" || !__DEV__) return;
1523
+ if (typeof console === "undefined" || !console.warn) return;
1524
+ try {
1525
+ const json = res == null ? void 0 : res.json;
1526
+ if (typeof json !== "function") return;
1527
+ void Promise.resolve(json.call(res)).then((body) => {
1528
+ const skipped = body == null ? void 0 : body.skipped;
1529
+ if (typeof skipped !== "number" || skipped <= 0) return;
1530
+ console.warn(
1531
+ `[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.`
1532
+ );
1533
+ }).catch(() => {
1534
+ });
1535
+ } catch {
1536
+ }
1537
+ };
1521
1538
  var reportClientEvents = (target, events) => {
1522
1539
  try {
1523
1540
  const req = buildEventsRequest(target, events);
@@ -3300,6 +3317,82 @@ var collectDeviceContext = () => {
3300
3317
  return ctx;
3301
3318
  };
3302
3319
 
3320
+ // src/context/deviceId.ts
3321
+ var AUTO_DEVICE_ID_PREFIX = "wdev_";
3322
+ var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
3323
+ var randomChunk = () => Math.floor(Math.random() * 4294967296).toString(36).padStart(6, "0");
3324
+ var mintDeviceId = () => {
3325
+ const time = Date.now().toString(36);
3326
+ return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
3327
+ };
3328
+ var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
3329
+ var deviceKeyGlobal = globalThis;
3330
+ var autoDeviceKeyRegistry = () => {
3331
+ const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
3332
+ if (existing) return existing;
3333
+ const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
3334
+ deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
3335
+ return created;
3336
+ };
3337
+ var startHydration = (registry, appId, storage, minted) => {
3338
+ if (!registry.pending) registry.pending = /* @__PURE__ */ new Map();
3339
+ const existing = registry.pending.get(appId);
3340
+ if (existing) return existing;
3341
+ const slot = deviceIdStorageKey(appId);
3342
+ const settled = () => {
3343
+ var _a2;
3344
+ return (_a2 = registry.keys.get(appId)) != null ? _a2 : minted;
3345
+ };
3346
+ let run;
3347
+ try {
3348
+ run = Promise.resolve(storage.getItem(slot)).then((saved) => {
3349
+ const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
3350
+ if (persisted) {
3351
+ registry.keys.set(appId, persisted);
3352
+ return persisted;
3353
+ }
3354
+ return Promise.resolve(storage.setItem(slot, minted)).then(settled, settled);
3355
+ }).catch(settled);
3356
+ } catch {
3357
+ run = Promise.resolve(settled());
3358
+ }
3359
+ registry.pending.set(appId, run);
3360
+ return run;
3361
+ };
3362
+ var resolveAutoDeviceKey = (opts = {}) => {
3363
+ var _a2, _b;
3364
+ const registry = autoDeviceKeyRegistry();
3365
+ const appId = (_a2 = opts.appId) != null ? _a2 : "default";
3366
+ let id = registry.keys.get(appId);
3367
+ if (!id) {
3368
+ id = mintDeviceId();
3369
+ registry.keys.set(appId, id);
3370
+ }
3371
+ const storage = opts.storage;
3372
+ if (storage && !registry.hydrating.has(appId)) {
3373
+ registry.hydrating.add(appId);
3374
+ void startHydration(registry, appId, storage, id);
3375
+ }
3376
+ return (_b = registry.keys.get(appId)) != null ? _b : id;
3377
+ };
3378
+ var hydrateAutoDeviceKey = async (opts = {}) => {
3379
+ var _a2, _b, _c;
3380
+ const id = resolveAutoDeviceKey(opts);
3381
+ if (!opts.storage) return id;
3382
+ const registry = autoDeviceKeyRegistry();
3383
+ const appId = (_a2 = opts.appId) != null ? _a2 : "default";
3384
+ const pending = (_b = registry.pending) == null ? void 0 : _b.get(appId);
3385
+ if (pending) await pending;
3386
+ return (_c = registry.keys.get(appId)) != null ? _c : id;
3387
+ };
3388
+ var resetAutoDeviceKeys = () => {
3389
+ var _a2;
3390
+ const registry = autoDeviceKeyRegistry();
3391
+ registry.keys.clear();
3392
+ registry.hydrating.clear();
3393
+ (_a2 = registry.pending) == null ? void 0 : _a2.clear();
3394
+ };
3395
+
3303
3396
  // src/analytics/currentSession.ts
3304
3397
  var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
3305
3398
  "@wireai/activation:currentSessionId"
@@ -3314,6 +3407,28 @@ var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
3314
3407
  var resetCurrentSessionId = () => {
3315
3408
  globalSlot[CURRENT_SESSION_ID_SLOT] = void 0;
3316
3409
  };
3410
+ var warnInDev = (message) => {
3411
+ if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
3412
+ console.warn(message);
3413
+ return true;
3414
+ }
3415
+ return false;
3416
+ };
3417
+ 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.";
3418
+ var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
3419
+ "@wireai/activation:currentSessionIdMintWarned"
3420
+ );
3421
+ var warnSlot = globalThis;
3422
+ var ensureCurrentSessionId = () => {
3423
+ const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
3424
+ if (typeof existing === "string" && existing.length > 0) return existing;
3425
+ const minted = makeSessionId();
3426
+ globalSlot[CURRENT_SESSION_ID_SLOT] = minted;
3427
+ if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
3428
+ warnSlot[MINT_WARNED_SLOT] = true;
3429
+ }
3430
+ return minted;
3431
+ };
3317
3432
 
3318
3433
  // src/session/persistedSession.ts
3319
3434
  var DEFAULT_SESSION_TTL_MS = 36e5;
@@ -3403,6 +3518,105 @@ var identifyOnboarding = async (opts) => {
3403
3518
  );
3404
3519
  return true;
3405
3520
  };
3521
+
3522
+ // src/context/userContext.ts
3523
+ var RESERVED_USER_CONTEXT_KEYS = [
3524
+ "device_key",
3525
+ "app_version",
3526
+ "app_build",
3527
+ "network_type",
3528
+ "session_count",
3529
+ "returning",
3530
+ "platform",
3531
+ "user_email",
3532
+ "user_email_hashed"
3533
+ ];
3534
+ var EXTRA_KEY_PREFIX = "custom.";
3535
+ var isWireScalar = (value) => {
3536
+ const t = typeof value;
3537
+ if (t === "string" || t === "boolean") return true;
3538
+ if (t === "number") return Number.isFinite(value);
3539
+ return false;
3540
+ };
3541
+ var hashEmailFnv1a = (email) => {
3542
+ const normalized = email.trim().toLowerCase();
3543
+ let hash = 2166136261;
3544
+ for (let i = 0; i < normalized.length; i++) {
3545
+ hash ^= normalized.charCodeAt(i);
3546
+ hash = Math.imul(hash, 16777619);
3547
+ }
3548
+ return (hash >>> 0).toString(16).padStart(8, "0");
3549
+ };
3550
+ var cleanString = (value) => {
3551
+ if (typeof value !== "string") return void 0;
3552
+ const trimmed = value.trim();
3553
+ return trimmed.length > 0 ? trimmed : void 0;
3554
+ };
3555
+ var namespaceExtra = (extra) => {
3556
+ const out = {};
3557
+ if (!extra || typeof extra !== "object") return out;
3558
+ for (const [key, value] of Object.entries(extra)) {
3559
+ const cleanKey = cleanString(key);
3560
+ if (!cleanKey) continue;
3561
+ if (!isWireScalar(value)) continue;
3562
+ out[`${EXTRA_KEY_PREFIX}${cleanKey}`] = value;
3563
+ }
3564
+ return out;
3565
+ };
3566
+ var analyticsUserIdStorageKey = (appId) => `wireai:analytics:userId:${appId != null ? appId : "default"}`;
3567
+ var clearPiiFromContext = (ctx = {}) => {
3568
+ const rest = {};
3569
+ if (typeof ctx.appVersion === "string") rest.appVersion = ctx.appVersion;
3570
+ if (typeof ctx.deviceKey === "string") rest.deviceKey = ctx.deviceKey;
3571
+ return rest;
3572
+ };
3573
+ var clearUserContext = async (opts = {}) => {
3574
+ const storage = opts.storage;
3575
+ if (!storage) return;
3576
+ try {
3577
+ await storage.removeItem(analyticsUserIdStorageKey(opts.appId));
3578
+ } catch {
3579
+ }
3580
+ };
3581
+ var activationJoinContext = (deviceKey) => {
3582
+ var _a2;
3583
+ return (_a2 = resolveUserContext({ deviceKey }).userContext) != null ? _a2 : {};
3584
+ };
3585
+ var resolveUserContext = (ctx = {}, opts = {}) => {
3586
+ var _a2;
3587
+ const result = {};
3588
+ const bucket = {};
3589
+ const userId = sanitizeUserId(ctx.userId);
3590
+ if (userId) result.userId = userId;
3591
+ const deviceKey = cleanString(ctx.deviceKey);
3592
+ if (deviceKey) {
3593
+ result.deviceKey = deviceKey;
3594
+ bucket.device_key = deviceKey;
3595
+ }
3596
+ const appVersion = (_a2 = cleanString(ctx.appVersion)) != null ? _a2 : cleanString(opts.autoAppVersion);
3597
+ if (appVersion) {
3598
+ result.appVersion = appVersion;
3599
+ bucket.app_version = appVersion;
3600
+ }
3601
+ const email = cleanString(ctx.userEmail);
3602
+ if (email) {
3603
+ if (ctx.hashEmail) {
3604
+ bucket.user_email = hashEmailFnv1a(email);
3605
+ bucket.user_email_hashed = true;
3606
+ } else {
3607
+ bucket.user_email = email;
3608
+ }
3609
+ }
3610
+ Object.assign(bucket, namespaceExtra(ctx.extra));
3611
+ if (Object.keys(bucket).length > 0) result.userContext = bucket;
3612
+ return result;
3613
+ };
3614
+ var warnInDev2 = (message) => {
3615
+ if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
3616
+ console.warn(message);
3617
+ }
3618
+ };
3619
+ var AUTO_JOIN_HYDRATION_TIMEOUT_MS = 1500;
3406
3620
  var WireOnboarding = ({
3407
3621
  config,
3408
3622
  theme,
@@ -3425,7 +3639,8 @@ var WireOnboarding = ({
3425
3639
  persistKey,
3426
3640
  retainSessionOnComplete,
3427
3641
  userContext,
3428
- userId
3642
+ userId,
3643
+ autoJoinKey = true
3429
3644
  }) => {
3430
3645
  var _a2, _b, _c;
3431
3646
  const boundUserId = React18.useMemo(() => sanitizeUserId(userId), [userId]);
@@ -3438,9 +3653,50 @@ var WireOnboarding = ({
3438
3653
  () => userContextKey ? JSON.parse(userContextKey) : void 0,
3439
3654
  [userContextKey]
3440
3655
  );
3656
+ const missingJoinKey = typeof (userContextStable == null ? void 0 : userContextStable.device_key) !== "string" || !userContextStable.device_key.trim();
3657
+ const autoJoinPossible = Boolean(storage) && autoJoinKey !== false;
3658
+ const wantsAutoJoin = missingJoinKey && autoJoinPossible;
3659
+ const [autoJoinValue, setAutoJoinValue] = React18.useState(void 0);
3660
+ React18.useEffect(() => {
3661
+ if (!wantsAutoJoin || autoJoinValue !== void 0 || !storage) return;
3662
+ let cancelled = false;
3663
+ const timer = setTimeout(() => {
3664
+ if (!cancelled) setAutoJoinValue(null);
3665
+ }, AUTO_JOIN_HYDRATION_TIMEOUT_MS);
3666
+ void hydrateAutoDeviceKey({ appId: config.appId, storage }).then((key) => {
3667
+ if (cancelled) return;
3668
+ clearTimeout(timer);
3669
+ setAutoJoinValue(key || null);
3670
+ });
3671
+ return () => {
3672
+ cancelled = true;
3673
+ clearTimeout(timer);
3674
+ };
3675
+ }, [wantsAutoJoin, autoJoinValue, config.appId, storage]);
3676
+ const injectedJoinKey = wantsAutoJoin && typeof autoJoinValue === "string" ? autoJoinValue : void 0;
3677
+ const autoJoinPending = wantsAutoJoin && autoJoinValue === void 0;
3678
+ const effectiveUserContext = React18.useMemo(
3679
+ () => injectedJoinKey ? { ...userContextStable, ...activationJoinContext(injectedJoinKey) } : userContextStable,
3680
+ [userContextStable, injectedJoinKey]
3681
+ );
3682
+ const warnMissingJoinKey = missingJoinKey && !injectedJoinKey && !autoJoinPending;
3683
+ const autoJoinReason = autoJoinKey === false ? "you passed autoJoinKey={false}." : !storage ? "it got no `storage` prop, and without persistence its key would be different on every launch, which corrupts min_sessions instead of merely leaving the join empty." : "your `storage` adapter did not answer in time.";
3684
+ React18.useEffect(() => {
3685
+ if (!warnMissingJoinKey) return;
3686
+ warnInDev2(
3687
+ "[wireai] <WireOnboarding> got no user_context.device_key, so this onboarding session can never be joined to the app's later events and the `activated` funnel will read zero. Pass userContext={activationJoinContext(deviceKey)} \u2014 and if your app owns no device id, userContext={activationJoinContext(resolveAutoDeviceKey({ appId, storage }))} returns the SAME id the analytics side stamps. Never hand-write userContext={{ deviceKey }}. The kit did NOT auto-inject its own key here because " + autoJoinReason
3688
+ );
3689
+ }, [warnMissingJoinKey, autoJoinReason]);
3690
+ const misspelledJoinKey = missingJoinKey && typeof (userContextStable == null ? void 0 : userContextStable.deviceKey) === "string" && Boolean(userContextStable.deviceKey.trim());
3691
+ React18.useEffect(() => {
3692
+ if (!misspelledJoinKey || !injectedJoinKey) return;
3693
+ warnInDev2(
3694
+ "[wireai] <WireOnboarding> got userContext={{ deviceKey }}, which is NOT the wire key \u2014 the server's device lookup reads `device_key`. The kit auto-injected its own device_key so this session is at least self-consistent, but your app's own events carry YOUR id, so the two still will not join and the `activated` funnel stays zero. Pass userContext={activationJoinContext(deviceKey)} instead."
3695
+ );
3696
+ }, [misspelledJoinKey, injectedJoinKey]);
3441
3697
  const clientContext = React18.useMemo(
3442
- () => ({ device, userContext: userContextStable, userId: boundUserId }),
3443
- [device, userContextStable, boundUserId]
3698
+ () => ({ device, userContext: effectiveUserContext, userId: boundUserId }),
3699
+ [device, effectiveUserContext, boundUserId]
3444
3700
  );
3445
3701
  const [session, setSession] = React18.useState(
3446
3702
  () => storage ? null : { id: makeSessionId(), resumed: false }
@@ -3487,7 +3743,7 @@ var WireOnboarding = ({
3487
3743
  // Device snapshot + host-injected user context ride the session-start metadata so the
3488
3744
  // backend can segment the funnel. Old servers ignore these unknown keys (backward compat).
3489
3745
  device,
3490
- ...userContextStable ? { userContext: userContextStable } : {},
3746
+ ...effectiveUserContext ? { userContext: effectiveUserContext } : {},
3491
3747
  // The session-start user id (from the ref) rides the session-start metadata so the
3492
3748
  // server binds the session to a real user at creation. Reading the ref — not
3493
3749
  // `boundUserId` — keeps this memo off the userId dependency, so a mid-session change
@@ -3496,7 +3752,7 @@ var WireOnboarding = ({
3496
3752
  },
3497
3753
  timeoutMs: 6e4
3498
3754
  };
3499
- }, [config.serverUrl, config.appId, config.apiKey, metadataStable, sessionId, componentsStable, device, userContextStable]);
3755
+ }, [config.serverUrl, config.appId, config.apiKey, metadataStable, sessionId, componentsStable, device, effectiveUserContext]);
3500
3756
  const reportTarget = React18.useMemo(
3501
3757
  () => ({ serverUrl: config.serverUrl, apiKey: config.apiKey }),
3502
3758
  [config.serverUrl, config.apiKey]
@@ -3518,7 +3774,7 @@ var WireOnboarding = ({
3518
3774
  });
3519
3775
  }, [session, sessionId, boundUserId, reportTarget]);
3520
3776
  const cards = components != null ? components : onboardingComponents;
3521
- if (!session) {
3777
+ if (!session || autoJoinPending) {
3522
3778
  return /* @__PURE__ */ jsxRuntime.jsx(OnboardingThemeProvider, { theme, children: /* @__PURE__ */ jsxRuntime.jsx(
3523
3779
  LoadingScreen,
3524
3780
  {
@@ -3984,16 +4240,37 @@ var useResolvedFeatures = (options) => {
3984
4240
  };
3985
4241
 
3986
4242
  // src/config/wireConfigFromEnv.ts
4243
+ var WIRE_ENV_VARS = [
4244
+ "EXPO_PUBLIC_WIREAI_API_KEY",
4245
+ "EXPO_PUBLIC_WIREAI_SERVER_URL",
4246
+ "EXPO_PUBLIC_WIREAI_APP_ID"
4247
+ ];
4248
+ var warnInDev3 = (message) => {
4249
+ if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
4250
+ console.warn(message);
4251
+ }
4252
+ };
3987
4253
  var wireConfigFromEnv = (overrides) => {
3988
- var _a2, _b, _c, _d;
4254
+ var _a2, _b, _c, _d, _e;
3989
4255
  const apiKey = (_a2 = overrides == null ? void 0 : overrides.apiKey) != null ? _a2 : process.env.EXPO_PUBLIC_WIREAI_API_KEY;
3990
4256
  const serverUrl = (_b = overrides == null ? void 0 : overrides.serverUrl) != null ? _b : process.env.EXPO_PUBLIC_WIREAI_SERVER_URL;
3991
4257
  const appId = (_d = (_c = overrides == null ? void 0 : overrides.appId) != null ? _c : process.env.EXPO_PUBLIC_WIREAI_APP_ID) != null ? _d : "default";
3992
- if (!apiKey || !serverUrl) return null;
4258
+ if (!apiKey || !serverUrl) {
4259
+ const missing = [
4260
+ apiKey ? void 0 : "EXPO_PUBLIC_WIREAI_API_KEY",
4261
+ serverUrl ? void 0 : "EXPO_PUBLIC_WIREAI_SERVER_URL"
4262
+ ].filter(Boolean);
4263
+ warnInDev3(
4264
+ `[wireai] wireConfigFromEnv() returned null: ${missing.join(" and ")} ${missing.length > 1 ? "are" : "is"} missing. The kit is now FULLY DISABLED (no onboarding, no analytics, no gates) and nothing else will report an error. Set the var(s) in your env / EAS secrets.`
4265
+ );
4266
+ return null;
4267
+ }
4268
+ const appVersion = (_e = overrides == null ? void 0 : overrides.appVersion) != null ? _e : detectAppVersion();
3993
4269
  return {
3994
4270
  apiKey,
3995
4271
  serverUrl,
3996
4272
  appId,
4273
+ ...appVersion ? { appVersion } : {},
3997
4274
  ...(overrides == null ? void 0 : overrides.metadata) ? { metadata: overrides.metadata } : {}
3998
4275
  };
3999
4276
  };
@@ -4054,104 +4331,6 @@ var attributionMetadata = (a) => {
4054
4331
  return { attribution };
4055
4332
  };
4056
4333
 
4057
- // src/context/userContext.ts
4058
- var RESERVED_USER_CONTEXT_KEYS = [
4059
- "device_key",
4060
- "app_version",
4061
- "app_build",
4062
- "network_type",
4063
- "session_count",
4064
- "returning",
4065
- "platform",
4066
- "user_email",
4067
- "user_email_hashed"
4068
- ];
4069
- var EXTRA_KEY_PREFIX = "custom.";
4070
- var isWireScalar = (value) => {
4071
- const t = typeof value;
4072
- if (t === "string" || t === "boolean") return true;
4073
- if (t === "number") return Number.isFinite(value);
4074
- return false;
4075
- };
4076
- var hashEmailFnv1a = (email) => {
4077
- const normalized = email.trim().toLowerCase();
4078
- let hash = 2166136261;
4079
- for (let i = 0; i < normalized.length; i++) {
4080
- hash ^= normalized.charCodeAt(i);
4081
- hash = Math.imul(hash, 16777619);
4082
- }
4083
- return (hash >>> 0).toString(16).padStart(8, "0");
4084
- };
4085
- var cleanString = (value) => {
4086
- if (typeof value !== "string") return void 0;
4087
- const trimmed = value.trim();
4088
- return trimmed.length > 0 ? trimmed : void 0;
4089
- };
4090
- var namespaceExtra = (extra) => {
4091
- const out = {};
4092
- if (!extra || typeof extra !== "object") return out;
4093
- for (const [key, value] of Object.entries(extra)) {
4094
- const cleanKey = cleanString(key);
4095
- if (!cleanKey) continue;
4096
- if (!isWireScalar(value)) continue;
4097
- out[`${EXTRA_KEY_PREFIX}${cleanKey}`] = value;
4098
- }
4099
- return out;
4100
- };
4101
- var analyticsUserIdStorageKey = (appId) => `wireai:analytics:userId:${appId != null ? appId : "default"}`;
4102
- var clearPiiFromContext = (ctx = {}) => {
4103
- const rest = {};
4104
- if (typeof ctx.appVersion === "string") rest.appVersion = ctx.appVersion;
4105
- if (typeof ctx.deviceKey === "string") rest.deviceKey = ctx.deviceKey;
4106
- return rest;
4107
- };
4108
- var clearUserContext = async (opts = {}) => {
4109
- const storage = opts.storage;
4110
- if (!storage) return;
4111
- try {
4112
- await storage.removeItem(analyticsUserIdStorageKey(opts.appId));
4113
- } catch {
4114
- }
4115
- };
4116
- var resolveUserContext = (ctx = {}, opts = {}) => {
4117
- var _a2;
4118
- const result = {};
4119
- const bucket = {};
4120
- const userId = sanitizeUserId(ctx.userId);
4121
- if (userId) result.userId = userId;
4122
- const deviceKey = cleanString(ctx.deviceKey);
4123
- if (deviceKey) {
4124
- result.deviceKey = deviceKey;
4125
- bucket.device_key = deviceKey;
4126
- }
4127
- const appVersion = (_a2 = cleanString(ctx.appVersion)) != null ? _a2 : cleanString(opts.autoAppVersion);
4128
- if (appVersion) {
4129
- result.appVersion = appVersion;
4130
- bucket.app_version = appVersion;
4131
- }
4132
- const email = cleanString(ctx.userEmail);
4133
- if (email) {
4134
- if (ctx.hashEmail) {
4135
- bucket.user_email = hashEmailFnv1a(email);
4136
- bucket.user_email_hashed = true;
4137
- } else {
4138
- bucket.user_email = email;
4139
- }
4140
- }
4141
- Object.assign(bucket, namespaceExtra(ctx.extra));
4142
- if (Object.keys(bucket).length > 0) result.userContext = bucket;
4143
- return result;
4144
- };
4145
-
4146
- // src/context/deviceId.ts
4147
- var AUTO_DEVICE_ID_PREFIX = "wdev_";
4148
- var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
4149
- var randomChunk = () => Math.floor(Math.random() * 4294967296).toString(36).padStart(6, "0");
4150
- var mintDeviceId = () => {
4151
- const time = Date.now().toString(36);
4152
- return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
4153
- };
4154
-
4155
4334
  // src/activation/revalidation.ts
4156
4335
  var REVALIDATION_SLOT = /* @__PURE__ */ Symbol.for(
4157
4336
  "@wireai/activation:activationRevalidation"
@@ -4198,32 +4377,31 @@ var createWireActivation = (config) => {
4198
4377
  var _a2, _b;
4199
4378
  const target = { serverUrl: config.serverUrl, apiKey: config.apiKey };
4200
4379
  const explicitDeviceKey = (_b = clean(config.deviceKey)) != null ? _b : clean((_a2 = config.userContext) == null ? void 0 : _a2.deviceKey);
4201
- let autoDeviceKey = explicitDeviceKey != null ? explicitDeviceKey : mintDeviceId();
4202
- if (config.storage && !explicitDeviceKey) {
4203
- const storage = config.storage;
4204
- const key = deviceIdStorageKey(config.appId);
4205
- void storage.getItem(key).then((saved) => {
4206
- const persisted = clean(saved != null ? saved : void 0);
4207
- if (persisted) autoDeviceKey = persisted;
4208
- else void storage.setItem(key, autoDeviceKey).catch(() => {
4209
- });
4210
- }).catch(() => {
4211
- });
4212
- }
4380
+ const autoDeviceKeyOptions = {
4381
+ appId: config.appId,
4382
+ // An explicit key opts out of minting AND persisting (unchanged contract).
4383
+ storage: explicitDeviceKey ? void 0 : config.storage
4384
+ };
4385
+ if (!explicitDeviceKey) resolveAutoDeviceKey(autoDeviceKeyOptions);
4386
+ const detectedAppVersion = detectAppVersion();
4213
4387
  const applyContext = (event) => {
4214
- var _a3, _b2;
4388
+ var _a3, _b2, _c;
4215
4389
  const resolved = resolveUserContext(
4216
- { ...(_a3 = config.userContext) != null ? _a3 : {}, deviceKey: explicitDeviceKey != null ? explicitDeviceKey : autoDeviceKey },
4217
- { autoAppVersion: config.appVersion }
4390
+ // `??` is lazy on purpose: an explicit key must never even touch the auto registry.
4391
+ {
4392
+ ...(_a3 = config.userContext) != null ? _a3 : {},
4393
+ deviceKey: explicitDeviceKey != null ? explicitDeviceKey : resolveAutoDeviceKey(autoDeviceKeyOptions)
4394
+ },
4395
+ { autoAppVersion: (_b2 = config.appVersion) != null ? _b2 : detectedAppVersion }
4218
4396
  );
4219
4397
  if (resolved.userContext) {
4220
- event.user_context = { ...resolved.userContext, ...(_b2 = event.user_context) != null ? _b2 : {} };
4398
+ event.user_context = { ...resolved.userContext, ...(_c = event.user_context) != null ? _c : {} };
4221
4399
  }
4222
4400
  if (resolved.userId && !event.user_id) event.user_id = resolved.userId;
4223
4401
  };
4224
4402
  const track = async (name2, meta) => {
4225
- const sessionId = getCurrentSessionId();
4226
- if (!clean(name2) || !sessionId) return false;
4403
+ if (!clean(name2)) return false;
4404
+ const sessionId = ensureCurrentSessionId();
4227
4405
  const event = {
4228
4406
  event_type: "app_event",
4229
4407
  session_id: sessionId,
@@ -4250,9 +4428,16 @@ var useActivationRevalidation = () => React18.useSyncExternalStore(
4250
4428
  getActivationRevalidationVersion
4251
4429
  );
4252
4430
  var useWireActivation = (config) => {
4431
+ var _a2;
4253
4432
  const ref = React18.useRef(void 0);
4254
4433
  const prevKeys = React18.useRef("");
4255
- const currentKeys = `${config.serverUrl}|${config.apiKey}|${config.appId}|${config.deviceKey}`;
4434
+ const currentKeys = [
4435
+ config.serverUrl,
4436
+ config.apiKey,
4437
+ config.appId,
4438
+ config.deviceKey,
4439
+ (_a2 = config.userContext) == null ? void 0 : _a2.deviceKey
4440
+ ].join("|");
4256
4441
  if (!ref.current || prevKeys.current !== currentKeys) {
4257
4442
  prevKeys.current = currentKeys;
4258
4443
  ref.current = createWireActivation(config);
@@ -4261,6 +4446,154 @@ var useWireActivation = (config) => {
4261
4446
  return { track: ref.current.track, sessionId: ref.current.sessionId, revalidation };
4262
4447
  };
4263
4448
 
4449
+ // src/revenuecat/purchaseEvents.ts
4450
+ var WIRE_PURCHASE_EVENTS = {
4451
+ /** The paywall became visible (offerings loaded). */
4452
+ paywallShown: "wire_paywall_shown",
4453
+ /** The user tapped buy; the store sheet is about to open. */
4454
+ checkoutStarted: "wire_checkout_started",
4455
+ /** The store confirmed the purchase AND the entitlement is now active. */
4456
+ purchased: "wire_purchase_completed",
4457
+ /** The purchase did not land: a user cancel or a real store/network error (see `reason`). */
4458
+ purchaseFailed: "wire_purchase_failed",
4459
+ /** A restore ran (whether or not it produced an entitlement; see `plan_tier`). */
4460
+ restored: "wire_purchase_restored"
4461
+ };
4462
+ var PLAN_TIER_CONTEXT_KEY = "plan_tier";
4463
+ var asRecord = (value) => typeof value === "object" && value !== null ? value : void 0;
4464
+ var activeEntitlement = (info, entitlementId) => {
4465
+ var _a2, _b;
4466
+ const active = asRecord((_b = asRecord((_a2 = asRecord(info)) == null ? void 0 : _a2.entitlements)) == null ? void 0 : _b.active);
4467
+ const found = active == null ? void 0 : active[entitlementId];
4468
+ const entitlement = asRecord(found);
4469
+ if (!entitlement) return void 0;
4470
+ return entitlement.isActive === false ? void 0 : entitlement;
4471
+ };
4472
+ var isTrial = (entitlement) => typeof entitlement.periodType === "string" && entitlement.periodType.toUpperCase() === "TRIAL";
4473
+ var resolvePlanTier = (info, entitlementId) => {
4474
+ const entitlement = activeEntitlement(info, entitlementId);
4475
+ if (!entitlement) return "free";
4476
+ return isTrial(entitlement) ? "trial" : "paid";
4477
+ };
4478
+ var describePackage = (pkg) => {
4479
+ const props = {};
4480
+ if (!pkg) return props;
4481
+ if (typeof pkg.identifier === "string") props.package_id = pkg.identifier;
4482
+ if (typeof pkg.packageType === "string") props.package_type = pkg.packageType;
4483
+ if (typeof pkg.offeringIdentifier === "string") props.offering_id = pkg.offeringIdentifier;
4484
+ const product = asRecord(pkg.product);
4485
+ if (product) {
4486
+ if (typeof product.identifier === "string") props.product_id = product.identifier;
4487
+ if (typeof product.price === "number" && Number.isFinite(product.price)) {
4488
+ props.price = product.price;
4489
+ }
4490
+ if (typeof product.currencyCode === "string") props.currency = product.currencyCode;
4491
+ }
4492
+ return props;
4493
+ };
4494
+ var describeEntitlement = (info, entitlementId) => {
4495
+ const entitlement = activeEntitlement(info, entitlementId);
4496
+ const props = {
4497
+ entitlement: entitlementId,
4498
+ plan_tier: entitlement ? isTrial(entitlement) ? "trial" : "paid" : "free"
4499
+ };
4500
+ if (!entitlement) return props;
4501
+ if (typeof entitlement.periodType === "string") props.period_type = entitlement.periodType;
4502
+ props.is_trial = isTrial(entitlement);
4503
+ if (typeof entitlement.willRenew === "boolean") props.will_renew = entitlement.willRenew;
4504
+ if (typeof entitlement.store === "string") props.store = entitlement.store;
4505
+ if (typeof entitlement.productIdentifier === "string") {
4506
+ props.product_id = entitlement.productIdentifier;
4507
+ }
4508
+ if (typeof entitlement.isSandbox === "boolean") props.is_sandbox = entitlement.isSandbox;
4509
+ return props;
4510
+ };
4511
+ var PURCHASE_CANCELLED_CODE = "1";
4512
+ var isUserCancelled = (error) => {
4513
+ const record = asRecord(error);
4514
+ if (!record) return false;
4515
+ if (record.userCancelled === true) return true;
4516
+ return record.code === PURCHASE_CANCELLED_CODE;
4517
+ };
4518
+ var describeFailure = (error) => {
4519
+ const record = asRecord(error);
4520
+ const code = typeof (record == null ? void 0 : record.code) === "string" ? record.code : "unknown";
4521
+ return { reason: isUserCancelled(error) ? "cancelled" : "error", code };
4522
+ };
4523
+
4524
+ // src/revenuecat/revenueCatBridge.ts
4525
+ var createRevenueCatBridge = (config) => {
4526
+ const { analytics, entitlementId } = config;
4527
+ const writeTier = config.writePlanTier !== false;
4528
+ const emit = (event, props) => {
4529
+ try {
4530
+ const result = analytics.track(event, { ...config.commonProps, ...props });
4531
+ if (result && typeof result.catch === "function") {
4532
+ void result.catch(() => {
4533
+ });
4534
+ }
4535
+ } catch {
4536
+ }
4537
+ };
4538
+ const syncPlanTier = (customerInfo) => {
4539
+ var _a2;
4540
+ const tier = resolvePlanTier(customerInfo, entitlementId);
4541
+ if (!writeTier) return tier;
4542
+ try {
4543
+ (_a2 = analytics.setUserContext) == null ? void 0 : _a2.call(analytics, { extra: { [PLAN_TIER_CONTEXT_KEY]: tier } });
4544
+ } catch {
4545
+ }
4546
+ return tier;
4547
+ };
4548
+ const paywallShown = (offering, props) => {
4549
+ var _a2, _b;
4550
+ const shown = {
4551
+ packages_count: (_b = (_a2 = offering == null ? void 0 : offering.availablePackages) == null ? void 0 : _a2.length) != null ? _b : 0
4552
+ };
4553
+ if (typeof (offering == null ? void 0 : offering.identifier) === "string") shown.offering_id = offering.identifier;
4554
+ emit(WIRE_PURCHASE_EVENTS.paywallShown, { ...shown, ...props });
4555
+ };
4556
+ const checkoutStarted = (pkg, props) => {
4557
+ emit(WIRE_PURCHASE_EVENTS.checkoutStarted, { ...describePackage(pkg), ...props });
4558
+ };
4559
+ const purchaseCompleted = (customerInfo, pkg, props) => {
4560
+ const tier = syncPlanTier(customerInfo);
4561
+ const entitled = tier !== "free";
4562
+ if (entitled) {
4563
+ emit(WIRE_PURCHASE_EVENTS.purchased, {
4564
+ ...describePackage(pkg),
4565
+ ...describeEntitlement(customerInfo, entitlementId),
4566
+ ...props
4567
+ });
4568
+ return true;
4569
+ }
4570
+ emit(WIRE_PURCHASE_EVENTS.purchaseFailed, {
4571
+ ...describePackage(pkg),
4572
+ reason: "not_entitled",
4573
+ code: "unknown",
4574
+ entitlement: entitlementId,
4575
+ ...props
4576
+ });
4577
+ return false;
4578
+ };
4579
+ const purchaseFailed = (error, pkg, props) => {
4580
+ emit(WIRE_PURCHASE_EVENTS.purchaseFailed, {
4581
+ ...describePackage(pkg),
4582
+ ...describeFailure(error),
4583
+ ...props
4584
+ });
4585
+ };
4586
+ const purchasesRestored = (customerInfo, props) => {
4587
+ const tier = syncPlanTier(customerInfo);
4588
+ emit(WIRE_PURCHASE_EVENTS.restored, {
4589
+ ...describeEntitlement(customerInfo, entitlementId),
4590
+ ...props
4591
+ });
4592
+ return tier !== "free";
4593
+ };
4594
+ return { paywallShown, checkoutStarted, purchaseCompleted, purchaseFailed, purchasesRestored, syncPlanTier };
4595
+ };
4596
+
4264
4597
  // src/session-analytics/reportSessionStart.ts
4265
4598
  var SESSION_STARTED_EVENT = "app.session_started";
4266
4599
  var _emitted = /* @__PURE__ */ new Set();
@@ -4313,6 +4646,8 @@ var reportSessionStart = (opts) => {
4313
4646
  method: "POST",
4314
4647
  headers,
4315
4648
  body: JSON.stringify({ events: [event] })
4649
+ }).then((res) => {
4650
+ warnOnSkippedEvents(res);
4316
4651
  }).catch(() => {
4317
4652
  });
4318
4653
  } catch {
@@ -4323,6 +4658,12 @@ var useSessionStart = (config, options = {}) => {
4323
4658
  const latest = React18.useRef({ config, options });
4324
4659
  latest.current = { config, options };
4325
4660
  React18.useEffect(() => {
4661
+ const resolveDeviceKey = (cfg, opts) => {
4662
+ const host = typeof opts.deviceKey === "string" && opts.deviceKey.trim() ? opts.deviceKey : void 0;
4663
+ if (host) return host;
4664
+ if (!(cfg == null ? void 0 : cfg.storage)) return void 0;
4665
+ return resolveAutoDeviceKey({ appId: cfg.appId, storage: cfg.storage });
4666
+ };
4326
4667
  const fire = () => {
4327
4668
  var _a2, _b;
4328
4669
  const { config: cfg, options: opts } = latest.current;
@@ -4335,7 +4676,7 @@ var useSessionStart = (config, options = {}) => {
4335
4676
  target,
4336
4677
  // A fresh per-open id each fire; the emitter's once-guard dedupes within the open.
4337
4678
  userId: opts.userId,
4338
- deviceKey: opts.deviceKey,
4679
+ deviceKey: resolveDeviceKey(cfg, opts),
4339
4680
  sessionCount: opts.sessionCount,
4340
4681
  appVersion: (_b = cfg.appVersion) != null ? _b : device.appVersion,
4341
4682
  platform: reactNative.Platform.OS,
@@ -4443,11 +4784,13 @@ var reportFirstOpen = (opts) => {
4443
4784
  })();
4444
4785
  };
4445
4786
  var wireLifecycleEvents = (opts) => {
4446
- reportFirstOpen(opts);
4787
+ var _a2;
4788
+ const sessionId = (_a2 = opts.sessionId) != null ? _a2 : makeSessionId();
4789
+ reportFirstOpen({ ...opts, sessionId });
4447
4790
  reportSessionStart({
4448
4791
  target: opts.target,
4449
4792
  sink: opts.sink,
4450
- sessionId: opts.sessionId,
4793
+ sessionId,
4451
4794
  userId: opts.userId,
4452
4795
  deviceKey: opts.deviceKey,
4453
4796
  sessionCount: opts.sessionCount,
@@ -4582,6 +4925,7 @@ var createEventQueue = (options) => {
4582
4925
  const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
4583
4926
  try {
4584
4927
  const res = await fetch(req.url, { ...req.init, signal: controller == null ? void 0 : controller.signal });
4928
+ warnOnSkippedEvents(res);
4585
4929
  return !!(res && res.ok);
4586
4930
  } catch {
4587
4931
  return false;
@@ -4666,15 +5010,14 @@ var useLifecycleEvents = (config, options = {}) => {
4666
5010
  latest.current = { config, options };
4667
5011
  const queueRef = React18.useRef(void 0);
4668
5012
  React18.useEffect(() => {
4669
- var _a2;
4670
5013
  const resolveSink = () => {
4671
- var _a3, _b;
5014
+ var _a2, _b;
4672
5015
  const { config: cfg, options: opts } = latest.current;
4673
5016
  if (opts.sink) return opts.sink;
4674
5017
  if (!(cfg == null ? void 0 : cfg.serverUrl)) return void 0;
4675
5018
  if (!queueRef.current) {
4676
5019
  queueRef.current = createEventQueue({
4677
- target: { serverUrl: cfg.serverUrl, apiKey: (_a3 = cfg.apiKey) != null ? _a3 : "" },
5020
+ target: { serverUrl: cfg.serverUrl, apiKey: (_a2 = cfg.apiKey) != null ? _a2 : "" },
4678
5021
  storage: cfg.storage,
4679
5022
  // Dedicated key so the hook's internal queue never collides with a host's main queue.
4680
5023
  storageKey: `wireai:evtq:lifecycle:${(_b = cfg.appId) != null ? _b : "default"}`,
@@ -4684,12 +5027,18 @@ var useLifecycleEvents = (config, options = {}) => {
4684
5027
  return queueRef.current.enqueue;
4685
5028
  };
4686
5029
  const targetOf = (cfg) => {
4687
- var _a3;
4688
- return (cfg == null ? void 0 : cfg.serverUrl) ? { serverUrl: cfg.serverUrl, apiKey: (_a3 = cfg.apiKey) != null ? _a3 : "" } : void 0;
5030
+ var _a2;
5031
+ return (cfg == null ? void 0 : cfg.serverUrl) ? { serverUrl: cfg.serverUrl, apiKey: (_a2 = cfg.apiKey) != null ? _a2 : "" } : void 0;
5032
+ };
5033
+ const resolveDeviceKey = (cfg, opts) => {
5034
+ const host = typeof opts.deviceKey === "string" && opts.deviceKey.trim() ? opts.deviceKey : void 0;
5035
+ if (host) return host;
5036
+ if (!(cfg == null ? void 0 : cfg.storage)) return void 0;
5037
+ return resolveAutoDeviceKey({ appId: cfg.appId, storage: cfg.storage });
4689
5038
  };
4690
5039
  const mountOpenSessionId = makeSessionId();
4691
5040
  const fireSession = (sessionId) => {
4692
- var _a3;
5041
+ var _a2;
4693
5042
  const { config: cfg, options: opts } = latest.current;
4694
5043
  if (opts.enabled === false) return;
4695
5044
  if (!(cfg == null ? void 0 : cfg.serverUrl) && !opts.sink) return;
@@ -4700,35 +5049,45 @@ var useLifecycleEvents = (config, options = {}) => {
4700
5049
  sink: resolveSink(),
4701
5050
  sessionId,
4702
5051
  userId: opts.userId,
4703
- deviceKey: opts.deviceKey,
5052
+ deviceKey: resolveDeviceKey(cfg, opts),
4704
5053
  sessionCount: opts.sessionCount,
4705
- appVersion: (_a3 = cfg == null ? void 0 : cfg.appVersion) != null ? _a3 : device.appVersion,
5054
+ appVersion: (_a2 = cfg == null ? void 0 : cfg.appVersion) != null ? _a2 : device.appVersion,
4706
5055
  platform: reactNative.Platform.OS,
4707
5056
  device,
4708
5057
  meta: opts.meta
4709
5058
  });
4710
5059
  };
4711
- fireSession(mountOpenSessionId);
4712
- {
5060
+ const fireMountOpen = () => {
5061
+ var _a2;
5062
+ fireSession(mountOpenSessionId);
4713
5063
  const { config: cfg, options: opts } = latest.current;
4714
- if (opts.enabled !== false) {
4715
- const device = collectDeviceContext();
4716
- if (cfg == null ? void 0 : cfg.appVersion) device.appVersion = cfg.appVersion;
4717
- reportFirstOpen({
4718
- target: targetOf(cfg),
4719
- sink: resolveSink(),
4720
- sessionId: mountOpenSessionId,
4721
- storage: cfg == null ? void 0 : cfg.storage,
4722
- appId: cfg == null ? void 0 : cfg.appId,
4723
- userId: opts.userId,
4724
- deviceKey: opts.deviceKey,
4725
- sessionCount: opts.sessionCount,
4726
- appVersion: (_a2 = cfg == null ? void 0 : cfg.appVersion) != null ? _a2 : device.appVersion,
4727
- platform: reactNative.Platform.OS,
4728
- device,
4729
- meta: opts.meta
4730
- });
4731
- }
5064
+ if (opts.enabled === false) return;
5065
+ const device = collectDeviceContext();
5066
+ if (cfg == null ? void 0 : cfg.appVersion) device.appVersion = cfg.appVersion;
5067
+ reportFirstOpen({
5068
+ target: targetOf(cfg),
5069
+ sink: resolveSink(),
5070
+ sessionId: mountOpenSessionId,
5071
+ storage: cfg == null ? void 0 : cfg.storage,
5072
+ appId: cfg == null ? void 0 : cfg.appId,
5073
+ userId: opts.userId,
5074
+ deviceKey: resolveDeviceKey(cfg, opts),
5075
+ sessionCount: opts.sessionCount,
5076
+ appVersion: (_a2 = cfg == null ? void 0 : cfg.appVersion) != null ? _a2 : device.appVersion,
5077
+ platform: reactNative.Platform.OS,
5078
+ device,
5079
+ meta: opts.meta
5080
+ });
5081
+ };
5082
+ let cancelled = false;
5083
+ const { config: mountCfg, options: mountOpts } = latest.current;
5084
+ const hostKey = typeof mountOpts.deviceKey === "string" && mountOpts.deviceKey.trim() ? mountOpts.deviceKey : void 0;
5085
+ if (!hostKey && (mountCfg == null ? void 0 : mountCfg.storage)) {
5086
+ void hydrateAutoDeviceKey({ appId: mountCfg.appId, storage: mountCfg.storage }).then(() => {
5087
+ if (!cancelled) fireMountOpen();
5088
+ });
5089
+ } else {
5090
+ fireMountOpen();
4732
5091
  }
4733
5092
  let backgroundedAt = null;
4734
5093
  const onChange = (state) => {
@@ -4744,6 +5103,7 @@ var useLifecycleEvents = (config, options = {}) => {
4744
5103
  };
4745
5104
  const sub = reactNative.AppState.addEventListener("change", onChange);
4746
5105
  return () => {
5106
+ cancelled = true;
4747
5107
  if (sub && typeof sub.remove === "function") sub.remove();
4748
5108
  };
4749
5109
  }, []);
@@ -4774,6 +5134,7 @@ exports.OnboardingButton = Button;
4774
5134
  exports.OnboardingFlow = OnboardingFlow;
4775
5135
  exports.OnboardingScaffold = OnboardingScaffold;
4776
5136
  exports.OnboardingThemeProvider = OnboardingThemeProvider;
5137
+ exports.PLAN_TIER_CONTEXT_KEY = PLAN_TIER_CONTEXT_KEY;
4777
5138
  exports.RESERVED_USER_CONTEXT_KEYS = RESERVED_USER_CONTEXT_KEYS;
4778
5139
  exports.SESSION_STARTED_EVENT = SESSION_STARTED_EVENT;
4779
5140
  exports.SelectionCard = SelectionCard;
@@ -4781,12 +5142,16 @@ exports.StatusCard = StatusCard;
4781
5142
  exports.StepProgress = StepProgress;
4782
5143
  exports.TextInputCard = TextInputCard;
4783
5144
  exports.USER_ID_MAX_LENGTH = USER_ID_MAX_LENGTH;
5145
+ exports.WIRE_ENV_VARS = WIRE_ENV_VARS;
4784
5146
  exports.WIRE_ICON_GLYPHS = WIRE_ICON_GLYPHS;
4785
5147
  exports.WIRE_ICON_NAMES = WIRE_ICON_NAMES;
4786
5148
  exports.WIRE_ONBOARDING_EVENTS = WIRE_ONBOARDING_EVENTS;
5149
+ exports.WIRE_PURCHASE_EVENTS = WIRE_PURCHASE_EVENTS;
4787
5150
  exports.WireFeaturesProvider = WireFeaturesProvider;
4788
5151
  exports.WireIcon = WireIcon;
4789
5152
  exports.WireOnboarding = WireOnboarding;
5153
+ exports.activationJoinContext = activationJoinContext;
5154
+ exports.activeEntitlement = activeEntitlement;
4790
5155
  exports.analyticsUserIdStorageKey = analyticsUserIdStorageKey;
4791
5156
  exports.attributionMetadata = attributionMetadata;
4792
5157
  exports.bumpActivationRevalidation = bumpActivationRevalidation;
@@ -4794,14 +5159,19 @@ exports.clearPersistedSession = clearPersistedSession;
4794
5159
  exports.clearPiiFromContext = clearPiiFromContext;
4795
5160
  exports.clearUserContext = clearUserContext;
4796
5161
  exports.collectDeviceContext = collectDeviceContext;
5162
+ exports.createRevenueCatBridge = createRevenueCatBridge;
4797
5163
  exports.createWireActivation = createWireActivation;
4798
5164
  exports.defaultIllustrations = defaultIllustrations;
4799
5165
  exports.defaultOnboardingTheme = defaultOnboardingTheme;
4800
5166
  exports.defaultWireFeatures = defaultWireFeatures;
4801
5167
  exports.deriveAnswers = deriveAnswers;
5168
+ exports.describeEntitlement = describeEntitlement;
5169
+ exports.describeFailure = describeFailure;
5170
+ exports.describePackage = describePackage;
4802
5171
  exports.detectAppVersion = detectAppVersion;
4803
5172
  exports.detectNativeModel = detectNativeModel;
4804
5173
  exports.deviceIdStorageKey = deviceIdStorageKey;
5174
+ exports.ensureCurrentSessionId = ensureCurrentSessionId;
4805
5175
  exports.featuresCacheKey = featuresCacheKey;
4806
5176
  exports.featuresEqual = featuresEqual;
4807
5177
  exports.fetchWireFeatures = fetchWireFeatures;
@@ -4809,9 +5179,11 @@ exports.firstOpenStorageKey = firstOpenStorageKey;
4809
5179
  exports.getActivationRevalidationVersion = getActivationRevalidationVersion;
4810
5180
  exports.getCurrentSessionId = getCurrentSessionId;
4811
5181
  exports.hashEmailFnv1a = hashEmailFnv1a;
5182
+ exports.hydrateAutoDeviceKey = hydrateAutoDeviceKey;
4812
5183
  exports.identifyOnboarding = identifyOnboarding;
4813
5184
  exports.isFeaturesFresh = isFeaturesFresh;
4814
5185
  exports.isOnboardingEnabled = isOnboardingEnabled;
5186
+ exports.isUserCancelled = isUserCancelled;
4815
5187
  exports.isWireScalar = isWireScalar;
4816
5188
  exports.loadPersistedSession = loadPersistedSession;
4817
5189
  exports.looksLikeEmail = looksLikeEmail;
@@ -4833,9 +5205,12 @@ exports.reportClientEventsAwait = reportClientEventsAwait;
4833
5205
  exports.reportFirstOpen = reportFirstOpen;
4834
5206
  exports.reportSessionStart = reportSessionStart;
4835
5207
  exports.resetActivationRevalidation = resetActivationRevalidation;
5208
+ exports.resetAutoDeviceKeys = resetAutoDeviceKeys;
4836
5209
  exports.resetCurrentSessionId = resetCurrentSessionId;
4837
5210
  exports.resetFirstOpenLatch = resetFirstOpenLatch;
4838
5211
  exports.resetSessionStartGuard = resetSessionStartGuard;
5212
+ exports.resolveAutoDeviceKey = resolveAutoDeviceKey;
5213
+ exports.resolvePlanTier = resolvePlanTier;
4839
5214
  exports.resolveUserContext = resolveUserContext;
4840
5215
  exports.sanitizeUserId = sanitizeUserId;
4841
5216
  exports.savePersistedSession = savePersistedSession;