@wireai/activation 0.13.6 → 0.14.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 +21 -9
- package/CHANGELOG.md +227 -1
- package/INTEGRATION_PROMPT.md +7 -4
- package/README.md +15 -2
- package/dist/analytics/index.d.mts +9 -2
- package/dist/analytics/index.d.ts +9 -2
- package/dist/analytics/index.js +56 -12
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +56 -12
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/{currentSession-DngW-QoD.d.mts → currentSession-CUvTOchb.d.mts} +35 -6
- package/dist/{currentSession-C5976akx.d.ts → currentSession-CW_5Mq4O.d.ts} +35 -6
- package/dist/index.d.mts +31 -7
- package/dist/index.d.ts +31 -7
- package/dist/index.js +643 -539
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +643 -539
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.js +14 -6
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +14 -6
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.js +14 -6
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +14 -6
- package/dist/reviews/index.mjs.map +1 -1
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/src/OnboardingFlow.tsx +4 -3
- package/src/WireOnboarding.tsx +48 -8
- package/src/activation/useWireActivation.ts +14 -1
- package/src/activation/wireActivation.ts +68 -2
- package/src/analytics/analyticsFacade.ts +24 -0
- package/src/analytics/currentSession.ts +3 -2
- package/src/analytics/eventQueue.ts +106 -11
- package/src/analytics/reportClientEvent.ts +31 -7
- package/src/analytics/useAnalytics.ts +17 -0
- package/src/context/deviceId.ts +10 -3
- package/src/permissions/permissionMemory.ts +41 -5
- package/src/reviews/runtime.ts +75 -23
- package/src/session/persistedSession.ts +32 -8
- package/src/session-analytics/lifecycle.ts +26 -5
- package/src/session-analytics/reportSessionStart.ts +14 -10
- package/src/session-analytics/useLifecycleEvents.ts +70 -32
- package/src/session-analytics/useSessionStart.ts +57 -15
- package/src/types.ts +16 -6
- package/src/utils/readPlan.ts +8 -5
package/dist/index.mjs
CHANGED
|
@@ -1228,20 +1228,21 @@ var reportClientEvents = (target, events) => {
|
|
|
1228
1228
|
}
|
|
1229
1229
|
};
|
|
1230
1230
|
var reportClientEvent = (target, event) => reportClientEvents(target, [event]);
|
|
1231
|
-
var reportClientEventsAwait = async (target, events) =>
|
|
1231
|
+
var reportClientEventsAwait = async (target, events) => await reportClientEventsOutcome(target, events) === "delivered";
|
|
1232
|
+
var reportClientEventsOutcome = async (target, events) => {
|
|
1232
1233
|
try {
|
|
1233
1234
|
const req = buildEventsRequest(target, events);
|
|
1234
|
-
if (!req) return
|
|
1235
|
+
if (!req) return "refused";
|
|
1235
1236
|
const res = await fetch(req.url, req.init);
|
|
1236
|
-
if (!res || !res.ok) return
|
|
1237
|
+
if (!res || !res.ok) return "unreachable";
|
|
1237
1238
|
const ack = await readEventsAck(res);
|
|
1238
|
-
if (!ack || ack.skipped <= 0) return
|
|
1239
|
+
if (!ack || ack.skipped <= 0) return "delivered";
|
|
1239
1240
|
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1240
1241
|
console.warn(describeDiscarded(ack));
|
|
1241
1242
|
}
|
|
1242
|
-
return
|
|
1243
|
+
return "refused";
|
|
1243
1244
|
} catch {
|
|
1244
|
-
return
|
|
1245
|
+
return "unreachable";
|
|
1245
1246
|
}
|
|
1246
1247
|
};
|
|
1247
1248
|
var reportClientEventAwait = (target, event) => reportClientEventsAwait(target, [event]);
|
|
@@ -3616,10 +3617,11 @@ var ensureCurrentSessionId = () => {
|
|
|
3616
3617
|
var DEFAULT_SESSION_TTL_MS = 36e5;
|
|
3617
3618
|
var READ_TIMEOUT_MS = 1500;
|
|
3618
3619
|
var sessionStorageKey = (appId) => `wireai:session:${appId}`;
|
|
3620
|
+
var READ_TIMED_OUT = /* @__PURE__ */ Symbol("wireai:storage-read-timeout");
|
|
3619
3621
|
var withTimeout = (p, ms) => {
|
|
3620
3622
|
let timer;
|
|
3621
3623
|
const timeout = new Promise((resolve) => {
|
|
3622
|
-
timer = setTimeout(() => resolve(
|
|
3624
|
+
timer = setTimeout(() => resolve(READ_TIMED_OUT), ms);
|
|
3623
3625
|
});
|
|
3624
3626
|
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
3625
3627
|
};
|
|
@@ -3635,12 +3637,14 @@ var parsePersisted = (raw) => {
|
|
|
3635
3637
|
return void 0;
|
|
3636
3638
|
};
|
|
3637
3639
|
var loadPersistedSession = async (storage, key, ttlMs = DEFAULT_SESSION_TTL_MS) => {
|
|
3638
|
-
let
|
|
3640
|
+
let raw;
|
|
3639
3641
|
try {
|
|
3640
|
-
|
|
3642
|
+
raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);
|
|
3641
3643
|
} catch {
|
|
3642
|
-
|
|
3644
|
+
raw = null;
|
|
3643
3645
|
}
|
|
3646
|
+
if (raw === READ_TIMED_OUT) return { id: makeSessionId(), resumed: false };
|
|
3647
|
+
const stored = parsePersisted(raw);
|
|
3644
3648
|
if (stored && Date.now() - stored.ts < ttlMs) {
|
|
3645
3649
|
return { id: stored.id, resumed: true };
|
|
3646
3650
|
}
|
|
@@ -3650,7 +3654,9 @@ var loadPersistedSession = async (storage, key, ttlMs = DEFAULT_SESSION_TTL_MS)
|
|
|
3650
3654
|
};
|
|
3651
3655
|
var peekPersistedSession = async (storage, key) => {
|
|
3652
3656
|
try {
|
|
3653
|
-
|
|
3657
|
+
const raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);
|
|
3658
|
+
if (raw === READ_TIMED_OUT) return void 0;
|
|
3659
|
+
return parsePersisted(raw);
|
|
3654
3660
|
} catch {
|
|
3655
3661
|
return void 0;
|
|
3656
3662
|
}
|
|
@@ -3801,13 +3807,19 @@ var readSettledPermissions = (raw, sessionId) => {
|
|
|
3801
3807
|
return [];
|
|
3802
3808
|
}
|
|
3803
3809
|
};
|
|
3804
|
-
var
|
|
3810
|
+
var loadSettledPermissionsOutcome = async (storage, key, sessionId) => {
|
|
3805
3811
|
try {
|
|
3806
|
-
|
|
3812
|
+
const raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);
|
|
3813
|
+
if (raw === READ_TIMED_OUT) return { status: "unknown" };
|
|
3814
|
+
return { status: "read", ids: readSettledPermissions(raw, sessionId) };
|
|
3807
3815
|
} catch {
|
|
3808
|
-
return
|
|
3816
|
+
return { status: "unknown" };
|
|
3809
3817
|
}
|
|
3810
3818
|
};
|
|
3819
|
+
var loadSettledPermissions = async (storage, key, sessionId) => {
|
|
3820
|
+
const outcome = await loadSettledPermissionsOutcome(storage, key, sessionId);
|
|
3821
|
+
return outcome.status === "read" ? outcome.ids : [];
|
|
3822
|
+
};
|
|
3811
3823
|
var saveSettledPermissions = (storage, key, sessionId, ids) => {
|
|
3812
3824
|
try {
|
|
3813
3825
|
const record = { sessionId, ids: [...ids] };
|
|
@@ -3912,7 +3924,7 @@ var WireOnboarding = ({
|
|
|
3912
3924
|
useEffect(() => {
|
|
3913
3925
|
if (!warnMissingJoinKey) return;
|
|
3914
3926
|
warnInDev(
|
|
3915
|
-
"[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)}
|
|
3927
|
+
"[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)} if your app owns a device id. If it does NOT, do not build one here \u2014 leave userContext alone and pass a working `storage` instead: the kit injects the same id the analytics side stamps, and only when it has confirmed the id actually persists. Never hand-write userContext={{ deviceKey }}. The kit did NOT auto-inject its own key here because " + autoJoinReason
|
|
3916
3928
|
);
|
|
3917
3929
|
}, [warnMissingJoinKey, autoJoinReason]);
|
|
3918
3930
|
const hostKeyElsewhere = missingJoinKey ? hostIdentity("device", config.appId) : void 0;
|
|
@@ -3962,16 +3974,33 @@ var WireOnboarding = ({
|
|
|
3962
3974
|
const sessionId = (_c = session == null ? void 0 : session.id) != null ? _c : "";
|
|
3963
3975
|
const wantsPermissionMemory = Boolean(storage) && ((_d = permissionScreens == null ? void 0 : permissionScreens.length) != null ? _d : 0) > 0;
|
|
3964
3976
|
const [settledPermissions, setSettledPermissions] = useState(void 0);
|
|
3977
|
+
const [permissionMemoryUnreadable, setPermissionMemoryUnreadable] = useState(false);
|
|
3965
3978
|
useEffect(() => {
|
|
3966
|
-
if (!wantsPermissionMemory || !storage || !sessionId
|
|
3979
|
+
if (!wantsPermissionMemory || !storage || !sessionId) return;
|
|
3980
|
+
if (settledPermissions !== void 0 || permissionMemoryUnreadable) return;
|
|
3967
3981
|
let cancelled = false;
|
|
3968
|
-
void
|
|
3969
|
-
if (
|
|
3982
|
+
void loadSettledPermissionsOutcome(storage, permissionsKey, sessionId).then((outcome) => {
|
|
3983
|
+
if (cancelled) return;
|
|
3984
|
+
if (outcome.status === "read") {
|
|
3985
|
+
setSettledPermissions(outcome.ids);
|
|
3986
|
+
return;
|
|
3987
|
+
}
|
|
3988
|
+
setPermissionMemoryUnreadable(true);
|
|
3989
|
+
warnInDev(
|
|
3990
|
+
"[wireai] the permission-screen memory could not be read (the storage adapter timed out or threw), so permission screens are suppressed for this session rather than re-asking a permission the OS grants once. Check the `storage` adapter passed to WireOnboarding."
|
|
3991
|
+
);
|
|
3970
3992
|
});
|
|
3971
3993
|
return () => {
|
|
3972
3994
|
cancelled = true;
|
|
3973
3995
|
};
|
|
3974
|
-
}, [
|
|
3996
|
+
}, [
|
|
3997
|
+
wantsPermissionMemory,
|
|
3998
|
+
storage,
|
|
3999
|
+
permissionsKey,
|
|
4000
|
+
sessionId,
|
|
4001
|
+
settledPermissions,
|
|
4002
|
+
permissionMemoryUnreadable
|
|
4003
|
+
]);
|
|
3975
4004
|
const settledPermissionsRef = useRef(void 0);
|
|
3976
4005
|
settledPermissionsRef.current = settledPermissions;
|
|
3977
4006
|
const handlePermissionSettled = useCallback(
|
|
@@ -4591,190 +4620,503 @@ var attributionMetadata = (a) => {
|
|
|
4591
4620
|
return { attribution };
|
|
4592
4621
|
};
|
|
4593
4622
|
|
|
4594
|
-
// src/
|
|
4595
|
-
var
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4623
|
+
// src/analytics/eventQueue.ts
|
|
4624
|
+
var DEFAULTS = {
|
|
4625
|
+
maxSize: 200,
|
|
4626
|
+
batchSize: 20,
|
|
4627
|
+
baseBackoffMs: 1e3,
|
|
4628
|
+
maxBackoffMs: 3e4,
|
|
4629
|
+
maxRetries: 6
|
|
4630
|
+
};
|
|
4631
|
+
var READ_TIMEOUT_MS3 = 1500;
|
|
4632
|
+
var QUEUE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:eventQueueKeys");
|
|
4633
|
+
var queueKeyGlobal = globalThis;
|
|
4634
|
+
var claimedQueueKeys = () => {
|
|
4635
|
+
const existing = queueKeyGlobal[QUEUE_KEY_SLOT];
|
|
4601
4636
|
if (existing) return existing;
|
|
4602
|
-
const created =
|
|
4603
|
-
|
|
4637
|
+
const created = /* @__PURE__ */ new Set();
|
|
4638
|
+
queueKeyGlobal[QUEUE_KEY_SLOT] = created;
|
|
4604
4639
|
return created;
|
|
4605
4640
|
};
|
|
4606
|
-
var
|
|
4607
|
-
const
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
listener();
|
|
4612
|
-
} catch {
|
|
4613
|
-
}
|
|
4641
|
+
var claimQueueKey = (preferred, explicit) => {
|
|
4642
|
+
const claimed = claimedQueueKeys();
|
|
4643
|
+
if (explicit || !claimed.has(preferred)) {
|
|
4644
|
+
claimed.add(preferred);
|
|
4645
|
+
return preferred;
|
|
4614
4646
|
}
|
|
4647
|
+
let ordinal = 2;
|
|
4648
|
+
while (claimed.has(`${preferred}#${ordinal}`)) ordinal++;
|
|
4649
|
+
const key = `${preferred}#${ordinal}`;
|
|
4650
|
+
claimed.add(key);
|
|
4651
|
+
warnInDev(
|
|
4652
|
+
`[wireai] a second event queue was created for the same appId, and "${preferred}" is already claimed by a live one. Two queues sharing one storage slot overwrite each other's backlog, delete each other's pending events when one drains to empty, and double-send on relaunch, so this queue was given "${key}" instead. Prefer ONE analytics instance per app; if you really need two, pass an explicit \`storageKey\` to each so the slots are yours to reason about.`
|
|
4653
|
+
);
|
|
4654
|
+
return key;
|
|
4615
4655
|
};
|
|
4616
|
-
var
|
|
4617
|
-
|
|
4618
|
-
s.listeners.add(listener);
|
|
4619
|
-
return () => {
|
|
4620
|
-
s.listeners.delete(listener);
|
|
4621
|
-
};
|
|
4622
|
-
};
|
|
4623
|
-
var getActivationRevalidationVersion = () => store().version;
|
|
4624
|
-
var resetActivationRevalidation = () => {
|
|
4625
|
-
const s = store();
|
|
4626
|
-
s.version = 0;
|
|
4627
|
-
s.listeners.clear();
|
|
4656
|
+
var releaseQueueKey = (key) => {
|
|
4657
|
+
claimedQueueKeys().delete(key);
|
|
4628
4658
|
};
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
const target = { serverUrl: config.serverUrl, apiKey: config.apiKey };
|
|
4635
|
-
const explicitDeviceKey = (_b = clean(config.deviceKey)) != null ? _b : clean((_a = config.userContext) == null ? void 0 : _a.deviceKey);
|
|
4636
|
-
resolveIdentity({
|
|
4637
|
-
value: explicitDeviceKey,
|
|
4638
|
-
space: "device",
|
|
4639
|
-
source: "host",
|
|
4640
|
-
scope: config.appId
|
|
4659
|
+
var READ_TIMED_OUT2 = /* @__PURE__ */ Symbol("wireai:storage-read-timeout");
|
|
4660
|
+
var withTimeout3 = (p, ms) => {
|
|
4661
|
+
let timer;
|
|
4662
|
+
const timeout = new Promise((resolve) => {
|
|
4663
|
+
timer = setTimeout(() => resolve(READ_TIMED_OUT2), ms);
|
|
4641
4664
|
});
|
|
4642
|
-
|
|
4643
|
-
appId: config.appId,
|
|
4644
|
-
// An explicit key opts out of minting AND persisting (unchanged contract).
|
|
4645
|
-
storage: explicitDeviceKey ? void 0 : config.storage
|
|
4646
|
-
};
|
|
4647
|
-
if (!explicitDeviceKey) resolveAutoDeviceKey(autoDeviceKeyOptions);
|
|
4648
|
-
const detectedAppVersion = detectAppVersion();
|
|
4649
|
-
const applyContext = (event) => {
|
|
4650
|
-
var _a2, _b2, _c;
|
|
4651
|
-
const resolved = resolveUserContext(
|
|
4652
|
-
// `??` is lazy on purpose: an explicit key must never even touch the auto registry.
|
|
4653
|
-
{
|
|
4654
|
-
...(_a2 = config.userContext) != null ? _a2 : {},
|
|
4655
|
-
deviceKey: explicitDeviceKey != null ? explicitDeviceKey : resolveAutoDeviceKey(autoDeviceKeyOptions)
|
|
4656
|
-
},
|
|
4657
|
-
{ autoAppVersion: (_b2 = config.appVersion) != null ? _b2 : detectedAppVersion }
|
|
4658
|
-
);
|
|
4659
|
-
if (resolved.userContext) {
|
|
4660
|
-
event.user_context = { ...resolved.userContext, ...(_c = event.user_context) != null ? _c : {} };
|
|
4661
|
-
}
|
|
4662
|
-
if (resolved.userId && !event.user_id) event.user_id = resolved.userId;
|
|
4663
|
-
};
|
|
4664
|
-
const track = async (name, meta) => {
|
|
4665
|
-
if (!clean(name)) return false;
|
|
4666
|
-
const sessionId = ensureCurrentSessionId();
|
|
4667
|
-
const event = {
|
|
4668
|
-
event_type: "app_event",
|
|
4669
|
-
session_id: sessionId,
|
|
4670
|
-
question_key: name
|
|
4671
|
-
};
|
|
4672
|
-
if (meta && Object.keys(meta).length > 0) event.meta = JSON.stringify(meta);
|
|
4673
|
-
applyContext(event);
|
|
4674
|
-
const ok = await reportClientEventAwait(target, event);
|
|
4675
|
-
if (ok) bumpActivationRevalidation();
|
|
4676
|
-
return ok;
|
|
4677
|
-
};
|
|
4678
|
-
return {
|
|
4679
|
-
track,
|
|
4680
|
-
get sessionId() {
|
|
4681
|
-
return getCurrentSessionId();
|
|
4682
|
-
},
|
|
4683
|
-
subscribeRevalidation: subscribeActivationRevalidation,
|
|
4684
|
-
getRevalidationVersion: getActivationRevalidationVersion
|
|
4685
|
-
};
|
|
4686
|
-
};
|
|
4687
|
-
var useActivationRevalidation = () => useSyncExternalStore(
|
|
4688
|
-
subscribeActivationRevalidation,
|
|
4689
|
-
getActivationRevalidationVersion,
|
|
4690
|
-
getActivationRevalidationVersion
|
|
4691
|
-
);
|
|
4692
|
-
var useWireActivation = (config) => {
|
|
4693
|
-
var _a;
|
|
4694
|
-
const ref = useRef(void 0);
|
|
4695
|
-
const prevKeys = useRef("");
|
|
4696
|
-
const currentKeys = [
|
|
4697
|
-
config.serverUrl,
|
|
4698
|
-
config.apiKey,
|
|
4699
|
-
config.appId,
|
|
4700
|
-
config.deviceKey,
|
|
4701
|
-
(_a = config.userContext) == null ? void 0 : _a.deviceKey
|
|
4702
|
-
].join("|");
|
|
4703
|
-
if (!ref.current || prevKeys.current !== currentKeys) {
|
|
4704
|
-
prevKeys.current = currentKeys;
|
|
4705
|
-
ref.current = createWireActivation(config);
|
|
4706
|
-
}
|
|
4707
|
-
const revalidation = useActivationRevalidation();
|
|
4708
|
-
return { track: ref.current.track, sessionId: ref.current.sessionId, revalidation };
|
|
4709
|
-
};
|
|
4710
|
-
|
|
4711
|
-
// src/revenuecat/purchaseEvents.ts
|
|
4712
|
-
var WIRE_PURCHASE_EVENTS = {
|
|
4713
|
-
/** The paywall became visible (offerings loaded). */
|
|
4714
|
-
paywallShown: "wire_paywall_shown",
|
|
4715
|
-
/** The user tapped buy; the store sheet is about to open. */
|
|
4716
|
-
checkoutStarted: "wire_checkout_started",
|
|
4717
|
-
/** The store confirmed the purchase AND the entitlement is now active. */
|
|
4718
|
-
purchased: "wire_purchase_completed",
|
|
4719
|
-
/** The purchase did not land: a user cancel or a real store/network error (see `reason`). */
|
|
4720
|
-
purchaseFailed: "wire_purchase_failed",
|
|
4721
|
-
/** A restore ran (whether or not it produced an entitlement; see `plan_tier`). */
|
|
4722
|
-
restored: "wire_purchase_restored"
|
|
4723
|
-
};
|
|
4724
|
-
var PLAN_TIER_CONTEXT_KEY = "plan_tier";
|
|
4725
|
-
var asRecord = (value) => typeof value === "object" && value !== null ? value : void 0;
|
|
4726
|
-
var activeEntitlement = (info, entitlementId) => {
|
|
4727
|
-
var _a, _b;
|
|
4728
|
-
const active = asRecord((_b = asRecord((_a = asRecord(info)) == null ? void 0 : _a.entitlements)) == null ? void 0 : _b.active);
|
|
4729
|
-
const found = active == null ? void 0 : active[entitlementId];
|
|
4730
|
-
const entitlement = asRecord(found);
|
|
4731
|
-
if (!entitlement) return void 0;
|
|
4732
|
-
return entitlement.isActive === false ? void 0 : entitlement;
|
|
4665
|
+
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
4733
4666
|
};
|
|
4734
|
-
var
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
if (!entitlement) return "free";
|
|
4738
|
-
return isTrial(entitlement) ? "trial" : "paid";
|
|
4667
|
+
var unrefTimer = (timer) => {
|
|
4668
|
+
const t = timer;
|
|
4669
|
+
if (typeof t.unref === "function") t.unref();
|
|
4739
4670
|
};
|
|
4740
|
-
var
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
props.price = product.price;
|
|
4671
|
+
var parsePersisted2 = (raw) => {
|
|
4672
|
+
if (!raw) return [];
|
|
4673
|
+
try {
|
|
4674
|
+
const parsed = JSON.parse(raw);
|
|
4675
|
+
if (!Array.isArray(parsed)) return [];
|
|
4676
|
+
const items = [];
|
|
4677
|
+
for (const entry of parsed) {
|
|
4678
|
+
if (entry && typeof entry === "object" && typeof entry.id === "number" && entry.event && typeof entry.event === "object") {
|
|
4679
|
+
items.push(entry);
|
|
4680
|
+
}
|
|
4751
4681
|
}
|
|
4752
|
-
|
|
4682
|
+
return items;
|
|
4683
|
+
} catch {
|
|
4684
|
+
return [];
|
|
4753
4685
|
}
|
|
4754
|
-
return props;
|
|
4755
4686
|
};
|
|
4756
|
-
var
|
|
4757
|
-
|
|
4758
|
-
const
|
|
4759
|
-
|
|
4760
|
-
|
|
4687
|
+
var createEventQueue = (options) => {
|
|
4688
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
4689
|
+
const target = options.target;
|
|
4690
|
+
const storage = options.storage;
|
|
4691
|
+
const defaultKey = (_b = options.storageKey) != null ? _b : `wireai:evtq:${(_a = options.appId) != null ? _a : "default"}`;
|
|
4692
|
+
const key = storage ? claimQueueKey(defaultKey, options.storageKey !== void 0) : defaultKey;
|
|
4693
|
+
const maxSize = (_c = options.maxSize) != null ? _c : DEFAULTS.maxSize;
|
|
4694
|
+
const batchSize = (_d = options.batchSize) != null ? _d : DEFAULTS.batchSize;
|
|
4695
|
+
const baseBackoffMs = (_e = options.baseBackoffMs) != null ? _e : DEFAULTS.baseBackoffMs;
|
|
4696
|
+
const maxBackoffMs = (_f = options.maxBackoffMs) != null ? _f : DEFAULTS.maxBackoffMs;
|
|
4697
|
+
const maxRetries = (_g = options.maxRetries) != null ? _g : DEFAULTS.maxRetries;
|
|
4698
|
+
let pending = [];
|
|
4699
|
+
let nextId = 0;
|
|
4700
|
+
let flushing = false;
|
|
4701
|
+
let attempt = 0;
|
|
4702
|
+
let retryTimer;
|
|
4703
|
+
let disposed = false;
|
|
4704
|
+
let backlogUnread = storage !== void 0;
|
|
4705
|
+
const resolveEnvelope = () => {
|
|
4706
|
+
try {
|
|
4707
|
+
return typeof options.envelope === "function" ? options.envelope() : options.envelope;
|
|
4708
|
+
} catch {
|
|
4709
|
+
return void 0;
|
|
4710
|
+
}
|
|
4761
4711
|
};
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4712
|
+
const stamp = (event) => {
|
|
4713
|
+
var _a2;
|
|
4714
|
+
const env = resolveEnvelope();
|
|
4715
|
+
const stamped = { ...event };
|
|
4716
|
+
if (stamped.ts === void 0) stamped.ts = Date.now();
|
|
4717
|
+
if (!env) return stamped;
|
|
4718
|
+
if (!stamped.device && env.device) stamped.device = env.device;
|
|
4719
|
+
if (!stamped.session_id && env.sessionId) stamped.session_id = env.sessionId;
|
|
4720
|
+
const uc = { ...(_a2 = stamped.user_context) != null ? _a2 : {} };
|
|
4721
|
+
if (env.appVersion && uc.app_version === void 0) uc.app_version = env.appVersion;
|
|
4722
|
+
if (env.appBuild && uc.app_build === void 0) uc.app_build = env.appBuild;
|
|
4723
|
+
if (env.networkType && uc.network_type === void 0) uc.network_type = env.networkType;
|
|
4724
|
+
if (Object.keys(uc).length > 0) stamped.user_context = uc;
|
|
4725
|
+
return stamped;
|
|
4726
|
+
};
|
|
4727
|
+
const persist = () => {
|
|
4728
|
+
if (!storage) return;
|
|
4729
|
+
if (disposed) return;
|
|
4730
|
+
if (backlogUnread) return;
|
|
4731
|
+
try {
|
|
4732
|
+
if (pending.length === 0) {
|
|
4733
|
+
void storage.removeItem(key).catch(() => {
|
|
4734
|
+
});
|
|
4735
|
+
return;
|
|
4736
|
+
}
|
|
4737
|
+
const payload = pending.map((item) => ({ id: item.id, event: item.event }));
|
|
4738
|
+
void storage.setItem(key, JSON.stringify(payload)).catch(() => {
|
|
4739
|
+
});
|
|
4740
|
+
} catch {
|
|
4741
|
+
}
|
|
4742
|
+
};
|
|
4743
|
+
const isCountedOpenEvent = (event) => typeof event.question_key === "string" && event.question_key.startsWith("app.");
|
|
4744
|
+
const enforceSizeCap = () => {
|
|
4745
|
+
let overflow = pending.length - maxSize;
|
|
4746
|
+
if (overflow <= 0) return;
|
|
4747
|
+
const kept = [];
|
|
4748
|
+
for (const item of pending) {
|
|
4749
|
+
if (overflow > 0 && !isCountedOpenEvent(item.event)) {
|
|
4750
|
+
overflow--;
|
|
4751
|
+
continue;
|
|
4752
|
+
}
|
|
4753
|
+
kept.push(item);
|
|
4754
|
+
}
|
|
4755
|
+
if (overflow > 0) kept.splice(0, overflow);
|
|
4756
|
+
pending = kept;
|
|
4757
|
+
};
|
|
4758
|
+
const safeSig = (event) => {
|
|
4759
|
+
try {
|
|
4760
|
+
return JSON.stringify(event);
|
|
4761
|
+
} catch {
|
|
4762
|
+
return `__nosig_${nextId}_${Math.random()}`;
|
|
4763
|
+
}
|
|
4764
|
+
};
|
|
4765
|
+
const mergePersisted = (persistedItems) => {
|
|
4766
|
+
if (persistedItems.length === 0) return;
|
|
4767
|
+
const seen = new Set(pending.map((item) => item.sig));
|
|
4768
|
+
const restored = [];
|
|
4769
|
+
for (const persisted of persistedItems) {
|
|
4770
|
+
const sig = safeSig(persisted.event);
|
|
4771
|
+
if (seen.has(sig)) continue;
|
|
4772
|
+
seen.add(sig);
|
|
4773
|
+
restored.push({ id: nextId++, event: persisted.event, sig });
|
|
4774
|
+
}
|
|
4775
|
+
if (restored.length === 0) return;
|
|
4776
|
+
pending = [...restored, ...pending];
|
|
4777
|
+
enforceSizeCap();
|
|
4778
|
+
persist();
|
|
4779
|
+
};
|
|
4780
|
+
const loadPromise = (async () => {
|
|
4781
|
+
if (!storage) return;
|
|
4782
|
+
try {
|
|
4783
|
+
const read = Promise.resolve(storage.getItem(key));
|
|
4784
|
+
read.catch(() => {
|
|
4785
|
+
});
|
|
4786
|
+
const raced = await withTimeout3(read, READ_TIMEOUT_MS3);
|
|
4787
|
+
if (raced === READ_TIMED_OUT2) {
|
|
4788
|
+
backlogUnread = true;
|
|
4789
|
+
warnInDev(
|
|
4790
|
+
`[wireai] the persisted analytics backlog at "${key}" took longer than ${READ_TIMEOUT_MS3}ms to read, so the queue started without it. The stored events are NOT discarded: writes are held back until the read lands, and the backlog is merged in then. If you see this on every cold start, your storage adapter is too slow to be on the launch path.`
|
|
4791
|
+
);
|
|
4792
|
+
void read.then((late) => {
|
|
4793
|
+
backlogUnread = false;
|
|
4794
|
+
mergePersisted(parsePersisted2(late));
|
|
4795
|
+
flush();
|
|
4796
|
+
}).catch(() => {
|
|
4797
|
+
backlogUnread = false;
|
|
4798
|
+
persist();
|
|
4799
|
+
});
|
|
4800
|
+
return;
|
|
4801
|
+
}
|
|
4802
|
+
backlogUnread = false;
|
|
4803
|
+
mergePersisted(parsePersisted2(raced));
|
|
4804
|
+
persist();
|
|
4805
|
+
} catch {
|
|
4806
|
+
backlogUnread = false;
|
|
4807
|
+
persist();
|
|
4808
|
+
}
|
|
4809
|
+
})();
|
|
4810
|
+
const postBatch = async (events) => {
|
|
4811
|
+
const req = buildEventsRequest(target, events);
|
|
4812
|
+
if (!req) return false;
|
|
4813
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
4814
|
+
const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
|
|
4815
|
+
try {
|
|
4816
|
+
const res = await fetch(req.url, { ...req.init, signal: controller == null ? void 0 : controller.signal });
|
|
4817
|
+
warnOnSkippedEvents(res);
|
|
4818
|
+
return !!(res && res.ok);
|
|
4819
|
+
} catch {
|
|
4820
|
+
return false;
|
|
4821
|
+
} finally {
|
|
4822
|
+
clearTimeout(timer);
|
|
4823
|
+
}
|
|
4824
|
+
};
|
|
4825
|
+
const clearRetry = () => {
|
|
4826
|
+
if (retryTimer !== void 0) {
|
|
4827
|
+
clearTimeout(retryTimer);
|
|
4828
|
+
retryTimer = void 0;
|
|
4829
|
+
}
|
|
4830
|
+
};
|
|
4831
|
+
const scheduleRetry = () => {
|
|
4832
|
+
if (attempt >= maxRetries) return;
|
|
4833
|
+
const delay = Math.min(baseBackoffMs * 2 ** attempt, maxBackoffMs);
|
|
4834
|
+
attempt++;
|
|
4835
|
+
clearRetry();
|
|
4836
|
+
retryTimer = setTimeout(() => {
|
|
4837
|
+
retryTimer = void 0;
|
|
4838
|
+
void drain();
|
|
4839
|
+
}, delay);
|
|
4840
|
+
unrefTimer(retryTimer);
|
|
4841
|
+
};
|
|
4842
|
+
const drain = async () => {
|
|
4843
|
+
if (disposed) return;
|
|
4844
|
+
try {
|
|
4845
|
+
await loadPromise;
|
|
4846
|
+
} catch {
|
|
4847
|
+
}
|
|
4848
|
+
if (disposed) return;
|
|
4849
|
+
if (flushing) return;
|
|
4850
|
+
flushing = true;
|
|
4851
|
+
try {
|
|
4852
|
+
while (pending.length > 0) {
|
|
4853
|
+
const batch = pending.slice(0, batchSize);
|
|
4854
|
+
const ok = await postBatch(batch.map((item) => item.event));
|
|
4855
|
+
if (!ok) {
|
|
4856
|
+
scheduleRetry();
|
|
4857
|
+
return;
|
|
4858
|
+
}
|
|
4859
|
+
const acked = new Set(batch.map((item) => item.id));
|
|
4860
|
+
pending = pending.filter((item) => !acked.has(item.id));
|
|
4861
|
+
persist();
|
|
4862
|
+
attempt = 0;
|
|
4863
|
+
clearRetry();
|
|
4864
|
+
}
|
|
4865
|
+
} finally {
|
|
4866
|
+
flushing = false;
|
|
4867
|
+
}
|
|
4868
|
+
};
|
|
4869
|
+
const flush = () => {
|
|
4870
|
+
try {
|
|
4871
|
+
void drain();
|
|
4872
|
+
} catch {
|
|
4873
|
+
}
|
|
4874
|
+
};
|
|
4875
|
+
const enqueue = (event) => {
|
|
4876
|
+
if (disposed) return;
|
|
4877
|
+
try {
|
|
4878
|
+
const stamped = stamp(event);
|
|
4879
|
+
const sig = safeSig(stamped);
|
|
4880
|
+
for (const item of pending) {
|
|
4881
|
+
if (item.sig === sig) return;
|
|
4882
|
+
}
|
|
4883
|
+
pending.push({ id: nextId++, event: stamped, sig });
|
|
4884
|
+
enforceSizeCap();
|
|
4885
|
+
persist();
|
|
4886
|
+
if (retryTimer === void 0) flush();
|
|
4887
|
+
} catch {
|
|
4888
|
+
}
|
|
4889
|
+
};
|
|
4890
|
+
const notifyOnline = () => {
|
|
4891
|
+
attempt = 0;
|
|
4892
|
+
clearRetry();
|
|
4893
|
+
flush();
|
|
4894
|
+
};
|
|
4895
|
+
const size = () => pending.length;
|
|
4896
|
+
const dispose = () => {
|
|
4897
|
+
if (disposed) return;
|
|
4898
|
+
disposed = true;
|
|
4899
|
+
clearRetry();
|
|
4900
|
+
if (storage) releaseQueueKey(key);
|
|
4901
|
+
};
|
|
4902
|
+
return { enqueue, flush, notifyOnline, size, dispose };
|
|
4903
|
+
};
|
|
4904
|
+
|
|
4905
|
+
// src/activation/revalidation.ts
|
|
4906
|
+
var REVALIDATION_SLOT = /* @__PURE__ */ Symbol.for(
|
|
4907
|
+
"@wireai/activation:activationRevalidation"
|
|
4908
|
+
);
|
|
4909
|
+
var globalSlot2 = globalThis;
|
|
4910
|
+
var store = () => {
|
|
4911
|
+
const existing = globalSlot2[REVALIDATION_SLOT];
|
|
4912
|
+
if (existing) return existing;
|
|
4913
|
+
const created = { version: 0, listeners: /* @__PURE__ */ new Set() };
|
|
4914
|
+
globalSlot2[REVALIDATION_SLOT] = created;
|
|
4915
|
+
return created;
|
|
4916
|
+
};
|
|
4917
|
+
var bumpActivationRevalidation = () => {
|
|
4918
|
+
const s = store();
|
|
4919
|
+
s.version += 1;
|
|
4920
|
+
for (const listener of Array.from(s.listeners)) {
|
|
4921
|
+
try {
|
|
4922
|
+
listener();
|
|
4923
|
+
} catch {
|
|
4924
|
+
}
|
|
4925
|
+
}
|
|
4926
|
+
};
|
|
4927
|
+
var subscribeActivationRevalidation = (listener) => {
|
|
4928
|
+
const s = store();
|
|
4929
|
+
s.listeners.add(listener);
|
|
4930
|
+
return () => {
|
|
4931
|
+
s.listeners.delete(listener);
|
|
4932
|
+
};
|
|
4933
|
+
};
|
|
4934
|
+
var getActivationRevalidationVersion = () => store().version;
|
|
4935
|
+
var resetActivationRevalidation = () => {
|
|
4936
|
+
const s = store();
|
|
4937
|
+
s.version = 0;
|
|
4938
|
+
s.listeners.clear();
|
|
4939
|
+
};
|
|
4940
|
+
|
|
4941
|
+
// src/activation/wireActivation.ts
|
|
4942
|
+
var clean = cleanString;
|
|
4943
|
+
var createWireActivation = (config) => {
|
|
4944
|
+
var _a, _b;
|
|
4945
|
+
const target = { serverUrl: config.serverUrl, apiKey: config.apiKey };
|
|
4946
|
+
const explicitDeviceKey = (_b = clean(config.deviceKey)) != null ? _b : clean((_a = config.userContext) == null ? void 0 : _a.deviceKey);
|
|
4947
|
+
resolveIdentity({
|
|
4948
|
+
value: explicitDeviceKey,
|
|
4949
|
+
space: "device",
|
|
4950
|
+
source: "host",
|
|
4951
|
+
scope: config.appId
|
|
4952
|
+
});
|
|
4953
|
+
const autoDeviceKeyOptions = {
|
|
4954
|
+
appId: config.appId,
|
|
4955
|
+
// An explicit key opts out of minting AND persisting (unchanged contract).
|
|
4956
|
+
storage: explicitDeviceKey ? void 0 : config.storage
|
|
4957
|
+
};
|
|
4958
|
+
if (!explicitDeviceKey) resolveAutoDeviceKey(autoDeviceKeyOptions);
|
|
4959
|
+
const detectedAppVersion = detectAppVersion();
|
|
4960
|
+
const applyContext = (event) => {
|
|
4961
|
+
var _a2, _b2, _c;
|
|
4962
|
+
const resolved = resolveUserContext(
|
|
4963
|
+
// `??` is lazy on purpose: an explicit key must never even touch the auto registry.
|
|
4964
|
+
{
|
|
4965
|
+
...(_a2 = config.userContext) != null ? _a2 : {},
|
|
4966
|
+
deviceKey: explicitDeviceKey != null ? explicitDeviceKey : resolveAutoDeviceKey(autoDeviceKeyOptions)
|
|
4967
|
+
},
|
|
4968
|
+
{ autoAppVersion: (_b2 = config.appVersion) != null ? _b2 : detectedAppVersion }
|
|
4969
|
+
);
|
|
4970
|
+
if (resolved.userContext) {
|
|
4971
|
+
event.user_context = { ...resolved.userContext, ...(_c = event.user_context) != null ? _c : {} };
|
|
4972
|
+
}
|
|
4973
|
+
if (resolved.userId && !event.user_id) event.user_id = resolved.userId;
|
|
4974
|
+
};
|
|
4975
|
+
let durability;
|
|
4976
|
+
const bufferFailedEvent = (event) => {
|
|
4977
|
+
var _a2;
|
|
4978
|
+
if (config.sink) {
|
|
4979
|
+
config.sink(event);
|
|
4980
|
+
return;
|
|
4981
|
+
}
|
|
4982
|
+
if (!durability) {
|
|
4983
|
+
durability = createEventQueue({
|
|
4984
|
+
target,
|
|
4985
|
+
storage: config.storage,
|
|
4986
|
+
storageKey: `wireai:evtq:activation:${(_a2 = config.appId) != null ? _a2 : "default"}`
|
|
4987
|
+
});
|
|
4988
|
+
}
|
|
4989
|
+
durability.enqueue(event);
|
|
4990
|
+
};
|
|
4991
|
+
const track = async (name, meta) => {
|
|
4992
|
+
if (!clean(name)) return false;
|
|
4993
|
+
const sessionId = ensureCurrentSessionId();
|
|
4994
|
+
const event = {
|
|
4995
|
+
event_type: "app_event",
|
|
4996
|
+
session_id: sessionId,
|
|
4997
|
+
question_key: name
|
|
4998
|
+
};
|
|
4999
|
+
if (meta && Object.keys(meta).length > 0) event.meta = JSON.stringify(meta);
|
|
5000
|
+
applyContext(event);
|
|
5001
|
+
const outcome = await reportClientEventsOutcome(target, [event]);
|
|
5002
|
+
const ok = outcome === "delivered";
|
|
5003
|
+
if (ok) bumpActivationRevalidation();
|
|
5004
|
+
if (outcome === "unreachable") bufferFailedEvent(event);
|
|
5005
|
+
return ok;
|
|
5006
|
+
};
|
|
5007
|
+
return {
|
|
5008
|
+
track,
|
|
5009
|
+
get sessionId() {
|
|
5010
|
+
return getCurrentSessionId();
|
|
5011
|
+
},
|
|
5012
|
+
subscribeRevalidation: subscribeActivationRevalidation,
|
|
5013
|
+
getRevalidationVersion: getActivationRevalidationVersion,
|
|
5014
|
+
dispose: () => {
|
|
5015
|
+
durability == null ? void 0 : durability.dispose();
|
|
5016
|
+
durability = void 0;
|
|
5017
|
+
}
|
|
5018
|
+
};
|
|
5019
|
+
};
|
|
5020
|
+
var useActivationRevalidation = () => useSyncExternalStore(
|
|
5021
|
+
subscribeActivationRevalidation,
|
|
5022
|
+
getActivationRevalidationVersion,
|
|
5023
|
+
getActivationRevalidationVersion
|
|
5024
|
+
);
|
|
5025
|
+
var useWireActivation = (config) => {
|
|
5026
|
+
var _a, _b;
|
|
5027
|
+
const ref = useRef(void 0);
|
|
5028
|
+
const prevKeys = useRef("");
|
|
5029
|
+
const currentKeys = [
|
|
5030
|
+
config.serverUrl,
|
|
5031
|
+
config.apiKey,
|
|
5032
|
+
config.appId,
|
|
5033
|
+
config.deviceKey,
|
|
5034
|
+
(_a = config.userContext) == null ? void 0 : _a.deviceKey
|
|
5035
|
+
].join("|");
|
|
5036
|
+
if (!ref.current || prevKeys.current !== currentKeys) {
|
|
5037
|
+
prevKeys.current = currentKeys;
|
|
5038
|
+
(_b = ref.current) == null ? void 0 : _b.dispose();
|
|
5039
|
+
ref.current = createWireActivation(config);
|
|
5040
|
+
}
|
|
5041
|
+
useEffect(
|
|
5042
|
+
() => () => {
|
|
5043
|
+
var _a2;
|
|
5044
|
+
(_a2 = ref.current) == null ? void 0 : _a2.dispose();
|
|
5045
|
+
ref.current = void 0;
|
|
5046
|
+
},
|
|
5047
|
+
[]
|
|
5048
|
+
);
|
|
5049
|
+
const revalidation = useActivationRevalidation();
|
|
5050
|
+
return { track: ref.current.track, sessionId: ref.current.sessionId, revalidation };
|
|
5051
|
+
};
|
|
5052
|
+
|
|
5053
|
+
// src/revenuecat/purchaseEvents.ts
|
|
5054
|
+
var WIRE_PURCHASE_EVENTS = {
|
|
5055
|
+
/** The paywall became visible (offerings loaded). */
|
|
5056
|
+
paywallShown: "wire_paywall_shown",
|
|
5057
|
+
/** The user tapped buy; the store sheet is about to open. */
|
|
5058
|
+
checkoutStarted: "wire_checkout_started",
|
|
5059
|
+
/** The store confirmed the purchase AND the entitlement is now active. */
|
|
5060
|
+
purchased: "wire_purchase_completed",
|
|
5061
|
+
/** The purchase did not land: a user cancel or a real store/network error (see `reason`). */
|
|
5062
|
+
purchaseFailed: "wire_purchase_failed",
|
|
5063
|
+
/** A restore ran (whether or not it produced an entitlement; see `plan_tier`). */
|
|
5064
|
+
restored: "wire_purchase_restored"
|
|
5065
|
+
};
|
|
5066
|
+
var PLAN_TIER_CONTEXT_KEY = "plan_tier";
|
|
5067
|
+
var asRecord = (value) => typeof value === "object" && value !== null ? value : void 0;
|
|
5068
|
+
var activeEntitlement = (info, entitlementId) => {
|
|
5069
|
+
var _a, _b;
|
|
5070
|
+
const active = asRecord((_b = asRecord((_a = asRecord(info)) == null ? void 0 : _a.entitlements)) == null ? void 0 : _b.active);
|
|
5071
|
+
const found = active == null ? void 0 : active[entitlementId];
|
|
5072
|
+
const entitlement = asRecord(found);
|
|
5073
|
+
if (!entitlement) return void 0;
|
|
5074
|
+
return entitlement.isActive === false ? void 0 : entitlement;
|
|
5075
|
+
};
|
|
5076
|
+
var isTrial = (entitlement) => typeof entitlement.periodType === "string" && entitlement.periodType.toUpperCase() === "TRIAL";
|
|
5077
|
+
var resolvePlanTier = (info, entitlementId) => {
|
|
5078
|
+
const entitlement = activeEntitlement(info, entitlementId);
|
|
5079
|
+
if (!entitlement) return "free";
|
|
5080
|
+
return isTrial(entitlement) ? "trial" : "paid";
|
|
5081
|
+
};
|
|
5082
|
+
var describePackage = (pkg) => {
|
|
5083
|
+
const props = {};
|
|
5084
|
+
if (!pkg) return props;
|
|
5085
|
+
if (typeof pkg.identifier === "string") props.package_id = pkg.identifier;
|
|
5086
|
+
if (typeof pkg.packageType === "string") props.package_type = pkg.packageType;
|
|
5087
|
+
if (typeof pkg.offeringIdentifier === "string") props.offering_id = pkg.offeringIdentifier;
|
|
5088
|
+
const product = asRecord(pkg.product);
|
|
5089
|
+
if (product) {
|
|
5090
|
+
if (typeof product.identifier === "string") props.product_id = product.identifier;
|
|
5091
|
+
if (typeof product.price === "number" && Number.isFinite(product.price)) {
|
|
5092
|
+
props.price = product.price;
|
|
5093
|
+
}
|
|
5094
|
+
if (typeof product.currencyCode === "string") props.currency = product.currencyCode;
|
|
5095
|
+
}
|
|
5096
|
+
return props;
|
|
5097
|
+
};
|
|
5098
|
+
var describeEntitlement = (info, entitlementId) => {
|
|
5099
|
+
const entitlement = activeEntitlement(info, entitlementId);
|
|
5100
|
+
const props = {
|
|
5101
|
+
entitlement: entitlementId,
|
|
5102
|
+
plan_tier: entitlement ? isTrial(entitlement) ? "trial" : "paid" : "free"
|
|
5103
|
+
};
|
|
5104
|
+
if (!entitlement) return props;
|
|
5105
|
+
if (typeof entitlement.periodType === "string") props.period_type = entitlement.periodType;
|
|
5106
|
+
props.is_trial = isTrial(entitlement);
|
|
5107
|
+
if (typeof entitlement.willRenew === "boolean") props.will_renew = entitlement.willRenew;
|
|
5108
|
+
if (typeof entitlement.store === "string") props.store = entitlement.store;
|
|
5109
|
+
if (typeof entitlement.productIdentifier === "string") {
|
|
5110
|
+
props.product_id = entitlement.productIdentifier;
|
|
5111
|
+
}
|
|
5112
|
+
if (typeof entitlement.isSandbox === "boolean") props.is_sandbox = entitlement.isSandbox;
|
|
5113
|
+
return props;
|
|
5114
|
+
};
|
|
5115
|
+
var PURCHASE_CANCELLED_CODE = "1";
|
|
5116
|
+
var isUserCancelled = (error) => {
|
|
5117
|
+
const record = asRecord(error);
|
|
5118
|
+
if (!record) return false;
|
|
5119
|
+
if (record.userCancelled === true) return true;
|
|
4778
5120
|
return record.code === PURCHASE_CANCELLED_CODE;
|
|
4779
5121
|
};
|
|
4780
5122
|
var describeFailure = (error) => {
|
|
@@ -4900,15 +5242,9 @@ var reportSessionStart = (opts) => {
|
|
|
4900
5242
|
opts.sink(event);
|
|
4901
5243
|
return;
|
|
4902
5244
|
}
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
4907
|
-
void fetch(url, {
|
|
4908
|
-
method: "POST",
|
|
4909
|
-
headers,
|
|
4910
|
-
body: JSON.stringify({ events: [event] })
|
|
4911
|
-
}).then((res) => {
|
|
5245
|
+
const req = buildEventsRequest(target, [event]);
|
|
5246
|
+
if (!req) return;
|
|
5247
|
+
void fetch(req.url, req.init).then((res) => {
|
|
4912
5248
|
warnOnSkippedEvents(res);
|
|
4913
5249
|
}).catch(() => {
|
|
4914
5250
|
});
|
|
@@ -4920,13 +5256,25 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4920
5256
|
const latest = useRef({ config, options });
|
|
4921
5257
|
latest.current = { config, options };
|
|
4922
5258
|
useEffect(() => {
|
|
4923
|
-
|
|
5259
|
+
let cancelled = false;
|
|
5260
|
+
const resolveDeviceKey = (opts, autoDeviceKey) => {
|
|
4924
5261
|
const host = typeof opts.deviceKey === "string" && opts.deviceKey.trim() ? opts.deviceKey : void 0;
|
|
4925
5262
|
if (host) return host;
|
|
4926
|
-
|
|
4927
|
-
return resolveAutoDeviceKey({ appId: cfg.appId, storage: cfg.storage });
|
|
5263
|
+
return autoDeviceKey;
|
|
4928
5264
|
};
|
|
4929
|
-
const
|
|
5265
|
+
const openAutoDeviceKey = (fireOpen) => {
|
|
5266
|
+
const { config: cfg, options: opts } = latest.current;
|
|
5267
|
+
const hostKey = typeof opts.deviceKey === "string" && opts.deviceKey.trim() ? opts.deviceKey : void 0;
|
|
5268
|
+
if (hostKey || !(cfg == null ? void 0 : cfg.storage)) {
|
|
5269
|
+
fireOpen(void 0);
|
|
5270
|
+
return;
|
|
5271
|
+
}
|
|
5272
|
+
void hydrateDeviceIdentity({ appId: cfg.appId, storage: cfg.storage }).then((identity) => {
|
|
5273
|
+
if (cancelled) return;
|
|
5274
|
+
fireOpen((identity == null ? void 0 : identity.durable) ? identity.value : void 0);
|
|
5275
|
+
});
|
|
5276
|
+
};
|
|
5277
|
+
const fire = (autoDeviceKey) => {
|
|
4930
5278
|
var _a, _b;
|
|
4931
5279
|
const { config: cfg, options: opts } = latest.current;
|
|
4932
5280
|
if (!(cfg == null ? void 0 : cfg.serverUrl)) return;
|
|
@@ -4938,7 +5286,7 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4938
5286
|
target,
|
|
4939
5287
|
// A fresh per-open id each fire; the emitter's once-guard dedupes within the open.
|
|
4940
5288
|
userId: opts.userId,
|
|
4941
|
-
deviceKey: resolveDeviceKey(
|
|
5289
|
+
deviceKey: resolveDeviceKey(opts, autoDeviceKey),
|
|
4942
5290
|
sessionCount: opts.sessionCount,
|
|
4943
5291
|
appVersion: (_b = cfg.appVersion) != null ? _b : device.appVersion,
|
|
4944
5292
|
platform: Platform.OS,
|
|
@@ -4946,7 +5294,7 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4946
5294
|
meta: opts.meta
|
|
4947
5295
|
});
|
|
4948
5296
|
};
|
|
4949
|
-
fire
|
|
5297
|
+
openAutoDeviceKey(fire);
|
|
4950
5298
|
let backgroundedAt = null;
|
|
4951
5299
|
const onChange = (state) => {
|
|
4952
5300
|
if (state === "background" || state === "inactive") {
|
|
@@ -4956,11 +5304,12 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4956
5304
|
if (state === "active") {
|
|
4957
5305
|
const since = backgroundedAt;
|
|
4958
5306
|
backgroundedAt = null;
|
|
4959
|
-
if (since != null && Date.now() - since >= BACKGROUND_SESSION_MS) fire
|
|
5307
|
+
if (since != null && Date.now() - since >= BACKGROUND_SESSION_MS) openAutoDeviceKey(fire);
|
|
4960
5308
|
}
|
|
4961
5309
|
};
|
|
4962
5310
|
const sub = AppState.addEventListener("change", onChange);
|
|
4963
5311
|
return () => {
|
|
5312
|
+
cancelled = true;
|
|
4964
5313
|
if (sub && typeof sub.remove === "function") sub.remove();
|
|
4965
5314
|
};
|
|
4966
5315
|
}, []);
|
|
@@ -4969,11 +5318,12 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4969
5318
|
// src/session-analytics/lifecycle.ts
|
|
4970
5319
|
var FIRST_OPEN_EVENT = "app.first_open";
|
|
4971
5320
|
var firstOpenStorageKey = (appId) => `wireai:first_open:${appId}`;
|
|
4972
|
-
var
|
|
4973
|
-
var
|
|
5321
|
+
var READ_TIMEOUT_MS4 = 1500;
|
|
5322
|
+
var READ_TIMED_OUT3 = /* @__PURE__ */ Symbol("wireai:first-open-read-timeout");
|
|
5323
|
+
var withTimeout4 = (p, ms) => {
|
|
4974
5324
|
let timer;
|
|
4975
5325
|
const timeout = new Promise((resolve) => {
|
|
4976
|
-
timer = setTimeout(() => resolve(
|
|
5326
|
+
timer = setTimeout(() => resolve(READ_TIMED_OUT3), ms);
|
|
4977
5327
|
});
|
|
4978
5328
|
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
4979
5329
|
};
|
|
@@ -4992,335 +5342,82 @@ var buildLifecycleEvent = (questionKey, opts) => {
|
|
|
4992
5342
|
if (opts.appVersion) userContext.app_version = opts.appVersion;
|
|
4993
5343
|
if (opts.platform) userContext.platform = opts.platform;
|
|
4994
5344
|
const event = {
|
|
4995
|
-
event_type: "app_event",
|
|
4996
|
-
question_key: questionKey,
|
|
4997
|
-
session_id: (_a = opts.sessionId) != null ? _a : makeSessionId()
|
|
4998
|
-
};
|
|
4999
|
-
const userId = sanitizeUserId(opts.userId);
|
|
5000
|
-
if (userId) event.user_id = userId;
|
|
5001
|
-
if (Object.keys(userContext).length > 0) event.user_context = userContext;
|
|
5002
|
-
if (opts.device) event.device = opts.device;
|
|
5003
|
-
if (opts.meta && Object.keys(opts.meta).length > 0) event.meta = JSON.stringify(opts.meta);
|
|
5004
|
-
return event;
|
|
5005
|
-
};
|
|
5006
|
-
var routeLifecycleEvent = (event, opts) => {
|
|
5007
|
-
try {
|
|
5008
|
-
if (opts.sink) {
|
|
5009
|
-
opts.sink(event);
|
|
5010
|
-
return;
|
|
5011
|
-
}
|
|
5012
|
-
const req = buildEventsRequest(opts.target, [event]);
|
|
5013
|
-
if (!req) return;
|
|
5014
|
-
void fetch(req.url, req.init).catch(() => {
|
|
5015
|
-
});
|
|
5016
|
-
} catch {
|
|
5017
|
-
}
|
|
5018
|
-
};
|
|
5019
|
-
var emitFirstOpen = (opts) => {
|
|
5020
|
-
routeLifecycleEvent(buildLifecycleEvent(FIRST_OPEN_EVENT, opts), opts);
|
|
5021
|
-
};
|
|
5022
|
-
var reportFirstOpen = (opts) => {
|
|
5023
|
-
var _a;
|
|
5024
|
-
const appId = (_a = opts.appId) != null ? _a : "default";
|
|
5025
|
-
if (_firstOpenLatched.has(appId)) return;
|
|
5026
|
-
_firstOpenLatched.add(appId);
|
|
5027
|
-
const storage = opts.storage;
|
|
5028
|
-
if (!storage) {
|
|
5029
|
-
emitFirstOpen(opts);
|
|
5030
|
-
return;
|
|
5031
|
-
}
|
|
5032
|
-
const key = firstOpenStorageKey(appId);
|
|
5033
|
-
void (async () => {
|
|
5034
|
-
try {
|
|
5035
|
-
const seen = await withTimeout3(storage.getItem(key), READ_TIMEOUT_MS3);
|
|
5036
|
-
if (seen) return;
|
|
5037
|
-
emitFirstOpen(opts);
|
|
5038
|
-
try {
|
|
5039
|
-
void storage.setItem(key, JSON.stringify({ ts: Date.now() })).catch(() => {
|
|
5040
|
-
});
|
|
5041
|
-
} catch {
|
|
5042
|
-
}
|
|
5043
|
-
} catch {
|
|
5044
|
-
emitFirstOpen(opts);
|
|
5045
|
-
}
|
|
5046
|
-
})();
|
|
5047
|
-
};
|
|
5048
|
-
var wireLifecycleEvents = (opts) => {
|
|
5049
|
-
var _a;
|
|
5050
|
-
const sessionId = (_a = opts.sessionId) != null ? _a : makeSessionId();
|
|
5051
|
-
reportFirstOpen({ ...opts, sessionId });
|
|
5052
|
-
reportSessionStart({
|
|
5053
|
-
target: opts.target,
|
|
5054
|
-
sink: opts.sink,
|
|
5055
|
-
sessionId,
|
|
5056
|
-
userId: opts.userId,
|
|
5057
|
-
deviceKey: opts.deviceKey,
|
|
5058
|
-
sessionCount: opts.sessionCount,
|
|
5059
|
-
appVersion: opts.appVersion,
|
|
5060
|
-
platform: opts.platform,
|
|
5061
|
-
device: opts.device,
|
|
5062
|
-
meta: opts.meta
|
|
5063
|
-
});
|
|
5064
|
-
};
|
|
5065
|
-
|
|
5066
|
-
// src/analytics/eventQueue.ts
|
|
5067
|
-
var DEFAULTS = {
|
|
5068
|
-
maxSize: 200,
|
|
5069
|
-
batchSize: 20,
|
|
5070
|
-
baseBackoffMs: 1e3,
|
|
5071
|
-
maxBackoffMs: 3e4,
|
|
5072
|
-
maxRetries: 6
|
|
5073
|
-
};
|
|
5074
|
-
var READ_TIMEOUT_MS4 = 1500;
|
|
5075
|
-
var QUEUE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:eventQueueKeys");
|
|
5076
|
-
var queueKeyGlobal = globalThis;
|
|
5077
|
-
var claimedQueueKeys = () => {
|
|
5078
|
-
const existing = queueKeyGlobal[QUEUE_KEY_SLOT];
|
|
5079
|
-
if (existing) return existing;
|
|
5080
|
-
const created = /* @__PURE__ */ new Set();
|
|
5081
|
-
queueKeyGlobal[QUEUE_KEY_SLOT] = created;
|
|
5082
|
-
return created;
|
|
5083
|
-
};
|
|
5084
|
-
var claimQueueKey = (preferred, explicit) => {
|
|
5085
|
-
const claimed = claimedQueueKeys();
|
|
5086
|
-
if (explicit || !claimed.has(preferred)) {
|
|
5087
|
-
claimed.add(preferred);
|
|
5088
|
-
return preferred;
|
|
5089
|
-
}
|
|
5090
|
-
let ordinal = 2;
|
|
5091
|
-
while (claimed.has(`${preferred}#${ordinal}`)) ordinal++;
|
|
5092
|
-
const key = `${preferred}#${ordinal}`;
|
|
5093
|
-
claimed.add(key);
|
|
5094
|
-
warnInDev(
|
|
5095
|
-
`[wireai] a second event queue was created for the same appId, and "${preferred}" is already claimed by a live one. Two queues sharing one storage slot overwrite each other's backlog, delete each other's pending events when one drains to empty, and double-send on relaunch, so this queue was given "${key}" instead. Prefer ONE analytics instance per app; if you really need two, pass an explicit \`storageKey\` to each so the slots are yours to reason about.`
|
|
5096
|
-
);
|
|
5097
|
-
return key;
|
|
5098
|
-
};
|
|
5099
|
-
var READ_TIMED_OUT = /* @__PURE__ */ Symbol("wireai:storage-read-timeout");
|
|
5100
|
-
var withTimeout4 = (p, ms) => {
|
|
5101
|
-
let timer;
|
|
5102
|
-
const timeout = new Promise((resolve) => {
|
|
5103
|
-
timer = setTimeout(() => resolve(READ_TIMED_OUT), ms);
|
|
5104
|
-
});
|
|
5105
|
-
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
5106
|
-
};
|
|
5107
|
-
var unrefTimer = (timer) => {
|
|
5108
|
-
const t = timer;
|
|
5109
|
-
if (typeof t.unref === "function") t.unref();
|
|
5345
|
+
event_type: "app_event",
|
|
5346
|
+
question_key: questionKey,
|
|
5347
|
+
session_id: (_a = opts.sessionId) != null ? _a : makeSessionId()
|
|
5348
|
+
};
|
|
5349
|
+
const userId = sanitizeUserId(opts.userId);
|
|
5350
|
+
if (userId) event.user_id = userId;
|
|
5351
|
+
if (Object.keys(userContext).length > 0) event.user_context = userContext;
|
|
5352
|
+
if (opts.device) event.device = opts.device;
|
|
5353
|
+
if (opts.meta && Object.keys(opts.meta).length > 0) event.meta = JSON.stringify(opts.meta);
|
|
5354
|
+
return event;
|
|
5110
5355
|
};
|
|
5111
|
-
var
|
|
5112
|
-
if (!raw) return [];
|
|
5356
|
+
var routeLifecycleEvent = (event, opts) => {
|
|
5113
5357
|
try {
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
for (const entry of parsed) {
|
|
5118
|
-
if (entry && typeof entry === "object" && typeof entry.id === "number" && entry.event && typeof entry.event === "object") {
|
|
5119
|
-
items.push(entry);
|
|
5120
|
-
}
|
|
5358
|
+
if (opts.sink) {
|
|
5359
|
+
opts.sink(event);
|
|
5360
|
+
return;
|
|
5121
5361
|
}
|
|
5122
|
-
|
|
5362
|
+
const req = buildEventsRequest(opts.target, [event]);
|
|
5363
|
+
if (!req) return;
|
|
5364
|
+
void fetch(req.url, req.init).catch(() => {
|
|
5365
|
+
});
|
|
5123
5366
|
} catch {
|
|
5124
|
-
return [];
|
|
5125
5367
|
}
|
|
5126
5368
|
};
|
|
5127
|
-
var
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
const
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
const
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
let retryTimer;
|
|
5143
|
-
let backlogUnread = false;
|
|
5144
|
-
const resolveEnvelope = () => {
|
|
5145
|
-
try {
|
|
5146
|
-
return typeof options.envelope === "function" ? options.envelope() : options.envelope;
|
|
5147
|
-
} catch {
|
|
5148
|
-
return void 0;
|
|
5149
|
-
}
|
|
5150
|
-
};
|
|
5151
|
-
const stamp = (event) => {
|
|
5152
|
-
var _a2;
|
|
5153
|
-
const env = resolveEnvelope();
|
|
5154
|
-
const stamped = { ...event };
|
|
5155
|
-
if (stamped.ts === void 0) stamped.ts = Date.now();
|
|
5156
|
-
if (!env) return stamped;
|
|
5157
|
-
if (!stamped.device && env.device) stamped.device = env.device;
|
|
5158
|
-
if (!stamped.session_id && env.sessionId) stamped.session_id = env.sessionId;
|
|
5159
|
-
const uc = { ...(_a2 = stamped.user_context) != null ? _a2 : {} };
|
|
5160
|
-
if (env.appVersion && uc.app_version === void 0) uc.app_version = env.appVersion;
|
|
5161
|
-
if (env.appBuild && uc.app_build === void 0) uc.app_build = env.appBuild;
|
|
5162
|
-
if (env.networkType && uc.network_type === void 0) uc.network_type = env.networkType;
|
|
5163
|
-
if (Object.keys(uc).length > 0) stamped.user_context = uc;
|
|
5164
|
-
return stamped;
|
|
5165
|
-
};
|
|
5166
|
-
const persist = () => {
|
|
5167
|
-
if (!storage) return;
|
|
5168
|
-
if (backlogUnread) return;
|
|
5169
|
-
try {
|
|
5170
|
-
if (pending.length === 0) {
|
|
5171
|
-
void storage.removeItem(key).catch(() => {
|
|
5172
|
-
});
|
|
5173
|
-
return;
|
|
5174
|
-
}
|
|
5175
|
-
const payload = pending.map((item) => ({ id: item.id, event: item.event }));
|
|
5176
|
-
void storage.setItem(key, JSON.stringify(payload)).catch(() => {
|
|
5177
|
-
});
|
|
5178
|
-
} catch {
|
|
5179
|
-
}
|
|
5180
|
-
};
|
|
5181
|
-
const enforceSizeCap = () => {
|
|
5182
|
-
if (pending.length > maxSize) pending.splice(0, pending.length - maxSize);
|
|
5183
|
-
};
|
|
5184
|
-
const safeSig = (event) => {
|
|
5185
|
-
try {
|
|
5186
|
-
return JSON.stringify(event);
|
|
5187
|
-
} catch {
|
|
5188
|
-
return `__nosig_${nextId}_${Math.random()}`;
|
|
5189
|
-
}
|
|
5190
|
-
};
|
|
5191
|
-
const mergePersisted = (persistedItems) => {
|
|
5192
|
-
if (persistedItems.length === 0) return;
|
|
5193
|
-
const seen = new Set(pending.map((item) => item.sig));
|
|
5194
|
-
const restored = [];
|
|
5195
|
-
for (const persisted of persistedItems) {
|
|
5196
|
-
const sig = safeSig(persisted.event);
|
|
5197
|
-
if (seen.has(sig)) continue;
|
|
5198
|
-
seen.add(sig);
|
|
5199
|
-
restored.push({ id: nextId++, event: persisted.event, sig });
|
|
5200
|
-
}
|
|
5201
|
-
if (restored.length === 0) return;
|
|
5202
|
-
pending = [...restored, ...pending];
|
|
5203
|
-
enforceSizeCap();
|
|
5204
|
-
persist();
|
|
5205
|
-
};
|
|
5206
|
-
const loadPromise = (async () => {
|
|
5207
|
-
if (!storage) return;
|
|
5369
|
+
var emitFirstOpen = (opts) => {
|
|
5370
|
+
routeLifecycleEvent(buildLifecycleEvent(FIRST_OPEN_EVENT, opts), opts);
|
|
5371
|
+
};
|
|
5372
|
+
var reportFirstOpen = (opts) => {
|
|
5373
|
+
var _a;
|
|
5374
|
+
const appId = (_a = opts.appId) != null ? _a : "default";
|
|
5375
|
+
if (_firstOpenLatched.has(appId)) return;
|
|
5376
|
+
_firstOpenLatched.add(appId);
|
|
5377
|
+
const storage = opts.storage;
|
|
5378
|
+
if (!storage) {
|
|
5379
|
+
emitFirstOpen(opts);
|
|
5380
|
+
return;
|
|
5381
|
+
}
|
|
5382
|
+
const key = firstOpenStorageKey(appId);
|
|
5383
|
+
void (async () => {
|
|
5208
5384
|
try {
|
|
5209
|
-
const
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
warnInDev(
|
|
5216
|
-
`[wireai] the persisted analytics backlog at "${key}" took longer than ${READ_TIMEOUT_MS4}ms to read, so the queue started without it. The stored events are NOT discarded: writes are held back until the read lands, and the backlog is merged in then. If you see this on every cold start, your storage adapter is too slow to be on the launch path.`
|
|
5217
|
-
);
|
|
5218
|
-
void read.then((late) => {
|
|
5219
|
-
backlogUnread = false;
|
|
5220
|
-
mergePersisted(parsePersisted2(late));
|
|
5221
|
-
flush();
|
|
5222
|
-
}).catch(() => {
|
|
5223
|
-
backlogUnread = false;
|
|
5224
|
-
persist();
|
|
5385
|
+
const seen = await withTimeout4(storage.getItem(key), READ_TIMEOUT_MS4);
|
|
5386
|
+
if (seen === READ_TIMED_OUT3) return;
|
|
5387
|
+
if (seen) return;
|
|
5388
|
+
emitFirstOpen(opts);
|
|
5389
|
+
try {
|
|
5390
|
+
void storage.setItem(key, JSON.stringify({ ts: Date.now() })).catch(() => {
|
|
5225
5391
|
});
|
|
5226
|
-
|
|
5392
|
+
} catch {
|
|
5227
5393
|
}
|
|
5228
|
-
mergePersisted(parsePersisted2(raced));
|
|
5229
5394
|
} catch {
|
|
5230
5395
|
}
|
|
5231
5396
|
})();
|
|
5232
|
-
const postBatch = async (events) => {
|
|
5233
|
-
const req = buildEventsRequest(target, events);
|
|
5234
|
-
if (!req) return false;
|
|
5235
|
-
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
5236
|
-
const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
|
|
5237
|
-
try {
|
|
5238
|
-
const res = await fetch(req.url, { ...req.init, signal: controller == null ? void 0 : controller.signal });
|
|
5239
|
-
warnOnSkippedEvents(res);
|
|
5240
|
-
return !!(res && res.ok);
|
|
5241
|
-
} catch {
|
|
5242
|
-
return false;
|
|
5243
|
-
} finally {
|
|
5244
|
-
clearTimeout(timer);
|
|
5245
|
-
}
|
|
5246
|
-
};
|
|
5247
|
-
const clearRetry = () => {
|
|
5248
|
-
if (retryTimer !== void 0) {
|
|
5249
|
-
clearTimeout(retryTimer);
|
|
5250
|
-
retryTimer = void 0;
|
|
5251
|
-
}
|
|
5252
|
-
};
|
|
5253
|
-
const scheduleRetry = () => {
|
|
5254
|
-
if (attempt >= maxRetries) return;
|
|
5255
|
-
const delay = Math.min(baseBackoffMs * 2 ** attempt, maxBackoffMs);
|
|
5256
|
-
attempt++;
|
|
5257
|
-
clearRetry();
|
|
5258
|
-
retryTimer = setTimeout(() => {
|
|
5259
|
-
retryTimer = void 0;
|
|
5260
|
-
void drain();
|
|
5261
|
-
}, delay);
|
|
5262
|
-
unrefTimer(retryTimer);
|
|
5263
|
-
};
|
|
5264
|
-
const drain = async () => {
|
|
5265
|
-
try {
|
|
5266
|
-
await loadPromise;
|
|
5267
|
-
} catch {
|
|
5268
|
-
}
|
|
5269
|
-
if (flushing) return;
|
|
5270
|
-
flushing = true;
|
|
5271
|
-
try {
|
|
5272
|
-
while (pending.length > 0) {
|
|
5273
|
-
const batch = pending.slice(0, batchSize);
|
|
5274
|
-
const ok = await postBatch(batch.map((item) => item.event));
|
|
5275
|
-
if (!ok) {
|
|
5276
|
-
scheduleRetry();
|
|
5277
|
-
return;
|
|
5278
|
-
}
|
|
5279
|
-
const acked = new Set(batch.map((item) => item.id));
|
|
5280
|
-
pending = pending.filter((item) => !acked.has(item.id));
|
|
5281
|
-
persist();
|
|
5282
|
-
attempt = 0;
|
|
5283
|
-
clearRetry();
|
|
5284
|
-
}
|
|
5285
|
-
} finally {
|
|
5286
|
-
flushing = false;
|
|
5287
|
-
}
|
|
5288
|
-
};
|
|
5289
|
-
const flush = () => {
|
|
5290
|
-
try {
|
|
5291
|
-
void drain();
|
|
5292
|
-
} catch {
|
|
5293
|
-
}
|
|
5294
|
-
};
|
|
5295
|
-
const enqueue = (event) => {
|
|
5296
|
-
try {
|
|
5297
|
-
const stamped = stamp(event);
|
|
5298
|
-
const sig = safeSig(stamped);
|
|
5299
|
-
for (const item of pending) {
|
|
5300
|
-
if (item.sig === sig) return;
|
|
5301
|
-
}
|
|
5302
|
-
pending.push({ id: nextId++, event: stamped, sig });
|
|
5303
|
-
enforceSizeCap();
|
|
5304
|
-
persist();
|
|
5305
|
-
if (retryTimer === void 0) flush();
|
|
5306
|
-
} catch {
|
|
5307
|
-
}
|
|
5308
|
-
};
|
|
5309
|
-
const notifyOnline = () => {
|
|
5310
|
-
attempt = 0;
|
|
5311
|
-
clearRetry();
|
|
5312
|
-
flush();
|
|
5313
|
-
};
|
|
5314
|
-
const size = () => pending.length;
|
|
5315
|
-
return { enqueue, flush, notifyOnline, size };
|
|
5316
5397
|
};
|
|
5317
|
-
|
|
5318
|
-
|
|
5398
|
+
var wireLifecycleEvents = (opts) => {
|
|
5399
|
+
var _a;
|
|
5400
|
+
const sessionId = (_a = opts.sessionId) != null ? _a : makeSessionId();
|
|
5401
|
+
reportFirstOpen({ ...opts, sessionId });
|
|
5402
|
+
reportSessionStart({
|
|
5403
|
+
target: opts.target,
|
|
5404
|
+
sink: opts.sink,
|
|
5405
|
+
sessionId,
|
|
5406
|
+
userId: opts.userId,
|
|
5407
|
+
deviceKey: opts.deviceKey,
|
|
5408
|
+
sessionCount: opts.sessionCount,
|
|
5409
|
+
appVersion: opts.appVersion,
|
|
5410
|
+
platform: opts.platform,
|
|
5411
|
+
device: opts.device,
|
|
5412
|
+
meta: opts.meta
|
|
5413
|
+
});
|
|
5414
|
+
};
|
|
5319
5415
|
var useLifecycleEvents = (config, options = {}) => {
|
|
5320
5416
|
const latest = useRef({ config, options });
|
|
5321
5417
|
latest.current = { config, options };
|
|
5322
5418
|
const queueRef = useRef(void 0);
|
|
5323
5419
|
useEffect(() => {
|
|
5420
|
+
let cancelled = false;
|
|
5324
5421
|
const resolveSink = () => {
|
|
5325
5422
|
var _a, _b;
|
|
5326
5423
|
const { config: cfg, options: opts } = latest.current;
|
|
@@ -5341,7 +5438,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5341
5438
|
var _a;
|
|
5342
5439
|
return (cfg == null ? void 0 : cfg.serverUrl) ? { serverUrl: cfg.serverUrl, apiKey: (_a = cfg.apiKey) != null ? _a : "" } : void 0;
|
|
5343
5440
|
};
|
|
5344
|
-
const resolveDeviceKey = (cfg, opts) => {
|
|
5441
|
+
const resolveDeviceKey = (cfg, opts, autoDeviceKey) => {
|
|
5345
5442
|
var _a;
|
|
5346
5443
|
const host = (_a = resolveIdentity({
|
|
5347
5444
|
value: opts.deviceKey,
|
|
@@ -5350,11 +5447,22 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5350
5447
|
scope: cfg == null ? void 0 : cfg.appId
|
|
5351
5448
|
})) == null ? void 0 : _a.value;
|
|
5352
5449
|
if (host) return host;
|
|
5353
|
-
|
|
5354
|
-
|
|
5450
|
+
return autoDeviceKey;
|
|
5451
|
+
};
|
|
5452
|
+
const openAutoDeviceKey = (fire) => {
|
|
5453
|
+
const { config: cfg, options: opts } = latest.current;
|
|
5454
|
+
const hostKey = typeof opts.deviceKey === "string" && opts.deviceKey.trim() ? opts.deviceKey : void 0;
|
|
5455
|
+
if (hostKey || !(cfg == null ? void 0 : cfg.storage)) {
|
|
5456
|
+
fire(void 0);
|
|
5457
|
+
return;
|
|
5458
|
+
}
|
|
5459
|
+
void hydrateDeviceIdentity({ appId: cfg.appId, storage: cfg.storage }).then((identity) => {
|
|
5460
|
+
if (cancelled) return;
|
|
5461
|
+
fire((identity == null ? void 0 : identity.durable) ? identity.value : void 0);
|
|
5462
|
+
});
|
|
5355
5463
|
};
|
|
5356
5464
|
const mountOpenSessionId = makeSessionId();
|
|
5357
|
-
const fireSession = (sessionId) => {
|
|
5465
|
+
const fireSession = (sessionId, autoDeviceKey) => {
|
|
5358
5466
|
var _a;
|
|
5359
5467
|
const { config: cfg, options: opts } = latest.current;
|
|
5360
5468
|
if (opts.enabled === false) return;
|
|
@@ -5366,7 +5474,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5366
5474
|
sink: resolveSink(),
|
|
5367
5475
|
sessionId,
|
|
5368
5476
|
userId: opts.userId,
|
|
5369
|
-
deviceKey: resolveDeviceKey(cfg, opts),
|
|
5477
|
+
deviceKey: resolveDeviceKey(cfg, opts, autoDeviceKey),
|
|
5370
5478
|
sessionCount: opts.sessionCount,
|
|
5371
5479
|
appVersion: (_a = cfg == null ? void 0 : cfg.appVersion) != null ? _a : device.appVersion,
|
|
5372
5480
|
platform: Platform.OS,
|
|
@@ -5374,9 +5482,9 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5374
5482
|
meta: opts.meta
|
|
5375
5483
|
});
|
|
5376
5484
|
};
|
|
5377
|
-
const fireMountOpen = () => {
|
|
5485
|
+
const fireMountOpen = (autoDeviceKey) => {
|
|
5378
5486
|
var _a;
|
|
5379
|
-
fireSession(mountOpenSessionId);
|
|
5487
|
+
fireSession(mountOpenSessionId, autoDeviceKey);
|
|
5380
5488
|
const { config: cfg, options: opts } = latest.current;
|
|
5381
5489
|
if (opts.enabled === false) return;
|
|
5382
5490
|
const device = collectDeviceContext();
|
|
@@ -5388,7 +5496,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5388
5496
|
storage: cfg == null ? void 0 : cfg.storage,
|
|
5389
5497
|
appId: cfg == null ? void 0 : cfg.appId,
|
|
5390
5498
|
userId: opts.userId,
|
|
5391
|
-
deviceKey: resolveDeviceKey(cfg, opts),
|
|
5499
|
+
deviceKey: resolveDeviceKey(cfg, opts, autoDeviceKey),
|
|
5392
5500
|
sessionCount: opts.sessionCount,
|
|
5393
5501
|
appVersion: (_a = cfg == null ? void 0 : cfg.appVersion) != null ? _a : device.appVersion,
|
|
5394
5502
|
platform: Platform.OS,
|
|
@@ -5396,16 +5504,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5396
5504
|
meta: opts.meta
|
|
5397
5505
|
});
|
|
5398
5506
|
};
|
|
5399
|
-
|
|
5400
|
-
const { config: mountCfg, options: mountOpts } = latest.current;
|
|
5401
|
-
const hostKey = typeof mountOpts.deviceKey === "string" && mountOpts.deviceKey.trim() ? mountOpts.deviceKey : void 0;
|
|
5402
|
-
if (!hostKey && (mountCfg == null ? void 0 : mountCfg.storage)) {
|
|
5403
|
-
void hydrateAutoDeviceKey({ appId: mountCfg.appId, storage: mountCfg.storage }).then(() => {
|
|
5404
|
-
if (!cancelled) fireMountOpen();
|
|
5405
|
-
});
|
|
5406
|
-
} else {
|
|
5407
|
-
fireMountOpen();
|
|
5408
|
-
}
|
|
5507
|
+
openAutoDeviceKey(fireMountOpen);
|
|
5409
5508
|
let backgroundedAt = null;
|
|
5410
5509
|
const onChange = (state) => {
|
|
5411
5510
|
if (state === "background" || state === "inactive") {
|
|
@@ -5415,13 +5514,18 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5415
5514
|
if (state === "active") {
|
|
5416
5515
|
const since = backgroundedAt;
|
|
5417
5516
|
backgroundedAt = null;
|
|
5418
|
-
if (since != null && Date.now() - since >= BACKGROUND_SESSION_MS)
|
|
5517
|
+
if (since != null && Date.now() - since >= BACKGROUND_SESSION_MS) {
|
|
5518
|
+
openAutoDeviceKey((autoDeviceKey) => fireSession(void 0, autoDeviceKey));
|
|
5519
|
+
}
|
|
5419
5520
|
}
|
|
5420
5521
|
};
|
|
5421
5522
|
const sub = AppState.addEventListener("change", onChange);
|
|
5422
5523
|
return () => {
|
|
5524
|
+
var _a;
|
|
5423
5525
|
cancelled = true;
|
|
5424
5526
|
if (sub && typeof sub.remove === "function") sub.remove();
|
|
5527
|
+
(_a = queueRef.current) == null ? void 0 : _a.dispose();
|
|
5528
|
+
queueRef.current = void 0;
|
|
5425
5529
|
};
|
|
5426
5530
|
}, []);
|
|
5427
5531
|
};
|