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