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