@wireai/activation 0.12.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 +13 -10
- package/CHANGELOG.md +130 -0
- package/INTEGRATION_PROMPT.md +14 -3
- package/README.md +40 -2
- package/dist/analytics/index.d.mts +5 -4
- package/dist/analytics/index.d.ts +5 -4
- package/dist/analytics/index.js +39 -24
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +39 -24
- 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-DsSDHqor.d.mts → currentSession-BlCeDP0f.d.mts} +59 -5
- package/dist/{currentSession-D6RiVtc8.d.ts → currentSession-BxEB37xt.d.ts} +59 -5
- package/dist/index.d.mts +10 -20
- package/dist/index.d.ts +10 -20
- package/dist/index.js +301 -187
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +300 -188
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.js +41 -19
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +41 -19
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.js +41 -19
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +41 -19
- 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/llms.txt +2 -0
- package/package.json +1 -1
- package/src/WireOnboarding.tsx +140 -5
- package/src/activation/wireActivation.ts +8 -1
- package/src/analytics/analyticsFacade.ts +15 -11
- package/src/analytics/reportClientEvent.ts +11 -7
- package/src/coachmarks/runtime.ts +53 -17
- package/src/config/wireConfigFromEnv.ts +46 -2
- package/src/context/deviceId.ts +82 -18
- package/src/index.ts +9 -1
- package/src/questionnaire/runtime.ts +1 -0
- package/src/questionnaire/useQuestionnaireGate.ts +8 -3
- package/src/reviews/runtime.ts +68 -7
- package/src/reviews/useReviewGate.ts +8 -3
- package/src/session-analytics/useLifecycleEvents.ts +57 -27
- package/src/session-analytics/useSessionStart.ts +37 -1
- package/src/types.ts +41 -4
package/dist/index.js
CHANGED
|
@@ -1519,17 +1519,17 @@ var buildEventsRequest = (target, events) => {
|
|
|
1519
1519
|
}
|
|
1520
1520
|
};
|
|
1521
1521
|
var warnOnSkippedEvents = (res) => {
|
|
1522
|
+
if (typeof __DEV__ === "undefined" || !__DEV__) return;
|
|
1523
|
+
if (typeof console === "undefined" || !console.warn) return;
|
|
1522
1524
|
try {
|
|
1523
1525
|
const json = res == null ? void 0 : res.json;
|
|
1524
1526
|
if (typeof json !== "function") return;
|
|
1525
1527
|
void Promise.resolve(json.call(res)).then((body) => {
|
|
1526
1528
|
const skipped = body == null ? void 0 : body.skipped;
|
|
1527
1529
|
if (typeof skipped !== "number" || skipped <= 0) return;
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
);
|
|
1532
|
-
}
|
|
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
1533
|
}).catch(() => {
|
|
1534
1534
|
});
|
|
1535
1535
|
} catch {
|
|
@@ -3317,6 +3317,82 @@ var collectDeviceContext = () => {
|
|
|
3317
3317
|
return ctx;
|
|
3318
3318
|
};
|
|
3319
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
|
+
|
|
3320
3396
|
// src/analytics/currentSession.ts
|
|
3321
3397
|
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
3322
3398
|
"@wireai/activation:currentSessionId"
|
|
@@ -3442,6 +3518,105 @@ var identifyOnboarding = async (opts) => {
|
|
|
3442
3518
|
);
|
|
3443
3519
|
return true;
|
|
3444
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;
|
|
3445
3620
|
var WireOnboarding = ({
|
|
3446
3621
|
config,
|
|
3447
3622
|
theme,
|
|
@@ -3464,7 +3639,8 @@ var WireOnboarding = ({
|
|
|
3464
3639
|
persistKey,
|
|
3465
3640
|
retainSessionOnComplete,
|
|
3466
3641
|
userContext,
|
|
3467
|
-
userId
|
|
3642
|
+
userId,
|
|
3643
|
+
autoJoinKey = true
|
|
3468
3644
|
}) => {
|
|
3469
3645
|
var _a2, _b, _c;
|
|
3470
3646
|
const boundUserId = React18.useMemo(() => sanitizeUserId(userId), [userId]);
|
|
@@ -3477,9 +3653,50 @@ var WireOnboarding = ({
|
|
|
3477
3653
|
() => userContextKey ? JSON.parse(userContextKey) : void 0,
|
|
3478
3654
|
[userContextKey]
|
|
3479
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]);
|
|
3480
3697
|
const clientContext = React18.useMemo(
|
|
3481
|
-
() => ({ device, userContext:
|
|
3482
|
-
[device,
|
|
3698
|
+
() => ({ device, userContext: effectiveUserContext, userId: boundUserId }),
|
|
3699
|
+
[device, effectiveUserContext, boundUserId]
|
|
3483
3700
|
);
|
|
3484
3701
|
const [session, setSession] = React18.useState(
|
|
3485
3702
|
() => storage ? null : { id: makeSessionId(), resumed: false }
|
|
@@ -3526,7 +3743,7 @@ var WireOnboarding = ({
|
|
|
3526
3743
|
// Device snapshot + host-injected user context ride the session-start metadata so the
|
|
3527
3744
|
// backend can segment the funnel. Old servers ignore these unknown keys (backward compat).
|
|
3528
3745
|
device,
|
|
3529
|
-
...
|
|
3746
|
+
...effectiveUserContext ? { userContext: effectiveUserContext } : {},
|
|
3530
3747
|
// The session-start user id (from the ref) rides the session-start metadata so the
|
|
3531
3748
|
// server binds the session to a real user at creation. Reading the ref — not
|
|
3532
3749
|
// `boundUserId` — keeps this memo off the userId dependency, so a mid-session change
|
|
@@ -3535,7 +3752,7 @@ var WireOnboarding = ({
|
|
|
3535
3752
|
},
|
|
3536
3753
|
timeoutMs: 6e4
|
|
3537
3754
|
};
|
|
3538
|
-
}, [config.serverUrl, config.appId, config.apiKey, metadataStable, sessionId, componentsStable, device,
|
|
3755
|
+
}, [config.serverUrl, config.appId, config.apiKey, metadataStable, sessionId, componentsStable, device, effectiveUserContext]);
|
|
3539
3756
|
const reportTarget = React18.useMemo(
|
|
3540
3757
|
() => ({ serverUrl: config.serverUrl, apiKey: config.apiKey }),
|
|
3541
3758
|
[config.serverUrl, config.apiKey]
|
|
@@ -3557,7 +3774,7 @@ var WireOnboarding = ({
|
|
|
3557
3774
|
});
|
|
3558
3775
|
}, [session, sessionId, boundUserId, reportTarget]);
|
|
3559
3776
|
const cards = components != null ? components : onboardingComponents;
|
|
3560
|
-
if (!session) {
|
|
3777
|
+
if (!session || autoJoinPending) {
|
|
3561
3778
|
return /* @__PURE__ */ jsxRuntime.jsx(OnboardingThemeProvider, { theme, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3562
3779
|
LoadingScreen,
|
|
3563
3780
|
{
|
|
@@ -4023,16 +4240,37 @@ var useResolvedFeatures = (options) => {
|
|
|
4023
4240
|
};
|
|
4024
4241
|
|
|
4025
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
|
+
};
|
|
4026
4253
|
var wireConfigFromEnv = (overrides) => {
|
|
4027
|
-
var _a2, _b, _c, _d;
|
|
4254
|
+
var _a2, _b, _c, _d, _e;
|
|
4028
4255
|
const apiKey = (_a2 = overrides == null ? void 0 : overrides.apiKey) != null ? _a2 : process.env.EXPO_PUBLIC_WIREAI_API_KEY;
|
|
4029
4256
|
const serverUrl = (_b = overrides == null ? void 0 : overrides.serverUrl) != null ? _b : process.env.EXPO_PUBLIC_WIREAI_SERVER_URL;
|
|
4030
4257
|
const appId = (_d = (_c = overrides == null ? void 0 : overrides.appId) != null ? _c : process.env.EXPO_PUBLIC_WIREAI_APP_ID) != null ? _d : "default";
|
|
4031
|
-
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();
|
|
4032
4269
|
return {
|
|
4033
4270
|
apiKey,
|
|
4034
4271
|
serverUrl,
|
|
4035
4272
|
appId,
|
|
4273
|
+
...appVersion ? { appVersion } : {},
|
|
4036
4274
|
...(overrides == null ? void 0 : overrides.metadata) ? { metadata: overrides.metadata } : {}
|
|
4037
4275
|
};
|
|
4038
4276
|
};
|
|
@@ -4093,149 +4331,6 @@ var attributionMetadata = (a) => {
|
|
|
4093
4331
|
return { attribution };
|
|
4094
4332
|
};
|
|
4095
4333
|
|
|
4096
|
-
// src/context/userContext.ts
|
|
4097
|
-
var RESERVED_USER_CONTEXT_KEYS = [
|
|
4098
|
-
"device_key",
|
|
4099
|
-
"app_version",
|
|
4100
|
-
"app_build",
|
|
4101
|
-
"network_type",
|
|
4102
|
-
"session_count",
|
|
4103
|
-
"returning",
|
|
4104
|
-
"platform",
|
|
4105
|
-
"user_email",
|
|
4106
|
-
"user_email_hashed"
|
|
4107
|
-
];
|
|
4108
|
-
var EXTRA_KEY_PREFIX = "custom.";
|
|
4109
|
-
var isWireScalar = (value) => {
|
|
4110
|
-
const t = typeof value;
|
|
4111
|
-
if (t === "string" || t === "boolean") return true;
|
|
4112
|
-
if (t === "number") return Number.isFinite(value);
|
|
4113
|
-
return false;
|
|
4114
|
-
};
|
|
4115
|
-
var hashEmailFnv1a = (email) => {
|
|
4116
|
-
const normalized = email.trim().toLowerCase();
|
|
4117
|
-
let hash = 2166136261;
|
|
4118
|
-
for (let i = 0; i < normalized.length; i++) {
|
|
4119
|
-
hash ^= normalized.charCodeAt(i);
|
|
4120
|
-
hash = Math.imul(hash, 16777619);
|
|
4121
|
-
}
|
|
4122
|
-
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
4123
|
-
};
|
|
4124
|
-
var cleanString = (value) => {
|
|
4125
|
-
if (typeof value !== "string") return void 0;
|
|
4126
|
-
const trimmed = value.trim();
|
|
4127
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
4128
|
-
};
|
|
4129
|
-
var namespaceExtra = (extra) => {
|
|
4130
|
-
const out = {};
|
|
4131
|
-
if (!extra || typeof extra !== "object") return out;
|
|
4132
|
-
for (const [key, value] of Object.entries(extra)) {
|
|
4133
|
-
const cleanKey = cleanString(key);
|
|
4134
|
-
if (!cleanKey) continue;
|
|
4135
|
-
if (!isWireScalar(value)) continue;
|
|
4136
|
-
out[`${EXTRA_KEY_PREFIX}${cleanKey}`] = value;
|
|
4137
|
-
}
|
|
4138
|
-
return out;
|
|
4139
|
-
};
|
|
4140
|
-
var analyticsUserIdStorageKey = (appId) => `wireai:analytics:userId:${appId != null ? appId : "default"}`;
|
|
4141
|
-
var clearPiiFromContext = (ctx = {}) => {
|
|
4142
|
-
const rest = {};
|
|
4143
|
-
if (typeof ctx.appVersion === "string") rest.appVersion = ctx.appVersion;
|
|
4144
|
-
if (typeof ctx.deviceKey === "string") rest.deviceKey = ctx.deviceKey;
|
|
4145
|
-
return rest;
|
|
4146
|
-
};
|
|
4147
|
-
var clearUserContext = async (opts = {}) => {
|
|
4148
|
-
const storage = opts.storage;
|
|
4149
|
-
if (!storage) return;
|
|
4150
|
-
try {
|
|
4151
|
-
await storage.removeItem(analyticsUserIdStorageKey(opts.appId));
|
|
4152
|
-
} catch {
|
|
4153
|
-
}
|
|
4154
|
-
};
|
|
4155
|
-
var activationJoinContext = (deviceKey) => {
|
|
4156
|
-
var _a2;
|
|
4157
|
-
return (_a2 = resolveUserContext({ deviceKey }).userContext) != null ? _a2 : {};
|
|
4158
|
-
};
|
|
4159
|
-
var resolveUserContext = (ctx = {}, opts = {}) => {
|
|
4160
|
-
var _a2;
|
|
4161
|
-
const result = {};
|
|
4162
|
-
const bucket = {};
|
|
4163
|
-
const userId = sanitizeUserId(ctx.userId);
|
|
4164
|
-
if (userId) result.userId = userId;
|
|
4165
|
-
const deviceKey = cleanString(ctx.deviceKey);
|
|
4166
|
-
if (deviceKey) {
|
|
4167
|
-
result.deviceKey = deviceKey;
|
|
4168
|
-
bucket.device_key = deviceKey;
|
|
4169
|
-
}
|
|
4170
|
-
const appVersion = (_a2 = cleanString(ctx.appVersion)) != null ? _a2 : cleanString(opts.autoAppVersion);
|
|
4171
|
-
if (appVersion) {
|
|
4172
|
-
result.appVersion = appVersion;
|
|
4173
|
-
bucket.app_version = appVersion;
|
|
4174
|
-
}
|
|
4175
|
-
const email = cleanString(ctx.userEmail);
|
|
4176
|
-
if (email) {
|
|
4177
|
-
if (ctx.hashEmail) {
|
|
4178
|
-
bucket.user_email = hashEmailFnv1a(email);
|
|
4179
|
-
bucket.user_email_hashed = true;
|
|
4180
|
-
} else {
|
|
4181
|
-
bucket.user_email = email;
|
|
4182
|
-
}
|
|
4183
|
-
}
|
|
4184
|
-
Object.assign(bucket, namespaceExtra(ctx.extra));
|
|
4185
|
-
if (Object.keys(bucket).length > 0) result.userContext = bucket;
|
|
4186
|
-
return result;
|
|
4187
|
-
};
|
|
4188
|
-
|
|
4189
|
-
// src/context/deviceId.ts
|
|
4190
|
-
var AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
4191
|
-
var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
|
|
4192
|
-
var randomChunk = () => Math.floor(Math.random() * 4294967296).toString(36).padStart(6, "0");
|
|
4193
|
-
var mintDeviceId = () => {
|
|
4194
|
-
const time = Date.now().toString(36);
|
|
4195
|
-
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
4196
|
-
};
|
|
4197
|
-
var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
|
|
4198
|
-
var deviceKeyGlobal = globalThis;
|
|
4199
|
-
var autoDeviceKeyRegistry = () => {
|
|
4200
|
-
const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
|
|
4201
|
-
if (existing) return existing;
|
|
4202
|
-
const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
|
|
4203
|
-
deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
|
|
4204
|
-
return created;
|
|
4205
|
-
};
|
|
4206
|
-
var resolveAutoDeviceKey = (opts = {}) => {
|
|
4207
|
-
var _a2, _b;
|
|
4208
|
-
const registry = autoDeviceKeyRegistry();
|
|
4209
|
-
const appId = (_a2 = opts.appId) != null ? _a2 : "default";
|
|
4210
|
-
let id = registry.keys.get(appId);
|
|
4211
|
-
if (!id) {
|
|
4212
|
-
id = mintDeviceId();
|
|
4213
|
-
registry.keys.set(appId, id);
|
|
4214
|
-
}
|
|
4215
|
-
const storage = opts.storage;
|
|
4216
|
-
if (storage && !registry.hydrating.has(appId)) {
|
|
4217
|
-
registry.hydrating.add(appId);
|
|
4218
|
-
const slot = deviceIdStorageKey(appId);
|
|
4219
|
-
const minted = id;
|
|
4220
|
-
try {
|
|
4221
|
-
void Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
4222
|
-
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
4223
|
-
if (persisted) registry.keys.set(appId, persisted);
|
|
4224
|
-
else void Promise.resolve(storage.setItem(slot, minted)).catch(() => {
|
|
4225
|
-
});
|
|
4226
|
-
}).catch(() => {
|
|
4227
|
-
});
|
|
4228
|
-
} catch {
|
|
4229
|
-
}
|
|
4230
|
-
}
|
|
4231
|
-
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
4232
|
-
};
|
|
4233
|
-
var resetAutoDeviceKeys = () => {
|
|
4234
|
-
const registry = autoDeviceKeyRegistry();
|
|
4235
|
-
registry.keys.clear();
|
|
4236
|
-
registry.hydrating.clear();
|
|
4237
|
-
};
|
|
4238
|
-
|
|
4239
4334
|
// src/activation/revalidation.ts
|
|
4240
4335
|
var REVALIDATION_SLOT = /* @__PURE__ */ Symbol.for(
|
|
4241
4336
|
"@wireai/activation:activationRevalidation"
|
|
@@ -4288,18 +4383,19 @@ var createWireActivation = (config) => {
|
|
|
4288
4383
|
storage: explicitDeviceKey ? void 0 : config.storage
|
|
4289
4384
|
};
|
|
4290
4385
|
if (!explicitDeviceKey) resolveAutoDeviceKey(autoDeviceKeyOptions);
|
|
4386
|
+
const detectedAppVersion = detectAppVersion();
|
|
4291
4387
|
const applyContext = (event) => {
|
|
4292
|
-
var _a3, _b2;
|
|
4388
|
+
var _a3, _b2, _c;
|
|
4293
4389
|
const resolved = resolveUserContext(
|
|
4294
4390
|
// `??` is lazy on purpose: an explicit key must never even touch the auto registry.
|
|
4295
4391
|
{
|
|
4296
4392
|
...(_a3 = config.userContext) != null ? _a3 : {},
|
|
4297
4393
|
deviceKey: explicitDeviceKey != null ? explicitDeviceKey : resolveAutoDeviceKey(autoDeviceKeyOptions)
|
|
4298
4394
|
},
|
|
4299
|
-
{ autoAppVersion: config.appVersion }
|
|
4395
|
+
{ autoAppVersion: (_b2 = config.appVersion) != null ? _b2 : detectedAppVersion }
|
|
4300
4396
|
);
|
|
4301
4397
|
if (resolved.userContext) {
|
|
4302
|
-
event.user_context = { ...resolved.userContext, ...(
|
|
4398
|
+
event.user_context = { ...resolved.userContext, ...(_c = event.user_context) != null ? _c : {} };
|
|
4303
4399
|
}
|
|
4304
4400
|
if (resolved.userId && !event.user_id) event.user_id = resolved.userId;
|
|
4305
4401
|
};
|
|
@@ -4562,6 +4658,12 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4562
4658
|
const latest = React18.useRef({ config, options });
|
|
4563
4659
|
latest.current = { config, options };
|
|
4564
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
|
+
};
|
|
4565
4667
|
const fire = () => {
|
|
4566
4668
|
var _a2, _b;
|
|
4567
4669
|
const { config: cfg, options: opts } = latest.current;
|
|
@@ -4574,7 +4676,7 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4574
4676
|
target,
|
|
4575
4677
|
// A fresh per-open id each fire; the emitter's once-guard dedupes within the open.
|
|
4576
4678
|
userId: opts.userId,
|
|
4577
|
-
deviceKey: opts
|
|
4679
|
+
deviceKey: resolveDeviceKey(cfg, opts),
|
|
4578
4680
|
sessionCount: opts.sessionCount,
|
|
4579
4681
|
appVersion: (_b = cfg.appVersion) != null ? _b : device.appVersion,
|
|
4580
4682
|
platform: reactNative.Platform.OS,
|
|
@@ -4908,15 +5010,14 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
4908
5010
|
latest.current = { config, options };
|
|
4909
5011
|
const queueRef = React18.useRef(void 0);
|
|
4910
5012
|
React18.useEffect(() => {
|
|
4911
|
-
var _a2;
|
|
4912
5013
|
const resolveSink = () => {
|
|
4913
|
-
var
|
|
5014
|
+
var _a2, _b;
|
|
4914
5015
|
const { config: cfg, options: opts } = latest.current;
|
|
4915
5016
|
if (opts.sink) return opts.sink;
|
|
4916
5017
|
if (!(cfg == null ? void 0 : cfg.serverUrl)) return void 0;
|
|
4917
5018
|
if (!queueRef.current) {
|
|
4918
5019
|
queueRef.current = createEventQueue({
|
|
4919
|
-
target: { serverUrl: cfg.serverUrl, apiKey: (
|
|
5020
|
+
target: { serverUrl: cfg.serverUrl, apiKey: (_a2 = cfg.apiKey) != null ? _a2 : "" },
|
|
4920
5021
|
storage: cfg.storage,
|
|
4921
5022
|
// Dedicated key so the hook's internal queue never collides with a host's main queue.
|
|
4922
5023
|
storageKey: `wireai:evtq:lifecycle:${(_b = cfg.appId) != null ? _b : "default"}`,
|
|
@@ -4926,8 +5027,8 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
4926
5027
|
return queueRef.current.enqueue;
|
|
4927
5028
|
};
|
|
4928
5029
|
const targetOf = (cfg) => {
|
|
4929
|
-
var
|
|
4930
|
-
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;
|
|
4931
5032
|
};
|
|
4932
5033
|
const resolveDeviceKey = (cfg, opts) => {
|
|
4933
5034
|
const host = typeof opts.deviceKey === "string" && opts.deviceKey.trim() ? opts.deviceKey : void 0;
|
|
@@ -4937,7 +5038,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
4937
5038
|
};
|
|
4938
5039
|
const mountOpenSessionId = makeSessionId();
|
|
4939
5040
|
const fireSession = (sessionId) => {
|
|
4940
|
-
var
|
|
5041
|
+
var _a2;
|
|
4941
5042
|
const { config: cfg, options: opts } = latest.current;
|
|
4942
5043
|
if (opts.enabled === false) return;
|
|
4943
5044
|
if (!(cfg == null ? void 0 : cfg.serverUrl) && !opts.sink) return;
|
|
@@ -4950,33 +5051,43 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
4950
5051
|
userId: opts.userId,
|
|
4951
5052
|
deviceKey: resolveDeviceKey(cfg, opts),
|
|
4952
5053
|
sessionCount: opts.sessionCount,
|
|
4953
|
-
appVersion: (
|
|
5054
|
+
appVersion: (_a2 = cfg == null ? void 0 : cfg.appVersion) != null ? _a2 : device.appVersion,
|
|
4954
5055
|
platform: reactNative.Platform.OS,
|
|
4955
5056
|
device,
|
|
4956
5057
|
meta: opts.meta
|
|
4957
5058
|
});
|
|
4958
5059
|
};
|
|
4959
|
-
|
|
4960
|
-
|
|
5060
|
+
const fireMountOpen = () => {
|
|
5061
|
+
var _a2;
|
|
5062
|
+
fireSession(mountOpenSessionId);
|
|
4961
5063
|
const { config: cfg, options: opts } = latest.current;
|
|
4962
|
-
if (opts.enabled
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
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();
|
|
4980
5091
|
}
|
|
4981
5092
|
let backgroundedAt = null;
|
|
4982
5093
|
const onChange = (state) => {
|
|
@@ -4992,6 +5103,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
4992
5103
|
};
|
|
4993
5104
|
const sub = reactNative.AppState.addEventListener("change", onChange);
|
|
4994
5105
|
return () => {
|
|
5106
|
+
cancelled = true;
|
|
4995
5107
|
if (sub && typeof sub.remove === "function") sub.remove();
|
|
4996
5108
|
};
|
|
4997
5109
|
}, []);
|
|
@@ -5030,6 +5142,7 @@ exports.StatusCard = StatusCard;
|
|
|
5030
5142
|
exports.StepProgress = StepProgress;
|
|
5031
5143
|
exports.TextInputCard = TextInputCard;
|
|
5032
5144
|
exports.USER_ID_MAX_LENGTH = USER_ID_MAX_LENGTH;
|
|
5145
|
+
exports.WIRE_ENV_VARS = WIRE_ENV_VARS;
|
|
5033
5146
|
exports.WIRE_ICON_GLYPHS = WIRE_ICON_GLYPHS;
|
|
5034
5147
|
exports.WIRE_ICON_NAMES = WIRE_ICON_NAMES;
|
|
5035
5148
|
exports.WIRE_ONBOARDING_EVENTS = WIRE_ONBOARDING_EVENTS;
|
|
@@ -5066,6 +5179,7 @@ exports.firstOpenStorageKey = firstOpenStorageKey;
|
|
|
5066
5179
|
exports.getActivationRevalidationVersion = getActivationRevalidationVersion;
|
|
5067
5180
|
exports.getCurrentSessionId = getCurrentSessionId;
|
|
5068
5181
|
exports.hashEmailFnv1a = hashEmailFnv1a;
|
|
5182
|
+
exports.hydrateAutoDeviceKey = hydrateAutoDeviceKey;
|
|
5069
5183
|
exports.identifyOnboarding = identifyOnboarding;
|
|
5070
5184
|
exports.isFeaturesFresh = isFeaturesFresh;
|
|
5071
5185
|
exports.isOnboardingEnabled = isOnboardingEnabled;
|