@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
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:
|
|
3443
|
-
[device,
|
|
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
|
-
...
|
|
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,
|
|
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)
|
|
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
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
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
|
-
|
|
4217
|
-
{
|
|
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, ...(
|
|
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
|
-
|
|
4226
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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: (
|
|
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
|
|
4688
|
-
return (cfg == null ? void 0 : cfg.serverUrl) ? { serverUrl: cfg.serverUrl, apiKey: (
|
|
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
|
|
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
|
|
5052
|
+
deviceKey: resolveDeviceKey(cfg, opts),
|
|
4704
5053
|
sessionCount: opts.sessionCount,
|
|
4705
|
-
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
|
-
|
|
4712
|
-
|
|
5060
|
+
const fireMountOpen = () => {
|
|
5061
|
+
var _a2;
|
|
5062
|
+
fireSession(mountOpenSessionId);
|
|
4713
5063
|
const { config: cfg, options: opts } = latest.current;
|
|
4714
|
-
if (opts.enabled
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
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;
|