@wireai/activation 0.1.0 → 0.2.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.
Files changed (57) hide show
  1. package/AGENTS.md +82 -40
  2. package/CHANGELOG.md +17 -2
  3. package/INTEGRATION_PROMPT.md +8 -8
  4. package/README.md +38 -38
  5. package/dist/analytics/index.d.mts +92 -0
  6. package/dist/analytics/index.d.ts +92 -0
  7. package/dist/analytics/index.js +439 -0
  8. package/dist/analytics/index.js.map +1 -0
  9. package/dist/analytics/index.mjs +426 -0
  10. package/dist/analytics/index.mjs.map +1 -0
  11. package/dist/coachmarks/index.js.map +1 -1
  12. package/dist/coachmarks/index.mjs.map +1 -1
  13. package/dist/decision-BzbiKwk3.d.mts +79 -0
  14. package/dist/decision-plDEOCkt.d.ts +79 -0
  15. package/dist/eventQueue-CxKi7Qd5.d.mts +546 -0
  16. package/dist/eventQueue-rV1dtJJR.d.ts +546 -0
  17. package/dist/index.d.mts +162 -418
  18. package/dist/index.d.ts +162 -418
  19. package/dist/index.js +407 -4
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +402 -5
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/questionnaire/index.d.mts +2 -1
  24. package/dist/questionnaire/index.d.ts +2 -1
  25. package/dist/questionnaire/index.js.map +1 -1
  26. package/dist/questionnaire/index.mjs.map +1 -1
  27. package/dist/reviews/index.d.mts +5 -40
  28. package/dist/reviews/index.d.ts +5 -40
  29. package/dist/reviews/index.js.map +1 -1
  30. package/dist/reviews/index.mjs.map +1 -1
  31. package/dist/showcase/index.js.map +1 -1
  32. package/dist/showcase/index.mjs.map +1 -1
  33. package/dist/transport-BeO_Brcu.d.mts +40 -0
  34. package/dist/transport-DLpd1v5_.d.ts +40 -0
  35. package/dist/{decision-Cl8OFYzu.d.mts → types-A6pTxIZV.d.mts} +1 -77
  36. package/dist/{decision-CFvGY6nP.d.ts → types-BhpXJGlg.d.ts} +1 -77
  37. package/llms.txt +7 -7
  38. package/metro/index.d.ts +3 -3
  39. package/metro/index.js +3 -3
  40. package/package.json +15 -1
  41. package/src/analytics/contextEnvelope.ts +72 -0
  42. package/src/analytics/eventQueue.ts +331 -0
  43. package/src/analytics/index.ts +47 -0
  44. package/src/analytics/screenTracking.ts +122 -0
  45. package/src/analytics/useScreenTracking.ts +48 -0
  46. package/src/coachmarks/index.ts +2 -2
  47. package/src/features/WireFeaturesProvider.tsx +1 -1
  48. package/src/features/index.ts +2 -2
  49. package/src/index.ts +18 -1
  50. package/src/questionnaire/index.ts +2 -2
  51. package/src/reviews/index.ts +2 -2
  52. package/src/session-analytics/index.ts +20 -0
  53. package/src/session-analytics/lifecycle.ts +236 -0
  54. package/src/session-analytics/reportSessionStart.ts +27 -7
  55. package/src/session-analytics/useLifecycleEvents.ts +184 -0
  56. package/src/showcase/index.ts +2 -2
  57. package/src/types.ts +1 -1
@@ -0,0 +1,92 @@
1
+ export { R as ReportAppEventOptions, r as reportAppEvent } from '../transport-DLpd1v5_.js';
2
+ export { A as AnalyticsEvent, C as ClientEvent, a as ClientEventTarget, b as ClientEventType, c as ContextEnvelope, d as ContextEnvelopeInput, E as EnvelopeSource, e as EventQueue, f as EventQueueOptions, W as WIRE_ONBOARDING_EVENTS, g as WireOnboardingEventName, h as buildContextEnvelope, i as createEventQueue, m as makeSessionId, r as reportClientEvent, j as reportClientEvents, t as toAnalyticsEvent } from '../eventQueue-rV1dtJJR.js';
3
+ import '../types-BhpXJGlg.js';
4
+ import '../types-BKfpdZzX.js';
5
+ import '../types-GL_hQ0TN.js';
6
+ import '../types-CMuOexw0.js';
7
+ import 'react';
8
+ import 'wireai-rn';
9
+ import 'react-native';
10
+
11
+ /** A single route inside a React-Navigation-shaped state (structural — no `@react-navigation`). */
12
+ interface NavigationRouteLike {
13
+ name: string;
14
+ /** A nested navigator's own state, when this route hosts one. */
15
+ state?: NavigationStateLike;
16
+ /** Route params are intentionally left `unknown` — this helper never reads them. */
17
+ params?: unknown;
18
+ }
19
+ /** A React-Navigation-shaped navigator state (structural type; no library import). */
20
+ interface NavigationStateLike {
21
+ /** Index of the active route within `routes`. */
22
+ index?: number;
23
+ routes?: NavigationRouteLike[];
24
+ }
25
+ /** Options for {@link createScreenTracker}. */
26
+ interface ScreenTrackerOptions {
27
+ /**
28
+ * Where to POST. `{ serverUrl, apiKey }` — same shape the kit's review/analytics config
29
+ * exposes. When omitted, the tracker still de-dups and fires `onScreen`, but sends nothing.
30
+ */
31
+ target?: {
32
+ serverUrl: string;
33
+ apiKey: string;
34
+ };
35
+ /** The onboarding/session id to correlate screen views with, when known. */
36
+ sessionId?: string;
37
+ /** A stable, non-PII device id — groups a device's sessions server-side. */
38
+ deviceKey?: string;
39
+ /** Called on every REAL screen change (after de-dup), before the network emit. */
40
+ onScreen?: (screen: string) => void;
41
+ /**
42
+ * Per-screen filter. Return `false` to skip the NETWORK emit for a screen (last-screen memory
43
+ * is still advanced + `onScreen` still fires) — e.g. to keep a sensitive route out of analytics.
44
+ */
45
+ shouldTrack?: (screen: string) => boolean;
46
+ }
47
+ /** The screen tracker returned by {@link createScreenTracker}. */
48
+ interface ScreenTracker {
49
+ /** Report the active screen. Ignores `undefined`/empty and de-dups repeats of the last screen. */
50
+ track: (screen: string | undefined) => void;
51
+ /** Clear the last-screen memory (e.g. on logout) so the next `track` always emits. */
52
+ reset: () => void;
53
+ }
54
+ /**
55
+ * Walk a React-Navigation-shaped state to the DEEPEST active route and return its NAME (never
56
+ * its params). Recurses `routes[index]` while a nested `.state` exists. Returns `undefined` for
57
+ * a missing/empty/malformed state — the caller treats that as "nothing to report".
58
+ */
59
+ declare const getActiveRouteName: (state: NavigationStateLike | undefined) => string | undefined;
60
+ /**
61
+ * Build a stateful screen tracker. `track` de-dups against the last reported screen so only a
62
+ * REAL change emits; `reset` clears that memory. Fire-and-forget throughout — a missing `target`
63
+ * skips the network but keeps the de-dup + `onScreen` behaviour intact.
64
+ */
65
+ declare const createScreenTracker: (options?: ScreenTrackerOptions) => ScreenTracker;
66
+ /**
67
+ * Adapt a tracker into a React-Navigation `onStateChange` handler — the one-place wiring:
68
+ *
69
+ * <NavigationContainer onStateChange={screenTrackingHandler(tracker)}>
70
+ *
71
+ * It resolves the deepest active route name and hands it to `tracker.track` (which de-dups).
72
+ */
73
+ declare const screenTrackingHandler: (tracker: ScreenTracker) => (state: NavigationStateLike | undefined) => void;
74
+
75
+ /**
76
+ * The structural slice of a React-Navigation container ref this hook needs — no
77
+ * `@react-navigation` import. `getCurrentRoute` yields the active route; `addListener("state", …)`
78
+ * fires on every navigation state change and returns its own unsubscribe.
79
+ */
80
+ interface NavigationRefLike {
81
+ getCurrentRoute?: () => {
82
+ name?: string;
83
+ } | undefined;
84
+ addListener?: (type: "state", callback: () => void) => () => void;
85
+ }
86
+ /**
87
+ * Subscribe screen tracking to a host navigation ref. Safe to call with a not-yet-ready ref
88
+ * (the effect no-ops until `addListener` exists). Returns nothing — it wires side effects only.
89
+ */
90
+ declare const useScreenTracking: (navigationRef: NavigationRefLike | undefined, options?: ScreenTrackerOptions) => void;
91
+
92
+ export { type NavigationRefLike, type NavigationRouteLike, type NavigationStateLike, type ScreenTracker, type ScreenTrackerOptions, createScreenTracker, getActiveRouteName, screenTrackingHandler, useScreenTracking };
@@ -0,0 +1,439 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var reactNative = require('react-native');
5
+
6
+ // src/reviews/transport.ts
7
+ var reportAppEvent = (target, name, options = {}) => {
8
+ if (!(target == null ? void 0 : target.serverUrl) || !name) return;
9
+ try {
10
+ const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
11
+ const headers = { "Content-Type": "application/json" };
12
+ if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
13
+ const event = {
14
+ event_type: "app_event",
15
+ question_key: name
16
+ };
17
+ if (options.sessionId) event.session_id = options.sessionId;
18
+ if (options.deviceKey) event.user_context = { device_key: options.deviceKey };
19
+ if (options.meta && Object.keys(options.meta).length > 0) {
20
+ event.meta = JSON.stringify(options.meta);
21
+ }
22
+ void fetch(url, {
23
+ method: "POST",
24
+ headers,
25
+ body: JSON.stringify({ events: [event] })
26
+ }).catch(() => {
27
+ });
28
+ } catch {
29
+ }
30
+ };
31
+
32
+ // src/analytics/screenTracking.ts
33
+ var getActiveRouteName = (state) => {
34
+ let current = state;
35
+ let name;
36
+ while (current && Array.isArray(current.routes) && current.routes.length > 0) {
37
+ const index = typeof current.index === "number" ? current.index : 0;
38
+ const route = current.routes[index];
39
+ if (!route) break;
40
+ name = route.name;
41
+ current = route.state;
42
+ }
43
+ return name;
44
+ };
45
+ var createScreenTracker = (options = {}) => {
46
+ let lastScreen;
47
+ return {
48
+ track: (screen) => {
49
+ var _a;
50
+ if (!screen || screen === lastScreen) return;
51
+ lastScreen = screen;
52
+ (_a = options.onScreen) == null ? void 0 : _a.call(options, screen);
53
+ if (options.shouldTrack && !options.shouldTrack(screen)) return;
54
+ reportAppEvent(options.target, "screen", {
55
+ sessionId: options.sessionId,
56
+ deviceKey: options.deviceKey,
57
+ meta: { screen }
58
+ });
59
+ },
60
+ reset: () => {
61
+ lastScreen = void 0;
62
+ }
63
+ };
64
+ };
65
+ var screenTrackingHandler = (tracker) => (state) => tracker.track(getActiveRouteName(state));
66
+ var useScreenTracking = (navigationRef, options = {}) => {
67
+ const trackerRef = react.useRef(void 0);
68
+ if (!trackerRef.current) trackerRef.current = createScreenTracker(options);
69
+ react.useEffect(() => {
70
+ const tracker = trackerRef.current;
71
+ if (!tracker || !(navigationRef == null ? void 0 : navigationRef.addListener)) return;
72
+ const report = () => {
73
+ var _a, _b;
74
+ return tracker.track((_b = (_a = navigationRef.getCurrentRoute) == null ? void 0 : _a.call(navigationRef)) == null ? void 0 : _b.name);
75
+ };
76
+ report();
77
+ const unsubscribe = navigationRef.addListener("state", report);
78
+ return unsubscribe;
79
+ }, [navigationRef]);
80
+ };
81
+
82
+ // src/analytics/reportClientEvent.ts
83
+ var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
84
+ var reportClientEvents = (target, events) => {
85
+ if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return;
86
+ try {
87
+ const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
88
+ const headers = { "Content-Type": "application/json" };
89
+ if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
90
+ void fetch(url, {
91
+ method: "POST",
92
+ headers,
93
+ body: JSON.stringify({ events })
94
+ }).catch(() => {
95
+ });
96
+ } catch {
97
+ }
98
+ };
99
+ var reportClientEvent = (target, event) => reportClientEvents(target, [event]);
100
+
101
+ // src/analytics/analyticsEvent.ts
102
+ var WIRE_ONBOARDING_EVENTS = {
103
+ started: "wire_onboarding_started",
104
+ /** A persisted session was restored after an app kill (fires instead of `started`). */
105
+ resumed: "wire_onboarding_resumed",
106
+ turn: "wire_onboarding_turn",
107
+ error: "wire_onboarding_error",
108
+ retry: "wire_onboarding_retry",
109
+ fallback: "wire_onboarding_fallback",
110
+ /** Logged by the host on `onComplete` (no matching kit `OnboardingEvent`). */
111
+ completed: "wire_onboarding_completed"
112
+ };
113
+ var toAnalyticsEvent = (event) => {
114
+ switch (event.type) {
115
+ case "started":
116
+ return { name: WIRE_ONBOARDING_EVENTS.started };
117
+ case "resumed":
118
+ return { name: WIRE_ONBOARDING_EVENTS.resumed };
119
+ case "turn":
120
+ return {
121
+ name: WIRE_ONBOARDING_EVENTS.turn,
122
+ params: { step: event.step, component: event.component }
123
+ };
124
+ case "error":
125
+ return { name: WIRE_ONBOARDING_EVENTS.error, params: { reason: event.reason } };
126
+ case "retry":
127
+ return {
128
+ name: WIRE_ONBOARDING_EVENTS.retry,
129
+ params: { reason: event.reason, attempt: event.attempt }
130
+ };
131
+ case "fallback":
132
+ return { name: WIRE_ONBOARDING_EVENTS.fallback, params: { reason: event.reason } };
133
+ default: {
134
+ const _exhaustive = event;
135
+ return _exhaustive;
136
+ }
137
+ }
138
+ };
139
+ var deriveFormFactor = (iosIdiom, width, height) => {
140
+ if (iosIdiom === "pad") return "tablet";
141
+ if (iosIdiom === "phone") return "phone";
142
+ if (typeof width === "number" && typeof height === "number") {
143
+ return Math.min(width, height) >= 600 ? "tablet" : "phone";
144
+ }
145
+ return void 0;
146
+ };
147
+ var collectDeviceContext = () => {
148
+ var _a;
149
+ const ctx = { platform: reactNative.Platform.OS };
150
+ try {
151
+ const version = reactNative.Platform.Version;
152
+ if (version !== void 0 && version !== null && String(version)) {
153
+ ctx.osVersion = String(version);
154
+ }
155
+ } catch {
156
+ }
157
+ let constants = {};
158
+ try {
159
+ constants = (_a = reactNative.Platform.constants) != null ? _a : {};
160
+ } catch {
161
+ constants = {};
162
+ }
163
+ let iosIdiom;
164
+ try {
165
+ if (reactNative.Platform.OS === "android") {
166
+ const brand = constants.Brand;
167
+ const model = constants.Model;
168
+ const release = constants.Release;
169
+ if (typeof brand === "string" && brand) ctx.brand = brand;
170
+ if (typeof model === "string" && model) ctx.model = model;
171
+ if (release !== void 0 && release !== null && String(release)) {
172
+ ctx.osVersion = String(release);
173
+ }
174
+ } else if (reactNative.Platform.OS === "ios") {
175
+ const osVersion = constants.osVersion;
176
+ const idiom = constants.interfaceIdiom;
177
+ if (osVersion !== void 0 && osVersion !== null && String(osVersion)) {
178
+ ctx.osVersion = String(osVersion);
179
+ }
180
+ if (typeof idiom === "string" && idiom) {
181
+ ctx.interfaceIdiom = idiom;
182
+ iosIdiom = idiom;
183
+ }
184
+ }
185
+ } catch {
186
+ }
187
+ try {
188
+ const screen = reactNative.Dimensions.get("screen");
189
+ if (screen) {
190
+ if (typeof screen.width === "number") ctx.screenWidth = screen.width;
191
+ if (typeof screen.height === "number") ctx.screenHeight = screen.height;
192
+ if (typeof screen.scale === "number") ctx.screenScale = screen.scale;
193
+ const formFactor = deriveFormFactor(iosIdiom, screen.width, screen.height);
194
+ if (formFactor) ctx.formFactor = formFactor;
195
+ }
196
+ } catch {
197
+ }
198
+ try {
199
+ ctx.isRTL = reactNative.I18nManager.isRTL;
200
+ } catch {
201
+ }
202
+ try {
203
+ const resolved = Intl.DateTimeFormat().resolvedOptions();
204
+ if (resolved.locale) ctx.locale = resolved.locale;
205
+ if (resolved.timeZone) ctx.timeZone = resolved.timeZone;
206
+ } catch {
207
+ }
208
+ return ctx;
209
+ };
210
+
211
+ // src/analytics/contextEnvelope.ts
212
+ var buildContextEnvelope = (input = {}) => {
213
+ const device = { ...collectDeviceContext() };
214
+ if (input.appVersion && !device.appVersion) device.appVersion = input.appVersion;
215
+ const envelope = { device };
216
+ if (input.sessionId) envelope.sessionId = input.sessionId;
217
+ if (input.appVersion) envelope.appVersion = input.appVersion;
218
+ if (input.appBuild) envelope.appBuild = input.appBuild;
219
+ if (input.networkType) envelope.networkType = input.networkType;
220
+ return envelope;
221
+ };
222
+
223
+ // src/analytics/eventQueue.ts
224
+ var DEFAULTS = {
225
+ maxSize: 200,
226
+ batchSize: 20,
227
+ baseBackoffMs: 1e3,
228
+ maxBackoffMs: 3e4,
229
+ maxRetries: 6
230
+ };
231
+ var READ_TIMEOUT_MS = 1500;
232
+ var withTimeout = (p, ms) => {
233
+ let timer;
234
+ const timeout = new Promise((resolve) => {
235
+ timer = setTimeout(() => resolve(void 0), ms);
236
+ });
237
+ return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
238
+ };
239
+ var unrefTimer = (timer) => {
240
+ const t = timer;
241
+ if (typeof t.unref === "function") t.unref();
242
+ };
243
+ var parsePersisted = (raw) => {
244
+ if (!raw) return [];
245
+ try {
246
+ const parsed = JSON.parse(raw);
247
+ if (!Array.isArray(parsed)) return [];
248
+ const items = [];
249
+ for (const entry of parsed) {
250
+ if (entry && typeof entry === "object" && typeof entry.id === "number" && entry.event && typeof entry.event === "object") {
251
+ items.push(entry);
252
+ }
253
+ }
254
+ return items;
255
+ } catch {
256
+ return [];
257
+ }
258
+ };
259
+ var createEventQueue = (options) => {
260
+ var _a, _b, _c, _d, _e, _f, _g;
261
+ const target = options.target;
262
+ const storage = options.storage;
263
+ const key = (_b = options.storageKey) != null ? _b : `wireai:evtq:${(_a = options.appId) != null ? _a : "default"}`;
264
+ const maxSize = (_c = options.maxSize) != null ? _c : DEFAULTS.maxSize;
265
+ const batchSize = (_d = options.batchSize) != null ? _d : DEFAULTS.batchSize;
266
+ const baseBackoffMs = (_e = options.baseBackoffMs) != null ? _e : DEFAULTS.baseBackoffMs;
267
+ const maxBackoffMs = (_f = options.maxBackoffMs) != null ? _f : DEFAULTS.maxBackoffMs;
268
+ const maxRetries = (_g = options.maxRetries) != null ? _g : DEFAULTS.maxRetries;
269
+ let pending = [];
270
+ let nextId = 0;
271
+ let flushing = false;
272
+ let attempt = 0;
273
+ let retryTimer;
274
+ const resolveEnvelope = () => {
275
+ try {
276
+ return typeof options.envelope === "function" ? options.envelope() : options.envelope;
277
+ } catch {
278
+ return void 0;
279
+ }
280
+ };
281
+ const stamp = (event) => {
282
+ var _a2;
283
+ const env = resolveEnvelope();
284
+ const stamped = { ...event };
285
+ if (!env) return stamped;
286
+ if (!stamped.device && env.device) stamped.device = env.device;
287
+ if (!stamped.session_id && env.sessionId) stamped.session_id = env.sessionId;
288
+ const uc = { ...(_a2 = stamped.user_context) != null ? _a2 : {} };
289
+ if (env.appVersion && uc.app_version === void 0) uc.app_version = env.appVersion;
290
+ if (env.appBuild && uc.app_build === void 0) uc.app_build = env.appBuild;
291
+ if (env.networkType && uc.network_type === void 0) uc.network_type = env.networkType;
292
+ if (Object.keys(uc).length > 0) stamped.user_context = uc;
293
+ return stamped;
294
+ };
295
+ const persist = () => {
296
+ if (!storage) return;
297
+ try {
298
+ if (pending.length === 0) {
299
+ void storage.removeItem(key).catch(() => {
300
+ });
301
+ return;
302
+ }
303
+ const payload = pending.map((item) => ({ id: item.id, event: item.event }));
304
+ void storage.setItem(key, JSON.stringify(payload)).catch(() => {
305
+ });
306
+ } catch {
307
+ }
308
+ };
309
+ const enforceSizeCap = () => {
310
+ if (pending.length > maxSize) pending.splice(0, pending.length - maxSize);
311
+ };
312
+ const safeSig = (event) => {
313
+ try {
314
+ return JSON.stringify(event);
315
+ } catch {
316
+ return `__nosig_${nextId}_${Math.random()}`;
317
+ }
318
+ };
319
+ const loadPromise = (async () => {
320
+ if (!storage) return;
321
+ try {
322
+ const persistedItems = parsePersisted(await withTimeout(storage.getItem(key), READ_TIMEOUT_MS));
323
+ if (persistedItems.length === 0) return;
324
+ const events = [...persistedItems.map((p) => p.event), ...pending.map((p) => p.event)];
325
+ pending = [];
326
+ nextId = 0;
327
+ const seen = /* @__PURE__ */ new Set();
328
+ for (const event of events) {
329
+ const sig = safeSig(event);
330
+ if (seen.has(sig)) continue;
331
+ seen.add(sig);
332
+ pending.push({ id: nextId++, event, sig });
333
+ }
334
+ enforceSizeCap();
335
+ persist();
336
+ } catch {
337
+ }
338
+ })();
339
+ const postBatch = async (events) => {
340
+ if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return false;
341
+ try {
342
+ const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
343
+ const headers = { "Content-Type": "application/json" };
344
+ if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
345
+ const res = await fetch(url, {
346
+ method: "POST",
347
+ headers,
348
+ body: JSON.stringify({ events })
349
+ });
350
+ return !!(res && res.ok);
351
+ } catch {
352
+ return false;
353
+ }
354
+ };
355
+ const clearRetry = () => {
356
+ if (retryTimer !== void 0) {
357
+ clearTimeout(retryTimer);
358
+ retryTimer = void 0;
359
+ }
360
+ };
361
+ const scheduleRetry = () => {
362
+ if (attempt >= maxRetries) return;
363
+ const delay = Math.min(baseBackoffMs * 2 ** attempt, maxBackoffMs);
364
+ attempt++;
365
+ clearRetry();
366
+ retryTimer = setTimeout(() => {
367
+ retryTimer = void 0;
368
+ void drain();
369
+ }, delay);
370
+ unrefTimer(retryTimer);
371
+ };
372
+ const drain = async () => {
373
+ try {
374
+ await loadPromise;
375
+ } catch {
376
+ }
377
+ if (flushing) return;
378
+ flushing = true;
379
+ try {
380
+ while (pending.length > 0) {
381
+ const batch = pending.slice(0, batchSize);
382
+ const ok = await postBatch(batch.map((item) => item.event));
383
+ if (!ok) {
384
+ scheduleRetry();
385
+ return;
386
+ }
387
+ const acked = new Set(batch.map((item) => item.id));
388
+ pending = pending.filter((item) => !acked.has(item.id));
389
+ persist();
390
+ attempt = 0;
391
+ clearRetry();
392
+ }
393
+ } finally {
394
+ flushing = false;
395
+ }
396
+ };
397
+ const flush = () => {
398
+ try {
399
+ void drain();
400
+ } catch {
401
+ }
402
+ };
403
+ const enqueue = (event) => {
404
+ try {
405
+ const stamped = stamp(event);
406
+ const sig = safeSig(stamped);
407
+ for (const item of pending) {
408
+ if (item.sig === sig) return;
409
+ }
410
+ pending.push({ id: nextId++, event: stamped, sig });
411
+ enforceSizeCap();
412
+ persist();
413
+ if (retryTimer === void 0) flush();
414
+ } catch {
415
+ }
416
+ };
417
+ const notifyOnline = () => {
418
+ attempt = 0;
419
+ clearRetry();
420
+ flush();
421
+ };
422
+ const size = () => pending.length;
423
+ return { enqueue, flush, notifyOnline, size };
424
+ };
425
+
426
+ exports.WIRE_ONBOARDING_EVENTS = WIRE_ONBOARDING_EVENTS;
427
+ exports.buildContextEnvelope = buildContextEnvelope;
428
+ exports.createEventQueue = createEventQueue;
429
+ exports.createScreenTracker = createScreenTracker;
430
+ exports.getActiveRouteName = getActiveRouteName;
431
+ exports.makeSessionId = makeSessionId;
432
+ exports.reportAppEvent = reportAppEvent;
433
+ exports.reportClientEvent = reportClientEvent;
434
+ exports.reportClientEvents = reportClientEvents;
435
+ exports.screenTrackingHandler = screenTrackingHandler;
436
+ exports.toAnalyticsEvent = toAnalyticsEvent;
437
+ exports.useScreenTracking = useScreenTracking;
438
+ //# sourceMappingURL=index.js.map
439
+ //# sourceMappingURL=index.js.map