@wireai/activation 0.1.1 → 0.3.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/dist/analytics/index.d.mts +70 -3
- package/dist/analytics/index.d.ts +70 -3
- package/dist/analytics/index.js +368 -0
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +365 -1
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/{analyticsEvent-B8v3BZjM.d.mts → eventQueue-CA1d8Fmn.d.mts} +135 -4
- package/dist/{analyticsEvent-DvjB92kK.d.ts → eventQueue-CrNB9gzH.d.ts} +135 -4
- package/dist/index.d.mts +157 -3
- package/dist/index.d.ts +157 -3
- package/dist/index.js +407 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +402 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/analytics/analyticsFacade.ts +171 -0
- package/src/analytics/contextEnvelope.ts +72 -0
- package/src/analytics/eventQueue.ts +331 -0
- package/src/analytics/index.ts +20 -0
- package/src/analytics/reportClientEvent.ts +11 -2
- package/src/analytics/useAnalytics.ts +36 -0
- package/src/index.ts +17 -0
- package/src/session-analytics/index.ts +20 -0
- package/src/session-analytics/lifecycle.ts +236 -0
- package/src/session-analytics/reportSessionStart.ts +27 -7
- package/src/session-analytics/useLifecycleEvents.ts +184 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export { R as ReportAppEventOptions, r as reportAppEvent } from '../transport-BeO_Brcu.mjs';
|
|
2
|
-
|
|
2
|
+
import { E as EventQueueOptions } from '../eventQueue-CA1d8Fmn.mjs';
|
|
3
|
+
export { A as AnalyticsEvent, C as ClientEvent, a as ClientEventTarget, b as ClientEventType, c as ContextEnvelope, d as ContextEnvelopeInput, e as EnvelopeSource, f as EventQueue, 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-CA1d8Fmn.mjs';
|
|
3
4
|
import '../types-A6pTxIZV.mjs';
|
|
4
5
|
import '../types-BKfpdZzX.mjs';
|
|
5
6
|
import '../types-GL_hQ0TN.mjs';
|
|
6
7
|
import '../types-CMuOexw0.mjs';
|
|
7
|
-
import 'react-native';
|
|
8
8
|
import 'react';
|
|
9
9
|
import 'wireai-rn';
|
|
10
|
+
import 'react-native';
|
|
10
11
|
|
|
11
12
|
/** A single route inside a React-Navigation-shaped state (structural — no `@react-navigation`). */
|
|
12
13
|
interface NavigationRouteLike {
|
|
@@ -89,4 +90,70 @@ interface NavigationRefLike {
|
|
|
89
90
|
*/
|
|
90
91
|
declare const useScreenTracking: (navigationRef: NavigationRefLike | undefined, options?: ScreenTrackerOptions) => void;
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
/** Arbitrary non-PII event properties. Serialized to the event's `meta` (a JSON string) on the wire. */
|
|
94
|
+
type AnalyticsProps = Record<string, unknown>;
|
|
95
|
+
/**
|
|
96
|
+
* Tenant transport + context inputs for {@link createAnalytics}. `serverUrl`/`apiKey` are the
|
|
97
|
+
* SAME creds as onboarding (never a second key). The rest feed the context envelope + the queue's
|
|
98
|
+
* offline persistence — all optional.
|
|
99
|
+
*/
|
|
100
|
+
type CreateAnalyticsConfig = {
|
|
101
|
+
/** Base server URL (same as `WireOnboardingConfig.serverUrl`); `/v1/events` is appended. */
|
|
102
|
+
serverUrl: string;
|
|
103
|
+
/** Tenant API key; sent as `Authorization: Bearer`. */
|
|
104
|
+
apiKey: string;
|
|
105
|
+
/**
|
|
106
|
+
* Correlation id shared by every event from this instance (and the `identify` event). Defaults
|
|
107
|
+
* to a fresh `makeSessionId()` at creation so all events agree on one id per analytics instance.
|
|
108
|
+
*/
|
|
109
|
+
sessionId?: string;
|
|
110
|
+
/** Tenant/app id used to namespace the queue's default storage key (`wireai:evtq:<appId>`). */
|
|
111
|
+
appId?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Host persistence (AsyncStorage-compatible subset) for offline-first durability. When omitted,
|
|
114
|
+
* the queue runs in the documented in-memory mode (survives re-renders, not app kills).
|
|
115
|
+
*/
|
|
116
|
+
storage?: EventQueueOptions["storage"];
|
|
117
|
+
/** Host app version, e.g. "1.4.2" (host-injected; stamped onto every event's context). */
|
|
118
|
+
appVersion?: string;
|
|
119
|
+
/** Host native build number, e.g. "412" (host-injected). */
|
|
120
|
+
appBuild?: string;
|
|
121
|
+
/** Host connectivity signal, e.g. "wifi" | "cellular" — read fresh per event via the provider. */
|
|
122
|
+
networkType?: string;
|
|
123
|
+
};
|
|
124
|
+
/** Optional queue tuning knobs, forwarded verbatim to {@link createEventQueue}. */
|
|
125
|
+
type AnalyticsOptions = Partial<Pick<EventQueueOptions, "maxSize" | "batchSize" | "baseBackoffMs" | "maxBackoffMs" | "maxRetries">>;
|
|
126
|
+
/**
|
|
127
|
+
* The developer-facing analytics surface. `track`/`screen`/`identify` are the Segment/PostHog-shaped
|
|
128
|
+
* API; `flush`/`notifyOnline`/`size` expose the underlying queue so a host can drive reconnect
|
|
129
|
+
* draining (load-bearing for offline-first) and inspect the pending buffer.
|
|
130
|
+
*/
|
|
131
|
+
type Analytics = {
|
|
132
|
+
/** Record an in-app event: `event_type='app_event'`, `question_key=<event>`, `props`→`meta`. */
|
|
133
|
+
track(event: string, props?: AnalyticsProps): void;
|
|
134
|
+
/** Record a screen view: `event_type='app_event'`, `question_key='screen'`, `meta={ screen, ...props }`. */
|
|
135
|
+
screen(name: string, props?: AnalyticsProps): void;
|
|
136
|
+
/** Bind the host's opaque user id (per-session, in-memory) and emit an `identify` event. */
|
|
137
|
+
identify(userId: string, traits?: AnalyticsProps): void;
|
|
138
|
+
/** Attempt an immediate drain of the pending buffer. Fire-and-forget. */
|
|
139
|
+
flush(): void;
|
|
140
|
+
/** Host reconnect signal: reset backoff and drain now. Fire-and-forget. */
|
|
141
|
+
notifyOnline(): void;
|
|
142
|
+
/** Current pending (in-memory) count. */
|
|
143
|
+
size(): number;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Create a bound analytics instance. Seeds one correlation `sessionId`, builds an offline-first
|
|
147
|
+
* queue over the tenant transport, and passes a fresh-per-event context envelope PROVIDER so the
|
|
148
|
+
* connectivity type is read at enqueue time. The bound user id starts unset (see `identify`).
|
|
149
|
+
*/
|
|
150
|
+
declare const createAnalytics: (config: CreateAnalyticsConfig, options?: AnalyticsOptions) => Analytics;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Build a per-mount analytics instance. `config`/`options` are read once at first render (the
|
|
154
|
+
* instance is stable for the component's lifetime, held in a ref). Returns the {@link Analytics}
|
|
155
|
+
* surface so the component can `track` / `screen` / `identify` and drive `notifyOnline` on reconnect.
|
|
156
|
+
*/
|
|
157
|
+
declare const useAnalytics: (config: CreateAnalyticsConfig, options?: AnalyticsOptions) => Analytics;
|
|
158
|
+
|
|
159
|
+
export { type Analytics, type AnalyticsOptions, type AnalyticsProps, type CreateAnalyticsConfig, EventQueueOptions, type NavigationRefLike, type NavigationRouteLike, type NavigationStateLike, type ScreenTracker, type ScreenTrackerOptions, createAnalytics, createScreenTracker, getActiveRouteName, screenTrackingHandler, useAnalytics, useScreenTracking };
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export { R as ReportAppEventOptions, r as reportAppEvent } from '../transport-DLpd1v5_.js';
|
|
2
|
-
|
|
2
|
+
import { E as EventQueueOptions } from '../eventQueue-CrNB9gzH.js';
|
|
3
|
+
export { A as AnalyticsEvent, C as ClientEvent, a as ClientEventTarget, b as ClientEventType, c as ContextEnvelope, d as ContextEnvelopeInput, e as EnvelopeSource, f as EventQueue, 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-CrNB9gzH.js';
|
|
3
4
|
import '../types-BhpXJGlg.js';
|
|
4
5
|
import '../types-BKfpdZzX.js';
|
|
5
6
|
import '../types-GL_hQ0TN.js';
|
|
6
7
|
import '../types-CMuOexw0.js';
|
|
7
|
-
import 'react-native';
|
|
8
8
|
import 'react';
|
|
9
9
|
import 'wireai-rn';
|
|
10
|
+
import 'react-native';
|
|
10
11
|
|
|
11
12
|
/** A single route inside a React-Navigation-shaped state (structural — no `@react-navigation`). */
|
|
12
13
|
interface NavigationRouteLike {
|
|
@@ -89,4 +90,70 @@ interface NavigationRefLike {
|
|
|
89
90
|
*/
|
|
90
91
|
declare const useScreenTracking: (navigationRef: NavigationRefLike | undefined, options?: ScreenTrackerOptions) => void;
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
/** Arbitrary non-PII event properties. Serialized to the event's `meta` (a JSON string) on the wire. */
|
|
94
|
+
type AnalyticsProps = Record<string, unknown>;
|
|
95
|
+
/**
|
|
96
|
+
* Tenant transport + context inputs for {@link createAnalytics}. `serverUrl`/`apiKey` are the
|
|
97
|
+
* SAME creds as onboarding (never a second key). The rest feed the context envelope + the queue's
|
|
98
|
+
* offline persistence — all optional.
|
|
99
|
+
*/
|
|
100
|
+
type CreateAnalyticsConfig = {
|
|
101
|
+
/** Base server URL (same as `WireOnboardingConfig.serverUrl`); `/v1/events` is appended. */
|
|
102
|
+
serverUrl: string;
|
|
103
|
+
/** Tenant API key; sent as `Authorization: Bearer`. */
|
|
104
|
+
apiKey: string;
|
|
105
|
+
/**
|
|
106
|
+
* Correlation id shared by every event from this instance (and the `identify` event). Defaults
|
|
107
|
+
* to a fresh `makeSessionId()` at creation so all events agree on one id per analytics instance.
|
|
108
|
+
*/
|
|
109
|
+
sessionId?: string;
|
|
110
|
+
/** Tenant/app id used to namespace the queue's default storage key (`wireai:evtq:<appId>`). */
|
|
111
|
+
appId?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Host persistence (AsyncStorage-compatible subset) for offline-first durability. When omitted,
|
|
114
|
+
* the queue runs in the documented in-memory mode (survives re-renders, not app kills).
|
|
115
|
+
*/
|
|
116
|
+
storage?: EventQueueOptions["storage"];
|
|
117
|
+
/** Host app version, e.g. "1.4.2" (host-injected; stamped onto every event's context). */
|
|
118
|
+
appVersion?: string;
|
|
119
|
+
/** Host native build number, e.g. "412" (host-injected). */
|
|
120
|
+
appBuild?: string;
|
|
121
|
+
/** Host connectivity signal, e.g. "wifi" | "cellular" — read fresh per event via the provider. */
|
|
122
|
+
networkType?: string;
|
|
123
|
+
};
|
|
124
|
+
/** Optional queue tuning knobs, forwarded verbatim to {@link createEventQueue}. */
|
|
125
|
+
type AnalyticsOptions = Partial<Pick<EventQueueOptions, "maxSize" | "batchSize" | "baseBackoffMs" | "maxBackoffMs" | "maxRetries">>;
|
|
126
|
+
/**
|
|
127
|
+
* The developer-facing analytics surface. `track`/`screen`/`identify` are the Segment/PostHog-shaped
|
|
128
|
+
* API; `flush`/`notifyOnline`/`size` expose the underlying queue so a host can drive reconnect
|
|
129
|
+
* draining (load-bearing for offline-first) and inspect the pending buffer.
|
|
130
|
+
*/
|
|
131
|
+
type Analytics = {
|
|
132
|
+
/** Record an in-app event: `event_type='app_event'`, `question_key=<event>`, `props`→`meta`. */
|
|
133
|
+
track(event: string, props?: AnalyticsProps): void;
|
|
134
|
+
/** Record a screen view: `event_type='app_event'`, `question_key='screen'`, `meta={ screen, ...props }`. */
|
|
135
|
+
screen(name: string, props?: AnalyticsProps): void;
|
|
136
|
+
/** Bind the host's opaque user id (per-session, in-memory) and emit an `identify` event. */
|
|
137
|
+
identify(userId: string, traits?: AnalyticsProps): void;
|
|
138
|
+
/** Attempt an immediate drain of the pending buffer. Fire-and-forget. */
|
|
139
|
+
flush(): void;
|
|
140
|
+
/** Host reconnect signal: reset backoff and drain now. Fire-and-forget. */
|
|
141
|
+
notifyOnline(): void;
|
|
142
|
+
/** Current pending (in-memory) count. */
|
|
143
|
+
size(): number;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Create a bound analytics instance. Seeds one correlation `sessionId`, builds an offline-first
|
|
147
|
+
* queue over the tenant transport, and passes a fresh-per-event context envelope PROVIDER so the
|
|
148
|
+
* connectivity type is read at enqueue time. The bound user id starts unset (see `identify`).
|
|
149
|
+
*/
|
|
150
|
+
declare const createAnalytics: (config: CreateAnalyticsConfig, options?: AnalyticsOptions) => Analytics;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Build a per-mount analytics instance. `config`/`options` are read once at first render (the
|
|
154
|
+
* instance is stable for the component's lifetime, held in a ref). Returns the {@link Analytics}
|
|
155
|
+
* surface so the component can `track` / `screen` / `identify` and drive `notifyOnline` on reconnect.
|
|
156
|
+
*/
|
|
157
|
+
declare const useAnalytics: (config: CreateAnalyticsConfig, options?: AnalyticsOptions) => Analytics;
|
|
158
|
+
|
|
159
|
+
export { type Analytics, type AnalyticsOptions, type AnalyticsProps, type CreateAnalyticsConfig, EventQueueOptions, type NavigationRefLike, type NavigationRouteLike, type NavigationStateLike, type ScreenTracker, type ScreenTrackerOptions, createAnalytics, createScreenTracker, getActiveRouteName, screenTrackingHandler, useAnalytics, useScreenTracking };
|
package/dist/analytics/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
|
+
var reactNative = require('react-native');
|
|
4
5
|
|
|
5
6
|
// src/reviews/transport.ts
|
|
6
7
|
var reportAppEvent = (target, name, options = {}) => {
|
|
@@ -135,8 +136,374 @@ var toAnalyticsEvent = (event) => {
|
|
|
135
136
|
}
|
|
136
137
|
}
|
|
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
|
+
// src/identity/userIdentity.ts
|
|
427
|
+
var USER_ID_MAX_LENGTH = 128;
|
|
428
|
+
var sanitizeUserId = (raw) => {
|
|
429
|
+
if (typeof raw !== "string") return void 0;
|
|
430
|
+
const trimmed = raw.trim();
|
|
431
|
+
if (!trimmed) return void 0;
|
|
432
|
+
return trimmed.length > USER_ID_MAX_LENGTH ? trimmed.slice(0, USER_ID_MAX_LENGTH) : trimmed;
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// src/analytics/analyticsFacade.ts
|
|
436
|
+
var createAnalytics = (config, options = {}) => {
|
|
437
|
+
var _a;
|
|
438
|
+
const sessionId = (_a = config.sessionId) != null ? _a : makeSessionId();
|
|
439
|
+
const envelope = () => buildContextEnvelope({
|
|
440
|
+
sessionId,
|
|
441
|
+
appVersion: config.appVersion,
|
|
442
|
+
appBuild: config.appBuild,
|
|
443
|
+
networkType: config.networkType
|
|
444
|
+
});
|
|
445
|
+
const queue = createEventQueue({
|
|
446
|
+
target: { serverUrl: config.serverUrl, apiKey: config.apiKey },
|
|
447
|
+
storage: config.storage,
|
|
448
|
+
appId: config.appId,
|
|
449
|
+
envelope,
|
|
450
|
+
...options
|
|
451
|
+
});
|
|
452
|
+
let boundUserId;
|
|
453
|
+
const track = (event, props) => {
|
|
454
|
+
if (!event) return;
|
|
455
|
+
const clientEvent = {
|
|
456
|
+
event_type: "app_event",
|
|
457
|
+
session_id: sessionId,
|
|
458
|
+
question_key: event
|
|
459
|
+
};
|
|
460
|
+
if (props && Object.keys(props).length > 0) clientEvent.meta = JSON.stringify(props);
|
|
461
|
+
if (boundUserId) clientEvent.user_id = boundUserId;
|
|
462
|
+
queue.enqueue(clientEvent);
|
|
463
|
+
};
|
|
464
|
+
const screen = (name, props) => {
|
|
465
|
+
if (!name) return;
|
|
466
|
+
const meta = { screen: name, ...props != null ? props : {} };
|
|
467
|
+
const clientEvent = {
|
|
468
|
+
event_type: "app_event",
|
|
469
|
+
session_id: sessionId,
|
|
470
|
+
question_key: "screen",
|
|
471
|
+
meta: JSON.stringify(meta)
|
|
472
|
+
};
|
|
473
|
+
if (boundUserId) clientEvent.user_id = boundUserId;
|
|
474
|
+
queue.enqueue(clientEvent);
|
|
475
|
+
};
|
|
476
|
+
const identify = (userId, traits) => {
|
|
477
|
+
const clean = sanitizeUserId(userId);
|
|
478
|
+
if (!clean) return;
|
|
479
|
+
boundUserId = clean;
|
|
480
|
+
const clientEvent = {
|
|
481
|
+
event_type: "identify",
|
|
482
|
+
session_id: sessionId,
|
|
483
|
+
user_id: clean
|
|
484
|
+
};
|
|
485
|
+
if (traits && Object.keys(traits).length > 0) clientEvent.meta = JSON.stringify(traits);
|
|
486
|
+
queue.enqueue(clientEvent);
|
|
487
|
+
};
|
|
488
|
+
return {
|
|
489
|
+
track,
|
|
490
|
+
screen,
|
|
491
|
+
identify,
|
|
492
|
+
flush: queue.flush,
|
|
493
|
+
notifyOnline: queue.notifyOnline,
|
|
494
|
+
size: queue.size
|
|
495
|
+
};
|
|
496
|
+
};
|
|
497
|
+
var useAnalytics = (config, options = {}) => {
|
|
498
|
+
const ref = react.useRef(void 0);
|
|
499
|
+
if (!ref.current) ref.current = createAnalytics(config, options);
|
|
500
|
+
return ref.current;
|
|
501
|
+
};
|
|
138
502
|
|
|
139
503
|
exports.WIRE_ONBOARDING_EVENTS = WIRE_ONBOARDING_EVENTS;
|
|
504
|
+
exports.buildContextEnvelope = buildContextEnvelope;
|
|
505
|
+
exports.createAnalytics = createAnalytics;
|
|
506
|
+
exports.createEventQueue = createEventQueue;
|
|
140
507
|
exports.createScreenTracker = createScreenTracker;
|
|
141
508
|
exports.getActiveRouteName = getActiveRouteName;
|
|
142
509
|
exports.makeSessionId = makeSessionId;
|
|
@@ -145,6 +512,7 @@ exports.reportClientEvent = reportClientEvent;
|
|
|
145
512
|
exports.reportClientEvents = reportClientEvents;
|
|
146
513
|
exports.screenTrackingHandler = screenTrackingHandler;
|
|
147
514
|
exports.toAnalyticsEvent = toAnalyticsEvent;
|
|
515
|
+
exports.useAnalytics = useAnalytics;
|
|
148
516
|
exports.useScreenTracking = useScreenTracking;
|
|
149
517
|
//# sourceMappingURL=index.js.map
|
|
150
518
|
//# sourceMappingURL=index.js.map
|