@wireai/activation 0.15.0 → 0.16.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 +95 -20
- package/CHANGELOG.md +707 -0
- package/INTEGRATION_PROMPT.md +61 -23
- package/README.md +100 -25
- package/dist/analytics/index.d.mts +32 -10
- package/dist/analytics/index.d.ts +32 -10
- package/dist/analytics/index.js +288 -127
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +288 -127
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/coachmarks/index.d.mts +14 -0
- package/dist/coachmarks/index.d.ts +14 -0
- package/dist/coachmarks/index.js +73 -20
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs +73 -20
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/{currentSession-orZy5p1e.d.mts → currentSession-Bz7G6lno.d.mts} +25 -35
- package/dist/{currentSession-CFSRZ2wg.d.ts → currentSession-z-CZ55ad.d.ts} +25 -35
- package/dist/index.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +125 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +125 -36
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.d.mts +0 -13
- package/dist/questionnaire/index.d.ts +0 -13
- package/dist/questionnaire/index.js +154 -43
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +155 -44
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.js +159 -91
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +160 -92
- package/dist/reviews/index.mjs.map +1 -1
- package/dist/showcase/index.js +59 -18
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/index.mjs +60 -19
- package/dist/showcase/index.mjs.map +1 -1
- package/llms.txt +9 -9
- package/package.json +6 -9
- package/src/analytics/currentSession.ts +141 -4
- package/src/analytics/index.ts +6 -1
- package/src/analytics/reportClientEvent.ts +19 -10
- package/src/analytics/useAnalytics.ts +74 -15
- package/src/analytics/wireDoctor.ts +152 -7
- package/src/coachmarks/CoachmarkProvider.tsx +26 -5
- package/src/coachmarks/runtime.ts +53 -0
- package/src/coachmarks/useCoachmarkTour.ts +51 -1
- package/src/context/deviceId.ts +49 -15
- package/src/features/WireFeaturesProvider.tsx +72 -12
- package/src/features/fetchWireFeatures.ts +49 -11
- package/src/features/useWireFeatures.ts +39 -3
- package/src/identity/identityRecord.ts +15 -2
- package/src/questionnaire/QuestionnaireGate.tsx +40 -1
- package/src/questionnaire/transport.ts +22 -8
- package/src/questionnaire/useQuestionnaireGate.ts +58 -7
- package/src/reviews/ReviewGate.tsx +39 -0
- package/src/reviews/idempotency.ts +38 -0
- package/src/reviews/runtime.ts +39 -10
- package/src/reviews/transport.ts +22 -8
- package/src/reviews/useReviewGate.ts +57 -7
- package/src/session-analytics/lifecycle.ts +16 -0
- package/src/session-analytics/useLifecycleEvents.ts +30 -2
- package/src/session-analytics/useSessionStart.ts +22 -2
- package/src/showcase/FeatureShowcase.tsx +50 -3
- package/src/types.ts +5 -4
- package/src/utils/withDeadline.ts +70 -0
package/dist/analytics/index.js
CHANGED
|
@@ -10,6 +10,28 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
10
10
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
+
// src/utils/withDeadline.ts
|
|
14
|
+
var DEADLINE_EXPIRED = /* @__PURE__ */ Symbol("wire-deadline-expired");
|
|
15
|
+
var DEFAULT_DEADLINE_MS = 15e3;
|
|
16
|
+
var withDeadline = async (exchange, timeoutMs = DEFAULT_DEADLINE_MS) => {
|
|
17
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
18
|
+
let timer;
|
|
19
|
+
const expired = new Promise((resolve) => {
|
|
20
|
+
timer = setTimeout(() => {
|
|
21
|
+
controller == null ? void 0 : controller.abort();
|
|
22
|
+
resolve(DEADLINE_EXPIRED);
|
|
23
|
+
}, timeoutMs);
|
|
24
|
+
});
|
|
25
|
+
const running = exchange(controller == null ? void 0 : controller.signal);
|
|
26
|
+
try {
|
|
27
|
+
return await Promise.race([running, expired]);
|
|
28
|
+
} finally {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
void running.catch(() => {
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
13
35
|
// src/analytics/reportClientEvent.ts
|
|
14
36
|
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
15
37
|
var toWireEvents = (events) => events.map((event) => {
|
|
@@ -81,14 +103,17 @@ var reportClientEventsOutcome = async (target, events) => {
|
|
|
81
103
|
try {
|
|
82
104
|
const req = buildEventsRequest(target, events);
|
|
83
105
|
if (!req) return "refused";
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
console.warn
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
const outcome = await withDeadline(async (signal) => {
|
|
107
|
+
const res = await fetch(req.url, { ...req.init, signal });
|
|
108
|
+
if (!res || !res.ok) return "unreachable";
|
|
109
|
+
const ack = await readEventsAck(res);
|
|
110
|
+
if (!ack || ack.skipped <= 0) return "delivered";
|
|
111
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
112
|
+
console.warn(describeDiscarded(ack));
|
|
113
|
+
}
|
|
114
|
+
return "refused";
|
|
115
|
+
});
|
|
116
|
+
return outcome === DEADLINE_EXPIRED ? "unreachable" : outcome;
|
|
92
117
|
} catch {
|
|
93
118
|
return "unreachable";
|
|
94
119
|
}
|
|
@@ -109,25 +134,51 @@ var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
|
109
134
|
"@wireai/activation:currentSessionId"
|
|
110
135
|
);
|
|
111
136
|
var globalSlot = globalThis;
|
|
137
|
+
var SESSION_ID_LISTENERS_SLOT = /* @__PURE__ */ Symbol.for(
|
|
138
|
+
"@wireai/activation:currentSessionIdListeners"
|
|
139
|
+
);
|
|
140
|
+
var listenerSlot = globalThis;
|
|
141
|
+
var listeners = () => {
|
|
142
|
+
const existing = listenerSlot[SESSION_ID_LISTENERS_SLOT];
|
|
143
|
+
if (existing) return existing;
|
|
144
|
+
const created = /* @__PURE__ */ new Set();
|
|
145
|
+
listenerSlot[SESSION_ID_LISTENERS_SLOT] = created;
|
|
146
|
+
return created;
|
|
147
|
+
};
|
|
148
|
+
var writeCurrentSessionId = (id) => {
|
|
149
|
+
if (globalSlot[CURRENT_SESSION_ID_SLOT] === id) return;
|
|
150
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
151
|
+
for (const listener of Array.from(listeners())) {
|
|
152
|
+
try {
|
|
153
|
+
listener();
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
112
158
|
var setCurrentSessionId = (id) => {
|
|
113
159
|
if (typeof id === "string" && id.length > 0) {
|
|
114
|
-
|
|
160
|
+
writeCurrentSessionId(id);
|
|
115
161
|
}
|
|
116
162
|
};
|
|
117
163
|
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
118
164
|
var resetCurrentSessionId = () => {
|
|
119
|
-
|
|
165
|
+
writeCurrentSessionId(void 0);
|
|
120
166
|
};
|
|
121
167
|
var MINT_WARNING = "[wireai] No app-open session was registered, so a session id was minted for this event (the server drops an event that has no session_id, and still answers 200). Mount useLifecycleEvents at your app root so events correlate to a real app-open.";
|
|
122
168
|
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
123
169
|
"@wireai/activation:currentSessionIdMintWarned"
|
|
124
170
|
);
|
|
125
171
|
var warnSlot = globalThis;
|
|
172
|
+
var MINTED_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
173
|
+
"@wireai/activation:mintedSessionId"
|
|
174
|
+
);
|
|
175
|
+
var mintedSlot = globalThis;
|
|
126
176
|
var ensureCurrentSessionId = () => {
|
|
127
177
|
const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
128
178
|
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
129
179
|
const minted = makeSessionId();
|
|
130
|
-
|
|
180
|
+
writeCurrentSessionId(minted);
|
|
181
|
+
mintedSlot[MINTED_SESSION_ID_SLOT] = minted;
|
|
131
182
|
if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
|
|
132
183
|
warnSlot[MINT_WARNED_SLOT] = true;
|
|
133
184
|
}
|
|
@@ -228,6 +279,109 @@ var useScreenTracking = (navigationRef, options = {}) => {
|
|
|
228
279
|
}, [navigationRef]);
|
|
229
280
|
};
|
|
230
281
|
|
|
282
|
+
// src/identity/identityRecord.ts
|
|
283
|
+
var IDENTITY_PROVENANCE_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:identityProvenance");
|
|
284
|
+
var provenanceGlobal = globalThis;
|
|
285
|
+
var provenanceRegistry = () => {
|
|
286
|
+
const existing = provenanceGlobal[IDENTITY_PROVENANCE_SLOT];
|
|
287
|
+
if (existing) return existing;
|
|
288
|
+
const created = { host: /* @__PURE__ */ new Map() };
|
|
289
|
+
provenanceGlobal[IDENTITY_PROVENANCE_SLOT] = created;
|
|
290
|
+
return created;
|
|
291
|
+
};
|
|
292
|
+
var provenanceKey = (space, scope) => `${space}:${scope != null ? scope : "default"}`;
|
|
293
|
+
var isUsableIdentityValue = (value) => typeof value === "string" && value.trim().length > 0;
|
|
294
|
+
var resolveIdentity = (input) => {
|
|
295
|
+
var _a;
|
|
296
|
+
if (!isUsableIdentityValue(input.value)) return void 0;
|
|
297
|
+
const value = input.value.trim();
|
|
298
|
+
const durable = (_a = input.durable) != null ? _a : input.source === "host";
|
|
299
|
+
{
|
|
300
|
+
provenanceRegistry().host.set(provenanceKey(input.space, input.scope), value);
|
|
301
|
+
}
|
|
302
|
+
return { value, space: input.space, source: input.source, durable };
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// src/context/deviceId.ts
|
|
306
|
+
var AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
307
|
+
var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
|
|
308
|
+
var randomChunk = () => Math.floor(Math.random() * 4294967296).toString(36).padStart(6, "0");
|
|
309
|
+
var mintDeviceId = () => {
|
|
310
|
+
const time = Date.now().toString(36);
|
|
311
|
+
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
312
|
+
};
|
|
313
|
+
var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
|
|
314
|
+
var deviceKeyGlobal = globalThis;
|
|
315
|
+
var autoDeviceKeyRegistry = () => {
|
|
316
|
+
const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
|
|
317
|
+
if (existing) return existing;
|
|
318
|
+
const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
|
|
319
|
+
deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
|
|
320
|
+
return created;
|
|
321
|
+
};
|
|
322
|
+
var startHydration = (registry, appId, storage, minted) => {
|
|
323
|
+
if (!registry.pending) registry.pending = /* @__PURE__ */ new Map();
|
|
324
|
+
const existing = registry.pending.get(appId);
|
|
325
|
+
if (existing) return existing;
|
|
326
|
+
const slot = deviceIdStorageKey(appId);
|
|
327
|
+
const degraded = () => {
|
|
328
|
+
var _a;
|
|
329
|
+
return { value: (_a = registry.keys.get(appId)) != null ? _a : minted, durable: false };
|
|
330
|
+
};
|
|
331
|
+
const adopted = (value) => ({ value, durable: true });
|
|
332
|
+
let run;
|
|
333
|
+
try {
|
|
334
|
+
run = Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
335
|
+
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
336
|
+
if (persisted) {
|
|
337
|
+
registry.keys.set(appId, persisted);
|
|
338
|
+
return adopted(persisted);
|
|
339
|
+
}
|
|
340
|
+
return Promise.resolve(storage.setItem(slot, minted)).then(
|
|
341
|
+
() => {
|
|
342
|
+
var _a;
|
|
343
|
+
return adopted((_a = registry.keys.get(appId)) != null ? _a : minted);
|
|
344
|
+
},
|
|
345
|
+
degraded
|
|
346
|
+
);
|
|
347
|
+
}).catch(degraded);
|
|
348
|
+
} catch {
|
|
349
|
+
run = Promise.resolve(degraded());
|
|
350
|
+
}
|
|
351
|
+
registry.pending.set(appId, run);
|
|
352
|
+
void run.then((outcome) => {
|
|
353
|
+
var _a;
|
|
354
|
+
if (outcome.durable) return;
|
|
355
|
+
(_a = registry.pending) == null ? void 0 : _a.delete(appId);
|
|
356
|
+
registry.hydrating.delete(appId);
|
|
357
|
+
});
|
|
358
|
+
return run;
|
|
359
|
+
};
|
|
360
|
+
var resolveAutoDeviceKey = (opts = {}) => {
|
|
361
|
+
var _a, _b;
|
|
362
|
+
const registry = autoDeviceKeyRegistry();
|
|
363
|
+
const appId = (_a = opts.appId) != null ? _a : "default";
|
|
364
|
+
let id = registry.keys.get(appId);
|
|
365
|
+
if (!id) {
|
|
366
|
+
id = mintDeviceId();
|
|
367
|
+
registry.keys.set(appId, id);
|
|
368
|
+
}
|
|
369
|
+
const storage = opts.storage;
|
|
370
|
+
if (storage && !registry.hydrating.has(appId)) {
|
|
371
|
+
registry.hydrating.add(appId);
|
|
372
|
+
void startHydration(registry, appId, storage, id);
|
|
373
|
+
}
|
|
374
|
+
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
375
|
+
};
|
|
376
|
+
var resetAutoDeviceKeys = () => {
|
|
377
|
+
var _a;
|
|
378
|
+
const registry = autoDeviceKeyRegistry();
|
|
379
|
+
registry.keys.clear();
|
|
380
|
+
registry.hydrating.clear();
|
|
381
|
+
(_a = registry.pending) == null ? void 0 : _a.clear();
|
|
382
|
+
registry.ambient = void 0;
|
|
383
|
+
};
|
|
384
|
+
|
|
231
385
|
// src/analytics/wireDoctor.ts
|
|
232
386
|
var PROBE_STORAGE_KEY = "wireai:doctor:probe";
|
|
233
387
|
var PROBE_QUESTION_KEY = "wire_doctor_probe";
|
|
@@ -358,6 +512,66 @@ var checkRoundTrip = async (target) => {
|
|
|
358
512
|
`the server DISCARDED the probe event: written=${(_a = ack.written) != null ? _a : "unknown"}, skipped=${ack.skipped}` + (ack.reasons.length > 0 ? `, reasons: ${ack.reasons.join(", ")}.` : ". It gave no reason; check the server's ingest log for this request.")
|
|
359
513
|
);
|
|
360
514
|
};
|
|
515
|
+
var checkJoinKey = async (join, storage, storageOk) => {
|
|
516
|
+
var _a;
|
|
517
|
+
if (!join) {
|
|
518
|
+
return check(
|
|
519
|
+
"join_key",
|
|
520
|
+
false,
|
|
521
|
+
"NOT EVALUATED: pass `join: { appId, userContext, autoJoinKey }` \u2014 the same values you pass <WireOnboarding> \u2014 so the doctor can tell whether this integration's sessions will be joinable. It is reported as a failure rather than skipped because every other check can pass while the funnel reads a permanent zero."
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
if (isUsableIdentityValue((_a = join.userContext) == null ? void 0 : _a.device_key)) {
|
|
525
|
+
return check(
|
|
526
|
+
"join_key",
|
|
527
|
+
true,
|
|
528
|
+
"user_context.device_key is supplied by the host, so onboarding sessions join the rest of the funnel."
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
if (join.autoJoinKey === false) {
|
|
532
|
+
return check(
|
|
533
|
+
"join_key",
|
|
534
|
+
false,
|
|
535
|
+
"no user_context.device_key AND autoJoinKey is false, so these onboarding sessions are UNLINKED by choice: they join nothing the app reports later and the activated funnel reads zero. Supply user_context.device_key, or drop autoJoinKey:false to let the kit inject its own."
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
if (!storage) {
|
|
539
|
+
return check(
|
|
540
|
+
"join_key",
|
|
541
|
+
false,
|
|
542
|
+
"no user_context.device_key, and the kit cannot auto-inject one without storage: an unpersisted key differs every launch, which corrupts the server's session counting rather than just leaving it empty, so the kit declines it. Pass AsyncStorage (or an MMKV wrapper) to <WireOnboarding>."
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
if (!storageOk) {
|
|
546
|
+
return check(
|
|
547
|
+
"join_key",
|
|
548
|
+
false,
|
|
549
|
+
"no user_context.device_key, and the storage check above FAILED \u2014 the kit refuses a non-durable auto key, so it will inject nothing and these sessions will not join. Fix the storage adapter first."
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
let persisted = null;
|
|
553
|
+
try {
|
|
554
|
+
persisted = await storage.getItem(deviceIdStorageKey(join.appId));
|
|
555
|
+
} catch {
|
|
556
|
+
return check(
|
|
557
|
+
"join_key",
|
|
558
|
+
false,
|
|
559
|
+
"no user_context.device_key, and reading the persisted auto key threw, so it is unknown whether the kit can supply one. Treat this as the storage adapter being unreliable."
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
if (isUsableIdentityValue(persisted)) {
|
|
563
|
+
return check(
|
|
564
|
+
"join_key",
|
|
565
|
+
true,
|
|
566
|
+
"no host device_key, but a durable auto join key is already persisted for this appId, so sessions join."
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
return check(
|
|
570
|
+
"join_key",
|
|
571
|
+
true,
|
|
572
|
+
"no host device_key and none persisted yet (first run): the kit will mint and persist its own on first mount, and the storage check above proved the write/read round trip that injection is gated on."
|
|
573
|
+
);
|
|
574
|
+
};
|
|
361
575
|
var wireDoctor = async (options) => {
|
|
362
576
|
if (typeof __DEV__ === "undefined" || !__DEV__) {
|
|
363
577
|
return {
|
|
@@ -373,14 +587,18 @@ var wireDoctor = async (options) => {
|
|
|
373
587
|
}
|
|
374
588
|
try {
|
|
375
589
|
const target = options == null ? void 0 : options.target;
|
|
376
|
-
const
|
|
377
|
-
|
|
590
|
+
const targetCheck = checkTarget(target);
|
|
591
|
+
const checks = [targetCheck];
|
|
592
|
+
const targetUsable = targetCheck.ok && !!target;
|
|
593
|
+
if (targetUsable && target) {
|
|
378
594
|
checks.push(await checkReachability(target.serverUrl));
|
|
379
|
-
|
|
595
|
+
}
|
|
596
|
+
const storageCheck = await checkStorage(options == null ? void 0 : options.storage);
|
|
597
|
+
checks.push(storageCheck);
|
|
598
|
+
if (targetUsable && target) {
|
|
380
599
|
checks.push(await checkRoundTrip(target));
|
|
381
|
-
} else {
|
|
382
|
-
checks.push(await checkStorage(options == null ? void 0 : options.storage));
|
|
383
600
|
}
|
|
601
|
+
checks.push(await checkJoinKey(options == null ? void 0 : options.join, options == null ? void 0 : options.storage, storageCheck.ok));
|
|
384
602
|
return { ok: checks.every((c) => c.ok), checks };
|
|
385
603
|
} catch {
|
|
386
604
|
return {
|
|
@@ -1031,108 +1249,6 @@ var resolveUserContext = (ctx = {}, opts = {}) => {
|
|
|
1031
1249
|
return result;
|
|
1032
1250
|
};
|
|
1033
1251
|
|
|
1034
|
-
// src/identity/identityRecord.ts
|
|
1035
|
-
var IDENTITY_PROVENANCE_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:identityProvenance");
|
|
1036
|
-
var provenanceGlobal = globalThis;
|
|
1037
|
-
var provenanceRegistry = () => {
|
|
1038
|
-
const existing = provenanceGlobal[IDENTITY_PROVENANCE_SLOT];
|
|
1039
|
-
if (existing) return existing;
|
|
1040
|
-
const created = { host: /* @__PURE__ */ new Map() };
|
|
1041
|
-
provenanceGlobal[IDENTITY_PROVENANCE_SLOT] = created;
|
|
1042
|
-
return created;
|
|
1043
|
-
};
|
|
1044
|
-
var provenanceKey = (space, scope) => `${space}:${scope != null ? scope : "default"}`;
|
|
1045
|
-
var resolveIdentity = (input) => {
|
|
1046
|
-
var _a;
|
|
1047
|
-
if (typeof input.value !== "string") return void 0;
|
|
1048
|
-
const value = input.value.trim();
|
|
1049
|
-
if (!value) return void 0;
|
|
1050
|
-
const durable = (_a = input.durable) != null ? _a : input.source === "host";
|
|
1051
|
-
{
|
|
1052
|
-
provenanceRegistry().host.set(provenanceKey(input.space, input.scope), value);
|
|
1053
|
-
}
|
|
1054
|
-
return { value, space: input.space, source: input.source, durable };
|
|
1055
|
-
};
|
|
1056
|
-
|
|
1057
|
-
// src/context/deviceId.ts
|
|
1058
|
-
var AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
1059
|
-
var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
|
|
1060
|
-
var randomChunk = () => Math.floor(Math.random() * 4294967296).toString(36).padStart(6, "0");
|
|
1061
|
-
var mintDeviceId = () => {
|
|
1062
|
-
const time = Date.now().toString(36);
|
|
1063
|
-
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
1064
|
-
};
|
|
1065
|
-
var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
|
|
1066
|
-
var deviceKeyGlobal = globalThis;
|
|
1067
|
-
var autoDeviceKeyRegistry = () => {
|
|
1068
|
-
const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
|
|
1069
|
-
if (existing) return existing;
|
|
1070
|
-
const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
|
|
1071
|
-
deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
|
|
1072
|
-
return created;
|
|
1073
|
-
};
|
|
1074
|
-
var startHydration = (registry, appId, storage, minted) => {
|
|
1075
|
-
if (!registry.pending) registry.pending = /* @__PURE__ */ new Map();
|
|
1076
|
-
const existing = registry.pending.get(appId);
|
|
1077
|
-
if (existing) return existing;
|
|
1078
|
-
const slot = deviceIdStorageKey(appId);
|
|
1079
|
-
const degraded = () => {
|
|
1080
|
-
var _a;
|
|
1081
|
-
return { value: (_a = registry.keys.get(appId)) != null ? _a : minted, durable: false };
|
|
1082
|
-
};
|
|
1083
|
-
const adopted = (value) => ({ value, durable: true });
|
|
1084
|
-
let run;
|
|
1085
|
-
try {
|
|
1086
|
-
run = Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
1087
|
-
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
1088
|
-
if (persisted) {
|
|
1089
|
-
registry.keys.set(appId, persisted);
|
|
1090
|
-
return adopted(persisted);
|
|
1091
|
-
}
|
|
1092
|
-
return Promise.resolve(storage.setItem(slot, minted)).then(
|
|
1093
|
-
() => {
|
|
1094
|
-
var _a;
|
|
1095
|
-
return adopted((_a = registry.keys.get(appId)) != null ? _a : minted);
|
|
1096
|
-
},
|
|
1097
|
-
degraded
|
|
1098
|
-
);
|
|
1099
|
-
}).catch(degraded);
|
|
1100
|
-
} catch {
|
|
1101
|
-
run = Promise.resolve(degraded());
|
|
1102
|
-
}
|
|
1103
|
-
registry.pending.set(appId, run);
|
|
1104
|
-
void run.then((outcome) => {
|
|
1105
|
-
var _a;
|
|
1106
|
-
if (outcome.durable) return;
|
|
1107
|
-
(_a = registry.pending) == null ? void 0 : _a.delete(appId);
|
|
1108
|
-
registry.hydrating.delete(appId);
|
|
1109
|
-
});
|
|
1110
|
-
return run;
|
|
1111
|
-
};
|
|
1112
|
-
var resolveAutoDeviceKey = (opts = {}) => {
|
|
1113
|
-
var _a, _b;
|
|
1114
|
-
const registry = autoDeviceKeyRegistry();
|
|
1115
|
-
const appId = (_a = opts.appId) != null ? _a : "default";
|
|
1116
|
-
let id = registry.keys.get(appId);
|
|
1117
|
-
if (!id) {
|
|
1118
|
-
id = mintDeviceId();
|
|
1119
|
-
registry.keys.set(appId, id);
|
|
1120
|
-
}
|
|
1121
|
-
const storage = opts.storage;
|
|
1122
|
-
if (storage && !registry.hydrating.has(appId)) {
|
|
1123
|
-
registry.hydrating.add(appId);
|
|
1124
|
-
void startHydration(registry, appId, storage, id);
|
|
1125
|
-
}
|
|
1126
|
-
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
1127
|
-
};
|
|
1128
|
-
var resetAutoDeviceKeys = () => {
|
|
1129
|
-
var _a;
|
|
1130
|
-
const registry = autoDeviceKeyRegistry();
|
|
1131
|
-
registry.keys.clear();
|
|
1132
|
-
registry.hydrating.clear();
|
|
1133
|
-
(_a = registry.pending) == null ? void 0 : _a.clear();
|
|
1134
|
-
};
|
|
1135
|
-
|
|
1136
1252
|
// src/analytics/analyticsFacade.ts
|
|
1137
1253
|
var createAnalytics = (config, options = {}) => {
|
|
1138
1254
|
var _a, _b, _c;
|
|
@@ -1285,30 +1401,75 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1285
1401
|
dispose: queue.dispose
|
|
1286
1402
|
};
|
|
1287
1403
|
};
|
|
1404
|
+
var stableAnalyticsFacade = (live) => ({
|
|
1405
|
+
track: (event, props) => {
|
|
1406
|
+
var _a;
|
|
1407
|
+
return (_a = live()) == null ? void 0 : _a.track(event, props);
|
|
1408
|
+
},
|
|
1409
|
+
screen: (name, props) => {
|
|
1410
|
+
var _a;
|
|
1411
|
+
return (_a = live()) == null ? void 0 : _a.screen(name, props);
|
|
1412
|
+
},
|
|
1413
|
+
identify: (userId, traits) => {
|
|
1414
|
+
var _a;
|
|
1415
|
+
return (_a = live()) == null ? void 0 : _a.identify(userId, traits);
|
|
1416
|
+
},
|
|
1417
|
+
setUserContext: (partial) => {
|
|
1418
|
+
var _a;
|
|
1419
|
+
return (_a = live()) == null ? void 0 : _a.setUserContext(partial);
|
|
1420
|
+
},
|
|
1421
|
+
reset: () => {
|
|
1422
|
+
var _a;
|
|
1423
|
+
return (_a = live()) == null ? void 0 : _a.reset();
|
|
1424
|
+
},
|
|
1425
|
+
flush: () => {
|
|
1426
|
+
var _a;
|
|
1427
|
+
return (_a = live()) == null ? void 0 : _a.flush();
|
|
1428
|
+
},
|
|
1429
|
+
notifyOnline: () => {
|
|
1430
|
+
var _a;
|
|
1431
|
+
return (_a = live()) == null ? void 0 : _a.notifyOnline();
|
|
1432
|
+
},
|
|
1433
|
+
// After a real unmount there is no instance and nothing pending on it — 0 is the honest answer.
|
|
1434
|
+
size: () => {
|
|
1435
|
+
var _a, _b;
|
|
1436
|
+
return (_b = (_a = live()) == null ? void 0 : _a.size()) != null ? _b : 0;
|
|
1437
|
+
},
|
|
1438
|
+
dispose: () => {
|
|
1439
|
+
var _a;
|
|
1440
|
+
return (_a = live()) == null ? void 0 : _a.dispose();
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1288
1443
|
var useAnalytics = (config, options = {}) => {
|
|
1289
1444
|
var _a, _b;
|
|
1290
1445
|
const ref = react.useRef(void 0);
|
|
1291
1446
|
const prevKeys = react.useRef("");
|
|
1447
|
+
const inputs = react.useRef({ config, options });
|
|
1448
|
+
inputs.current = { config, options };
|
|
1292
1449
|
const currentKeys = `${config.serverUrl}|${config.apiKey}|${config.appId}`;
|
|
1293
1450
|
if (!ref.current || prevKeys.current !== currentKeys) {
|
|
1294
1451
|
prevKeys.current = currentKeys;
|
|
1295
1452
|
(_a = ref.current) == null ? void 0 : _a.dispose();
|
|
1296
1453
|
ref.current = createAnalytics(config, options);
|
|
1297
1454
|
}
|
|
1455
|
+
const facade = react.useRef(void 0);
|
|
1456
|
+
if (!facade.current) facade.current = stableAnalyticsFacade(() => ref.current);
|
|
1457
|
+
react.useEffect(() => {
|
|
1458
|
+
if (!ref.current) {
|
|
1459
|
+
ref.current = createAnalytics(inputs.current.config, inputs.current.options);
|
|
1460
|
+
}
|
|
1461
|
+
return () => {
|
|
1462
|
+
var _a2;
|
|
1463
|
+
(_a2 = ref.current) == null ? void 0 : _a2.dispose();
|
|
1464
|
+
ref.current = void 0;
|
|
1465
|
+
};
|
|
1466
|
+
}, []);
|
|
1298
1467
|
const hostDeviceKey = typeof ((_b = config.userContext) == null ? void 0 : _b.deviceKey) === "string" && config.userContext.deviceKey.trim() ? config.userContext.deviceKey.trim() : void 0;
|
|
1299
1468
|
react.useEffect(() => {
|
|
1300
1469
|
var _a2;
|
|
1301
1470
|
if (hostDeviceKey) (_a2 = ref.current) == null ? void 0 : _a2.setUserContext({ deviceKey: hostDeviceKey });
|
|
1302
1471
|
}, [hostDeviceKey]);
|
|
1303
|
-
|
|
1304
|
-
() => () => {
|
|
1305
|
-
var _a2;
|
|
1306
|
-
(_a2 = ref.current) == null ? void 0 : _a2.dispose();
|
|
1307
|
-
ref.current = void 0;
|
|
1308
|
-
},
|
|
1309
|
-
[]
|
|
1310
|
-
);
|
|
1311
|
-
return ref.current;
|
|
1472
|
+
return facade.current;
|
|
1312
1473
|
};
|
|
1313
1474
|
|
|
1314
1475
|
exports.AUTO_DEVICE_ID_PREFIX = AUTO_DEVICE_ID_PREFIX;
|