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