@wireai/activation 0.13.6 → 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 +172 -1
- package/INTEGRATION_PROMPT.md +7 -4
- package/README.md +10 -2
- package/dist/analytics/index.d.mts +9 -2
- package/dist/analytics/index.d.ts +9 -2
- package/dist/analytics/index.js +56 -12
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +56 -12
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/{currentSession-DngW-QoD.d.mts → currentSession-CUvTOchb.d.mts} +35 -6
- package/dist/{currentSession-C5976akx.d.ts → currentSession-CW_5Mq4O.d.ts} +35 -6
- package/dist/index.d.mts +24 -6
- package/dist/index.d.ts +24 -6
- package/dist/index.js +631 -548
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +631 -548
- 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 +2 -2
- package/package.json +1 -1
- package/src/OnboardingFlow.tsx +4 -3
- 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 +16 -6
- package/src/utils/readPlan.ts +8 -5
package/dist/index.js
CHANGED
|
@@ -1234,20 +1234,21 @@ var reportClientEvents = (target, events) => {
|
|
|
1234
1234
|
}
|
|
1235
1235
|
};
|
|
1236
1236
|
var reportClientEvent = (target, event) => reportClientEvents(target, [event]);
|
|
1237
|
-
var reportClientEventsAwait = async (target, events) =>
|
|
1237
|
+
var reportClientEventsAwait = async (target, events) => await reportClientEventsOutcome(target, events) === "delivered";
|
|
1238
|
+
var reportClientEventsOutcome = async (target, events) => {
|
|
1238
1239
|
try {
|
|
1239
1240
|
const req = buildEventsRequest(target, events);
|
|
1240
|
-
if (!req) return
|
|
1241
|
+
if (!req) return "refused";
|
|
1241
1242
|
const res = await fetch(req.url, req.init);
|
|
1242
|
-
if (!res || !res.ok) return
|
|
1243
|
+
if (!res || !res.ok) return "unreachable";
|
|
1243
1244
|
const ack = await readEventsAck(res);
|
|
1244
|
-
if (!ack || ack.skipped <= 0) return
|
|
1245
|
+
if (!ack || ack.skipped <= 0) return "delivered";
|
|
1245
1246
|
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1246
1247
|
console.warn(describeDiscarded(ack));
|
|
1247
1248
|
}
|
|
1248
|
-
return
|
|
1249
|
+
return "refused";
|
|
1249
1250
|
} catch {
|
|
1250
|
-
return
|
|
1251
|
+
return "unreachable";
|
|
1251
1252
|
}
|
|
1252
1253
|
};
|
|
1253
1254
|
var reportClientEventAwait = (target, event) => reportClientEventsAwait(target, [event]);
|
|
@@ -3622,10 +3623,11 @@ var ensureCurrentSessionId = () => {
|
|
|
3622
3623
|
var DEFAULT_SESSION_TTL_MS = 36e5;
|
|
3623
3624
|
var READ_TIMEOUT_MS = 1500;
|
|
3624
3625
|
var sessionStorageKey = (appId) => `wireai:session:${appId}`;
|
|
3626
|
+
var READ_TIMED_OUT = /* @__PURE__ */ Symbol("wireai:storage-read-timeout");
|
|
3625
3627
|
var withTimeout = (p, ms) => {
|
|
3626
3628
|
let timer;
|
|
3627
3629
|
const timeout = new Promise((resolve) => {
|
|
3628
|
-
timer = setTimeout(() => resolve(
|
|
3630
|
+
timer = setTimeout(() => resolve(READ_TIMED_OUT), ms);
|
|
3629
3631
|
});
|
|
3630
3632
|
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
3631
3633
|
};
|
|
@@ -3641,12 +3643,14 @@ var parsePersisted = (raw) => {
|
|
|
3641
3643
|
return void 0;
|
|
3642
3644
|
};
|
|
3643
3645
|
var loadPersistedSession = async (storage, key, ttlMs = DEFAULT_SESSION_TTL_MS) => {
|
|
3644
|
-
let
|
|
3646
|
+
let raw;
|
|
3645
3647
|
try {
|
|
3646
|
-
|
|
3648
|
+
raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);
|
|
3647
3649
|
} catch {
|
|
3648
|
-
|
|
3650
|
+
raw = null;
|
|
3649
3651
|
}
|
|
3652
|
+
if (raw === READ_TIMED_OUT) return { id: makeSessionId(), resumed: false };
|
|
3653
|
+
const stored = parsePersisted(raw);
|
|
3650
3654
|
if (stored && Date.now() - stored.ts < ttlMs) {
|
|
3651
3655
|
return { id: stored.id, resumed: true };
|
|
3652
3656
|
}
|
|
@@ -3656,7 +3660,9 @@ var loadPersistedSession = async (storage, key, ttlMs = DEFAULT_SESSION_TTL_MS)
|
|
|
3656
3660
|
};
|
|
3657
3661
|
var peekPersistedSession = async (storage, key) => {
|
|
3658
3662
|
try {
|
|
3659
|
-
|
|
3663
|
+
const raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);
|
|
3664
|
+
if (raw === READ_TIMED_OUT) return void 0;
|
|
3665
|
+
return parsePersisted(raw);
|
|
3660
3666
|
} catch {
|
|
3661
3667
|
return void 0;
|
|
3662
3668
|
}
|
|
@@ -3809,7 +3815,9 @@ var readSettledPermissions = (raw, sessionId) => {
|
|
|
3809
3815
|
};
|
|
3810
3816
|
var loadSettledPermissions = async (storage, key, sessionId) => {
|
|
3811
3817
|
try {
|
|
3812
|
-
|
|
3818
|
+
const raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);
|
|
3819
|
+
if (raw === READ_TIMED_OUT) return [];
|
|
3820
|
+
return readSettledPermissions(raw, sessionId);
|
|
3813
3821
|
} catch {
|
|
3814
3822
|
return [];
|
|
3815
3823
|
}
|
|
@@ -3918,7 +3926,7 @@ var WireOnboarding = ({
|
|
|
3918
3926
|
React19.useEffect(() => {
|
|
3919
3927
|
if (!warnMissingJoinKey) return;
|
|
3920
3928
|
warnInDev(
|
|
3921
|
-
"[wireai] <WireOnboarding> got no user_context.device_key, so this onboarding session can never be joined to the app's later events and the `activated` funnel will read zero. Pass userContext={activationJoinContext(deviceKey)}
|
|
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
|
|
3922
3930
|
);
|
|
3923
3931
|
}, [warnMissingJoinKey, autoJoinReason]);
|
|
3924
3932
|
const hostKeyElsewhere = missingJoinKey ? hostIdentity("device", config.appId) : void 0;
|
|
@@ -4597,205 +4605,518 @@ var attributionMetadata = (a) => {
|
|
|
4597
4605
|
return { attribution };
|
|
4598
4606
|
};
|
|
4599
4607
|
|
|
4600
|
-
// src/
|
|
4601
|
-
var
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
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];
|
|
4607
4621
|
if (existing) return existing;
|
|
4608
|
-
const created =
|
|
4609
|
-
|
|
4622
|
+
const created = /* @__PURE__ */ new Set();
|
|
4623
|
+
queueKeyGlobal[QUEUE_KEY_SLOT] = created;
|
|
4610
4624
|
return created;
|
|
4611
4625
|
};
|
|
4612
|
-
var
|
|
4613
|
-
const
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
listener();
|
|
4618
|
-
} catch {
|
|
4619
|
-
}
|
|
4626
|
+
var claimQueueKey = (preferred, explicit) => {
|
|
4627
|
+
const claimed = claimedQueueKeys();
|
|
4628
|
+
if (explicit || !claimed.has(preferred)) {
|
|
4629
|
+
claimed.add(preferred);
|
|
4630
|
+
return preferred;
|
|
4620
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;
|
|
4621
4640
|
};
|
|
4622
|
-
var
|
|
4623
|
-
|
|
4624
|
-
s.listeners.add(listener);
|
|
4625
|
-
return () => {
|
|
4626
|
-
s.listeners.delete(listener);
|
|
4627
|
-
};
|
|
4628
|
-
};
|
|
4629
|
-
var getActivationRevalidationVersion = () => store().version;
|
|
4630
|
-
var resetActivationRevalidation = () => {
|
|
4631
|
-
const s = store();
|
|
4632
|
-
s.version = 0;
|
|
4633
|
-
s.listeners.clear();
|
|
4641
|
+
var releaseQueueKey = (key) => {
|
|
4642
|
+
claimedQueueKeys().delete(key);
|
|
4634
4643
|
};
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
const target = { serverUrl: config.serverUrl, apiKey: config.apiKey };
|
|
4641
|
-
const explicitDeviceKey = (_b = clean(config.deviceKey)) != null ? _b : clean((_a = config.userContext) == null ? void 0 : _a.deviceKey);
|
|
4642
|
-
resolveIdentity({
|
|
4643
|
-
value: explicitDeviceKey,
|
|
4644
|
-
space: "device",
|
|
4645
|
-
source: "host",
|
|
4646
|
-
scope: config.appId
|
|
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);
|
|
4647
4649
|
});
|
|
4648
|
-
|
|
4649
|
-
appId: config.appId,
|
|
4650
|
-
// An explicit key opts out of minting AND persisting (unchanged contract).
|
|
4651
|
-
storage: explicitDeviceKey ? void 0 : config.storage
|
|
4652
|
-
};
|
|
4653
|
-
if (!explicitDeviceKey) resolveAutoDeviceKey(autoDeviceKeyOptions);
|
|
4654
|
-
const detectedAppVersion = detectAppVersion();
|
|
4655
|
-
const applyContext = (event) => {
|
|
4656
|
-
var _a2, _b2, _c;
|
|
4657
|
-
const resolved = resolveUserContext(
|
|
4658
|
-
// `??` is lazy on purpose: an explicit key must never even touch the auto registry.
|
|
4659
|
-
{
|
|
4660
|
-
...(_a2 = config.userContext) != null ? _a2 : {},
|
|
4661
|
-
deviceKey: explicitDeviceKey != null ? explicitDeviceKey : resolveAutoDeviceKey(autoDeviceKeyOptions)
|
|
4662
|
-
},
|
|
4663
|
-
{ autoAppVersion: (_b2 = config.appVersion) != null ? _b2 : detectedAppVersion }
|
|
4664
|
-
);
|
|
4665
|
-
if (resolved.userContext) {
|
|
4666
|
-
event.user_context = { ...resolved.userContext, ...(_c = event.user_context) != null ? _c : {} };
|
|
4667
|
-
}
|
|
4668
|
-
if (resolved.userId && !event.user_id) event.user_id = resolved.userId;
|
|
4669
|
-
};
|
|
4670
|
-
const track = async (name, meta) => {
|
|
4671
|
-
if (!clean(name)) return false;
|
|
4672
|
-
const sessionId = ensureCurrentSessionId();
|
|
4673
|
-
const event = {
|
|
4674
|
-
event_type: "app_event",
|
|
4675
|
-
session_id: sessionId,
|
|
4676
|
-
question_key: name
|
|
4677
|
-
};
|
|
4678
|
-
if (meta && Object.keys(meta).length > 0) event.meta = JSON.stringify(meta);
|
|
4679
|
-
applyContext(event);
|
|
4680
|
-
const ok = await reportClientEventAwait(target, event);
|
|
4681
|
-
if (ok) bumpActivationRevalidation();
|
|
4682
|
-
return ok;
|
|
4683
|
-
};
|
|
4684
|
-
return {
|
|
4685
|
-
track,
|
|
4686
|
-
get sessionId() {
|
|
4687
|
-
return getCurrentSessionId();
|
|
4688
|
-
},
|
|
4689
|
-
subscribeRevalidation: subscribeActivationRevalidation,
|
|
4690
|
-
getRevalidationVersion: getActivationRevalidationVersion
|
|
4691
|
-
};
|
|
4692
|
-
};
|
|
4693
|
-
var useActivationRevalidation = () => React19.useSyncExternalStore(
|
|
4694
|
-
subscribeActivationRevalidation,
|
|
4695
|
-
getActivationRevalidationVersion,
|
|
4696
|
-
getActivationRevalidationVersion
|
|
4697
|
-
);
|
|
4698
|
-
var useWireActivation = (config) => {
|
|
4699
|
-
var _a;
|
|
4700
|
-
const ref = React19.useRef(void 0);
|
|
4701
|
-
const prevKeys = React19.useRef("");
|
|
4702
|
-
const currentKeys = [
|
|
4703
|
-
config.serverUrl,
|
|
4704
|
-
config.apiKey,
|
|
4705
|
-
config.appId,
|
|
4706
|
-
config.deviceKey,
|
|
4707
|
-
(_a = config.userContext) == null ? void 0 : _a.deviceKey
|
|
4708
|
-
].join("|");
|
|
4709
|
-
if (!ref.current || prevKeys.current !== currentKeys) {
|
|
4710
|
-
prevKeys.current = currentKeys;
|
|
4711
|
-
ref.current = createWireActivation(config);
|
|
4712
|
-
}
|
|
4713
|
-
const revalidation = useActivationRevalidation();
|
|
4714
|
-
return { track: ref.current.track, sessionId: ref.current.sessionId, revalidation };
|
|
4715
|
-
};
|
|
4716
|
-
|
|
4717
|
-
// src/revenuecat/purchaseEvents.ts
|
|
4718
|
-
var WIRE_PURCHASE_EVENTS = {
|
|
4719
|
-
/** The paywall became visible (offerings loaded). */
|
|
4720
|
-
paywallShown: "wire_paywall_shown",
|
|
4721
|
-
/** The user tapped buy; the store sheet is about to open. */
|
|
4722
|
-
checkoutStarted: "wire_checkout_started",
|
|
4723
|
-
/** The store confirmed the purchase AND the entitlement is now active. */
|
|
4724
|
-
purchased: "wire_purchase_completed",
|
|
4725
|
-
/** The purchase did not land: a user cancel or a real store/network error (see `reason`). */
|
|
4726
|
-
purchaseFailed: "wire_purchase_failed",
|
|
4727
|
-
/** A restore ran (whether or not it produced an entitlement; see `plan_tier`). */
|
|
4728
|
-
restored: "wire_purchase_restored"
|
|
4729
|
-
};
|
|
4730
|
-
var PLAN_TIER_CONTEXT_KEY = "plan_tier";
|
|
4731
|
-
var asRecord = (value) => typeof value === "object" && value !== null ? value : void 0;
|
|
4732
|
-
var activeEntitlement = (info, entitlementId) => {
|
|
4733
|
-
var _a, _b;
|
|
4734
|
-
const active = asRecord((_b = asRecord((_a = asRecord(info)) == null ? void 0 : _a.entitlements)) == null ? void 0 : _b.active);
|
|
4735
|
-
const found = active == null ? void 0 : active[entitlementId];
|
|
4736
|
-
const entitlement = asRecord(found);
|
|
4737
|
-
if (!entitlement) return void 0;
|
|
4738
|
-
return entitlement.isActive === false ? void 0 : entitlement;
|
|
4650
|
+
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
4739
4651
|
};
|
|
4740
|
-
var
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
if (!entitlement) return "free";
|
|
4744
|
-
return isTrial(entitlement) ? "trial" : "paid";
|
|
4652
|
+
var unrefTimer = (timer) => {
|
|
4653
|
+
const t = timer;
|
|
4654
|
+
if (typeof t.unref === "function") t.unref();
|
|
4745
4655
|
};
|
|
4746
|
-
var
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
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
|
+
}
|
|
4757
4666
|
}
|
|
4758
|
-
|
|
4759
|
-
}
|
|
4760
|
-
|
|
4761
|
-
};
|
|
4762
|
-
var describeEntitlement = (info, entitlementId) => {
|
|
4763
|
-
const entitlement = activeEntitlement(info, entitlementId);
|
|
4764
|
-
const props = {
|
|
4765
|
-
entitlement: entitlementId,
|
|
4766
|
-
plan_tier: entitlement ? isTrial(entitlement) ? "trial" : "paid" : "free"
|
|
4767
|
-
};
|
|
4768
|
-
if (!entitlement) return props;
|
|
4769
|
-
if (typeof entitlement.periodType === "string") props.period_type = entitlement.periodType;
|
|
4770
|
-
props.is_trial = isTrial(entitlement);
|
|
4771
|
-
if (typeof entitlement.willRenew === "boolean") props.will_renew = entitlement.willRenew;
|
|
4772
|
-
if (typeof entitlement.store === "string") props.store = entitlement.store;
|
|
4773
|
-
if (typeof entitlement.productIdentifier === "string") {
|
|
4774
|
-
props.product_id = entitlement.productIdentifier;
|
|
4667
|
+
return items;
|
|
4668
|
+
} catch {
|
|
4669
|
+
return [];
|
|
4775
4670
|
}
|
|
4776
|
-
if (typeof entitlement.isSandbox === "boolean") props.is_sandbox = entitlement.isSandbox;
|
|
4777
|
-
return props;
|
|
4778
|
-
};
|
|
4779
|
-
var PURCHASE_CANCELLED_CODE = "1";
|
|
4780
|
-
var isUserCancelled = (error) => {
|
|
4781
|
-
const record = asRecord(error);
|
|
4782
|
-
if (!record) return false;
|
|
4783
|
-
if (record.userCancelled === true) return true;
|
|
4784
|
-
return record.code === PURCHASE_CANCELLED_CODE;
|
|
4785
|
-
};
|
|
4786
|
-
var describeFailure = (error) => {
|
|
4787
|
-
const record = asRecord(error);
|
|
4788
|
-
const code = typeof (record == null ? void 0 : record.code) === "string" ? record.code : "unknown";
|
|
4789
|
-
return { reason: isUserCancelled(error) ? "cancelled" : "error", code };
|
|
4790
4671
|
};
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
const
|
|
4795
|
-
const
|
|
4796
|
-
const
|
|
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 = () => {
|
|
4797
4691
|
try {
|
|
4798
|
-
|
|
4692
|
+
return typeof options.envelope === "function" ? options.envelope() : options.envelope;
|
|
4693
|
+
} catch {
|
|
4694
|
+
return void 0;
|
|
4695
|
+
}
|
|
4696
|
+
};
|
|
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
|
+
|
|
5113
|
+
// src/revenuecat/revenueCatBridge.ts
|
|
5114
|
+
var createRevenueCatBridge = (config) => {
|
|
5115
|
+
const { analytics, entitlementId } = config;
|
|
5116
|
+
const writeTier = config.writePlanTier !== false;
|
|
5117
|
+
const emit = (event, props) => {
|
|
5118
|
+
try {
|
|
5119
|
+
const result = analytics.track(event, { ...config.commonProps, ...props });
|
|
4799
5120
|
if (result && typeof result.catch === "function") {
|
|
4800
5121
|
void result.catch(() => {
|
|
4801
5122
|
});
|
|
@@ -4906,15 +5227,9 @@ var reportSessionStart = (opts) => {
|
|
|
4906
5227
|
opts.sink(event);
|
|
4907
5228
|
return;
|
|
4908
5229
|
}
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
4913
|
-
void fetch(url, {
|
|
4914
|
-
method: "POST",
|
|
4915
|
-
headers,
|
|
4916
|
-
body: JSON.stringify({ events: [event] })
|
|
4917
|
-
}).then((res) => {
|
|
5230
|
+
const req = buildEventsRequest(target, [event]);
|
|
5231
|
+
if (!req) return;
|
|
5232
|
+
void fetch(req.url, req.init).then((res) => {
|
|
4918
5233
|
warnOnSkippedEvents(res);
|
|
4919
5234
|
}).catch(() => {
|
|
4920
5235
|
});
|
|
@@ -4926,13 +5241,25 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4926
5241
|
const latest = React19.useRef({ config, options });
|
|
4927
5242
|
latest.current = { config, options };
|
|
4928
5243
|
React19.useEffect(() => {
|
|
4929
|
-
|
|
5244
|
+
let cancelled = false;
|
|
5245
|
+
const resolveDeviceKey = (opts, autoDeviceKey) => {
|
|
4930
5246
|
const host = typeof opts.deviceKey === "string" && opts.deviceKey.trim() ? opts.deviceKey : void 0;
|
|
4931
5247
|
if (host) return host;
|
|
4932
|
-
|
|
4933
|
-
|
|
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
|
+
});
|
|
4934
5261
|
};
|
|
4935
|
-
const fire = () => {
|
|
5262
|
+
const fire = (autoDeviceKey) => {
|
|
4936
5263
|
var _a, _b;
|
|
4937
5264
|
const { config: cfg, options: opts } = latest.current;
|
|
4938
5265
|
if (!(cfg == null ? void 0 : cfg.serverUrl)) return;
|
|
@@ -4944,7 +5271,7 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4944
5271
|
target,
|
|
4945
5272
|
// A fresh per-open id each fire; the emitter's once-guard dedupes within the open.
|
|
4946
5273
|
userId: opts.userId,
|
|
4947
|
-
deviceKey: resolveDeviceKey(
|
|
5274
|
+
deviceKey: resolveDeviceKey(opts, autoDeviceKey),
|
|
4948
5275
|
sessionCount: opts.sessionCount,
|
|
4949
5276
|
appVersion: (_b = cfg.appVersion) != null ? _b : device.appVersion,
|
|
4950
5277
|
platform: reactNative.Platform.OS,
|
|
@@ -4952,7 +5279,7 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4952
5279
|
meta: opts.meta
|
|
4953
5280
|
});
|
|
4954
5281
|
};
|
|
4955
|
-
fire
|
|
5282
|
+
openAutoDeviceKey(fire);
|
|
4956
5283
|
let backgroundedAt = null;
|
|
4957
5284
|
const onChange = (state) => {
|
|
4958
5285
|
if (state === "background" || state === "inactive") {
|
|
@@ -4962,11 +5289,12 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4962
5289
|
if (state === "active") {
|
|
4963
5290
|
const since = backgroundedAt;
|
|
4964
5291
|
backgroundedAt = null;
|
|
4965
|
-
if (since != null && Date.now() - since >= BACKGROUND_SESSION_MS) fire
|
|
5292
|
+
if (since != null && Date.now() - since >= BACKGROUND_SESSION_MS) openAutoDeviceKey(fire);
|
|
4966
5293
|
}
|
|
4967
5294
|
};
|
|
4968
5295
|
const sub = reactNative.AppState.addEventListener("change", onChange);
|
|
4969
5296
|
return () => {
|
|
5297
|
+
cancelled = true;
|
|
4970
5298
|
if (sub && typeof sub.remove === "function") sub.remove();
|
|
4971
5299
|
};
|
|
4972
5300
|
}, []);
|
|
@@ -4975,11 +5303,12 @@ var useSessionStart = (config, options = {}) => {
|
|
|
4975
5303
|
// src/session-analytics/lifecycle.ts
|
|
4976
5304
|
var FIRST_OPEN_EVENT = "app.first_open";
|
|
4977
5305
|
var firstOpenStorageKey = (appId) => `wireai:first_open:${appId}`;
|
|
4978
|
-
var
|
|
4979
|
-
var
|
|
5306
|
+
var READ_TIMEOUT_MS4 = 1500;
|
|
5307
|
+
var READ_TIMED_OUT3 = /* @__PURE__ */ Symbol("wireai:first-open-read-timeout");
|
|
5308
|
+
var withTimeout4 = (p, ms) => {
|
|
4980
5309
|
let timer;
|
|
4981
5310
|
const timeout = new Promise((resolve) => {
|
|
4982
|
-
timer = setTimeout(() => resolve(
|
|
5311
|
+
timer = setTimeout(() => resolve(READ_TIMED_OUT3), ms);
|
|
4983
5312
|
});
|
|
4984
5313
|
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
4985
5314
|
};
|
|
@@ -4998,335 +5327,82 @@ var buildLifecycleEvent = (questionKey, opts) => {
|
|
|
4998
5327
|
if (opts.appVersion) userContext.app_version = opts.appVersion;
|
|
4999
5328
|
if (opts.platform) userContext.platform = opts.platform;
|
|
5000
5329
|
const event = {
|
|
5001
|
-
event_type: "app_event",
|
|
5002
|
-
question_key: questionKey,
|
|
5003
|
-
session_id: (_a = opts.sessionId) != null ? _a : makeSessionId()
|
|
5004
|
-
};
|
|
5005
|
-
const userId = sanitizeUserId(opts.userId);
|
|
5006
|
-
if (userId) event.user_id = userId;
|
|
5007
|
-
if (Object.keys(userContext).length > 0) event.user_context = userContext;
|
|
5008
|
-
if (opts.device) event.device = opts.device;
|
|
5009
|
-
if (opts.meta && Object.keys(opts.meta).length > 0) event.meta = JSON.stringify(opts.meta);
|
|
5010
|
-
return event;
|
|
5011
|
-
};
|
|
5012
|
-
var routeLifecycleEvent = (event, opts) => {
|
|
5013
|
-
try {
|
|
5014
|
-
if (opts.sink) {
|
|
5015
|
-
opts.sink(event);
|
|
5016
|
-
return;
|
|
5017
|
-
}
|
|
5018
|
-
const req = buildEventsRequest(opts.target, [event]);
|
|
5019
|
-
if (!req) return;
|
|
5020
|
-
void fetch(req.url, req.init).catch(() => {
|
|
5021
|
-
});
|
|
5022
|
-
} catch {
|
|
5023
|
-
}
|
|
5024
|
-
};
|
|
5025
|
-
var emitFirstOpen = (opts) => {
|
|
5026
|
-
routeLifecycleEvent(buildLifecycleEvent(FIRST_OPEN_EVENT, opts), opts);
|
|
5027
|
-
};
|
|
5028
|
-
var reportFirstOpen = (opts) => {
|
|
5029
|
-
var _a;
|
|
5030
|
-
const appId = (_a = opts.appId) != null ? _a : "default";
|
|
5031
|
-
if (_firstOpenLatched.has(appId)) return;
|
|
5032
|
-
_firstOpenLatched.add(appId);
|
|
5033
|
-
const storage = opts.storage;
|
|
5034
|
-
if (!storage) {
|
|
5035
|
-
emitFirstOpen(opts);
|
|
5036
|
-
return;
|
|
5037
|
-
}
|
|
5038
|
-
const key = firstOpenStorageKey(appId);
|
|
5039
|
-
void (async () => {
|
|
5040
|
-
try {
|
|
5041
|
-
const seen = await withTimeout3(storage.getItem(key), READ_TIMEOUT_MS3);
|
|
5042
|
-
if (seen) return;
|
|
5043
|
-
emitFirstOpen(opts);
|
|
5044
|
-
try {
|
|
5045
|
-
void storage.setItem(key, JSON.stringify({ ts: Date.now() })).catch(() => {
|
|
5046
|
-
});
|
|
5047
|
-
} catch {
|
|
5048
|
-
}
|
|
5049
|
-
} catch {
|
|
5050
|
-
emitFirstOpen(opts);
|
|
5051
|
-
}
|
|
5052
|
-
})();
|
|
5053
|
-
};
|
|
5054
|
-
var wireLifecycleEvents = (opts) => {
|
|
5055
|
-
var _a;
|
|
5056
|
-
const sessionId = (_a = opts.sessionId) != null ? _a : makeSessionId();
|
|
5057
|
-
reportFirstOpen({ ...opts, sessionId });
|
|
5058
|
-
reportSessionStart({
|
|
5059
|
-
target: opts.target,
|
|
5060
|
-
sink: opts.sink,
|
|
5061
|
-
sessionId,
|
|
5062
|
-
userId: opts.userId,
|
|
5063
|
-
deviceKey: opts.deviceKey,
|
|
5064
|
-
sessionCount: opts.sessionCount,
|
|
5065
|
-
appVersion: opts.appVersion,
|
|
5066
|
-
platform: opts.platform,
|
|
5067
|
-
device: opts.device,
|
|
5068
|
-
meta: opts.meta
|
|
5069
|
-
});
|
|
5070
|
-
};
|
|
5071
|
-
|
|
5072
|
-
// src/analytics/eventQueue.ts
|
|
5073
|
-
var DEFAULTS = {
|
|
5074
|
-
maxSize: 200,
|
|
5075
|
-
batchSize: 20,
|
|
5076
|
-
baseBackoffMs: 1e3,
|
|
5077
|
-
maxBackoffMs: 3e4,
|
|
5078
|
-
maxRetries: 6
|
|
5079
|
-
};
|
|
5080
|
-
var READ_TIMEOUT_MS4 = 1500;
|
|
5081
|
-
var QUEUE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:eventQueueKeys");
|
|
5082
|
-
var queueKeyGlobal = globalThis;
|
|
5083
|
-
var claimedQueueKeys = () => {
|
|
5084
|
-
const existing = queueKeyGlobal[QUEUE_KEY_SLOT];
|
|
5085
|
-
if (existing) return existing;
|
|
5086
|
-
const created = /* @__PURE__ */ new Set();
|
|
5087
|
-
queueKeyGlobal[QUEUE_KEY_SLOT] = created;
|
|
5088
|
-
return created;
|
|
5089
|
-
};
|
|
5090
|
-
var claimQueueKey = (preferred, explicit) => {
|
|
5091
|
-
const claimed = claimedQueueKeys();
|
|
5092
|
-
if (explicit || !claimed.has(preferred)) {
|
|
5093
|
-
claimed.add(preferred);
|
|
5094
|
-
return preferred;
|
|
5095
|
-
}
|
|
5096
|
-
let ordinal = 2;
|
|
5097
|
-
while (claimed.has(`${preferred}#${ordinal}`)) ordinal++;
|
|
5098
|
-
const key = `${preferred}#${ordinal}`;
|
|
5099
|
-
claimed.add(key);
|
|
5100
|
-
warnInDev(
|
|
5101
|
-
`[wireai] a second event queue was created for the same appId, and "${preferred}" is already claimed by a live one. Two queues sharing one storage slot overwrite each other's backlog, delete each other's pending events when one drains to empty, and double-send on relaunch, so this queue was given "${key}" instead. Prefer ONE analytics instance per app; if you really need two, pass an explicit \`storageKey\` to each so the slots are yours to reason about.`
|
|
5102
|
-
);
|
|
5103
|
-
return key;
|
|
5104
|
-
};
|
|
5105
|
-
var READ_TIMED_OUT = /* @__PURE__ */ Symbol("wireai:storage-read-timeout");
|
|
5106
|
-
var withTimeout4 = (p, ms) => {
|
|
5107
|
-
let timer;
|
|
5108
|
-
const timeout = new Promise((resolve) => {
|
|
5109
|
-
timer = setTimeout(() => resolve(READ_TIMED_OUT), ms);
|
|
5110
|
-
});
|
|
5111
|
-
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
5112
|
-
};
|
|
5113
|
-
var unrefTimer = (timer) => {
|
|
5114
|
-
const t = timer;
|
|
5115
|
-
if (typeof t.unref === "function") t.unref();
|
|
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;
|
|
5116
5340
|
};
|
|
5117
|
-
var
|
|
5118
|
-
if (!raw) return [];
|
|
5341
|
+
var routeLifecycleEvent = (event, opts) => {
|
|
5119
5342
|
try {
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
for (const entry of parsed) {
|
|
5124
|
-
if (entry && typeof entry === "object" && typeof entry.id === "number" && entry.event && typeof entry.event === "object") {
|
|
5125
|
-
items.push(entry);
|
|
5126
|
-
}
|
|
5343
|
+
if (opts.sink) {
|
|
5344
|
+
opts.sink(event);
|
|
5345
|
+
return;
|
|
5127
5346
|
}
|
|
5128
|
-
|
|
5347
|
+
const req = buildEventsRequest(opts.target, [event]);
|
|
5348
|
+
if (!req) return;
|
|
5349
|
+
void fetch(req.url, req.init).catch(() => {
|
|
5350
|
+
});
|
|
5129
5351
|
} catch {
|
|
5130
|
-
return [];
|
|
5131
5352
|
}
|
|
5132
5353
|
};
|
|
5133
|
-
var
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
const
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
const
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
let retryTimer;
|
|
5149
|
-
let backlogUnread = false;
|
|
5150
|
-
const resolveEnvelope = () => {
|
|
5151
|
-
try {
|
|
5152
|
-
return typeof options.envelope === "function" ? options.envelope() : options.envelope;
|
|
5153
|
-
} catch {
|
|
5154
|
-
return void 0;
|
|
5155
|
-
}
|
|
5156
|
-
};
|
|
5157
|
-
const stamp = (event) => {
|
|
5158
|
-
var _a2;
|
|
5159
|
-
const env = resolveEnvelope();
|
|
5160
|
-
const stamped = { ...event };
|
|
5161
|
-
if (stamped.ts === void 0) stamped.ts = Date.now();
|
|
5162
|
-
if (!env) return stamped;
|
|
5163
|
-
if (!stamped.device && env.device) stamped.device = env.device;
|
|
5164
|
-
if (!stamped.session_id && env.sessionId) stamped.session_id = env.sessionId;
|
|
5165
|
-
const uc = { ...(_a2 = stamped.user_context) != null ? _a2 : {} };
|
|
5166
|
-
if (env.appVersion && uc.app_version === void 0) uc.app_version = env.appVersion;
|
|
5167
|
-
if (env.appBuild && uc.app_build === void 0) uc.app_build = env.appBuild;
|
|
5168
|
-
if (env.networkType && uc.network_type === void 0) uc.network_type = env.networkType;
|
|
5169
|
-
if (Object.keys(uc).length > 0) stamped.user_context = uc;
|
|
5170
|
-
return stamped;
|
|
5171
|
-
};
|
|
5172
|
-
const persist = () => {
|
|
5173
|
-
if (!storage) return;
|
|
5174
|
-
if (backlogUnread) return;
|
|
5175
|
-
try {
|
|
5176
|
-
if (pending.length === 0) {
|
|
5177
|
-
void storage.removeItem(key).catch(() => {
|
|
5178
|
-
});
|
|
5179
|
-
return;
|
|
5180
|
-
}
|
|
5181
|
-
const payload = pending.map((item) => ({ id: item.id, event: item.event }));
|
|
5182
|
-
void storage.setItem(key, JSON.stringify(payload)).catch(() => {
|
|
5183
|
-
});
|
|
5184
|
-
} catch {
|
|
5185
|
-
}
|
|
5186
|
-
};
|
|
5187
|
-
const enforceSizeCap = () => {
|
|
5188
|
-
if (pending.length > maxSize) pending.splice(0, pending.length - maxSize);
|
|
5189
|
-
};
|
|
5190
|
-
const safeSig = (event) => {
|
|
5191
|
-
try {
|
|
5192
|
-
return JSON.stringify(event);
|
|
5193
|
-
} catch {
|
|
5194
|
-
return `__nosig_${nextId}_${Math.random()}`;
|
|
5195
|
-
}
|
|
5196
|
-
};
|
|
5197
|
-
const mergePersisted = (persistedItems) => {
|
|
5198
|
-
if (persistedItems.length === 0) return;
|
|
5199
|
-
const seen = new Set(pending.map((item) => item.sig));
|
|
5200
|
-
const restored = [];
|
|
5201
|
-
for (const persisted of persistedItems) {
|
|
5202
|
-
const sig = safeSig(persisted.event);
|
|
5203
|
-
if (seen.has(sig)) continue;
|
|
5204
|
-
seen.add(sig);
|
|
5205
|
-
restored.push({ id: nextId++, event: persisted.event, sig });
|
|
5206
|
-
}
|
|
5207
|
-
if (restored.length === 0) return;
|
|
5208
|
-
pending = [...restored, ...pending];
|
|
5209
|
-
enforceSizeCap();
|
|
5210
|
-
persist();
|
|
5211
|
-
};
|
|
5212
|
-
const loadPromise = (async () => {
|
|
5213
|
-
if (!storage) return;
|
|
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 () => {
|
|
5214
5369
|
try {
|
|
5215
|
-
const
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
warnInDev(
|
|
5222
|
-
`[wireai] the persisted analytics backlog at "${key}" took longer than ${READ_TIMEOUT_MS4}ms to read, so the queue started without it. The stored events are NOT discarded: writes are held back until the read lands, and the backlog is merged in then. If you see this on every cold start, your storage adapter is too slow to be on the launch path.`
|
|
5223
|
-
);
|
|
5224
|
-
void read.then((late) => {
|
|
5225
|
-
backlogUnread = false;
|
|
5226
|
-
mergePersisted(parsePersisted2(late));
|
|
5227
|
-
flush();
|
|
5228
|
-
}).catch(() => {
|
|
5229
|
-
backlogUnread = false;
|
|
5230
|
-
persist();
|
|
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(() => {
|
|
5231
5376
|
});
|
|
5232
|
-
|
|
5377
|
+
} catch {
|
|
5233
5378
|
}
|
|
5234
|
-
mergePersisted(parsePersisted2(raced));
|
|
5235
5379
|
} catch {
|
|
5236
5380
|
}
|
|
5237
5381
|
})();
|
|
5238
|
-
const postBatch = async (events) => {
|
|
5239
|
-
const req = buildEventsRequest(target, events);
|
|
5240
|
-
if (!req) return false;
|
|
5241
|
-
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
5242
|
-
const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
|
|
5243
|
-
try {
|
|
5244
|
-
const res = await fetch(req.url, { ...req.init, signal: controller == null ? void 0 : controller.signal });
|
|
5245
|
-
warnOnSkippedEvents(res);
|
|
5246
|
-
return !!(res && res.ok);
|
|
5247
|
-
} catch {
|
|
5248
|
-
return false;
|
|
5249
|
-
} finally {
|
|
5250
|
-
clearTimeout(timer);
|
|
5251
|
-
}
|
|
5252
|
-
};
|
|
5253
|
-
const clearRetry = () => {
|
|
5254
|
-
if (retryTimer !== void 0) {
|
|
5255
|
-
clearTimeout(retryTimer);
|
|
5256
|
-
retryTimer = void 0;
|
|
5257
|
-
}
|
|
5258
|
-
};
|
|
5259
|
-
const scheduleRetry = () => {
|
|
5260
|
-
if (attempt >= maxRetries) return;
|
|
5261
|
-
const delay = Math.min(baseBackoffMs * 2 ** attempt, maxBackoffMs);
|
|
5262
|
-
attempt++;
|
|
5263
|
-
clearRetry();
|
|
5264
|
-
retryTimer = setTimeout(() => {
|
|
5265
|
-
retryTimer = void 0;
|
|
5266
|
-
void drain();
|
|
5267
|
-
}, delay);
|
|
5268
|
-
unrefTimer(retryTimer);
|
|
5269
|
-
};
|
|
5270
|
-
const drain = async () => {
|
|
5271
|
-
try {
|
|
5272
|
-
await loadPromise;
|
|
5273
|
-
} catch {
|
|
5274
|
-
}
|
|
5275
|
-
if (flushing) return;
|
|
5276
|
-
flushing = true;
|
|
5277
|
-
try {
|
|
5278
|
-
while (pending.length > 0) {
|
|
5279
|
-
const batch = pending.slice(0, batchSize);
|
|
5280
|
-
const ok = await postBatch(batch.map((item) => item.event));
|
|
5281
|
-
if (!ok) {
|
|
5282
|
-
scheduleRetry();
|
|
5283
|
-
return;
|
|
5284
|
-
}
|
|
5285
|
-
const acked = new Set(batch.map((item) => item.id));
|
|
5286
|
-
pending = pending.filter((item) => !acked.has(item.id));
|
|
5287
|
-
persist();
|
|
5288
|
-
attempt = 0;
|
|
5289
|
-
clearRetry();
|
|
5290
|
-
}
|
|
5291
|
-
} finally {
|
|
5292
|
-
flushing = false;
|
|
5293
|
-
}
|
|
5294
|
-
};
|
|
5295
|
-
const flush = () => {
|
|
5296
|
-
try {
|
|
5297
|
-
void drain();
|
|
5298
|
-
} catch {
|
|
5299
|
-
}
|
|
5300
|
-
};
|
|
5301
|
-
const enqueue = (event) => {
|
|
5302
|
-
try {
|
|
5303
|
-
const stamped = stamp(event);
|
|
5304
|
-
const sig = safeSig(stamped);
|
|
5305
|
-
for (const item of pending) {
|
|
5306
|
-
if (item.sig === sig) return;
|
|
5307
|
-
}
|
|
5308
|
-
pending.push({ id: nextId++, event: stamped, sig });
|
|
5309
|
-
enforceSizeCap();
|
|
5310
|
-
persist();
|
|
5311
|
-
if (retryTimer === void 0) flush();
|
|
5312
|
-
} catch {
|
|
5313
|
-
}
|
|
5314
|
-
};
|
|
5315
|
-
const notifyOnline = () => {
|
|
5316
|
-
attempt = 0;
|
|
5317
|
-
clearRetry();
|
|
5318
|
-
flush();
|
|
5319
|
-
};
|
|
5320
|
-
const size = () => pending.length;
|
|
5321
|
-
return { enqueue, flush, notifyOnline, size };
|
|
5322
5382
|
};
|
|
5323
|
-
|
|
5324
|
-
|
|
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
|
+
};
|
|
5325
5400
|
var useLifecycleEvents = (config, options = {}) => {
|
|
5326
5401
|
const latest = React19.useRef({ config, options });
|
|
5327
5402
|
latest.current = { config, options };
|
|
5328
5403
|
const queueRef = React19.useRef(void 0);
|
|
5329
5404
|
React19.useEffect(() => {
|
|
5405
|
+
let cancelled = false;
|
|
5330
5406
|
const resolveSink = () => {
|
|
5331
5407
|
var _a, _b;
|
|
5332
5408
|
const { config: cfg, options: opts } = latest.current;
|
|
@@ -5347,7 +5423,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5347
5423
|
var _a;
|
|
5348
5424
|
return (cfg == null ? void 0 : cfg.serverUrl) ? { serverUrl: cfg.serverUrl, apiKey: (_a = cfg.apiKey) != null ? _a : "" } : void 0;
|
|
5349
5425
|
};
|
|
5350
|
-
const resolveDeviceKey = (cfg, opts) => {
|
|
5426
|
+
const resolveDeviceKey = (cfg, opts, autoDeviceKey) => {
|
|
5351
5427
|
var _a;
|
|
5352
5428
|
const host = (_a = resolveIdentity({
|
|
5353
5429
|
value: opts.deviceKey,
|
|
@@ -5356,11 +5432,22 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5356
5432
|
scope: cfg == null ? void 0 : cfg.appId
|
|
5357
5433
|
})) == null ? void 0 : _a.value;
|
|
5358
5434
|
if (host) return host;
|
|
5359
|
-
|
|
5360
|
-
|
|
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
|
+
});
|
|
5361
5448
|
};
|
|
5362
5449
|
const mountOpenSessionId = makeSessionId();
|
|
5363
|
-
const fireSession = (sessionId) => {
|
|
5450
|
+
const fireSession = (sessionId, autoDeviceKey) => {
|
|
5364
5451
|
var _a;
|
|
5365
5452
|
const { config: cfg, options: opts } = latest.current;
|
|
5366
5453
|
if (opts.enabled === false) return;
|
|
@@ -5372,7 +5459,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5372
5459
|
sink: resolveSink(),
|
|
5373
5460
|
sessionId,
|
|
5374
5461
|
userId: opts.userId,
|
|
5375
|
-
deviceKey: resolveDeviceKey(cfg, opts),
|
|
5462
|
+
deviceKey: resolveDeviceKey(cfg, opts, autoDeviceKey),
|
|
5376
5463
|
sessionCount: opts.sessionCount,
|
|
5377
5464
|
appVersion: (_a = cfg == null ? void 0 : cfg.appVersion) != null ? _a : device.appVersion,
|
|
5378
5465
|
platform: reactNative.Platform.OS,
|
|
@@ -5380,9 +5467,9 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5380
5467
|
meta: opts.meta
|
|
5381
5468
|
});
|
|
5382
5469
|
};
|
|
5383
|
-
const fireMountOpen = () => {
|
|
5470
|
+
const fireMountOpen = (autoDeviceKey) => {
|
|
5384
5471
|
var _a;
|
|
5385
|
-
fireSession(mountOpenSessionId);
|
|
5472
|
+
fireSession(mountOpenSessionId, autoDeviceKey);
|
|
5386
5473
|
const { config: cfg, options: opts } = latest.current;
|
|
5387
5474
|
if (opts.enabled === false) return;
|
|
5388
5475
|
const device = collectDeviceContext();
|
|
@@ -5394,7 +5481,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5394
5481
|
storage: cfg == null ? void 0 : cfg.storage,
|
|
5395
5482
|
appId: cfg == null ? void 0 : cfg.appId,
|
|
5396
5483
|
userId: opts.userId,
|
|
5397
|
-
deviceKey: resolveDeviceKey(cfg, opts),
|
|
5484
|
+
deviceKey: resolveDeviceKey(cfg, opts, autoDeviceKey),
|
|
5398
5485
|
sessionCount: opts.sessionCount,
|
|
5399
5486
|
appVersion: (_a = cfg == null ? void 0 : cfg.appVersion) != null ? _a : device.appVersion,
|
|
5400
5487
|
platform: reactNative.Platform.OS,
|
|
@@ -5402,16 +5489,7 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5402
5489
|
meta: opts.meta
|
|
5403
5490
|
});
|
|
5404
5491
|
};
|
|
5405
|
-
|
|
5406
|
-
const { config: mountCfg, options: mountOpts } = latest.current;
|
|
5407
|
-
const hostKey = typeof mountOpts.deviceKey === "string" && mountOpts.deviceKey.trim() ? mountOpts.deviceKey : void 0;
|
|
5408
|
-
if (!hostKey && (mountCfg == null ? void 0 : mountCfg.storage)) {
|
|
5409
|
-
void hydrateAutoDeviceKey({ appId: mountCfg.appId, storage: mountCfg.storage }).then(() => {
|
|
5410
|
-
if (!cancelled) fireMountOpen();
|
|
5411
|
-
});
|
|
5412
|
-
} else {
|
|
5413
|
-
fireMountOpen();
|
|
5414
|
-
}
|
|
5492
|
+
openAutoDeviceKey(fireMountOpen);
|
|
5415
5493
|
let backgroundedAt = null;
|
|
5416
5494
|
const onChange = (state) => {
|
|
5417
5495
|
if (state === "background" || state === "inactive") {
|
|
@@ -5421,13 +5499,18 @@ var useLifecycleEvents = (config, options = {}) => {
|
|
|
5421
5499
|
if (state === "active") {
|
|
5422
5500
|
const since = backgroundedAt;
|
|
5423
5501
|
backgroundedAt = null;
|
|
5424
|
-
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
|
+
}
|
|
5425
5505
|
}
|
|
5426
5506
|
};
|
|
5427
5507
|
const sub = reactNative.AppState.addEventListener("change", onChange);
|
|
5428
5508
|
return () => {
|
|
5509
|
+
var _a;
|
|
5429
5510
|
cancelled = true;
|
|
5430
5511
|
if (sub && typeof sub.remove === "function") sub.remove();
|
|
5512
|
+
(_a = queueRef.current) == null ? void 0 : _a.dispose();
|
|
5513
|
+
queueRef.current = void 0;
|
|
5431
5514
|
};
|
|
5432
5515
|
}, []);
|
|
5433
5516
|
};
|