@mrrocket/sdk 0.2.0 → 0.4.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/CHANGELOG.md +16 -0
- package/README.md +142 -2
- package/dist/batch.d.ts +33 -0
- package/dist/batch.js +50 -0
- package/dist/batch.js.map +1 -0
- package/dist/browser/mrrocket-replay.js +78 -0
- package/dist/browser/mrrocket.js +1 -0
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +3 -1
- package/dist/browser.js.map +1 -1
- package/dist/client.d.ts +23 -3
- package/dist/client.js +44 -83
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +94 -1
- package/dist/core.js +88 -4
- package/dist/core.js.map +1 -1
- package/dist/errors.d.ts +35 -0
- package/dist/errors.js +83 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/react-native/index.d.cts +259 -0
- package/dist/react-native/index.d.ts +259 -0
- package/dist/react-native/index.js +922 -0
- package/dist/react-native/index.js.map +1 -0
- package/dist/react-native/package.json +3 -0
- package/dist/referral.d.ts +6 -0
- package/dist/referral.js +7 -3
- package/dist/referral.js.map +1 -1
- package/dist/server.d.ts +13 -1
- package/dist/server.js +8 -1
- package/dist/server.js.map +1 -1
- package/dist/styles.d.ts +1 -1
- package/dist/styles.js +2 -2
- package/dist/styles.js.map +1 -1
- package/dist/web-state.d.ts +36 -0
- package/dist/web-state.js +64 -0
- package/dist/web-state.js.map +1 -0
- package/package.json +56 -6
- package/react-native/package.json +7 -0
|
@@ -0,0 +1,922 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/native/index.tsx
|
|
21
|
+
var native_exports = {};
|
|
22
|
+
__export(native_exports, {
|
|
23
|
+
MRRocketProvider: () => MRRocketProvider,
|
|
24
|
+
SDK_VERSION: () => SDK_VERSION,
|
|
25
|
+
activeRouteName: () => activeRouteName,
|
|
26
|
+
createMRRocket: () => createMRRocket,
|
|
27
|
+
getMRRocket: () => getMRRocket,
|
|
28
|
+
useExpoRouterTracking: () => useExpoRouterTracking,
|
|
29
|
+
useFlag: () => useFlag,
|
|
30
|
+
useFlagVariant: () => useFlagVariant,
|
|
31
|
+
useMRRocket: () => useMRRocket,
|
|
32
|
+
useNavigationTracking: () => useNavigationTracking,
|
|
33
|
+
useVariant: () => useVariant
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(native_exports);
|
|
36
|
+
var import_react = require("react");
|
|
37
|
+
|
|
38
|
+
// src/core.ts
|
|
39
|
+
var SDK_VERSION = "0.4.0";
|
|
40
|
+
var propOf = (person, name) => {
|
|
41
|
+
const want = name.trim().toLowerCase();
|
|
42
|
+
for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;
|
|
43
|
+
return void 0;
|
|
44
|
+
};
|
|
45
|
+
var same = (actual, value) => {
|
|
46
|
+
const a = actual.trim().toLowerCase();
|
|
47
|
+
const v = value.trim().toLowerCase();
|
|
48
|
+
return v.startsWith("@") ? a.endsWith(v) : a === v;
|
|
49
|
+
};
|
|
50
|
+
var matchesCondition = (c, person) => {
|
|
51
|
+
if (c.op === "in cohort") return false;
|
|
52
|
+
const raw = propOf(person, c.property);
|
|
53
|
+
const actual = raw === void 0 || raw === null ? "" : String(raw);
|
|
54
|
+
if (c.op === "\u2265") {
|
|
55
|
+
const a = Number(actual);
|
|
56
|
+
const b = Number(c.values[0]);
|
|
57
|
+
return actual !== "" && Number.isFinite(a) && Number.isFinite(b) && a >= b;
|
|
58
|
+
}
|
|
59
|
+
const hit = actual !== "" && c.values.some((v) => same(actual, v));
|
|
60
|
+
return c.op === "is" || c.op === "in" ? hit : !hit;
|
|
61
|
+
};
|
|
62
|
+
var evaluateFlag = (flag, distinctId, person = {}) => {
|
|
63
|
+
if (!flag.on) return false;
|
|
64
|
+
const subject = { id: distinctId, ...person };
|
|
65
|
+
const set = flag.sets.length ? flag.sets.find((s) => s.conditions.every((c) => matchesCondition(c, subject))) : { conditions: [], rollout: 100 };
|
|
66
|
+
if (!set) return false;
|
|
67
|
+
if (flagUnit(`${flag.key}:rollout:${distinctId}`) * 100 >= set.rollout) return false;
|
|
68
|
+
if (!flag.variants?.length) return true;
|
|
69
|
+
const u = flagUnit(`${flag.key}:variant:${distinctId}`) * 100;
|
|
70
|
+
let acc = 0;
|
|
71
|
+
for (const v of flag.variants) {
|
|
72
|
+
acc += v.split;
|
|
73
|
+
if (u < acc) return v.key;
|
|
74
|
+
}
|
|
75
|
+
return flag.variants[flag.variants.length - 1].key;
|
|
76
|
+
};
|
|
77
|
+
var flagFor = (key, config, distinctId, person = {}) => {
|
|
78
|
+
const flag = config?.flags?.find((f) => f.key === key);
|
|
79
|
+
return flag ? evaluateFlag(flag, distinctId, person) : false;
|
|
80
|
+
};
|
|
81
|
+
var UTM_KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
|
|
82
|
+
var SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
83
|
+
var utmFrom = (get) => {
|
|
84
|
+
const out = {};
|
|
85
|
+
for (const key of UTM_KEYS) {
|
|
86
|
+
const value = get(key)?.trim();
|
|
87
|
+
if (value) out[key] = value.slice(0, 150);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
};
|
|
91
|
+
var parseQuery = (url) => {
|
|
92
|
+
const out = {};
|
|
93
|
+
const q = url.split("#")[0];
|
|
94
|
+
const at = q.indexOf("?");
|
|
95
|
+
if (at < 0) return out;
|
|
96
|
+
const dec = (s) => {
|
|
97
|
+
try {
|
|
98
|
+
return decodeURIComponent(s.replace(/\+/g, " "));
|
|
99
|
+
} catch {
|
|
100
|
+
return s;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
for (const pair of q.slice(at + 1).split("&")) {
|
|
104
|
+
if (!pair) continue;
|
|
105
|
+
const eq = pair.indexOf("=");
|
|
106
|
+
const k = dec(eq < 0 ? pair : pair.slice(0, eq));
|
|
107
|
+
if (k && !(k in out)) out[k] = eq < 0 ? "" : dec(pair.slice(eq + 1));
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
};
|
|
111
|
+
var hashUnit = (input) => {
|
|
112
|
+
let h = 2166136261;
|
|
113
|
+
for (let i = 0; i < input.length; i++) {
|
|
114
|
+
h ^= input.charCodeAt(i);
|
|
115
|
+
h = Math.imul(h, 16777619);
|
|
116
|
+
}
|
|
117
|
+
return (h >>> 0) / 4294967296;
|
|
118
|
+
};
|
|
119
|
+
var flagUnit = (input) => {
|
|
120
|
+
let h = 2166136261;
|
|
121
|
+
for (let i = 0; i < input.length; i++) {
|
|
122
|
+
h ^= input.charCodeAt(i);
|
|
123
|
+
h = Math.imul(h, 16777619);
|
|
124
|
+
}
|
|
125
|
+
h ^= h >>> 16;
|
|
126
|
+
h = Math.imul(h, 2246822507);
|
|
127
|
+
h ^= h >>> 13;
|
|
128
|
+
h = Math.imul(h, 3266489909);
|
|
129
|
+
h ^= h >>> 16;
|
|
130
|
+
return (h >>> 0) / 4294967296;
|
|
131
|
+
};
|
|
132
|
+
var assign = (experiment, distinctId) => {
|
|
133
|
+
const control = experiment.variants[0]?.id ?? "control";
|
|
134
|
+
if (experiment.winner) return experiment.winner;
|
|
135
|
+
if (experiment.status !== "running") return control;
|
|
136
|
+
const u = hashUnit(`${experiment.key}:${distinctId}`);
|
|
137
|
+
let acc = 0;
|
|
138
|
+
for (const v of experiment.variants) {
|
|
139
|
+
acc += v.weight;
|
|
140
|
+
if (u < acc) return v.id;
|
|
141
|
+
}
|
|
142
|
+
return experiment.variants[experiment.variants.length - 1]?.id ?? control;
|
|
143
|
+
};
|
|
144
|
+
var resolveVariant = (key, config, distinctId, preview) => {
|
|
145
|
+
if (preview[key]) return preview[key];
|
|
146
|
+
const experiment = config?.experiments.find((e) => e.key === key);
|
|
147
|
+
return experiment ? assign(experiment, distinctId) : "control";
|
|
148
|
+
};
|
|
149
|
+
var newDistinctId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
150
|
+
|
|
151
|
+
// src/batch.ts
|
|
152
|
+
var MAX_BATCH = 100;
|
|
153
|
+
var MAX_BYTES = 6e4;
|
|
154
|
+
var byteLength = (s) => {
|
|
155
|
+
let n = 0;
|
|
156
|
+
for (let i = 0; i < s.length; i++) {
|
|
157
|
+
const c = s.charCodeAt(i);
|
|
158
|
+
if (c < 128) n += 1;
|
|
159
|
+
else if (c < 2048) n += 2;
|
|
160
|
+
else if (c >= 55296 && c <= 56319 && i + 1 < s.length && (s.charCodeAt(i + 1) & 64512) === 56320) {
|
|
161
|
+
n += 4;
|
|
162
|
+
i++;
|
|
163
|
+
} else n += 3;
|
|
164
|
+
}
|
|
165
|
+
return n;
|
|
166
|
+
};
|
|
167
|
+
var batchRequests = (key, events, sdk) => {
|
|
168
|
+
const head = `{"key":${JSON.stringify(key)},"sdk":${JSON.stringify(sdk)},"events":[`;
|
|
169
|
+
const empty = byteLength(head) + 2;
|
|
170
|
+
const out = [];
|
|
171
|
+
let chunk = [];
|
|
172
|
+
let items = [];
|
|
173
|
+
let size = empty;
|
|
174
|
+
const close = () => {
|
|
175
|
+
if (chunk.length) out.push({ body: `${head}${chunk.join(",")}]}`, events: items });
|
|
176
|
+
chunk = [];
|
|
177
|
+
items = [];
|
|
178
|
+
size = empty;
|
|
179
|
+
};
|
|
180
|
+
for (const event of events) {
|
|
181
|
+
const json = JSON.stringify(event);
|
|
182
|
+
const n = byteLength(json) + 1;
|
|
183
|
+
if (empty + n > MAX_BYTES) continue;
|
|
184
|
+
if (chunk.length === MAX_BATCH || size + n > MAX_BYTES) close();
|
|
185
|
+
chunk.push(json);
|
|
186
|
+
items.push(event);
|
|
187
|
+
size += n;
|
|
188
|
+
}
|
|
189
|
+
close();
|
|
190
|
+
return out;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/errors.ts
|
|
194
|
+
var MAX_STACK = 8 * 1024;
|
|
195
|
+
var clip = (s, n) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
|
|
196
|
+
var describeError = (error) => {
|
|
197
|
+
if (error instanceof Error) return { type: error.name || "Error", message: clip(error.message || String(error), 1e3), stack: error.stack ? clip(error.stack, MAX_STACK) : void 0 };
|
|
198
|
+
if (typeof error === "string") return { type: "Error", message: clip(error, 1e3) };
|
|
199
|
+
try {
|
|
200
|
+
return { type: "Error", message: clip(JSON.stringify(error) ?? String(error), 1e3) };
|
|
201
|
+
} catch {
|
|
202
|
+
return { type: "Error", message: String(error) };
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// src/referral.ts
|
|
207
|
+
var REF_PARAM = "mrr_ref";
|
|
208
|
+
var CLICK_PARAM = "mrr_click";
|
|
209
|
+
var CODE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
210
|
+
var referralFrom = (get, param = "ref") => {
|
|
211
|
+
const marked = get(REF_PARAM);
|
|
212
|
+
if (marked && CODE.test(marked)) {
|
|
213
|
+
const click = get(CLICK_PARAM);
|
|
214
|
+
return { code: marked, clickId: click && CODE.test(click) ? click : null, direct: false };
|
|
215
|
+
}
|
|
216
|
+
const direct = get(param);
|
|
217
|
+
return direct && CODE.test(direct) ? { code: direct, clickId: null, direct: true } : null;
|
|
218
|
+
};
|
|
219
|
+
var carriesReferral = (event) => event === "$identify" || !event.startsWith("$");
|
|
220
|
+
|
|
221
|
+
// src/native/client.ts
|
|
222
|
+
var DEFAULT_HOST = "https://app.mrrocket.dev";
|
|
223
|
+
var KEYS = {
|
|
224
|
+
id: "mrrocket:id",
|
|
225
|
+
queue: "mrrocket:queue",
|
|
226
|
+
config: "mrrocket:config",
|
|
227
|
+
installed: "mrrocket:installed",
|
|
228
|
+
referral: "mrrocket:referral"
|
|
229
|
+
};
|
|
230
|
+
var MAX_ERRORS = 20;
|
|
231
|
+
var MAX_CRUMBS = 20;
|
|
232
|
+
var MAX_BACKOFF_MS = 5 * 60 * 1e3;
|
|
233
|
+
var REQUEST_TIMEOUT_MS = 15e3;
|
|
234
|
+
var EAGER_COUNT = 20;
|
|
235
|
+
var clip2 = (s, n) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
|
|
236
|
+
var createClient = (options, deps) => {
|
|
237
|
+
const apiKey = (options.apiKey ?? "").trim();
|
|
238
|
+
const host = (options.host ?? DEFAULT_HOST).trim().replace(/\/+$/, "");
|
|
239
|
+
const now = deps.now ?? (() => Date.now());
|
|
240
|
+
const setIntervalFn = deps.setInterval ?? ((fn, ms) => setInterval(fn, ms));
|
|
241
|
+
const clearIntervalFn = deps.clearInterval ?? ((id) => clearInterval(id));
|
|
242
|
+
const setTimeoutFn = deps.setTimeout ?? ((fn, ms) => setTimeout(fn, ms));
|
|
243
|
+
const clearTimeoutFn = deps.clearTimeout ?? ((id) => clearTimeout(id));
|
|
244
|
+
const flushEvery = Math.max(1e3, options.flushIntervalMs ?? 1e4);
|
|
245
|
+
const maxQueue = Math.max(10, options.maxQueue ?? 1e3);
|
|
246
|
+
const errorsOn = options.errors !== false;
|
|
247
|
+
const storage = deps.storage;
|
|
248
|
+
const warned = /* @__PURE__ */ new Set();
|
|
249
|
+
const warn = (message) => {
|
|
250
|
+
if (warned.has(message)) return;
|
|
251
|
+
warned.add(message);
|
|
252
|
+
(deps.log ?? ((m) => console.warn(m)))(`[MRRocket] ${message}`);
|
|
253
|
+
};
|
|
254
|
+
const debug = (message) => {
|
|
255
|
+
if (options.debug) (deps.log ?? ((m) => console.log(m)))(`[MRRocket] ${message}`);
|
|
256
|
+
};
|
|
257
|
+
if (!apiKey) warn("No public key. Pass apiKey to MRRocketProvider; nothing is sent until then.");
|
|
258
|
+
let context = { platform: "unknown" };
|
|
259
|
+
try {
|
|
260
|
+
context = deps.context();
|
|
261
|
+
} catch {
|
|
262
|
+
}
|
|
263
|
+
const appId = (deps.appId || "app").replace(/[^A-Za-z0-9._-]/g, "") || "app";
|
|
264
|
+
const release = options.release ?? (context.appVersion ? `${context.appVersion}${context.build ? ` (${context.build})` : ""}` : void 0);
|
|
265
|
+
const sdk = { v: SDK_VERSION, errors: errorsOn, platform: context.platform };
|
|
266
|
+
let consent = options.consent ?? "anonymous";
|
|
267
|
+
let installId = null;
|
|
268
|
+
let memoryId = newDistinctId();
|
|
269
|
+
let identified;
|
|
270
|
+
let person;
|
|
271
|
+
let config = null;
|
|
272
|
+
let queue = [];
|
|
273
|
+
let session = null;
|
|
274
|
+
let utm = null;
|
|
275
|
+
let referral = null;
|
|
276
|
+
let currentScreen;
|
|
277
|
+
let loaded = false;
|
|
278
|
+
let closed = false;
|
|
279
|
+
let isReady = false;
|
|
280
|
+
let version = 0;
|
|
281
|
+
let failures = 0;
|
|
282
|
+
let backoffUntil = 0;
|
|
283
|
+
let inFlight = null;
|
|
284
|
+
let persistTimer = null;
|
|
285
|
+
let flushTimer = null;
|
|
286
|
+
let errorTimer = null;
|
|
287
|
+
let appState = deps.appState?.currentState ?? "active";
|
|
288
|
+
let backgroundSince = null;
|
|
289
|
+
const early = [];
|
|
290
|
+
const exposed = /* @__PURE__ */ new Set();
|
|
291
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
292
|
+
const crumbs = [];
|
|
293
|
+
const errorQueue = [];
|
|
294
|
+
const errorsSeen = /* @__PURE__ */ new Set();
|
|
295
|
+
let errorsSent = 0;
|
|
296
|
+
const changed = () => {
|
|
297
|
+
version++;
|
|
298
|
+
for (const l of [...listeners]) {
|
|
299
|
+
try {
|
|
300
|
+
l();
|
|
301
|
+
} catch {
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
const crumb = (type, message) => {
|
|
306
|
+
crumbs.push({ ts: now(), type, message: clip2(message, 120) });
|
|
307
|
+
if (crumbs.length > MAX_CRUMBS) crumbs.shift();
|
|
308
|
+
};
|
|
309
|
+
const get = async (key) => {
|
|
310
|
+
if (!storage) return null;
|
|
311
|
+
try {
|
|
312
|
+
return await storage.getItem(key);
|
|
313
|
+
} catch {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
const set = async (key, value) => {
|
|
318
|
+
if (!storage) return;
|
|
319
|
+
try {
|
|
320
|
+
await storage.setItem(key, value);
|
|
321
|
+
} catch {
|
|
322
|
+
warn("Could not write to the device storage; events wait in memory only.");
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
const remove = async (key) => {
|
|
326
|
+
if (!storage) return;
|
|
327
|
+
try {
|
|
328
|
+
await storage.removeItem(key);
|
|
329
|
+
} catch {
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
const persistQueue = async () => {
|
|
333
|
+
if (persistTimer !== null) {
|
|
334
|
+
clearTimeoutFn(persistTimer);
|
|
335
|
+
persistTimer = null;
|
|
336
|
+
}
|
|
337
|
+
if (!storage || consent === "off") return;
|
|
338
|
+
if (queue.length) await set(KEYS.queue, JSON.stringify(queue));
|
|
339
|
+
else await remove(KEYS.queue);
|
|
340
|
+
};
|
|
341
|
+
const schedulePersist = () => {
|
|
342
|
+
if (!storage || persistTimer !== null) return;
|
|
343
|
+
persistTimer = setTimeoutFn(() => {
|
|
344
|
+
persistTimer = null;
|
|
345
|
+
void persistQueue();
|
|
346
|
+
}, 1e3);
|
|
347
|
+
};
|
|
348
|
+
const distinctId = () => identified ?? (consent === "persistent" && installId ? installId : memoryId);
|
|
349
|
+
const sendId = () => consent === "persistent" ? distinctId() : identified;
|
|
350
|
+
const touchSession = () => {
|
|
351
|
+
const t = now();
|
|
352
|
+
if (!session || t - session.last >= SESSION_TIMEOUT_MS) session = { id: newDistinctId(), last: t };
|
|
353
|
+
else session.last = t;
|
|
354
|
+
return session.id;
|
|
355
|
+
};
|
|
356
|
+
const urlOf = (path = currentScreen) => `app://${appId}${path ?? "/"}`;
|
|
357
|
+
const record = (event, props, ts = now()) => {
|
|
358
|
+
if (closed) return;
|
|
359
|
+
if (!loaded) {
|
|
360
|
+
early.push(() => record(event, props, ts));
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (consent === "off" || !apiKey) return;
|
|
364
|
+
const sessionId = touchSession();
|
|
365
|
+
const ref = referral && carriesReferral(event) ? referral : null;
|
|
366
|
+
const size = deps.screen?.();
|
|
367
|
+
queue.push({
|
|
368
|
+
event,
|
|
369
|
+
props: ref ? { ...props, $referral: ref } : props,
|
|
370
|
+
distinctId: sendId(),
|
|
371
|
+
sessionId,
|
|
372
|
+
ts,
|
|
373
|
+
url: urlOf(),
|
|
374
|
+
...utm && utm.session === sessionId ? utm.values : {},
|
|
375
|
+
...size ? { screen: { w: Math.round(size.w), h: Math.round(size.h) } } : {},
|
|
376
|
+
app: context
|
|
377
|
+
});
|
|
378
|
+
if (queue.length > maxQueue) queue.splice(0, queue.length - maxQueue);
|
|
379
|
+
schedulePersist();
|
|
380
|
+
if (queue.length >= EAGER_COUNT) void flush();
|
|
381
|
+
};
|
|
382
|
+
const post = async (path, body) => {
|
|
383
|
+
const ctl = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
384
|
+
const timer = ctl ? setTimeoutFn(() => ctl.abort(), REQUEST_TIMEOUT_MS) : null;
|
|
385
|
+
try {
|
|
386
|
+
const r = await deps.fetch(`${host}${path}`, { method: "POST", headers: { "content-type": "application/json" }, body, signal: ctl?.signal });
|
|
387
|
+
return r.status;
|
|
388
|
+
} catch {
|
|
389
|
+
return 0;
|
|
390
|
+
} finally {
|
|
391
|
+
if (timer !== null) clearTimeoutFn(timer);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
const sendQueue = async () => {
|
|
395
|
+
if (!queue.length || consent === "off" || !apiKey) return;
|
|
396
|
+
if (now() < backoffUntil) return;
|
|
397
|
+
const snapshot = queue.slice();
|
|
398
|
+
const requests = batchRequests(apiKey, snapshot, sdk);
|
|
399
|
+
const included = new Set(requests.flatMap((r) => r.events));
|
|
400
|
+
const done = /* @__PURE__ */ new Set();
|
|
401
|
+
for (const r of requests) {
|
|
402
|
+
const status = await post("/api/sdk/events", r.body);
|
|
403
|
+
if (status >= 200 && status < 300) {
|
|
404
|
+
r.events.forEach((e) => done.add(e));
|
|
405
|
+
failures = 0;
|
|
406
|
+
backoffUntil = 0;
|
|
407
|
+
} else if (status >= 400 && status < 500 && status !== 408 && status !== 429) {
|
|
408
|
+
r.events.forEach((e) => done.add(e));
|
|
409
|
+
warn(`Events were rejected with ${status}. Check the public key (MRRocket \u203A Settings \u203A Products) and the host.`);
|
|
410
|
+
} else {
|
|
411
|
+
failures++;
|
|
412
|
+
backoffUntil = now() + Math.min(MAX_BACKOFF_MS, 1e3 * 2 ** failures);
|
|
413
|
+
debug(`Sending failed (${status || "offline"}); next try in ${Math.round((backoffUntil - now()) / 1e3)} s.`);
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
queue = queue.filter((e) => !done.has(e) && (included.has(e) || !snapshot.includes(e)));
|
|
418
|
+
await persistQueue();
|
|
419
|
+
};
|
|
420
|
+
let nextFlush = null;
|
|
421
|
+
const flush = () => {
|
|
422
|
+
if (inFlight)
|
|
423
|
+
return nextFlush ?? (nextFlush = inFlight.then(() => {
|
|
424
|
+
nextFlush = null;
|
|
425
|
+
return flush();
|
|
426
|
+
}));
|
|
427
|
+
inFlight = sendQueue().finally(() => {
|
|
428
|
+
inFlight = null;
|
|
429
|
+
});
|
|
430
|
+
return inFlight;
|
|
431
|
+
};
|
|
432
|
+
const startTimer = () => {
|
|
433
|
+
if (flushTimer !== null || closed) return;
|
|
434
|
+
flushTimer = setIntervalFn(() => void flush(), flushEvery);
|
|
435
|
+
};
|
|
436
|
+
const stopTimer = () => {
|
|
437
|
+
if (flushTimer === null) return;
|
|
438
|
+
clearIntervalFn(flushTimer);
|
|
439
|
+
flushTimer = null;
|
|
440
|
+
};
|
|
441
|
+
const sendErrors = async () => {
|
|
442
|
+
if (errorTimer !== null) {
|
|
443
|
+
clearTimeoutFn(errorTimer);
|
|
444
|
+
errorTimer = null;
|
|
445
|
+
}
|
|
446
|
+
while (errorQueue.length) {
|
|
447
|
+
const batch = errorQueue.splice(0, 4);
|
|
448
|
+
const status = await post("/api/sdk/errors", JSON.stringify({ key: apiKey, sdk, errors: batch }));
|
|
449
|
+
if (status >= 400 && status < 500 && status !== 429) warn(`Error reports were rejected with ${status}. Check the public key and the host.`);
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
const capture = (error, mechanism, fatal = false) => {
|
|
453
|
+
if (!errorsOn || closed || consent === "off" || !apiKey || errorsSent >= MAX_ERRORS) return;
|
|
454
|
+
const d = describeError(error);
|
|
455
|
+
if (/\/api\/sdk\/errors/.test(d.message)) return;
|
|
456
|
+
const mark = `${d.type}:${d.message}`;
|
|
457
|
+
if (errorsSeen.has(mark)) return;
|
|
458
|
+
errorsSeen.add(mark);
|
|
459
|
+
errorsSent++;
|
|
460
|
+
const who = sendId();
|
|
461
|
+
errorQueue.push({
|
|
462
|
+
...d,
|
|
463
|
+
mechanism,
|
|
464
|
+
handled: mechanism === "manual",
|
|
465
|
+
url: urlOf(),
|
|
466
|
+
ts: now(),
|
|
467
|
+
crumbs: crumbs.slice(),
|
|
468
|
+
...release ? { release } : {},
|
|
469
|
+
...who ? { distinctId: who } : {},
|
|
470
|
+
...session ? { sessionId: session.id } : {}
|
|
471
|
+
});
|
|
472
|
+
if (fatal) void sendErrors();
|
|
473
|
+
else if (errorTimer === null) errorTimer = setTimeoutFn(() => void sendErrors(), 2e3);
|
|
474
|
+
};
|
|
475
|
+
let previousHandler;
|
|
476
|
+
let handlerInstalled = false;
|
|
477
|
+
const installErrorHandler = () => {
|
|
478
|
+
if (!errorsOn || !deps.errorUtils || handlerInstalled) return;
|
|
479
|
+
handlerInstalled = true;
|
|
480
|
+
previousHandler = deps.errorUtils.getGlobalHandler();
|
|
481
|
+
deps.errorUtils.setGlobalHandler((error, isFatal) => {
|
|
482
|
+
try {
|
|
483
|
+
capture(error, "onerror", !!isFatal);
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
previousHandler?.(error, isFatal);
|
|
487
|
+
});
|
|
488
|
+
};
|
|
489
|
+
let appStateSub;
|
|
490
|
+
const onAppState = (next) => {
|
|
491
|
+
if (closed) return;
|
|
492
|
+
const prev = appState;
|
|
493
|
+
appState = next;
|
|
494
|
+
if (next === "active" && prev !== "active") {
|
|
495
|
+
startTimer();
|
|
496
|
+
if (backgroundSince !== null) {
|
|
497
|
+
backgroundSince = null;
|
|
498
|
+
crumb("lifecycle", "app_open");
|
|
499
|
+
if (options.lifecycle !== false) record("$app_open", { cold: false });
|
|
500
|
+
}
|
|
501
|
+
} else if (next === "background" && prev !== "background") {
|
|
502
|
+
backgroundSince = now();
|
|
503
|
+
crumb("lifecycle", "app_background");
|
|
504
|
+
if (options.lifecycle !== false) record("$app_background");
|
|
505
|
+
stopTimer();
|
|
506
|
+
void persistQueue();
|
|
507
|
+
void flush();
|
|
508
|
+
if (errorQueue.length) void sendErrors();
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
const fetchConfig = async () => {
|
|
512
|
+
if (!apiKey) return;
|
|
513
|
+
try {
|
|
514
|
+
const r = await deps.fetch(`${host}/api/sdk/config?key=${encodeURIComponent(apiKey)}`, { method: "GET" });
|
|
515
|
+
if (!r.ok) {
|
|
516
|
+
warn(`Config request answered ${r.status}. Check the public key and the host.`);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const c = await r.json();
|
|
520
|
+
if (c && Array.isArray(c.experiments)) {
|
|
521
|
+
config = c;
|
|
522
|
+
if (consent !== "off") await set(KEYS.config, JSON.stringify(c));
|
|
523
|
+
changed();
|
|
524
|
+
}
|
|
525
|
+
} catch {
|
|
526
|
+
debug("Config not reachable; using the cached one.");
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
let resolveReady = () => {
|
|
530
|
+
};
|
|
531
|
+
const ready = new Promise((r) => {
|
|
532
|
+
resolveReady = r;
|
|
533
|
+
});
|
|
534
|
+
const markReady = () => {
|
|
535
|
+
if (isReady) return;
|
|
536
|
+
isReady = true;
|
|
537
|
+
resolveReady();
|
|
538
|
+
changed();
|
|
539
|
+
};
|
|
540
|
+
const start = async () => {
|
|
541
|
+
let firstOpen = false;
|
|
542
|
+
if (consent !== "off") {
|
|
543
|
+
const [id, stored, cached, installed, ref] = await Promise.all([
|
|
544
|
+
get(KEYS.id),
|
|
545
|
+
get(KEYS.queue),
|
|
546
|
+
get(KEYS.config),
|
|
547
|
+
get(KEYS.installed),
|
|
548
|
+
get(KEYS.referral)
|
|
549
|
+
]);
|
|
550
|
+
if (consent === "persistent") {
|
|
551
|
+
installId = id || newDistinctId();
|
|
552
|
+
if (!id) await set(KEYS.id, installId);
|
|
553
|
+
} else if (id) await remove(KEYS.id);
|
|
554
|
+
try {
|
|
555
|
+
const old = stored ? JSON.parse(stored) : [];
|
|
556
|
+
if (Array.isArray(old)) queue = [...old.filter((e) => e && typeof e.event === "string"), ...queue].slice(-maxQueue);
|
|
557
|
+
} catch {
|
|
558
|
+
await remove(KEYS.queue);
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
const c = cached ? JSON.parse(cached) : null;
|
|
562
|
+
if (c && Array.isArray(c.experiments) && !config) config = c;
|
|
563
|
+
} catch {
|
|
564
|
+
await remove(KEYS.config);
|
|
565
|
+
}
|
|
566
|
+
try {
|
|
567
|
+
const r = ref && consent === "persistent" ? JSON.parse(ref) : null;
|
|
568
|
+
if (r && typeof r.code === "string") referral = r;
|
|
569
|
+
} catch {
|
|
570
|
+
}
|
|
571
|
+
firstOpen = !!storage && !installed;
|
|
572
|
+
if (firstOpen) await set(KEYS.installed, String(now()));
|
|
573
|
+
} else {
|
|
574
|
+
await Promise.all(Object.values(KEYS).map(remove));
|
|
575
|
+
}
|
|
576
|
+
if (closed) return;
|
|
577
|
+
loaded = true;
|
|
578
|
+
if (config) markReady();
|
|
579
|
+
let install = null;
|
|
580
|
+
if (firstOpen) {
|
|
581
|
+
install = {};
|
|
582
|
+
if (options.attribution !== false && deps.initialUrl) {
|
|
583
|
+
const url = await deps.initialUrl().catch(() => null);
|
|
584
|
+
if (url) {
|
|
585
|
+
const q = parseQuery(url);
|
|
586
|
+
const values = utmFrom((k) => q[k]);
|
|
587
|
+
if (Object.keys(values).length) utm = { session: touchSession(), values };
|
|
588
|
+
const found = referralFrom((k) => q[k]);
|
|
589
|
+
if (found) {
|
|
590
|
+
referral = { code: found.code, clickId: found.clickId };
|
|
591
|
+
install.$referral = referral;
|
|
592
|
+
if (consent === "persistent") await set(KEYS.referral, JSON.stringify(referral));
|
|
593
|
+
}
|
|
594
|
+
install.link = clip2(url.split("?")[0], 300);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
crumb("lifecycle", "app_open");
|
|
599
|
+
if (options.lifecycle !== false) record("$app_open", { cold: true });
|
|
600
|
+
if (install) record("$install", install);
|
|
601
|
+
for (const run of early.splice(0)) run();
|
|
602
|
+
if (deps.appState) appStateSub = deps.appState.addEventListener("change", onAppState);
|
|
603
|
+
installErrorHandler();
|
|
604
|
+
if (appState === "active" || !deps.appState) startTimer();
|
|
605
|
+
if (queue.length) void flush();
|
|
606
|
+
await fetchConfig();
|
|
607
|
+
markReady();
|
|
608
|
+
};
|
|
609
|
+
void start().catch(() => {
|
|
610
|
+
loaded = true;
|
|
611
|
+
markReady();
|
|
612
|
+
});
|
|
613
|
+
const client = {
|
|
614
|
+
track: (event, props) => {
|
|
615
|
+
if (typeof event !== "string" || !event) return;
|
|
616
|
+
record(event, props);
|
|
617
|
+
},
|
|
618
|
+
screen: (name, props) => {
|
|
619
|
+
if (typeof name !== "string" || !name.trim()) return;
|
|
620
|
+
const path = (name.startsWith("/") ? name : `/${name}`).slice(0, 300);
|
|
621
|
+
if (path === currentScreen && !props) return;
|
|
622
|
+
const previous = currentScreen;
|
|
623
|
+
currentScreen = path;
|
|
624
|
+
crumb("navigation", path);
|
|
625
|
+
record("$screen", { ...props, path, screen: name, ...previous ? { previous } : {} });
|
|
626
|
+
},
|
|
627
|
+
identify: (id, props) => {
|
|
628
|
+
if (typeof id !== "string" || !id) return;
|
|
629
|
+
if (!loaded) {
|
|
630
|
+
early.push(() => client.identify(id, props));
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
const previous = sendId();
|
|
634
|
+
identified = id;
|
|
635
|
+
person = { ...props, id };
|
|
636
|
+
record("$identify", { ...props, id, ...previous && previous !== id ? { previousId: previous } : {} });
|
|
637
|
+
if (consent === "persistent") void set(KEYS.id, id);
|
|
638
|
+
changed();
|
|
639
|
+
},
|
|
640
|
+
reset: () => {
|
|
641
|
+
if (!loaded) {
|
|
642
|
+
early.push(() => client.reset());
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
identified = void 0;
|
|
646
|
+
person = void 0;
|
|
647
|
+
session = null;
|
|
648
|
+
utm = null;
|
|
649
|
+
exposed.clear();
|
|
650
|
+
memoryId = newDistinctId();
|
|
651
|
+
if (consent === "persistent") {
|
|
652
|
+
installId = newDistinctId();
|
|
653
|
+
void set(KEYS.id, installId);
|
|
654
|
+
}
|
|
655
|
+
changed();
|
|
656
|
+
},
|
|
657
|
+
setConsent: (next) => {
|
|
658
|
+
if (next !== "anonymous" && next !== "persistent" && next !== "off") {
|
|
659
|
+
warn(`setConsent: "${String(next)}" is not a consent mode. Use "anonymous", "persistent" or "off".`);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (!loaded) {
|
|
663
|
+
early.push(() => client.setConsent(next));
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (next === consent) return;
|
|
667
|
+
const before = consent;
|
|
668
|
+
consent = next;
|
|
669
|
+
if (next === "off") {
|
|
670
|
+
queue = [];
|
|
671
|
+
session = null;
|
|
672
|
+
errorQueue.length = 0;
|
|
673
|
+
void Promise.all(Object.values(KEYS).map(remove));
|
|
674
|
+
} else if (next === "anonymous") {
|
|
675
|
+
installId = null;
|
|
676
|
+
void remove(KEYS.id);
|
|
677
|
+
void remove(KEYS.referral);
|
|
678
|
+
} else {
|
|
679
|
+
installId = identified ?? newDistinctId();
|
|
680
|
+
void set(KEYS.id, installId);
|
|
681
|
+
if (referral) void set(KEYS.referral, JSON.stringify(referral));
|
|
682
|
+
}
|
|
683
|
+
if (before === "off" && currentScreen && loaded) record("$screen", { path: currentScreen, screen: currentScreen });
|
|
684
|
+
changed();
|
|
685
|
+
},
|
|
686
|
+
captureError: (error) => capture(error, "manual"),
|
|
687
|
+
getFlag: (key) => flagFor(key, config, distinctId(), { ...person }) !== false,
|
|
688
|
+
getFlagVariant: (key) => {
|
|
689
|
+
const v = flagFor(key, config, distinctId(), { ...person });
|
|
690
|
+
return typeof v === "string" ? v : null;
|
|
691
|
+
},
|
|
692
|
+
getVariant: (key) => {
|
|
693
|
+
const variant = resolveVariant(key, config, distinctId(), {});
|
|
694
|
+
client.expose(key, variant);
|
|
695
|
+
return variant;
|
|
696
|
+
},
|
|
697
|
+
peekVariant: (key) => resolveVariant(key, config, distinctId(), {}),
|
|
698
|
+
expose: (key, variant) => {
|
|
699
|
+
if (!config || consent === "off") return;
|
|
700
|
+
const mark = `${currentScreen ?? "/"}|${key}`;
|
|
701
|
+
if (exposed.has(mark)) return;
|
|
702
|
+
exposed.add(mark);
|
|
703
|
+
record("$exposure", { experiment: key, variant });
|
|
704
|
+
},
|
|
705
|
+
flush,
|
|
706
|
+
ready,
|
|
707
|
+
isReady: () => isReady,
|
|
708
|
+
getDistinctId: distinctId,
|
|
709
|
+
getConsent: () => consent,
|
|
710
|
+
getConfig: () => config,
|
|
711
|
+
subscribe: (listener) => {
|
|
712
|
+
listeners.add(listener);
|
|
713
|
+
return () => listeners.delete(listener);
|
|
714
|
+
},
|
|
715
|
+
getVersion: () => version,
|
|
716
|
+
close: async () => {
|
|
717
|
+
if (closed) return;
|
|
718
|
+
stopTimer();
|
|
719
|
+
appStateSub?.remove();
|
|
720
|
+
if (handlerInstalled && deps.errorUtils && previousHandler) deps.errorUtils.setGlobalHandler(previousHandler);
|
|
721
|
+
await flush();
|
|
722
|
+
await sendErrors();
|
|
723
|
+
await persistQueue();
|
|
724
|
+
closed = true;
|
|
725
|
+
},
|
|
726
|
+
queued: () => queue
|
|
727
|
+
};
|
|
728
|
+
return client;
|
|
729
|
+
};
|
|
730
|
+
var activeRouteName = (state) => {
|
|
731
|
+
let s = state;
|
|
732
|
+
let name;
|
|
733
|
+
for (let depth = 0; s && Array.isArray(s.routes) && s.routes.length && depth < 20; depth++) {
|
|
734
|
+
const route = s.routes[typeof s.index === "number" ? s.index : s.routes.length - 1];
|
|
735
|
+
if (!route) break;
|
|
736
|
+
name = route.name ?? name;
|
|
737
|
+
s = route.state;
|
|
738
|
+
}
|
|
739
|
+
return name;
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
// src/native/platform.ts
|
|
743
|
+
var RN = require("react-native");
|
|
744
|
+
var asyncStorageModule = null;
|
|
745
|
+
try {
|
|
746
|
+
asyncStorageModule = require("@react-native-async-storage/async-storage");
|
|
747
|
+
} catch {
|
|
748
|
+
asyncStorageModule = null;
|
|
749
|
+
}
|
|
750
|
+
var expoDevice = null;
|
|
751
|
+
try {
|
|
752
|
+
expoDevice = require("expo-device");
|
|
753
|
+
} catch {
|
|
754
|
+
expoDevice = null;
|
|
755
|
+
}
|
|
756
|
+
var expoApplication = null;
|
|
757
|
+
try {
|
|
758
|
+
expoApplication = require("expo-application");
|
|
759
|
+
} catch {
|
|
760
|
+
expoApplication = null;
|
|
761
|
+
}
|
|
762
|
+
var expoLocalization = null;
|
|
763
|
+
try {
|
|
764
|
+
expoLocalization = require("expo-localization");
|
|
765
|
+
} catch {
|
|
766
|
+
expoLocalization = null;
|
|
767
|
+
}
|
|
768
|
+
var asyncStorage = () => {
|
|
769
|
+
const m = asyncStorageModule;
|
|
770
|
+
const s = m?.default ?? m;
|
|
771
|
+
return s && typeof s.getItem === "function" && typeof s.setItem === "function" && typeof s.removeItem === "function" ? s : null;
|
|
772
|
+
};
|
|
773
|
+
var DEVICE_TYPES = { 1: "phone", 2: "tablet", 3: "desktop", 4: "tv" };
|
|
774
|
+
var text = (v, max = 80) => typeof v === "string" && v.trim() ? v.trim().slice(0, max) : typeof v === "number" ? String(v) : void 0;
|
|
775
|
+
var readContext = () => {
|
|
776
|
+
const p = RN.Platform;
|
|
777
|
+
let locale;
|
|
778
|
+
try {
|
|
779
|
+
locale = text(expoLocalization?.getLocales?.()[0]?.languageTag, 35);
|
|
780
|
+
} catch {
|
|
781
|
+
locale = void 0;
|
|
782
|
+
}
|
|
783
|
+
if (!locale) {
|
|
784
|
+
try {
|
|
785
|
+
locale = text(Intl.DateTimeFormat().resolvedOptions().locale, 35);
|
|
786
|
+
} catch {
|
|
787
|
+
locale = void 0;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
const ctx = {
|
|
791
|
+
platform: p.OS,
|
|
792
|
+
appVersion: text(expoApplication?.nativeApplicationVersion),
|
|
793
|
+
build: text(expoApplication?.nativeBuildVersion),
|
|
794
|
+
osVersion: text(expoDevice?.osVersion) ?? text(p.Version),
|
|
795
|
+
deviceModel: text(expoDevice?.modelName) ?? text(p.constants?.Model),
|
|
796
|
+
deviceType: (expoDevice?.deviceType ? DEVICE_TYPES[expoDevice.deviceType] : void 0) ?? (p.isPad ? "tablet" : void 0),
|
|
797
|
+
locale
|
|
798
|
+
};
|
|
799
|
+
for (const k of Object.keys(ctx)) if (ctx[k] === void 0) delete ctx[k];
|
|
800
|
+
return ctx;
|
|
801
|
+
};
|
|
802
|
+
var nativeDeps = (overrides = {}) => ({
|
|
803
|
+
storage: overrides.storage !== void 0 ? overrides.storage : asyncStorage(),
|
|
804
|
+
fetch: ((url, init) => fetch(url, init)),
|
|
805
|
+
appState: RN.AppState,
|
|
806
|
+
errorUtils: globalThis.ErrorUtils ?? null,
|
|
807
|
+
context: readContext,
|
|
808
|
+
appId: text(expoApplication?.applicationId, 150),
|
|
809
|
+
screen: () => {
|
|
810
|
+
const s = RN.Dimensions.get("screen");
|
|
811
|
+
return s ? { w: s.width, h: s.height } : void 0;
|
|
812
|
+
},
|
|
813
|
+
initialUrl: () => RN.Linking.getInitialURL()
|
|
814
|
+
});
|
|
815
|
+
var optionalModules = () => ({
|
|
816
|
+
asyncStorage: !!asyncStorage(),
|
|
817
|
+
expoDevice: !!expoDevice,
|
|
818
|
+
expoApplication: !!expoApplication,
|
|
819
|
+
expoLocalization: !!expoLocalization
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
// src/native/index.tsx
|
|
823
|
+
var shared = null;
|
|
824
|
+
var sharedKey = "";
|
|
825
|
+
var createMRRocket = (options) => {
|
|
826
|
+
if (shared) {
|
|
827
|
+
if (sharedKey !== options.apiKey) console.warn("[MRRocket] The SDK is already running with another key; keeping the first one.");
|
|
828
|
+
return shared;
|
|
829
|
+
}
|
|
830
|
+
const { storage, ...rest } = options;
|
|
831
|
+
const deps = nativeDeps({ storage });
|
|
832
|
+
shared = createClient(rest, deps);
|
|
833
|
+
sharedKey = options.apiKey;
|
|
834
|
+
if (options.debug) console.log("[MRRocket] optional modules", JSON.stringify(optionalModules()));
|
|
835
|
+
return shared;
|
|
836
|
+
};
|
|
837
|
+
var getMRRocket = () => shared;
|
|
838
|
+
var Ctx = (0, import_react.createContext)(null);
|
|
839
|
+
var MRRocketProvider = ({ client: given, children, consent, ...options }) => {
|
|
840
|
+
const [client] = (0, import_react.useState)(() => given ?? createMRRocket({ ...options, consent }));
|
|
841
|
+
(0, import_react.useEffect)(() => {
|
|
842
|
+
if (consent) client.setConsent(consent);
|
|
843
|
+
}, [client, consent]);
|
|
844
|
+
return (0, import_react.createElement)(Ctx.Provider, { value: client }, children);
|
|
845
|
+
};
|
|
846
|
+
var useClient = () => {
|
|
847
|
+
const c = (0, import_react.useContext)(Ctx);
|
|
848
|
+
if (!c) throw new Error("MRRocket: wrap your app in <MRRocketProvider apiKey=\u2026>.");
|
|
849
|
+
return c;
|
|
850
|
+
};
|
|
851
|
+
var useVersion = (client) => (0, import_react.useSyncExternalStore)(client.subscribe, client.getVersion, client.getVersion);
|
|
852
|
+
var useMRRocket = () => {
|
|
853
|
+
const client = useClient();
|
|
854
|
+
useVersion(client);
|
|
855
|
+
return {
|
|
856
|
+
track: client.track,
|
|
857
|
+
screen: client.screen,
|
|
858
|
+
identify: client.identify,
|
|
859
|
+
reset: client.reset,
|
|
860
|
+
setConsent: client.setConsent,
|
|
861
|
+
captureError: client.captureError,
|
|
862
|
+
flush: client.flush,
|
|
863
|
+
distinctId: client.getDistinctId(),
|
|
864
|
+
consent: client.getConsent(),
|
|
865
|
+
ready: client.isReady()
|
|
866
|
+
};
|
|
867
|
+
};
|
|
868
|
+
var useFlag = (key) => {
|
|
869
|
+
const client = useClient();
|
|
870
|
+
useVersion(client);
|
|
871
|
+
return client.getFlag(key);
|
|
872
|
+
};
|
|
873
|
+
var useFlagVariant = (key) => {
|
|
874
|
+
const client = useClient();
|
|
875
|
+
useVersion(client);
|
|
876
|
+
return client.getFlagVariant(key);
|
|
877
|
+
};
|
|
878
|
+
var useVariant = (key) => {
|
|
879
|
+
const client = useClient();
|
|
880
|
+
useVersion(client);
|
|
881
|
+
const config = client.getConfig();
|
|
882
|
+
const variant = client.peekVariant(key);
|
|
883
|
+
(0, import_react.useEffect)(() => {
|
|
884
|
+
if (config) client.expose(key, variant);
|
|
885
|
+
}, [client, config, key, variant]);
|
|
886
|
+
return variant;
|
|
887
|
+
};
|
|
888
|
+
var expoRouter;
|
|
889
|
+
var loadExpoRouter = () => {
|
|
890
|
+
if (expoRouter !== void 0) return expoRouter;
|
|
891
|
+
try {
|
|
892
|
+
expoRouter = require("expo-router");
|
|
893
|
+
} catch {
|
|
894
|
+
expoRouter = null;
|
|
895
|
+
}
|
|
896
|
+
return expoRouter;
|
|
897
|
+
};
|
|
898
|
+
var noPathname = () => void 0;
|
|
899
|
+
var routerWarned = false;
|
|
900
|
+
var useExpoRouterTracking = () => {
|
|
901
|
+
const client = useClient();
|
|
902
|
+
const router = loadExpoRouter();
|
|
903
|
+
if (router === null && !routerWarned) {
|
|
904
|
+
routerWarned = true;
|
|
905
|
+
console.warn("[MRRocket] useExpoRouterTracking needs expo-router. With React Navigation use useNavigationTracking.");
|
|
906
|
+
}
|
|
907
|
+
const pathname = (router?.usePathname ?? noPathname)();
|
|
908
|
+
(0, import_react.useEffect)(() => {
|
|
909
|
+
if (pathname) client.screen(pathname);
|
|
910
|
+
}, [client, pathname]);
|
|
911
|
+
};
|
|
912
|
+
var useNavigationTracking = () => {
|
|
913
|
+
const client = useClient();
|
|
914
|
+
return (0, import_react.useCallback)(
|
|
915
|
+
(state) => {
|
|
916
|
+
const name = activeRouteName(state);
|
|
917
|
+
if (name) client.screen(name);
|
|
918
|
+
},
|
|
919
|
+
[client]
|
|
920
|
+
);
|
|
921
|
+
};
|
|
922
|
+
//# sourceMappingURL=index.js.map
|