@wireai/activation 0.14.3 → 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 +96 -21
- package/CHANGELOG.md +780 -0
- package/INTEGRATION_PROMPT.md +61 -23
- package/README.md +110 -31
- package/dist/analytics/index.d.mts +35 -13
- package/dist/analytics/index.d.ts +35 -13
- 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 +15 -1
- package/dist/coachmarks/index.d.ts +15 -1
- package/dist/coachmarks/index.js +120 -22
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs +120 -22
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/{currentSession-CUvTOchb.d.mts → currentSession-Bz7G6lno.d.mts} +35 -39
- package/dist/{currentSession-CW_5Mq4O.d.ts → currentSession-z-CZ55ad.d.ts} +35 -39
- package/dist/{decision-Bgo17oH7.d.mts → decision-3vWLuBlO.d.ts} +11 -2
- package/dist/{decision-Bkh_LigV.d.ts → decision-yBj2AyPW.d.mts} +11 -2
- 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 +3 -16
- package/dist/questionnaire/index.d.ts +3 -16
- package/dist/questionnaire/index.js +202 -46
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +203 -47
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.d.mts +10 -6
- package/dist/reviews/index.d.ts +10 -6
- package/dist/reviews/index.js +269 -52
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +270 -53
- package/dist/reviews/index.mjs.map +1 -1
- package/dist/showcase/index.d.mts +1 -1
- package/dist/showcase/index.d.ts +1 -1
- package/dist/showcase/index.js +106 -20
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/index.mjs +107 -21
- package/dist/showcase/index.mjs.map +1 -1
- package/dist/{transport-j5gFfJhK.d.mts → transport-s5QxA-ci.d.mts} +18 -13
- package/dist/{transport-B_0SgCBe.d.ts → transport-xqqSFqxs.d.ts} +18 -13
- package/dist/{types-Cju-1_jT.d.mts → types-Byx306Kv.d.mts} +25 -10
- package/dist/{types-BcmagF6K.d.mts → types-D_0B0yay.d.mts} +7 -2
- package/dist/{types-BcmagF6K.d.ts → types-D_0B0yay.d.ts} +7 -2
- package/dist/{types-h2BZvl1t.d.ts → types-tdATL5z0.d.ts} +25 -10
- package/llms.txt +9 -9
- package/package.json +6 -9
- package/src/WireOnboarding.tsx +4 -1
- 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 +156 -2
- package/src/coachmarks/types.ts +7 -2
- package/src/coachmarks/useCoachmarkTour.ts +51 -1
- package/src/context/deviceId.ts +72 -6
- 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 +125 -37
- package/src/reviews/decision.ts +11 -1
- package/src/reviews/idempotency.ts +109 -0
- package/src/reviews/index.ts +4 -2
- package/src/reviews/runtime.ts +44 -13
- package/src/reviews/transport.ts +39 -20
- package/src/reviews/types.ts +7 -0
- 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 +15 -8
- package/src/utils/submitResult.ts +17 -9
- package/src/utils/withDeadline.ts +70 -0
package/dist/analytics/index.mjs
CHANGED
|
@@ -8,6 +8,28 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
8
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
+
// src/utils/withDeadline.ts
|
|
12
|
+
var DEADLINE_EXPIRED = /* @__PURE__ */ Symbol("wire-deadline-expired");
|
|
13
|
+
var DEFAULT_DEADLINE_MS = 15e3;
|
|
14
|
+
var withDeadline = async (exchange, timeoutMs = DEFAULT_DEADLINE_MS) => {
|
|
15
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
16
|
+
let timer;
|
|
17
|
+
const expired = new Promise((resolve) => {
|
|
18
|
+
timer = setTimeout(() => {
|
|
19
|
+
controller == null ? void 0 : controller.abort();
|
|
20
|
+
resolve(DEADLINE_EXPIRED);
|
|
21
|
+
}, timeoutMs);
|
|
22
|
+
});
|
|
23
|
+
const running = exchange(controller == null ? void 0 : controller.signal);
|
|
24
|
+
try {
|
|
25
|
+
return await Promise.race([running, expired]);
|
|
26
|
+
} finally {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
void running.catch(() => {
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
11
33
|
// src/analytics/reportClientEvent.ts
|
|
12
34
|
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
13
35
|
var toWireEvents = (events) => events.map((event) => {
|
|
@@ -79,14 +101,17 @@ var reportClientEventsOutcome = async (target, events) => {
|
|
|
79
101
|
try {
|
|
80
102
|
const req = buildEventsRequest(target, events);
|
|
81
103
|
if (!req) return "refused";
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
console.warn
|
|
88
|
-
|
|
89
|
-
|
|
104
|
+
const outcome = await withDeadline(async (signal) => {
|
|
105
|
+
const res = await fetch(req.url, { ...req.init, signal });
|
|
106
|
+
if (!res || !res.ok) return "unreachable";
|
|
107
|
+
const ack = await readEventsAck(res);
|
|
108
|
+
if (!ack || ack.skipped <= 0) return "delivered";
|
|
109
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
110
|
+
console.warn(describeDiscarded(ack));
|
|
111
|
+
}
|
|
112
|
+
return "refused";
|
|
113
|
+
});
|
|
114
|
+
return outcome === DEADLINE_EXPIRED ? "unreachable" : outcome;
|
|
90
115
|
} catch {
|
|
91
116
|
return "unreachable";
|
|
92
117
|
}
|
|
@@ -107,25 +132,51 @@ var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
|
107
132
|
"@wireai/activation:currentSessionId"
|
|
108
133
|
);
|
|
109
134
|
var globalSlot = globalThis;
|
|
135
|
+
var SESSION_ID_LISTENERS_SLOT = /* @__PURE__ */ Symbol.for(
|
|
136
|
+
"@wireai/activation:currentSessionIdListeners"
|
|
137
|
+
);
|
|
138
|
+
var listenerSlot = globalThis;
|
|
139
|
+
var listeners = () => {
|
|
140
|
+
const existing = listenerSlot[SESSION_ID_LISTENERS_SLOT];
|
|
141
|
+
if (existing) return existing;
|
|
142
|
+
const created = /* @__PURE__ */ new Set();
|
|
143
|
+
listenerSlot[SESSION_ID_LISTENERS_SLOT] = created;
|
|
144
|
+
return created;
|
|
145
|
+
};
|
|
146
|
+
var writeCurrentSessionId = (id) => {
|
|
147
|
+
if (globalSlot[CURRENT_SESSION_ID_SLOT] === id) return;
|
|
148
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
149
|
+
for (const listener of Array.from(listeners())) {
|
|
150
|
+
try {
|
|
151
|
+
listener();
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
110
156
|
var setCurrentSessionId = (id) => {
|
|
111
157
|
if (typeof id === "string" && id.length > 0) {
|
|
112
|
-
|
|
158
|
+
writeCurrentSessionId(id);
|
|
113
159
|
}
|
|
114
160
|
};
|
|
115
161
|
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
116
162
|
var resetCurrentSessionId = () => {
|
|
117
|
-
|
|
163
|
+
writeCurrentSessionId(void 0);
|
|
118
164
|
};
|
|
119
165
|
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.";
|
|
120
166
|
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
121
167
|
"@wireai/activation:currentSessionIdMintWarned"
|
|
122
168
|
);
|
|
123
169
|
var warnSlot = globalThis;
|
|
170
|
+
var MINTED_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
171
|
+
"@wireai/activation:mintedSessionId"
|
|
172
|
+
);
|
|
173
|
+
var mintedSlot = globalThis;
|
|
124
174
|
var ensureCurrentSessionId = () => {
|
|
125
175
|
const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
126
176
|
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
127
177
|
const minted = makeSessionId();
|
|
128
|
-
|
|
178
|
+
writeCurrentSessionId(minted);
|
|
179
|
+
mintedSlot[MINTED_SESSION_ID_SLOT] = minted;
|
|
129
180
|
if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
|
|
130
181
|
warnSlot[MINT_WARNED_SLOT] = true;
|
|
131
182
|
}
|
|
@@ -226,6 +277,109 @@ var useScreenTracking = (navigationRef, options = {}) => {
|
|
|
226
277
|
}, [navigationRef]);
|
|
227
278
|
};
|
|
228
279
|
|
|
280
|
+
// src/identity/identityRecord.ts
|
|
281
|
+
var IDENTITY_PROVENANCE_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:identityProvenance");
|
|
282
|
+
var provenanceGlobal = globalThis;
|
|
283
|
+
var provenanceRegistry = () => {
|
|
284
|
+
const existing = provenanceGlobal[IDENTITY_PROVENANCE_SLOT];
|
|
285
|
+
if (existing) return existing;
|
|
286
|
+
const created = { host: /* @__PURE__ */ new Map() };
|
|
287
|
+
provenanceGlobal[IDENTITY_PROVENANCE_SLOT] = created;
|
|
288
|
+
return created;
|
|
289
|
+
};
|
|
290
|
+
var provenanceKey = (space, scope) => `${space}:${scope != null ? scope : "default"}`;
|
|
291
|
+
var isUsableIdentityValue = (value) => typeof value === "string" && value.trim().length > 0;
|
|
292
|
+
var resolveIdentity = (input) => {
|
|
293
|
+
var _a;
|
|
294
|
+
if (!isUsableIdentityValue(input.value)) return void 0;
|
|
295
|
+
const value = input.value.trim();
|
|
296
|
+
const durable = (_a = input.durable) != null ? _a : input.source === "host";
|
|
297
|
+
{
|
|
298
|
+
provenanceRegistry().host.set(provenanceKey(input.space, input.scope), value);
|
|
299
|
+
}
|
|
300
|
+
return { value, space: input.space, source: input.source, durable };
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// src/context/deviceId.ts
|
|
304
|
+
var AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
305
|
+
var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
|
|
306
|
+
var randomChunk = () => Math.floor(Math.random() * 4294967296).toString(36).padStart(6, "0");
|
|
307
|
+
var mintDeviceId = () => {
|
|
308
|
+
const time = Date.now().toString(36);
|
|
309
|
+
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
310
|
+
};
|
|
311
|
+
var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
|
|
312
|
+
var deviceKeyGlobal = globalThis;
|
|
313
|
+
var autoDeviceKeyRegistry = () => {
|
|
314
|
+
const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
|
|
315
|
+
if (existing) return existing;
|
|
316
|
+
const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
|
|
317
|
+
deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
|
|
318
|
+
return created;
|
|
319
|
+
};
|
|
320
|
+
var startHydration = (registry, appId, storage, minted) => {
|
|
321
|
+
if (!registry.pending) registry.pending = /* @__PURE__ */ new Map();
|
|
322
|
+
const existing = registry.pending.get(appId);
|
|
323
|
+
if (existing) return existing;
|
|
324
|
+
const slot = deviceIdStorageKey(appId);
|
|
325
|
+
const degraded = () => {
|
|
326
|
+
var _a;
|
|
327
|
+
return { value: (_a = registry.keys.get(appId)) != null ? _a : minted, durable: false };
|
|
328
|
+
};
|
|
329
|
+
const adopted = (value) => ({ value, durable: true });
|
|
330
|
+
let run;
|
|
331
|
+
try {
|
|
332
|
+
run = Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
333
|
+
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
334
|
+
if (persisted) {
|
|
335
|
+
registry.keys.set(appId, persisted);
|
|
336
|
+
return adopted(persisted);
|
|
337
|
+
}
|
|
338
|
+
return Promise.resolve(storage.setItem(slot, minted)).then(
|
|
339
|
+
() => {
|
|
340
|
+
var _a;
|
|
341
|
+
return adopted((_a = registry.keys.get(appId)) != null ? _a : minted);
|
|
342
|
+
},
|
|
343
|
+
degraded
|
|
344
|
+
);
|
|
345
|
+
}).catch(degraded);
|
|
346
|
+
} catch {
|
|
347
|
+
run = Promise.resolve(degraded());
|
|
348
|
+
}
|
|
349
|
+
registry.pending.set(appId, run);
|
|
350
|
+
void run.then((outcome) => {
|
|
351
|
+
var _a;
|
|
352
|
+
if (outcome.durable) return;
|
|
353
|
+
(_a = registry.pending) == null ? void 0 : _a.delete(appId);
|
|
354
|
+
registry.hydrating.delete(appId);
|
|
355
|
+
});
|
|
356
|
+
return run;
|
|
357
|
+
};
|
|
358
|
+
var resolveAutoDeviceKey = (opts = {}) => {
|
|
359
|
+
var _a, _b;
|
|
360
|
+
const registry = autoDeviceKeyRegistry();
|
|
361
|
+
const appId = (_a = opts.appId) != null ? _a : "default";
|
|
362
|
+
let id = registry.keys.get(appId);
|
|
363
|
+
if (!id) {
|
|
364
|
+
id = mintDeviceId();
|
|
365
|
+
registry.keys.set(appId, id);
|
|
366
|
+
}
|
|
367
|
+
const storage = opts.storage;
|
|
368
|
+
if (storage && !registry.hydrating.has(appId)) {
|
|
369
|
+
registry.hydrating.add(appId);
|
|
370
|
+
void startHydration(registry, appId, storage, id);
|
|
371
|
+
}
|
|
372
|
+
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
373
|
+
};
|
|
374
|
+
var resetAutoDeviceKeys = () => {
|
|
375
|
+
var _a;
|
|
376
|
+
const registry = autoDeviceKeyRegistry();
|
|
377
|
+
registry.keys.clear();
|
|
378
|
+
registry.hydrating.clear();
|
|
379
|
+
(_a = registry.pending) == null ? void 0 : _a.clear();
|
|
380
|
+
registry.ambient = void 0;
|
|
381
|
+
};
|
|
382
|
+
|
|
229
383
|
// src/analytics/wireDoctor.ts
|
|
230
384
|
var PROBE_STORAGE_KEY = "wireai:doctor:probe";
|
|
231
385
|
var PROBE_QUESTION_KEY = "wire_doctor_probe";
|
|
@@ -356,6 +510,66 @@ var checkRoundTrip = async (target) => {
|
|
|
356
510
|
`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.")
|
|
357
511
|
);
|
|
358
512
|
};
|
|
513
|
+
var checkJoinKey = async (join, storage, storageOk) => {
|
|
514
|
+
var _a;
|
|
515
|
+
if (!join) {
|
|
516
|
+
return check(
|
|
517
|
+
"join_key",
|
|
518
|
+
false,
|
|
519
|
+
"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."
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
if (isUsableIdentityValue((_a = join.userContext) == null ? void 0 : _a.device_key)) {
|
|
523
|
+
return check(
|
|
524
|
+
"join_key",
|
|
525
|
+
true,
|
|
526
|
+
"user_context.device_key is supplied by the host, so onboarding sessions join the rest of the funnel."
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
if (join.autoJoinKey === false) {
|
|
530
|
+
return check(
|
|
531
|
+
"join_key",
|
|
532
|
+
false,
|
|
533
|
+
"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."
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if (!storage) {
|
|
537
|
+
return check(
|
|
538
|
+
"join_key",
|
|
539
|
+
false,
|
|
540
|
+
"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>."
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
if (!storageOk) {
|
|
544
|
+
return check(
|
|
545
|
+
"join_key",
|
|
546
|
+
false,
|
|
547
|
+
"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."
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
let persisted = null;
|
|
551
|
+
try {
|
|
552
|
+
persisted = await storage.getItem(deviceIdStorageKey(join.appId));
|
|
553
|
+
} catch {
|
|
554
|
+
return check(
|
|
555
|
+
"join_key",
|
|
556
|
+
false,
|
|
557
|
+
"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."
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
if (isUsableIdentityValue(persisted)) {
|
|
561
|
+
return check(
|
|
562
|
+
"join_key",
|
|
563
|
+
true,
|
|
564
|
+
"no host device_key, but a durable auto join key is already persisted for this appId, so sessions join."
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
return check(
|
|
568
|
+
"join_key",
|
|
569
|
+
true,
|
|
570
|
+
"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."
|
|
571
|
+
);
|
|
572
|
+
};
|
|
359
573
|
var wireDoctor = async (options) => {
|
|
360
574
|
if (typeof __DEV__ === "undefined" || !__DEV__) {
|
|
361
575
|
return {
|
|
@@ -371,14 +585,18 @@ var wireDoctor = async (options) => {
|
|
|
371
585
|
}
|
|
372
586
|
try {
|
|
373
587
|
const target = options == null ? void 0 : options.target;
|
|
374
|
-
const
|
|
375
|
-
|
|
588
|
+
const targetCheck = checkTarget(target);
|
|
589
|
+
const checks = [targetCheck];
|
|
590
|
+
const targetUsable = targetCheck.ok && !!target;
|
|
591
|
+
if (targetUsable && target) {
|
|
376
592
|
checks.push(await checkReachability(target.serverUrl));
|
|
377
|
-
|
|
593
|
+
}
|
|
594
|
+
const storageCheck = await checkStorage(options == null ? void 0 : options.storage);
|
|
595
|
+
checks.push(storageCheck);
|
|
596
|
+
if (targetUsable && target) {
|
|
378
597
|
checks.push(await checkRoundTrip(target));
|
|
379
|
-
} else {
|
|
380
|
-
checks.push(await checkStorage(options == null ? void 0 : options.storage));
|
|
381
598
|
}
|
|
599
|
+
checks.push(await checkJoinKey(options == null ? void 0 : options.join, options == null ? void 0 : options.storage, storageCheck.ok));
|
|
382
600
|
return { ok: checks.every((c) => c.ok), checks };
|
|
383
601
|
} catch {
|
|
384
602
|
return {
|
|
@@ -1029,108 +1247,6 @@ var resolveUserContext = (ctx = {}, opts = {}) => {
|
|
|
1029
1247
|
return result;
|
|
1030
1248
|
};
|
|
1031
1249
|
|
|
1032
|
-
// src/identity/identityRecord.ts
|
|
1033
|
-
var IDENTITY_PROVENANCE_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:identityProvenance");
|
|
1034
|
-
var provenanceGlobal = globalThis;
|
|
1035
|
-
var provenanceRegistry = () => {
|
|
1036
|
-
const existing = provenanceGlobal[IDENTITY_PROVENANCE_SLOT];
|
|
1037
|
-
if (existing) return existing;
|
|
1038
|
-
const created = { host: /* @__PURE__ */ new Map() };
|
|
1039
|
-
provenanceGlobal[IDENTITY_PROVENANCE_SLOT] = created;
|
|
1040
|
-
return created;
|
|
1041
|
-
};
|
|
1042
|
-
var provenanceKey = (space, scope) => `${space}:${scope != null ? scope : "default"}`;
|
|
1043
|
-
var resolveIdentity = (input) => {
|
|
1044
|
-
var _a;
|
|
1045
|
-
if (typeof input.value !== "string") return void 0;
|
|
1046
|
-
const value = input.value.trim();
|
|
1047
|
-
if (!value) return void 0;
|
|
1048
|
-
const durable = (_a = input.durable) != null ? _a : input.source === "host";
|
|
1049
|
-
{
|
|
1050
|
-
provenanceRegistry().host.set(provenanceKey(input.space, input.scope), value);
|
|
1051
|
-
}
|
|
1052
|
-
return { value, space: input.space, source: input.source, durable };
|
|
1053
|
-
};
|
|
1054
|
-
|
|
1055
|
-
// src/context/deviceId.ts
|
|
1056
|
-
var AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
1057
|
-
var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
|
|
1058
|
-
var randomChunk = () => Math.floor(Math.random() * 4294967296).toString(36).padStart(6, "0");
|
|
1059
|
-
var mintDeviceId = () => {
|
|
1060
|
-
const time = Date.now().toString(36);
|
|
1061
|
-
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
1062
|
-
};
|
|
1063
|
-
var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
|
|
1064
|
-
var deviceKeyGlobal = globalThis;
|
|
1065
|
-
var autoDeviceKeyRegistry = () => {
|
|
1066
|
-
const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
|
|
1067
|
-
if (existing) return existing;
|
|
1068
|
-
const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
|
|
1069
|
-
deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
|
|
1070
|
-
return created;
|
|
1071
|
-
};
|
|
1072
|
-
var startHydration = (registry, appId, storage, minted) => {
|
|
1073
|
-
if (!registry.pending) registry.pending = /* @__PURE__ */ new Map();
|
|
1074
|
-
const existing = registry.pending.get(appId);
|
|
1075
|
-
if (existing) return existing;
|
|
1076
|
-
const slot = deviceIdStorageKey(appId);
|
|
1077
|
-
const degraded = () => {
|
|
1078
|
-
var _a;
|
|
1079
|
-
return { value: (_a = registry.keys.get(appId)) != null ? _a : minted, durable: false };
|
|
1080
|
-
};
|
|
1081
|
-
const adopted = (value) => ({ value, durable: true });
|
|
1082
|
-
let run;
|
|
1083
|
-
try {
|
|
1084
|
-
run = Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
1085
|
-
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
1086
|
-
if (persisted) {
|
|
1087
|
-
registry.keys.set(appId, persisted);
|
|
1088
|
-
return adopted(persisted);
|
|
1089
|
-
}
|
|
1090
|
-
return Promise.resolve(storage.setItem(slot, minted)).then(
|
|
1091
|
-
() => {
|
|
1092
|
-
var _a;
|
|
1093
|
-
return adopted((_a = registry.keys.get(appId)) != null ? _a : minted);
|
|
1094
|
-
},
|
|
1095
|
-
degraded
|
|
1096
|
-
);
|
|
1097
|
-
}).catch(degraded);
|
|
1098
|
-
} catch {
|
|
1099
|
-
run = Promise.resolve(degraded());
|
|
1100
|
-
}
|
|
1101
|
-
registry.pending.set(appId, run);
|
|
1102
|
-
void run.then((outcome) => {
|
|
1103
|
-
var _a;
|
|
1104
|
-
if (outcome.durable) return;
|
|
1105
|
-
(_a = registry.pending) == null ? void 0 : _a.delete(appId);
|
|
1106
|
-
registry.hydrating.delete(appId);
|
|
1107
|
-
});
|
|
1108
|
-
return run;
|
|
1109
|
-
};
|
|
1110
|
-
var resolveAutoDeviceKey = (opts = {}) => {
|
|
1111
|
-
var _a, _b;
|
|
1112
|
-
const registry = autoDeviceKeyRegistry();
|
|
1113
|
-
const appId = (_a = opts.appId) != null ? _a : "default";
|
|
1114
|
-
let id = registry.keys.get(appId);
|
|
1115
|
-
if (!id) {
|
|
1116
|
-
id = mintDeviceId();
|
|
1117
|
-
registry.keys.set(appId, id);
|
|
1118
|
-
}
|
|
1119
|
-
const storage = opts.storage;
|
|
1120
|
-
if (storage && !registry.hydrating.has(appId)) {
|
|
1121
|
-
registry.hydrating.add(appId);
|
|
1122
|
-
void startHydration(registry, appId, storage, id);
|
|
1123
|
-
}
|
|
1124
|
-
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
1125
|
-
};
|
|
1126
|
-
var resetAutoDeviceKeys = () => {
|
|
1127
|
-
var _a;
|
|
1128
|
-
const registry = autoDeviceKeyRegistry();
|
|
1129
|
-
registry.keys.clear();
|
|
1130
|
-
registry.hydrating.clear();
|
|
1131
|
-
(_a = registry.pending) == null ? void 0 : _a.clear();
|
|
1132
|
-
};
|
|
1133
|
-
|
|
1134
1250
|
// src/analytics/analyticsFacade.ts
|
|
1135
1251
|
var createAnalytics = (config, options = {}) => {
|
|
1136
1252
|
var _a, _b, _c;
|
|
@@ -1283,30 +1399,75 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1283
1399
|
dispose: queue.dispose
|
|
1284
1400
|
};
|
|
1285
1401
|
};
|
|
1402
|
+
var stableAnalyticsFacade = (live) => ({
|
|
1403
|
+
track: (event, props) => {
|
|
1404
|
+
var _a;
|
|
1405
|
+
return (_a = live()) == null ? void 0 : _a.track(event, props);
|
|
1406
|
+
},
|
|
1407
|
+
screen: (name, props) => {
|
|
1408
|
+
var _a;
|
|
1409
|
+
return (_a = live()) == null ? void 0 : _a.screen(name, props);
|
|
1410
|
+
},
|
|
1411
|
+
identify: (userId, traits) => {
|
|
1412
|
+
var _a;
|
|
1413
|
+
return (_a = live()) == null ? void 0 : _a.identify(userId, traits);
|
|
1414
|
+
},
|
|
1415
|
+
setUserContext: (partial) => {
|
|
1416
|
+
var _a;
|
|
1417
|
+
return (_a = live()) == null ? void 0 : _a.setUserContext(partial);
|
|
1418
|
+
},
|
|
1419
|
+
reset: () => {
|
|
1420
|
+
var _a;
|
|
1421
|
+
return (_a = live()) == null ? void 0 : _a.reset();
|
|
1422
|
+
},
|
|
1423
|
+
flush: () => {
|
|
1424
|
+
var _a;
|
|
1425
|
+
return (_a = live()) == null ? void 0 : _a.flush();
|
|
1426
|
+
},
|
|
1427
|
+
notifyOnline: () => {
|
|
1428
|
+
var _a;
|
|
1429
|
+
return (_a = live()) == null ? void 0 : _a.notifyOnline();
|
|
1430
|
+
},
|
|
1431
|
+
// After a real unmount there is no instance and nothing pending on it — 0 is the honest answer.
|
|
1432
|
+
size: () => {
|
|
1433
|
+
var _a, _b;
|
|
1434
|
+
return (_b = (_a = live()) == null ? void 0 : _a.size()) != null ? _b : 0;
|
|
1435
|
+
},
|
|
1436
|
+
dispose: () => {
|
|
1437
|
+
var _a;
|
|
1438
|
+
return (_a = live()) == null ? void 0 : _a.dispose();
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1286
1441
|
var useAnalytics = (config, options = {}) => {
|
|
1287
1442
|
var _a, _b;
|
|
1288
1443
|
const ref = useRef(void 0);
|
|
1289
1444
|
const prevKeys = useRef("");
|
|
1445
|
+
const inputs = useRef({ config, options });
|
|
1446
|
+
inputs.current = { config, options };
|
|
1290
1447
|
const currentKeys = `${config.serverUrl}|${config.apiKey}|${config.appId}`;
|
|
1291
1448
|
if (!ref.current || prevKeys.current !== currentKeys) {
|
|
1292
1449
|
prevKeys.current = currentKeys;
|
|
1293
1450
|
(_a = ref.current) == null ? void 0 : _a.dispose();
|
|
1294
1451
|
ref.current = createAnalytics(config, options);
|
|
1295
1452
|
}
|
|
1453
|
+
const facade = useRef(void 0);
|
|
1454
|
+
if (!facade.current) facade.current = stableAnalyticsFacade(() => ref.current);
|
|
1455
|
+
useEffect(() => {
|
|
1456
|
+
if (!ref.current) {
|
|
1457
|
+
ref.current = createAnalytics(inputs.current.config, inputs.current.options);
|
|
1458
|
+
}
|
|
1459
|
+
return () => {
|
|
1460
|
+
var _a2;
|
|
1461
|
+
(_a2 = ref.current) == null ? void 0 : _a2.dispose();
|
|
1462
|
+
ref.current = void 0;
|
|
1463
|
+
};
|
|
1464
|
+
}, []);
|
|
1296
1465
|
const hostDeviceKey = typeof ((_b = config.userContext) == null ? void 0 : _b.deviceKey) === "string" && config.userContext.deviceKey.trim() ? config.userContext.deviceKey.trim() : void 0;
|
|
1297
1466
|
useEffect(() => {
|
|
1298
1467
|
var _a2;
|
|
1299
1468
|
if (hostDeviceKey) (_a2 = ref.current) == null ? void 0 : _a2.setUserContext({ deviceKey: hostDeviceKey });
|
|
1300
1469
|
}, [hostDeviceKey]);
|
|
1301
|
-
|
|
1302
|
-
() => () => {
|
|
1303
|
-
var _a2;
|
|
1304
|
-
(_a2 = ref.current) == null ? void 0 : _a2.dispose();
|
|
1305
|
-
ref.current = void 0;
|
|
1306
|
-
},
|
|
1307
|
-
[]
|
|
1308
|
-
);
|
|
1309
|
-
return ref.current;
|
|
1470
|
+
return facade.current;
|
|
1310
1471
|
};
|
|
1311
1472
|
|
|
1312
1473
|
export { AUTO_DEVICE_ID_PREFIX, WIRE_ONBOARDING_EVENTS, analyticsUserIdStorageKey, buildContextEnvelope, clearPiiFromContext, clearUserContext, createAnalytics, createEventQueue, createScreenTracker, deviceIdStorageKey, ensureCurrentSessionId, getActiveRouteName, getCurrentSessionId, looksLikeEmail, makeSessionId, reportAppEvent, reportClientEvent, reportClientEventAwait, reportClientEvents, reportClientEventsAwait, resetAutoDeviceKeys, resetCurrentSessionId, resetEventQueueKeys, resolveAutoDeviceKey, screenTrackingHandler, setCurrentSessionId, toAnalyticsEvent, useAnalytics, useScreenTracking, wireDoctor };
|