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