@oneuptime/react-native-replay 13.0.4

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 (43) hide show
  1. package/OneUptimeReactNativeReplay.podspec +20 -0
  2. package/README.md +264 -0
  3. package/android/build.gradle +36 -0
  4. package/android/consumer-rules.pro +26 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/java/com/oneuptime/replay/OneUptimeReplayPackage.java +29 -0
  7. package/android/src/main/java/com/oneuptime/replay/OneUptimeReplayViewTreeModule.java +541 -0
  8. package/dist/index.cjs +4111 -0
  9. package/dist/index.cjs.map +7 -0
  10. package/dist/index.js +4088 -0
  11. package/dist/index.js.map +7 -0
  12. package/dist/types/ChunkBuffer.d.ts +47 -0
  13. package/dist/types/ChunkBuffer.d.ts.map +1 -0
  14. package/dist/types/Config.d.ts +59 -0
  15. package/dist/types/Config.d.ts.map +1 -0
  16. package/dist/types/Contract.d.ts +176 -0
  17. package/dist/types/Contract.d.ts.map +1 -0
  18. package/dist/types/Events.d.ts +14 -0
  19. package/dist/types/Events.d.ts.map +1 -0
  20. package/dist/types/MobileReplayRecorder.d.ts +176 -0
  21. package/dist/types/MobileReplayRecorder.d.ts.map +1 -0
  22. package/dist/types/NativeViewTree.d.ts +49 -0
  23. package/dist/types/NativeViewTree.d.ts.map +1 -0
  24. package/dist/types/Outbox.d.ts +29 -0
  25. package/dist/types/Outbox.d.ts.map +1 -0
  26. package/dist/types/ReplayProvider.d.ts +28 -0
  27. package/dist/types/ReplayProvider.d.ts.map +1 -0
  28. package/dist/types/Sanitize.d.ts +29 -0
  29. package/dist/types/Sanitize.d.ts.map +1 -0
  30. package/dist/types/Storage.d.ts +30 -0
  31. package/dist/types/Storage.d.ts.map +1 -0
  32. package/dist/types/TouchPrivacy.d.ts +15 -0
  33. package/dist/types/TouchPrivacy.d.ts.map +1 -0
  34. package/dist/types/Transport.d.ts +36 -0
  35. package/dist/types/Transport.d.ts.map +1 -0
  36. package/dist/types/ViewTreeSerializer.d.ts +29 -0
  37. package/dist/types/ViewTreeSerializer.d.ts.map +1 -0
  38. package/dist/types/index.d.ts +12 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/ios/OneUptimeReplayViewTreeModule.m +17 -0
  41. package/ios/OneUptimeReplayViewTreeModule.swift +239 -0
  42. package/package.json +66 -0
  43. package/react-native.config.js +15 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,4111 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
31
+
32
+ // src/index.ts
33
+ var index_exports = {};
34
+ __export(index_exports, {
35
+ MOBILE_RECORDER_KIND: () => MOBILE_RECORDER_KIND,
36
+ MobileReplayRecorder: () => MobileReplayRecorder,
37
+ OneUptimeReplay: () => OneUptimeReplay,
38
+ OneUptimeReplayProvider: () => OneUptimeReplayProvider,
39
+ ReplayMask: () => ReplayMask,
40
+ ReplayProvider: () => ReplayProvider,
41
+ SESSION_REPLAY_MOBILE_RECORDER_CAPABILITIES: () => SESSION_REPLAY_MOBILE_RECORDER_CAPABILITIES,
42
+ SessionReplayCaptureTrigger: () => SessionReplayCaptureTrigger,
43
+ SessionReplayConsentMode: () => SessionReplayConsentMode,
44
+ SessionReplayFidelityNotice: () => SessionReplayFidelityNotice,
45
+ SessionReplayMaskingMode: () => SessionReplayMaskingMode,
46
+ SessionReplayTriggerReason: () => SessionReplayTriggerReason,
47
+ default: () => index_default,
48
+ useOneUptimeReplay: () => useOneUptimeReplay
49
+ });
50
+ module.exports = __toCommonJS(index_exports);
51
+
52
+ // src/ReplayProvider.tsx
53
+ var import_react = require("react");
54
+ var import_react_native3 = require("react-native");
55
+
56
+ // src/MobileReplayRecorder.ts
57
+ var import_react_native2 = require("react-native");
58
+
59
+ // src/Contract.ts
60
+ var SESSION_REPLAY_WIRE_VERSION = 1;
61
+ var SESSION_REPLAY_SCHEMA_VERSION = 1;
62
+ var SESSION_REPLAY_CONTENT_TYPE = "application/vnd.oneuptime.session-replay.v1";
63
+ var SESSION_REPLAY_APP_IDENTIFIER_HEADER = "x-oneuptime-app-identifier";
64
+ var SESSION_REPLAY_RECORDER_KIND_HEADER = "x-oneuptime-replay-recorder-kind";
65
+ var SESSION_REPLAY_MOBILE_APP_IDENTIFIER_HEADER = "x-oneuptime-mobile-app-identifier";
66
+ var SESSION_REPLAY_USER_REF_HEADER = "x-oneuptime-user-ref";
67
+ var SESSION_REPLAY_MAX_USER_REF_LENGTH = 512;
68
+ var AUTH_TOKEN_HEADER = "x-oneuptime-token";
69
+ var CONFIG_PATH = "/telemetry/session-replay/v1/config";
70
+ var CHUNK_PATH = "/telemetry/session-replay/v1/chunk";
71
+ var SESSION_REPLAY_FLUSH_INTERVAL_MS = 15e3;
72
+ var SESSION_REPLAY_FLUSH_BYTES = 256 * 1024;
73
+ var SESSION_REPLAY_CHECKOUT_INTERVAL_MS = 6e4;
74
+ var SESSION_REPLAY_ROLLING_BUFFER_MS = 6e4;
75
+ var SESSION_REPLAY_ROLLING_BUFFER_BYTES = 2 * 1024 * 1024;
76
+ var SESSION_REPLAY_IDLE_ROLLOVER_MS = 30 * 6e4;
77
+ var SESSION_REPLAY_MAX_SESSION_MS = 4 * 60 * 6e4;
78
+ var SESSION_REPLAY_MAX_DECOMPRESSED_FRAME_BYTES = 8 * 1024 * 1024;
79
+ var MAX_SESSION_REPLAY_CHUNK_BYTES = 2 * 1024 * 1024;
80
+ var MAX_SESSION_REPLAY_CHUNKS_PER_SESSION = 480;
81
+ var MOBILE_CAPTURE_INTERVAL_MS = 500;
82
+ var MOBILE_RECORDER_KIND = "rn-view-tree";
83
+ var SYNTHETIC_RRWEB_VERSION = "synthetic-1";
84
+ var RECORDER_VERSION = true ? "13.0.4" : "0.0.0-dev";
85
+ var SESSION_REPLAY_MOBILE_RECORDER_CAPABILITIES = [
86
+ "mobile-view-tree",
87
+ "mobile-touch-events",
88
+ "custom-events",
89
+ "traits",
90
+ "tags",
91
+ "visibility",
92
+ "visitor-id",
93
+ "route-events",
94
+ "js-errors"
95
+ ];
96
+ var SessionReplayMaskingMode = /* @__PURE__ */ ((SessionReplayMaskingMode2) => {
97
+ SessionReplayMaskingMode2["MaskSensitiveInputsOnly"] = "MaskSensitiveInputsOnly";
98
+ SessionReplayMaskingMode2["MaskInputsOnly"] = "MaskInputsOnly";
99
+ SessionReplayMaskingMode2["MaskAllText"] = "MaskAllText";
100
+ return SessionReplayMaskingMode2;
101
+ })(SessionReplayMaskingMode || {});
102
+ var SessionReplayConsentMode = /* @__PURE__ */ ((SessionReplayConsentMode2) => {
103
+ SessionReplayConsentMode2["RequireExplicit"] = "RequireExplicit";
104
+ SessionReplayConsentMode2["NotRequired"] = "NotRequired";
105
+ return SessionReplayConsentMode2;
106
+ })(SessionReplayConsentMode || {});
107
+ var SessionReplayCaptureTrigger = /* @__PURE__ */ ((SessionReplayCaptureTrigger2) => {
108
+ SessionReplayCaptureTrigger2["Always"] = "Always";
109
+ SessionReplayCaptureTrigger2["OnErrorOrFrustration"] = "OnErrorOrFrustration";
110
+ return SessionReplayCaptureTrigger2;
111
+ })(SessionReplayCaptureTrigger || {});
112
+ var SessionReplayTriggerReason = /* @__PURE__ */ ((SessionReplayTriggerReason2) => {
113
+ SessionReplayTriggerReason2["Error"] = "error";
114
+ SessionReplayTriggerReason2["Frustration"] = "frustration";
115
+ SessionReplayTriggerReason2["Sampled"] = "sampled";
116
+ SessionReplayTriggerReason2["Manual"] = "manual";
117
+ SessionReplayTriggerReason2["Performance"] = "performance";
118
+ return SessionReplayTriggerReason2;
119
+ })(SessionReplayTriggerReason || {});
120
+ var SessionReplayFidelityNotice = /* @__PURE__ */ ((SessionReplayFidelityNotice2) => {
121
+ SessionReplayFidelityNotice2["MobileImagesOpaque"] = "mobile-images-opaque";
122
+ SessionReplayFidelityNotice2["MobileWebViewOpaque"] = "mobile-webview-opaque";
123
+ SessionReplayFidelityNotice2["MobileCanvasOpaque"] = "mobile-canvas-opaque";
124
+ SessionReplayFidelityNotice2["MobileAnimationSampled"] = "mobile-animation-sampled";
125
+ SessionReplayFidelityNotice2["BufferOverflow"] = "buffer-overflow";
126
+ SessionReplayFidelityNotice2["SnapshotTooLarge"] = "snapshot-too-large";
127
+ return SessionReplayFidelityNotice2;
128
+ })(SessionReplayFidelityNotice || {});
129
+ var ROUTE_CUSTOM_EVENT_TAG = "oneuptime.route";
130
+ var ERROR_CUSTOM_EVENT_TAG = "oneuptime.error";
131
+ var CUSTOM_EVENT_TAG = "oneuptime.custom";
132
+ var IDENTIFY_CUSTOM_EVENT_TAG = "oneuptime.identify";
133
+ var TAGS_CUSTOM_EVENT_TAG = "oneuptime.tags";
134
+ var VISIBILITY_CUSTOM_EVENT_TAG = "oneuptime.visibility";
135
+ var TOUCH_CUSTOM_EVENT_TAG = "oneuptime.touch";
136
+
137
+ // src/Sanitize.ts
138
+ var MAX_TRAIT_KEYS = 20;
139
+ var MAX_TRAIT_KEY_LENGTH = 40;
140
+ var MAX_TRAIT_VALUE_LENGTH = 200;
141
+ var MAX_TAG_KEYS = 20;
142
+ var MAX_TAG_KEY_LENGTH = 32;
143
+ var MAX_TAG_VALUE_LENGTH = 128;
144
+ var MAX_CUSTOM_EVENT_NAME_LENGTH = 64;
145
+ var MAX_CUSTOM_EVENT_PROPERTY_KEYS = 20;
146
+ var MAX_CAPTURE_REASON_LENGTH = 80;
147
+ var MAX_USER_REFERENCE_LENGTH = 200;
148
+ var BLOCKED_KEYS = /* @__PURE__ */ new Set([
149
+ "__proto__",
150
+ "constructor",
151
+ "prototype"
152
+ ]);
153
+ var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
154
+ var OBJECT_ID_SEGMENT = /^[0-9a-f]{24}$/iu;
155
+ var EMAIL_SEGMENT = /^[^@\s/]+@[^@\s/]+\.[^@\s/]+$/u;
156
+ var LONG_DIGIT_SEGMENT = /^\d{9,}$/u;
157
+ var OPAQUE_TOKEN_SEGMENT = /^[A-Za-z0-9_-]{32,}$/u;
158
+ var ABSOLUTE_URL_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
159
+ var CONTROL_CHARACTER_PATTERN = /[\u0000-\u001F\u007F]/gu;
160
+ function truncate(value, length) {
161
+ return value.slice(0, Math.max(0, length));
162
+ }
163
+ function stringifyPrimitive(value) {
164
+ switch (typeof value) {
165
+ case "string":
166
+ return value;
167
+ case "number":
168
+ return Number.isFinite(value) ? String(value) : null;
169
+ case "boolean":
170
+ return value ? "true" : "false";
171
+ default:
172
+ return value === null ? "null" : null;
173
+ }
174
+ }
175
+ function sanitizeStringMap(value, limits) {
176
+ const result = /* @__PURE__ */ Object.create(null);
177
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
178
+ return result;
179
+ }
180
+ for (const [rawKey, rawValue] of Object.entries(
181
+ value
182
+ )) {
183
+ if (Object.keys(result).length >= limits.keys) {
184
+ break;
185
+ }
186
+ const key = truncate(rawKey.trim(), limits.keyLength);
187
+ const stringValue = stringifyPrimitive(rawValue);
188
+ if (!key || BLOCKED_KEYS.has(key) || stringValue === null) {
189
+ continue;
190
+ }
191
+ result[key] = truncate(stringValue, limits.valueLength);
192
+ }
193
+ return result;
194
+ }
195
+ function sanitizeTraits(value) {
196
+ return sanitizeStringMap(value, {
197
+ keys: MAX_TRAIT_KEYS,
198
+ keyLength: MAX_TRAIT_KEY_LENGTH,
199
+ valueLength: MAX_TRAIT_VALUE_LENGTH
200
+ });
201
+ }
202
+ function sanitizeTags(value) {
203
+ return sanitizeStringMap(value, {
204
+ keys: MAX_TAG_KEYS,
205
+ keyLength: MAX_TAG_KEY_LENGTH,
206
+ valueLength: MAX_TAG_VALUE_LENGTH
207
+ });
208
+ }
209
+ function sanitizeEventProperties(value) {
210
+ return sanitizeStringMap(value, {
211
+ keys: MAX_CUSTOM_EVENT_PROPERTY_KEYS,
212
+ keyLength: MAX_TRAIT_KEY_LENGTH,
213
+ valueLength: MAX_TRAIT_VALUE_LENGTH
214
+ });
215
+ }
216
+ function maskTextValue(value) {
217
+ if (!value || value.trim().length === 0) {
218
+ return value;
219
+ }
220
+ const length = value.trim().length;
221
+ const maskedLength = length <= 8 ? 4 : length <= 32 ? 16 : 40;
222
+ return "\u2022".repeat(maskedLength);
223
+ }
224
+ function maskStringMapValues(values) {
225
+ const masked = /* @__PURE__ */ Object.create(null);
226
+ for (const [key, value] of Object.entries(values)) {
227
+ masked[key] = maskTextValue(value);
228
+ }
229
+ return masked;
230
+ }
231
+ function sanitizeRoute(route) {
232
+ if (typeof route !== "string") {
233
+ return "/";
234
+ }
235
+ let path = route.trim();
236
+ if (!path) {
237
+ return "/";
238
+ }
239
+ try {
240
+ if (ABSOLUTE_URL_PATTERN.test(path)) {
241
+ path = new URL(path).pathname;
242
+ }
243
+ } catch {
244
+ path = "/";
245
+ }
246
+ path = path.split(/[?#]/u, 1)[0] || "/";
247
+ if (!path.startsWith("/")) {
248
+ path = `/${path}`;
249
+ }
250
+ path = path.split("/").map((segment) => {
251
+ let decoded = segment;
252
+ try {
253
+ decoded = decodeURIComponent(segment);
254
+ } catch {
255
+ decoded = segment;
256
+ }
257
+ return UUID_SEGMENT.test(decoded) || OBJECT_ID_SEGMENT.test(decoded) || EMAIL_SEGMENT.test(decoded) || LONG_DIGIT_SEGMENT.test(decoded) || OPAQUE_TOKEN_SEGMENT.test(decoded) ? "[redacted]" : segment;
258
+ }).join("/");
259
+ return truncate(path.replace(CONTROL_CHARACTER_PATTERN, ""), 2048);
260
+ }
261
+ function toAppUrl(mobileAppIdentifier, route) {
262
+ return `app://${mobileAppIdentifier}${sanitizeRoute(route)}`;
263
+ }
264
+ function emptySignalCounts() {
265
+ return {
266
+ errorCount: 0,
267
+ rageClickCount: 0,
268
+ deadClickCount: 0,
269
+ errorClickCount: 0,
270
+ refreshRageCount: 0,
271
+ routeCount: 0,
272
+ clickCount: 0,
273
+ customEventCount: 0
274
+ };
275
+ }
276
+ function byteLength(value) {
277
+ if (typeof TextEncoder !== "undefined") {
278
+ return new TextEncoder().encode(value).byteLength;
279
+ }
280
+ return unescape(encodeURIComponent(value)).length;
281
+ }
282
+
283
+ // src/ChunkBuffer.ts
284
+ var ReplayChunkBuffer = class {
285
+ constructor() {
286
+ __publicField(this, "events", []);
287
+ __publicField(this, "approximateBytes", 2);
288
+ __publicField(this, "startUnixMs", 0);
289
+ __publicField(this, "endUnixMs", 0);
290
+ __publicField(this, "hasFullSnapshot", false);
291
+ __publicField(this, "routes", []);
292
+ __publicField(this, "fidelity", /* @__PURE__ */ new Set());
293
+ __publicField(this, "signals", emptySignalCounts());
294
+ __publicField(this, "droppedEvents", 0);
295
+ }
296
+ append(event, options) {
297
+ let serialized;
298
+ try {
299
+ serialized = JSON.stringify(event);
300
+ } catch {
301
+ this.droppedEvents += 1;
302
+ return false;
303
+ }
304
+ const eventBytes = byteLength(serialized) + 1;
305
+ if (eventBytes > SESSION_REPLAY_MAX_DECOMPRESSED_FRAME_BYTES) {
306
+ this.droppedEvents += 1;
307
+ this.fidelity.add("snapshot-too-large" /* SnapshotTooLarge */);
308
+ return false;
309
+ }
310
+ this.events.push(event);
311
+ this.approximateBytes += eventBytes;
312
+ this.startUnixMs = this.startUnixMs === 0 ? event.timestamp : this.startUnixMs;
313
+ this.endUnixMs = Math.max(this.endUnixMs, event.timestamp);
314
+ if (event.type === 2 /* FullSnapshot */) {
315
+ this.hasFullSnapshot = true;
316
+ }
317
+ if (options?.route && !this.routes.includes(options.route)) {
318
+ this.routes.push(options.route);
319
+ }
320
+ for (const notice of options?.fidelityNotices ?? []) {
321
+ this.fidelity.add(notice);
322
+ }
323
+ if (options?.signal) {
324
+ const current = this.signals[options.signal] ?? 0;
325
+ this.signals[options.signal] = current + 1;
326
+ }
327
+ return true;
328
+ }
329
+ appendMany(events, options) {
330
+ let accepted = 0;
331
+ for (const event of events) {
332
+ if (this.append(event, options)) {
333
+ accepted += 1;
334
+ }
335
+ }
336
+ return accepted;
337
+ }
338
+ shouldFlush() {
339
+ return this.approximateBytes >= SESSION_REPLAY_FLUSH_BYTES;
340
+ }
341
+ isEmpty() {
342
+ return this.events.length === 0;
343
+ }
344
+ take() {
345
+ if (this.events.length === 0) {
346
+ return null;
347
+ }
348
+ const payload = JSON.stringify(this.events);
349
+ const segment = {
350
+ payload,
351
+ eventCount: this.events.length,
352
+ startUnixMs: this.startUnixMs,
353
+ endUnixMs: this.endUnixMs,
354
+ hasFullSnapshot: this.hasFullSnapshot,
355
+ routes: this.routes.slice(),
356
+ signals: { ...this.signals },
357
+ fidelityNotices: Array.from(this.fidelity),
358
+ droppedEvents: this.droppedEvents
359
+ };
360
+ this.reset();
361
+ return segment;
362
+ }
363
+ clear() {
364
+ this.reset();
365
+ }
366
+ reset() {
367
+ this.events = [];
368
+ this.approximateBytes = 2;
369
+ this.startUnixMs = 0;
370
+ this.endUnixMs = 0;
371
+ this.hasFullSnapshot = false;
372
+ this.routes = [];
373
+ this.fidelity.clear();
374
+ this.signals = emptySignalCounts();
375
+ this.droppedEvents = 0;
376
+ }
377
+ };
378
+ var RollingReplayBuffer = class {
379
+ constructor() {
380
+ __publicField(this, "segments", []);
381
+ __publicField(this, "bytes", 0);
382
+ __publicField(this, "overflowed", false);
383
+ }
384
+ push(segment, nowUnixMs) {
385
+ this.segments.push(segment);
386
+ this.bytes += byteLength(segment.payload);
387
+ const cutoff = nowUnixMs - SESSION_REPLAY_ROLLING_BUFFER_MS;
388
+ while (this.segments.length > 0 && (this.bytes > SESSION_REPLAY_ROLLING_BUFFER_BYTES || (this.segments[0]?.endUnixMs ?? nowUnixMs) < cutoff)) {
389
+ const removed = this.segments.shift();
390
+ if (removed) {
391
+ this.bytes -= byteLength(removed.payload);
392
+ this.overflowed = true;
393
+ }
394
+ }
395
+ }
396
+ drain() {
397
+ const segments = this.segments;
398
+ if (this.overflowed && segments[0]) {
399
+ const notices = new Set(
400
+ segments[0].fidelityNotices
401
+ );
402
+ notices.add("buffer-overflow" /* BufferOverflow */);
403
+ segments[0] = {
404
+ ...segments[0],
405
+ fidelityNotices: Array.from(notices)
406
+ };
407
+ }
408
+ this.clear();
409
+ return segments;
410
+ }
411
+ clear() {
412
+ this.segments = [];
413
+ this.bytes = 0;
414
+ this.overflowed = false;
415
+ }
416
+ get length() {
417
+ return this.segments.length;
418
+ }
419
+ };
420
+
421
+ // src/Config.ts
422
+ var CONFIG_FETCH_TIMEOUT_MS = 5e3;
423
+ var MOBILE_APP_IDENTIFIER_LABEL_PATTERN = /^[a-z0-9_-]+$/u;
424
+ var WHITESPACE_PATTERN = /\s/u;
425
+ var CONTROL_CHARACTER_PATTERN2 = /[\u0000-\u001F\u007F]/u;
426
+ function normalizeMobileAppIdentifier(value) {
427
+ if (typeof value !== "string") {
428
+ return null;
429
+ }
430
+ const identifier = value.trim().toLowerCase();
431
+ if (!identifier || identifier.length > 255 || WHITESPACE_PATTERN.test(identifier)) {
432
+ return null;
433
+ }
434
+ const labels = identifier.split(".");
435
+ if (labels.length < 2) {
436
+ return null;
437
+ }
438
+ for (const label of labels) {
439
+ if (!label || !MOBILE_APP_IDENTIFIER_LABEL_PATTERN.test(label) || label.startsWith("-") || label.endsWith("-") || label.startsWith("_") || label.endsWith("_")) {
440
+ return null;
441
+ }
442
+ }
443
+ return identifier;
444
+ }
445
+ function normalizeIngestHost(value) {
446
+ if (typeof value !== "string") {
447
+ return null;
448
+ }
449
+ try {
450
+ const parsed = new URL(value.trim());
451
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
452
+ return null;
453
+ }
454
+ return parsed.origin;
455
+ } catch {
456
+ return null;
457
+ }
458
+ }
459
+ function normalizeRumAppIdentifier(value) {
460
+ if (typeof value !== "string") {
461
+ return null;
462
+ }
463
+ const identifier = value.trim();
464
+ if (!identifier || identifier.length > 255 || CONTROL_CHARACTER_PATTERN2.test(identifier)) {
465
+ return null;
466
+ }
467
+ return identifier;
468
+ }
469
+ function defaultFetch(input, init) {
470
+ const fetchRef = globalThis.fetch;
471
+ if (!fetchRef) {
472
+ return Promise.reject(new Error("fetch-unavailable"));
473
+ }
474
+ return fetchRef(input, init);
475
+ }
476
+ function validateStartOptions(options) {
477
+ if (!options || typeof options !== "object") {
478
+ return null;
479
+ }
480
+ const host = normalizeIngestHost(options.host);
481
+ const token = typeof options.token === "string" ? options.token.trim() : "";
482
+ const appIdentifier = normalizeRumAppIdentifier(
483
+ options.appIdentifier
484
+ );
485
+ const mobileAppIdentifier = normalizeMobileAppIdentifier(
486
+ options.mobileAppIdentifier
487
+ );
488
+ const rawUserRef = typeof options.userRef === "string" ? options.userRef.trim() : "";
489
+ const userRef = rawUserRef ? rawUserRef.slice(0, SESSION_REPLAY_MAX_USER_REF_LENGTH) : void 0;
490
+ if (!host || !token || !appIdentifier || !mobileAppIdentifier) {
491
+ return null;
492
+ }
493
+ const validated = {
494
+ ...options,
495
+ host,
496
+ token,
497
+ appIdentifier,
498
+ mobileAppIdentifier,
499
+ fetch: options.fetch ?? defaultFetch
500
+ };
501
+ if (userRef) {
502
+ validated.userRef = userRef;
503
+ } else {
504
+ delete validated.userRef;
505
+ }
506
+ return validated;
507
+ }
508
+ function mobileRequestHeaders(options, includeUserRef = false) {
509
+ const headers = {
510
+ [AUTH_TOKEN_HEADER]: options.token,
511
+ [SESSION_REPLAY_APP_IDENTIFIER_HEADER]: options.appIdentifier,
512
+ [SESSION_REPLAY_RECORDER_KIND_HEADER]: MOBILE_RECORDER_KIND,
513
+ [SESSION_REPLAY_MOBILE_APP_IDENTIFIER_HEADER]: options.mobileAppIdentifier
514
+ };
515
+ if (includeUserRef && options.userRef) {
516
+ try {
517
+ headers[SESSION_REPLAY_USER_REF_HEADER] = encodeURIComponent(
518
+ options.userRef.slice(0, SESSION_REPLAY_MAX_USER_REF_LENGTH)
519
+ );
520
+ } catch {
521
+ }
522
+ }
523
+ return headers;
524
+ }
525
+ function failClosed(reason) {
526
+ return {
527
+ enabled: false,
528
+ captureTrigger: "OnErrorOrFrustration" /* OnErrorOrFrustration */,
529
+ consentMode: "RequireExplicit" /* RequireExplicit */,
530
+ maskingMode: "MaskAllText" /* MaskAllText */,
531
+ samplePercentage: 0,
532
+ captureUserIdentity: false,
533
+ configEpoch: 0,
534
+ isTargeted: false,
535
+ disabledReason: reason
536
+ };
537
+ }
538
+ function normalizeReplayConfig(value) {
539
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
540
+ return failClosed("invalid-config");
541
+ }
542
+ const config = value;
543
+ const captureTrigger = config.captureTrigger === "Always" /* Always */ ? "Always" /* Always */ : "OnErrorOrFrustration" /* OnErrorOrFrustration */;
544
+ const consentMode = config.consentMode === "NotRequired" /* NotRequired */ ? "NotRequired" /* NotRequired */ : "RequireExplicit" /* RequireExplicit */;
545
+ const samplePercentage = typeof config.samplePercentage === "number" && Number.isFinite(config.samplePercentage) && config.samplePercentage >= 0 && config.samplePercentage <= 100 ? config.samplePercentage : 0;
546
+ const enabled = config.enabled === true && config.directive === "continue";
547
+ const resolved = {
548
+ enabled,
549
+ captureTrigger,
550
+ consentMode,
551
+ /* Mobile never relaxes text masking, even if web policy is looser. */
552
+ maskingMode: "MaskAllText" /* MaskAllText */,
553
+ samplePercentage,
554
+ captureUserIdentity: config.captureUserIdentity === true,
555
+ configEpoch: typeof config.configEpoch === "number" && Number.isSafeInteger(config.configEpoch) && config.configEpoch >= 0 ? config.configEpoch : 0,
556
+ isTargeted: config.isTargeted === true
557
+ };
558
+ if (typeof config.disabledReason === "string") {
559
+ resolved.disabledReason = config.disabledReason.slice(0, 100);
560
+ } else if (!enabled) {
561
+ resolved.disabledReason = "disabled";
562
+ }
563
+ return resolved;
564
+ }
565
+ async function fetchReplayConfig(options, includeUserRef = false, revalidate = false) {
566
+ const controller = typeof AbortController === "undefined" ? null : new AbortController();
567
+ let timeout = null;
568
+ try {
569
+ const request = (async () => {
570
+ const headers = {
571
+ ...mobileRequestHeaders(options, includeUserRef),
572
+ Accept: "application/json"
573
+ };
574
+ if (revalidate) {
575
+ headers["Cache-Control"] = "no-cache";
576
+ headers["Pragma"] = "no-cache";
577
+ }
578
+ const requestInit = {
579
+ method: "GET",
580
+ headers
581
+ };
582
+ if (controller) {
583
+ requestInit.signal = controller.signal;
584
+ }
585
+ const response = await options.fetch(
586
+ `${options.host}${CONFIG_PATH}`,
587
+ requestInit
588
+ );
589
+ if (!response.ok) {
590
+ return failClosed(`config-http-${response.status}`);
591
+ }
592
+ return normalizeReplayConfig(await response.json());
593
+ })();
594
+ const deadline = new Promise(
595
+ (_resolve, reject) => {
596
+ timeout = setTimeout(() => {
597
+ controller?.abort();
598
+ reject(new Error("config-fetch-timeout"));
599
+ }, CONFIG_FETCH_TIMEOUT_MS);
600
+ }
601
+ );
602
+ return await Promise.race([request, deadline]);
603
+ } catch {
604
+ return failClosed("config-fetch-failed");
605
+ } finally {
606
+ if (timeout) {
607
+ clearTimeout(timeout);
608
+ }
609
+ }
610
+ }
611
+
612
+ // src/ViewTreeSerializer.ts
613
+ var RRWEB_DOCUMENT_NODE_ID = 1;
614
+ var RRWEB_HTML_NODE_ID = 3;
615
+ var RRWEB_HEAD_NODE_ID = 4;
616
+ var RRWEB_BODY_NODE_ID = 5;
617
+ var FIRST_VIEW_NODE_ID = 10;
618
+ var MAX_VIEW_TREE_DEPTH = 64;
619
+ var MAX_VIEW_TREE_NODES = 5e3;
620
+ var MAX_GEOMETRY_VALUE = 1e5;
621
+ var SAFE_KINDS = /* @__PURE__ */ new Set([
622
+ "view",
623
+ "scroll",
624
+ "button",
625
+ "text",
626
+ "input",
627
+ "image",
628
+ "webview",
629
+ "canvas",
630
+ "masked",
631
+ "unknown"
632
+ ]);
633
+ function finiteGeometry(value) {
634
+ if (typeof value !== "number" || !Number.isFinite(value)) {
635
+ return 0;
636
+ }
637
+ return Math.min(MAX_GEOMETRY_VALUE, Math.max(-MAX_GEOMETRY_VALUE, value));
638
+ }
639
+ function safeKind(value) {
640
+ if (typeof value === "string" && SAFE_KINDS.has(value)) {
641
+ return value;
642
+ }
643
+ return "unknown";
644
+ }
645
+ function geometryStyle(node) {
646
+ const x2 = finiteGeometry(node.x);
647
+ const y = finiteGeometry(node.y);
648
+ const width = Math.max(0, finiteGeometry(node.width));
649
+ const height = Math.max(0, finiteGeometry(node.height));
650
+ const style = [
651
+ "position:absolute",
652
+ `left:${x2}px`,
653
+ `top:${y}px`,
654
+ `width:${width}px`,
655
+ `height:${height}px`,
656
+ "box-sizing:border-box",
657
+ "overflow:hidden",
658
+ "outline:1px solid rgba(148,163,184,0.18)"
659
+ ];
660
+ const backgroundColor = safeColor(node.backgroundColor);
661
+ const borderColor = safeColor(node.borderColor);
662
+ const borderWidth = clampedStyleNumber(node.borderWidth, 0, 100, 0);
663
+ const borderRadius = clampedStyleNumber(
664
+ node.borderRadius,
665
+ 0,
666
+ MAX_GEOMETRY_VALUE,
667
+ 0
668
+ );
669
+ const opacity = clampedStyleNumber(node.opacity, 0, 1, 1);
670
+ const zIndex = clampedStyleNumber(node.zIndex, -1e4, 1e4, 0);
671
+ if (backgroundColor) {
672
+ style.push(`background-color:${backgroundColor}`);
673
+ }
674
+ if (borderColor && borderWidth > 0) {
675
+ style.push(`border:${borderWidth}px solid ${borderColor}`);
676
+ }
677
+ if (borderRadius > 0) {
678
+ style.push(`border-radius:${borderRadius}px`);
679
+ }
680
+ if (opacity < 1) {
681
+ style.push(`opacity:${opacity}`);
682
+ }
683
+ if (zIndex !== 0) {
684
+ style.push(`z-index:${zIndex}`);
685
+ }
686
+ return style.join(";");
687
+ }
688
+ var HEX_COLOR_PATTERN = /^#[0-9a-f]{6}([0-9a-f]{2})?$/u;
689
+ function safeColor(value) {
690
+ if (typeof value !== "string") {
691
+ return null;
692
+ }
693
+ const normalized = value.trim().toLowerCase();
694
+ return HEX_COLOR_PATTERN.test(normalized) ? normalized : null;
695
+ }
696
+ function clampedStyleNumber(value, minimum, maximum, fallback) {
697
+ if (typeof value !== "number" || !Number.isFinite(value)) {
698
+ return fallback;
699
+ }
700
+ return Math.max(minimum, Math.min(maximum, value));
701
+ }
702
+ function cloneNode(node) {
703
+ return {
704
+ ...node,
705
+ attributes: { ...node.attributes },
706
+ childNodes: node.childNodes.map(cloneNode)
707
+ };
708
+ }
709
+ function sameAttributes(left, right) {
710
+ const leftKeys = Object.keys(left);
711
+ const rightKeys = Object.keys(right);
712
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => {
713
+ return left[key] === right[key];
714
+ });
715
+ }
716
+ var ViewTreeSerializer = class {
717
+ constructor() {
718
+ __publicField(this, "nodeIds", /* @__PURE__ */ new Map());
719
+ __publicField(this, "nextNodeId", FIRST_VIEW_NODE_ID);
720
+ __publicField(this, "previous", null);
721
+ }
722
+ capture(input, options) {
723
+ const shouldTakeFullSnapshot = !this.previous || options.forceFullSnapshot === true;
724
+ if (options.forceFullSnapshot === true && this.previous) {
725
+ this.previous = null;
726
+ this.nodeIds.clear();
727
+ this.nextNodeId = FIRST_VIEW_NODE_ID;
728
+ }
729
+ const current = this.build(input);
730
+ const events = shouldTakeFullSnapshot ? this.fullSnapshot(current, options) : this.mutations(
731
+ this.previous,
732
+ current,
733
+ options.timestamp
734
+ );
735
+ this.previous = current;
736
+ for (const key of this.nodeIds.keys()) {
737
+ if (!current.records.has(key)) {
738
+ this.nodeIds.delete(key);
739
+ }
740
+ }
741
+ return {
742
+ events,
743
+ hasFullSnapshot: shouldTakeFullSnapshot,
744
+ fidelityNotices: Array.from(current.fidelity),
745
+ droppedNodes: current.droppedNodes
746
+ };
747
+ }
748
+ reset() {
749
+ this.previous = null;
750
+ this.nodeIds.clear();
751
+ this.nextNodeId = FIRST_VIEW_NODE_ID;
752
+ }
753
+ build(input) {
754
+ const records = /* @__PURE__ */ new Map();
755
+ const byId = /* @__PURE__ */ new Map();
756
+ const fidelity = /* @__PURE__ */ new Set([
757
+ "mobile-animation-sampled" /* MobileAnimationSampled */
758
+ ]);
759
+ let visited = 0;
760
+ let droppedNodes = typeof input.truncatedNodes === "number" && Number.isSafeInteger(input.truncatedNodes) && input.truncatedNodes > 0 ? Math.min(input.truncatedNodes, 1e6) : 0;
761
+ const visit = (candidate, parentId, path, depth) => {
762
+ if (depth > MAX_VIEW_TREE_DEPTH || visited >= MAX_VIEW_TREE_NODES || !candidate || typeof candidate !== "object") {
763
+ droppedNodes += 1;
764
+ return null;
765
+ }
766
+ visited += 1;
767
+ const kind = candidate.masked ? "masked" : safeKind(candidate.kind);
768
+ const key = `${typeof candidate.nativeId}:${String(
769
+ candidate.nativeId
770
+ )}:${path}`;
771
+ let id = this.nodeIds.get(key);
772
+ if (id === void 0) {
773
+ id = this.nextNodeId;
774
+ this.nextNodeId += 1;
775
+ this.nodeIds.set(key, id);
776
+ }
777
+ const attributes = {
778
+ style: geometryStyle(candidate),
779
+ "data-oneuptime-mobile-view": kind
780
+ };
781
+ const isOpaque = candidate.opaque === true || kind === "image" || kind === "webview" || kind === "canvas" || kind === "masked";
782
+ if (isOpaque) {
783
+ attributes["data-oneuptime-replay-placeholder"] = kind;
784
+ if (!safeColor(candidate.backgroundColor)) {
785
+ attributes["style"] += ";background-color:#94a3b833";
786
+ }
787
+ } else if (kind === "text" || kind === "input") {
788
+ attributes["style"] += ";background-color:#94a3b826";
789
+ }
790
+ switch (kind) {
791
+ case "image":
792
+ fidelity.add("mobile-images-opaque" /* MobileImagesOpaque */);
793
+ break;
794
+ case "webview":
795
+ fidelity.add("mobile-webview-opaque" /* MobileWebViewOpaque */);
796
+ break;
797
+ case "canvas":
798
+ fidelity.add("mobile-canvas-opaque" /* MobileCanvasOpaque */);
799
+ break;
800
+ default:
801
+ break;
802
+ }
803
+ const element = {
804
+ type: 2,
805
+ tagName: "div",
806
+ attributes,
807
+ childNodes: [],
808
+ id
809
+ };
810
+ const record = {
811
+ key,
812
+ id,
813
+ parentId,
814
+ attributes,
815
+ node: element
816
+ };
817
+ records.set(key, record);
818
+ byId.set(id, record);
819
+ if (!isOpaque && Array.isArray(candidate.children)) {
820
+ candidate.children.forEach(
821
+ (child, index) => {
822
+ const serialized = visit(
823
+ child,
824
+ id,
825
+ `${path}.${index}`,
826
+ depth + 1
827
+ );
828
+ if (serialized) {
829
+ element.childNodes.push(serialized);
830
+ }
831
+ }
832
+ );
833
+ }
834
+ return element;
835
+ };
836
+ const root = visit(
837
+ input,
838
+ RRWEB_BODY_NODE_ID,
839
+ "0",
840
+ 0
841
+ ) ?? {
842
+ type: 2,
843
+ tagName: "div",
844
+ attributes: {
845
+ style: "position:absolute;left:0px;top:0px;width:0px;height:0px;box-sizing:border-box;overflow:hidden",
846
+ "data-oneuptime-mobile-view": "unknown"
847
+ },
848
+ childNodes: [],
849
+ id: this.nextNodeId++
850
+ };
851
+ if (droppedNodes > 0) {
852
+ fidelity.add("buffer-overflow" /* BufferOverflow */);
853
+ }
854
+ return { root, records, byId, fidelity, droppedNodes };
855
+ }
856
+ fullSnapshot(current, options) {
857
+ const width = Math.max(0, finiteGeometry(options.viewportWidth));
858
+ const height = Math.max(0, finiteGeometry(options.viewportHeight));
859
+ return [
860
+ {
861
+ type: 4 /* Meta */,
862
+ data: {
863
+ href: options.url,
864
+ width,
865
+ height
866
+ },
867
+ timestamp: options.timestamp
868
+ },
869
+ {
870
+ type: 2 /* FullSnapshot */,
871
+ data: {
872
+ node: {
873
+ type: 0,
874
+ childNodes: [
875
+ {
876
+ type: 1,
877
+ name: "html",
878
+ publicId: "",
879
+ systemId: "",
880
+ id: 2
881
+ },
882
+ {
883
+ type: 2,
884
+ tagName: "html",
885
+ attributes: {},
886
+ childNodes: [
887
+ {
888
+ type: 2,
889
+ tagName: "head",
890
+ attributes: {},
891
+ childNodes: [],
892
+ id: RRWEB_HEAD_NODE_ID
893
+ },
894
+ {
895
+ type: 2,
896
+ tagName: "body",
897
+ attributes: {
898
+ style: `margin:0;position:relative;width:${width}px;height:${height}px;overflow:hidden`,
899
+ "data-oneuptime-recorder-kind": "rn-view-tree"
900
+ },
901
+ childNodes: [cloneNode(current.root)],
902
+ id: RRWEB_BODY_NODE_ID
903
+ }
904
+ ],
905
+ id: RRWEB_HTML_NODE_ID
906
+ }
907
+ ],
908
+ id: RRWEB_DOCUMENT_NODE_ID
909
+ },
910
+ initialOffset: { top: 0, left: 0 }
911
+ },
912
+ timestamp: options.timestamp
913
+ }
914
+ ];
915
+ }
916
+ mutations(previous, current, timestamp) {
917
+ const additions = [];
918
+ const removals = [];
919
+ const attributes = [];
920
+ for (const record of current.records.values()) {
921
+ const before = previous.records.get(
922
+ record.key
923
+ );
924
+ if (!before) {
925
+ const parentWasAdded = record.parentId !== RRWEB_BODY_NODE_ID && !previous.byId.has(record.parentId);
926
+ if (!parentWasAdded) {
927
+ additions.push({
928
+ parentId: record.parentId,
929
+ nextId: null,
930
+ node: cloneNode(record.node)
931
+ });
932
+ }
933
+ } else if (!sameAttributes(before.attributes, record.attributes)) {
934
+ attributes.push({
935
+ id: record.id,
936
+ attributes: { ...record.attributes }
937
+ });
938
+ }
939
+ }
940
+ for (const record of previous.records.values()) {
941
+ if (!current.records.has(record.key)) {
942
+ const parentWasRemoved = record.parentId !== RRWEB_BODY_NODE_ID && !current.byId.has(record.parentId);
943
+ if (!parentWasRemoved) {
944
+ removals.push({ parentId: record.parentId, id: record.id });
945
+ }
946
+ }
947
+ }
948
+ if (additions.length === 0 && removals.length === 0 && attributes.length === 0) {
949
+ return [];
950
+ }
951
+ return [
952
+ {
953
+ type: 3 /* IncrementalSnapshot */,
954
+ data: {
955
+ source: 0 /* Mutation */,
956
+ texts: [],
957
+ attributes,
958
+ removes: removals,
959
+ adds: additions
960
+ },
961
+ timestamp
962
+ }
963
+ ];
964
+ }
965
+ };
966
+
967
+ // src/Events.ts
968
+ var MAX_TOUCH_CLOCK_SKEW_MS = 6e4;
969
+ function coordinate(value) {
970
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(-1e5, Math.min(1e5, value)) : 0;
971
+ }
972
+ function sanitizeRecordedTouch(value, nowUnixMs) {
973
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
974
+ return null;
975
+ }
976
+ const raw = value;
977
+ if (raw.phase !== "start" && raw.phase !== "move" && raw.phase !== "end") {
978
+ return null;
979
+ }
980
+ const safeNowUnixMs = Number.isFinite(nowUnixMs) ? nowUnixMs : Date.now();
981
+ const candidateTimestamp = raw.timestamp;
982
+ const timestamp = typeof candidateTimestamp === "number" && Number.isFinite(candidateTimestamp) && candidateTimestamp >= safeNowUnixMs - MAX_TOUCH_CLOCK_SKEW_MS && candidateTimestamp <= safeNowUnixMs + MAX_TOUCH_CLOCK_SKEW_MS ? candidateTimestamp : safeNowUnixMs;
983
+ const touch = {
984
+ phase: raw.phase,
985
+ x: coordinate(raw.x),
986
+ y: coordinate(raw.y),
987
+ timestamp
988
+ };
989
+ if (typeof raw.targetTag === "number" && Number.isSafeInteger(raw.targetTag) && raw.targetTag > 0) {
990
+ touch.targetTag = raw.targetTag;
991
+ }
992
+ if (typeof raw.pointerId === "number" && Number.isSafeInteger(raw.pointerId) || typeof raw.pointerId === "string" && raw.pointerId.length <= 100) {
993
+ touch.pointerId = raw.pointerId;
994
+ }
995
+ return touch;
996
+ }
997
+ function createTouchEvent(touch) {
998
+ const x2 = coordinate(touch.x);
999
+ const y = coordinate(touch.y);
1000
+ if (touch.phase === "move") {
1001
+ return {
1002
+ type: 3 /* IncrementalSnapshot */,
1003
+ data: {
1004
+ source: 6 /* TouchMove */,
1005
+ positions: [
1006
+ {
1007
+ x: x2,
1008
+ y,
1009
+ id: RRWEB_BODY_NODE_ID,
1010
+ timeOffset: 0
1011
+ }
1012
+ ]
1013
+ },
1014
+ timestamp: touch.timestamp
1015
+ };
1016
+ }
1017
+ return {
1018
+ type: 3 /* IncrementalSnapshot */,
1019
+ data: {
1020
+ source: 2 /* MouseInteraction */,
1021
+ type: touch.phase === "start" ? 7 /* TouchStart */ : 9 /* TouchEnd */,
1022
+ id: RRWEB_BODY_NODE_ID,
1023
+ x: x2,
1024
+ y
1025
+ },
1026
+ timestamp: touch.timestamp
1027
+ };
1028
+ }
1029
+ function createCustomEvent(tag, payload, timestamp) {
1030
+ return {
1031
+ type: 5 /* Custom */,
1032
+ data: { tag, payload },
1033
+ timestamp
1034
+ };
1035
+ }
1036
+
1037
+ // src/NativeViewTree.ts
1038
+ var import_react_native = require("react-native");
1039
+ function viewport() {
1040
+ const dimensions = import_react_native.Dimensions.get(
1041
+ "window"
1042
+ );
1043
+ return {
1044
+ width: typeof dimensions.width === "number" && Number.isFinite(dimensions.width) ? dimensions.width : 0,
1045
+ height: typeof dimensions.height === "number" && Number.isFinite(dimensions.height) ? dimensions.height : 0
1046
+ };
1047
+ }
1048
+ var ReactNativeViewTreeAdapter = class {
1049
+ isAvailable() {
1050
+ const nativeModule = import_react_native.NativeModules["OneUptimeReplayViewTree"];
1051
+ return typeof nativeModule?.captureViewTree === "function";
1052
+ }
1053
+ async captureViewTree(rootTag) {
1054
+ const nativeModule = import_react_native.NativeModules["OneUptimeReplayViewTree"];
1055
+ if (nativeModule?.captureViewTree) {
1056
+ return await nativeModule.captureViewTree(rootTag);
1057
+ }
1058
+ throw new Error(
1059
+ "OneUptime replay native module is unavailable. Expo Go is not supported; use an autolinked native or Expo development build."
1060
+ );
1061
+ }
1062
+ async getAppMetadata() {
1063
+ const nativeModule = import_react_native.NativeModules["OneUptimeReplayViewTree"];
1064
+ let metadata = {};
1065
+ try {
1066
+ metadata = await nativeModule?.getAppMetadata?.() ?? {};
1067
+ } catch {
1068
+ metadata = {};
1069
+ }
1070
+ return {
1071
+ appName: typeof metadata.appName === "string" ? metadata.appName : "React Native",
1072
+ appVersion: typeof metadata.appVersion === "string" ? metadata.appVersion : "unknown",
1073
+ osName: typeof metadata.osName === "string" ? metadata.osName : import_react_native.Platform.OS,
1074
+ osVersion: typeof metadata.osVersion === "string" ? metadata.osVersion : String(import_react_native.Platform.Version)
1075
+ };
1076
+ }
1077
+ async isTouchTargetPrivate(targetTag, replayRootTag) {
1078
+ const nativeModule = import_react_native.NativeModules["OneUptimeReplayViewTree"];
1079
+ if (typeof nativeModule?.isTouchTargetPrivate !== "function") {
1080
+ return true;
1081
+ }
1082
+ try {
1083
+ return await nativeModule.isTouchTargetPrivate(targetTag, replayRootTag) !== false;
1084
+ } catch {
1085
+ return true;
1086
+ }
1087
+ }
1088
+ };
1089
+ var defaultNativeViewTreeAdapter = new ReactNativeViewTreeAdapter();
1090
+ function getViewport() {
1091
+ return viewport();
1092
+ }
1093
+
1094
+ // src/Outbox.ts
1095
+ var MAX_OUTBOX_ENTRIES = 64;
1096
+ var MAX_OUTBOX_BYTES = 8 * 1024 * 1024;
1097
+ function isFrame(value) {
1098
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1099
+ return false;
1100
+ }
1101
+ const frame = value;
1102
+ return typeof frame.id === "string" && typeof frame.payload === "string" && typeof frame.attempts === "number" && Number.isSafeInteger(frame.attempts) && Boolean(frame.envelope) && typeof frame.envelope === "object" && typeof frame.envelope.sessionId === "string" && typeof frame.envelope.tabId === "string" && typeof frame.envelope.chunkIndex === "number";
1103
+ }
1104
+ function frameId(envelope) {
1105
+ return `${envelope.sessionId}:${envelope.tabId}:${envelope.chunkIndex}`;
1106
+ }
1107
+ var ReplayOutbox = class {
1108
+ constructor(storage, namespace) {
1109
+ __publicField(this, "storage");
1110
+ __publicField(this, "key");
1111
+ __publicField(this, "mutationQueue", Promise.resolve());
1112
+ this.storage = storage;
1113
+ this.key = `@oneuptime/replay/${namespace}/outbox`;
1114
+ }
1115
+ async list() {
1116
+ return await this.withLock(
1117
+ async () => {
1118
+ return this.readUnlocked();
1119
+ }
1120
+ );
1121
+ }
1122
+ async readUnlocked() {
1123
+ try {
1124
+ const raw = await this.storage.getItem(this.key);
1125
+ if (!raw) {
1126
+ return [];
1127
+ }
1128
+ const parsed = JSON.parse(raw);
1129
+ if (!Array.isArray(parsed) || !parsed.every(isFrame)) {
1130
+ await this.removeUnlocked();
1131
+ return [];
1132
+ }
1133
+ return parsed.slice().sort(
1134
+ (left, right) => {
1135
+ return left.createdAtUnixMs - right.createdAtUnixMs;
1136
+ }
1137
+ );
1138
+ } catch {
1139
+ await this.removeUnlocked();
1140
+ return [];
1141
+ }
1142
+ }
1143
+ async enqueue(frame) {
1144
+ return await this.withLock(async () => {
1145
+ const frames = await this.readUnlocked();
1146
+ if (frames.some((candidate) => {
1147
+ return candidate.id === frame.id;
1148
+ })) {
1149
+ return { dropped: 0 };
1150
+ }
1151
+ frames.push(frame);
1152
+ let dropped = 0;
1153
+ while (frames.length > MAX_OUTBOX_ENTRIES || byteLength(JSON.stringify(frames)) > MAX_OUTBOX_BYTES) {
1154
+ frames.shift();
1155
+ dropped += 1;
1156
+ }
1157
+ await this.writeUnlocked(frames);
1158
+ return { dropped };
1159
+ });
1160
+ }
1161
+ async remove(id) {
1162
+ await this.withLock(async () => {
1163
+ const frames = (await this.readUnlocked()).filter((frame) => {
1164
+ return frame.id !== id;
1165
+ });
1166
+ await this.writeUnlocked(frames);
1167
+ });
1168
+ }
1169
+ async incrementAttempts(id) {
1170
+ return await this.withLock(async () => {
1171
+ const frames = await this.readUnlocked();
1172
+ let attempts = 0;
1173
+ for (const frame of frames) {
1174
+ if (frame.id === id) {
1175
+ frame.attempts += 1;
1176
+ attempts = frame.attempts;
1177
+ break;
1178
+ }
1179
+ }
1180
+ await this.writeUnlocked(frames);
1181
+ return attempts;
1182
+ });
1183
+ }
1184
+ async clear() {
1185
+ await this.withLock(async () => {
1186
+ return this.removeUnlocked();
1187
+ });
1188
+ }
1189
+ async removeUnlocked() {
1190
+ try {
1191
+ await this.storage.removeItem(this.key);
1192
+ } catch {
1193
+ }
1194
+ }
1195
+ async writeUnlocked(frames) {
1196
+ try {
1197
+ if (frames.length === 0) {
1198
+ await this.storage.removeItem(this.key);
1199
+ return;
1200
+ }
1201
+ await this.storage.setItem(this.key, JSON.stringify(frames));
1202
+ } catch {
1203
+ }
1204
+ }
1205
+ async withLock(operation) {
1206
+ const result = this.mutationQueue.then(operation, operation);
1207
+ this.mutationQueue = result.then(
1208
+ () => {
1209
+ return void 0;
1210
+ },
1211
+ () => {
1212
+ return void 0;
1213
+ }
1214
+ );
1215
+ return await result;
1216
+ }
1217
+ };
1218
+
1219
+ // src/Storage.ts
1220
+ var import_async_storage = __toESM(require("@react-native-async-storage/async-storage"));
1221
+ var REPLAY_ID_PATTERN = /^[0-9a-f]{32}$/u;
1222
+ var defaultReplayStorage = import_async_storage.default;
1223
+ function generateReplayId() {
1224
+ const bytes = new Uint8Array(16);
1225
+ const cryptoRef = globalThis.crypto;
1226
+ if (cryptoRef && typeof cryptoRef.getRandomValues === "function") {
1227
+ cryptoRef.getRandomValues(bytes);
1228
+ } else {
1229
+ for (let index = 0; index < bytes.length; index += 1) {
1230
+ bytes[index] = Math.floor(Math.random() * 256);
1231
+ }
1232
+ }
1233
+ return Array.from(bytes).map((byte) => {
1234
+ return byte.toString(16).padStart(2, "0");
1235
+ }).join("");
1236
+ }
1237
+ function isReplayId(value) {
1238
+ return typeof value === "string" && REPLAY_ID_PATTERN.test(value);
1239
+ }
1240
+ function parseStoredSession(value) {
1241
+ if (!value) {
1242
+ return null;
1243
+ }
1244
+ try {
1245
+ const parsed = JSON.parse(value);
1246
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1247
+ return null;
1248
+ }
1249
+ const candidate = parsed;
1250
+ if (!isReplayId(candidate.sessionId) || !Number.isSafeInteger(candidate.sessionStartUnixMs) || !Number.isSafeInteger(candidate.lastActivityUnixMs) || !Number.isSafeInteger(candidate.nextChunkIndex) || (candidate.nextChunkIndex ?? -1) < 0) {
1251
+ return null;
1252
+ }
1253
+ return candidate;
1254
+ } catch {
1255
+ return null;
1256
+ }
1257
+ }
1258
+ var ReplaySessionStore = class {
1259
+ constructor(storage, namespace) {
1260
+ __publicField(this, "storage");
1261
+ __publicField(this, "sessionKey");
1262
+ __publicField(this, "visitorKey");
1263
+ __publicField(this, "identity", null);
1264
+ this.storage = storage;
1265
+ this.sessionKey = `@oneuptime/replay/${namespace}/session`;
1266
+ this.visitorKey = `@oneuptime/replay/${namespace}/visitor`;
1267
+ }
1268
+ async resolve(nowUnixMs) {
1269
+ const stored = parseStoredSession(
1270
+ await this.safeGet(this.sessionKey)
1271
+ );
1272
+ const shouldRotate = !stored || stored.nextChunkIndex >= MAX_SESSION_REPLAY_CHUNKS_PER_SESSION || nowUnixMs - stored.lastActivityUnixMs >= SESSION_REPLAY_IDLE_ROLLOVER_MS || nowUnixMs - stored.sessionStartUnixMs >= SESSION_REPLAY_MAX_SESSION_MS;
1273
+ let session;
1274
+ if (shouldRotate || !stored) {
1275
+ session = {
1276
+ sessionId: generateReplayId(),
1277
+ sessionStartUnixMs: nowUnixMs,
1278
+ lastActivityUnixMs: nowUnixMs,
1279
+ nextChunkIndex: 0
1280
+ };
1281
+ } else {
1282
+ session = {
1283
+ sessionId: stored.sessionId,
1284
+ sessionStartUnixMs: stored.sessionStartUnixMs,
1285
+ lastActivityUnixMs: nowUnixMs,
1286
+ nextChunkIndex: stored.nextChunkIndex
1287
+ };
1288
+ }
1289
+ let visitorId = await this.safeGet(this.visitorKey);
1290
+ if (!isReplayId(visitorId)) {
1291
+ visitorId = generateReplayId();
1292
+ await this.safeSet(this.visitorKey, visitorId);
1293
+ }
1294
+ await this.safeSet(this.sessionKey, JSON.stringify(session));
1295
+ this.identity = {
1296
+ sessionId: session.sessionId,
1297
+ tabId: generateReplayId(),
1298
+ visitorId,
1299
+ sessionStartUnixMs: session.sessionStartUnixMs,
1300
+ chunkIndex: session.nextChunkIndex
1301
+ };
1302
+ return { ...this.identity };
1303
+ }
1304
+ async takeChunkIndex(nowUnixMs) {
1305
+ if (!this.identity) {
1306
+ await this.resolve(nowUnixMs);
1307
+ }
1308
+ const identity = this.identity;
1309
+ const index = identity.chunkIndex;
1310
+ identity.chunkIndex += 1;
1311
+ const state = {
1312
+ sessionId: identity.sessionId,
1313
+ sessionStartUnixMs: identity.sessionStartUnixMs,
1314
+ lastActivityUnixMs: nowUnixMs,
1315
+ nextChunkIndex: identity.chunkIndex
1316
+ };
1317
+ await this.safeSet(this.sessionKey, JSON.stringify(state));
1318
+ return index;
1319
+ }
1320
+ getNextChunkIndex() {
1321
+ return this.identity?.chunkIndex ?? null;
1322
+ }
1323
+ async rotate(nowUnixMs) {
1324
+ let visitorId = await this.safeGet(this.visitorKey);
1325
+ if (!isReplayId(visitorId)) {
1326
+ visitorId = generateReplayId();
1327
+ await this.safeSet(this.visitorKey, visitorId);
1328
+ }
1329
+ const session = {
1330
+ sessionId: generateReplayId(),
1331
+ sessionStartUnixMs: nowUnixMs,
1332
+ lastActivityUnixMs: nowUnixMs,
1333
+ nextChunkIndex: 0
1334
+ };
1335
+ await this.safeSet(this.sessionKey, JSON.stringify(session));
1336
+ this.identity = {
1337
+ sessionId: session.sessionId,
1338
+ tabId: generateReplayId(),
1339
+ visitorId,
1340
+ sessionStartUnixMs: nowUnixMs,
1341
+ chunkIndex: 0
1342
+ };
1343
+ return { ...this.identity };
1344
+ }
1345
+ async clear() {
1346
+ this.identity = null;
1347
+ await Promise.all([
1348
+ this.safeRemove(this.sessionKey),
1349
+ this.safeRemove(this.visitorKey)
1350
+ ]);
1351
+ }
1352
+ async safeGet(key) {
1353
+ try {
1354
+ return await this.storage.getItem(key);
1355
+ } catch {
1356
+ return null;
1357
+ }
1358
+ }
1359
+ async safeSet(key, value) {
1360
+ try {
1361
+ await this.storage.setItem(key, value);
1362
+ } catch {
1363
+ }
1364
+ }
1365
+ async safeRemove(key) {
1366
+ try {
1367
+ await this.storage.removeItem(key);
1368
+ } catch {
1369
+ }
1370
+ }
1371
+ };
1372
+
1373
+ // node_modules/fflate/esm/browser.js
1374
+ var u8 = Uint8Array;
1375
+ var u16 = Uint16Array;
1376
+ var i32 = Int32Array;
1377
+ var fleb = new u8([
1378
+ 0,
1379
+ 0,
1380
+ 0,
1381
+ 0,
1382
+ 0,
1383
+ 0,
1384
+ 0,
1385
+ 0,
1386
+ 1,
1387
+ 1,
1388
+ 1,
1389
+ 1,
1390
+ 2,
1391
+ 2,
1392
+ 2,
1393
+ 2,
1394
+ 3,
1395
+ 3,
1396
+ 3,
1397
+ 3,
1398
+ 4,
1399
+ 4,
1400
+ 4,
1401
+ 4,
1402
+ 5,
1403
+ 5,
1404
+ 5,
1405
+ 5,
1406
+ 0,
1407
+ /* unused */
1408
+ 0,
1409
+ 0,
1410
+ /* impossible */
1411
+ 0
1412
+ ]);
1413
+ var fdeb = new u8([
1414
+ 0,
1415
+ 0,
1416
+ 0,
1417
+ 0,
1418
+ 1,
1419
+ 1,
1420
+ 2,
1421
+ 2,
1422
+ 3,
1423
+ 3,
1424
+ 4,
1425
+ 4,
1426
+ 5,
1427
+ 5,
1428
+ 6,
1429
+ 6,
1430
+ 7,
1431
+ 7,
1432
+ 8,
1433
+ 8,
1434
+ 9,
1435
+ 9,
1436
+ 10,
1437
+ 10,
1438
+ 11,
1439
+ 11,
1440
+ 12,
1441
+ 12,
1442
+ 13,
1443
+ 13,
1444
+ /* unused */
1445
+ 0,
1446
+ 0
1447
+ ]);
1448
+ var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
1449
+ var freb = function(eb, start) {
1450
+ var b = new u16(31);
1451
+ for (var i2 = 0; i2 < 31; ++i2) {
1452
+ b[i2] = start += 1 << eb[i2 - 1];
1453
+ }
1454
+ var r = new i32(b[30]);
1455
+ for (var i2 = 1; i2 < 30; ++i2) {
1456
+ for (var j = b[i2]; j < b[i2 + 1]; ++j) {
1457
+ r[j] = j - b[i2] << 5 | i2;
1458
+ }
1459
+ }
1460
+ return { b, r };
1461
+ };
1462
+ var _a = freb(fleb, 2);
1463
+ var fl = _a.b;
1464
+ var revfl = _a.r;
1465
+ fl[28] = 258, revfl[258] = 28;
1466
+ var _b = freb(fdeb, 0);
1467
+ var fd = _b.b;
1468
+ var revfd = _b.r;
1469
+ var rev = new u16(32768);
1470
+ for (i = 0; i < 32768; ++i) {
1471
+ x = (i & 43690) >> 1 | (i & 21845) << 1;
1472
+ x = (x & 52428) >> 2 | (x & 13107) << 2;
1473
+ x = (x & 61680) >> 4 | (x & 3855) << 4;
1474
+ rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
1475
+ }
1476
+ var x;
1477
+ var i;
1478
+ var hMap = (function(cd, mb, r) {
1479
+ var s = cd.length;
1480
+ var i2 = 0;
1481
+ var l = new u16(mb);
1482
+ for (; i2 < s; ++i2) {
1483
+ if (cd[i2])
1484
+ ++l[cd[i2] - 1];
1485
+ }
1486
+ var le = new u16(mb);
1487
+ for (i2 = 1; i2 < mb; ++i2) {
1488
+ le[i2] = le[i2 - 1] + l[i2 - 1] << 1;
1489
+ }
1490
+ var co;
1491
+ if (r) {
1492
+ co = new u16(1 << mb);
1493
+ var rvb = 15 - mb;
1494
+ for (i2 = 0; i2 < s; ++i2) {
1495
+ if (cd[i2]) {
1496
+ var sv = i2 << 4 | cd[i2];
1497
+ var r_1 = mb - cd[i2];
1498
+ var v = le[cd[i2] - 1]++ << r_1;
1499
+ for (var m = v | (1 << r_1) - 1; v <= m; ++v) {
1500
+ co[rev[v] >> rvb] = sv;
1501
+ }
1502
+ }
1503
+ }
1504
+ } else {
1505
+ co = new u16(s);
1506
+ for (i2 = 0; i2 < s; ++i2) {
1507
+ if (cd[i2]) {
1508
+ co[i2] = rev[le[cd[i2] - 1]++] >> 15 - cd[i2];
1509
+ }
1510
+ }
1511
+ }
1512
+ return co;
1513
+ });
1514
+ var flt = new u8(288);
1515
+ for (i = 0; i < 144; ++i)
1516
+ flt[i] = 8;
1517
+ var i;
1518
+ for (i = 144; i < 256; ++i)
1519
+ flt[i] = 9;
1520
+ var i;
1521
+ for (i = 256; i < 280; ++i)
1522
+ flt[i] = 7;
1523
+ var i;
1524
+ for (i = 280; i < 288; ++i)
1525
+ flt[i] = 8;
1526
+ var i;
1527
+ var fdt = new u8(32);
1528
+ for (i = 0; i < 32; ++i)
1529
+ fdt[i] = 5;
1530
+ var i;
1531
+ var flm = /* @__PURE__ */ hMap(flt, 9, 0);
1532
+ var fdm = /* @__PURE__ */ hMap(fdt, 5, 0);
1533
+ var shft = function(p) {
1534
+ return (p + 7) / 8 | 0;
1535
+ };
1536
+ var slc = function(v, s, e) {
1537
+ if (s == null || s < 0)
1538
+ s = 0;
1539
+ if (e == null || e > v.length)
1540
+ e = v.length;
1541
+ return new u8(v.subarray(s, e));
1542
+ };
1543
+ var wbits = function(d, p, v) {
1544
+ v <<= p & 7;
1545
+ var o = p / 8 | 0;
1546
+ d[o] |= v;
1547
+ d[o + 1] |= v >> 8;
1548
+ };
1549
+ var wbits16 = function(d, p, v) {
1550
+ v <<= p & 7;
1551
+ var o = p / 8 | 0;
1552
+ d[o] |= v;
1553
+ d[o + 1] |= v >> 8;
1554
+ d[o + 2] |= v >> 16;
1555
+ };
1556
+ var hTree = function(d, mb) {
1557
+ var t = [];
1558
+ for (var i2 = 0; i2 < d.length; ++i2) {
1559
+ if (d[i2])
1560
+ t.push({ s: i2, f: d[i2] });
1561
+ }
1562
+ var s = t.length;
1563
+ var t2 = t.slice();
1564
+ if (!s)
1565
+ return { t: et, l: 0 };
1566
+ if (s == 1) {
1567
+ var v = new u8(t[0].s + 1);
1568
+ v[t[0].s] = 1;
1569
+ return { t: v, l: 1 };
1570
+ }
1571
+ t.sort(function(a, b) {
1572
+ return a.f - b.f;
1573
+ });
1574
+ t.push({ s: -1, f: 25001 });
1575
+ var l = t[0], r = t[1], i0 = 0, i1 = 1, i22 = 2;
1576
+ t[0] = { s: -1, f: l.f + r.f, l, r };
1577
+ while (i1 != s - 1) {
1578
+ l = t[t[i0].f < t[i22].f ? i0++ : i22++];
1579
+ r = t[i0 != i1 && t[i0].f < t[i22].f ? i0++ : i22++];
1580
+ t[i1++] = { s: -1, f: l.f + r.f, l, r };
1581
+ }
1582
+ var maxSym = t2[0].s;
1583
+ for (var i2 = 1; i2 < s; ++i2) {
1584
+ if (t2[i2].s > maxSym)
1585
+ maxSym = t2[i2].s;
1586
+ }
1587
+ var tr = new u16(maxSym + 1);
1588
+ var mbt = ln(t[i1 - 1], tr, 0);
1589
+ if (mbt > mb) {
1590
+ var i2 = 0, dt = 0;
1591
+ var lft = mbt - mb, cst = 1 << lft;
1592
+ t2.sort(function(a, b) {
1593
+ return tr[b.s] - tr[a.s] || a.f - b.f;
1594
+ });
1595
+ for (; i2 < s; ++i2) {
1596
+ var i2_1 = t2[i2].s;
1597
+ if (tr[i2_1] > mb) {
1598
+ dt += cst - (1 << mbt - tr[i2_1]);
1599
+ tr[i2_1] = mb;
1600
+ } else
1601
+ break;
1602
+ }
1603
+ dt >>= lft;
1604
+ while (dt > 0) {
1605
+ var i2_2 = t2[i2].s;
1606
+ if (tr[i2_2] < mb)
1607
+ dt -= 1 << mb - tr[i2_2]++ - 1;
1608
+ else
1609
+ ++i2;
1610
+ }
1611
+ for (; i2 >= 0 && dt; --i2) {
1612
+ var i2_3 = t2[i2].s;
1613
+ if (tr[i2_3] == mb) {
1614
+ --tr[i2_3];
1615
+ ++dt;
1616
+ }
1617
+ }
1618
+ mbt = mb;
1619
+ }
1620
+ return { t: new u8(tr), l: mbt };
1621
+ };
1622
+ var ln = function(n, l, d) {
1623
+ return n.s == -1 ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1)) : l[n.s] = d;
1624
+ };
1625
+ var lc = function(c) {
1626
+ var s = c.length;
1627
+ while (s && !c[--s])
1628
+ ;
1629
+ var cl = new u16(++s);
1630
+ var cli = 0, cln = c[0], cls = 1;
1631
+ var w = function(v) {
1632
+ cl[cli++] = v;
1633
+ };
1634
+ for (var i2 = 1; i2 <= s; ++i2) {
1635
+ if (c[i2] == cln && i2 != s)
1636
+ ++cls;
1637
+ else {
1638
+ if (!cln && cls > 2) {
1639
+ for (; cls > 138; cls -= 138)
1640
+ w(32754);
1641
+ if (cls > 2) {
1642
+ w(cls > 10 ? cls - 11 << 5 | 28690 : cls - 3 << 5 | 12305);
1643
+ cls = 0;
1644
+ }
1645
+ } else if (cls > 3) {
1646
+ w(cln), --cls;
1647
+ for (; cls > 6; cls -= 6)
1648
+ w(8304);
1649
+ if (cls > 2)
1650
+ w(cls - 3 << 5 | 8208), cls = 0;
1651
+ }
1652
+ while (cls--)
1653
+ w(cln);
1654
+ cls = 1;
1655
+ cln = c[i2];
1656
+ }
1657
+ }
1658
+ return { c: cl.subarray(0, cli), n: s };
1659
+ };
1660
+ var clen = function(cf, cl) {
1661
+ var l = 0;
1662
+ for (var i2 = 0; i2 < cl.length; ++i2)
1663
+ l += cf[i2] * cl[i2];
1664
+ return l;
1665
+ };
1666
+ var wfblk = function(out, pos, dat) {
1667
+ var s = dat.length;
1668
+ var o = shft(pos + 2);
1669
+ out[o] = s & 255;
1670
+ out[o + 1] = s >> 8;
1671
+ out[o + 2] = out[o] ^ 255;
1672
+ out[o + 3] = out[o + 1] ^ 255;
1673
+ for (var i2 = 0; i2 < s; ++i2)
1674
+ out[o + i2 + 4] = dat[i2];
1675
+ return (o + 4 + s) * 8;
1676
+ };
1677
+ var wblk = function(dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
1678
+ wbits(out, p++, final);
1679
+ ++lf[256];
1680
+ var _a2 = hTree(lf, 15), dlt = _a2.t, mlb = _a2.l;
1681
+ var _b2 = hTree(df, 15), ddt = _b2.t, mdb = _b2.l;
1682
+ var _c = lc(dlt), lclt = _c.c, nlc = _c.n;
1683
+ var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;
1684
+ var lcfreq = new u16(19);
1685
+ for (var i2 = 0; i2 < lclt.length; ++i2)
1686
+ ++lcfreq[lclt[i2] & 31];
1687
+ for (var i2 = 0; i2 < lcdt.length; ++i2)
1688
+ ++lcfreq[lcdt[i2] & 31];
1689
+ var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;
1690
+ var nlcc = 19;
1691
+ for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)
1692
+ ;
1693
+ var flen = bl + 5 << 3;
1694
+ var ftlen = clen(lf, flt) + clen(df, fdt) + eb;
1695
+ var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];
1696
+ if (bs >= 0 && flen <= ftlen && flen <= dtlen)
1697
+ return wfblk(out, p, dat.subarray(bs, bs + bl));
1698
+ var lm, ll, dm, dl;
1699
+ wbits(out, p, 1 + (dtlen < ftlen)), p += 2;
1700
+ if (dtlen < ftlen) {
1701
+ lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;
1702
+ var llm = hMap(lct, mlcb, 0);
1703
+ wbits(out, p, nlc - 257);
1704
+ wbits(out, p + 5, ndc - 1);
1705
+ wbits(out, p + 10, nlcc - 4);
1706
+ p += 14;
1707
+ for (var i2 = 0; i2 < nlcc; ++i2)
1708
+ wbits(out, p + 3 * i2, lct[clim[i2]]);
1709
+ p += 3 * nlcc;
1710
+ var lcts = [lclt, lcdt];
1711
+ for (var it = 0; it < 2; ++it) {
1712
+ var clct = lcts[it];
1713
+ for (var i2 = 0; i2 < clct.length; ++i2) {
1714
+ var len = clct[i2] & 31;
1715
+ wbits(out, p, llm[len]), p += lct[len];
1716
+ if (len > 15)
1717
+ wbits(out, p, clct[i2] >> 5 & 127), p += clct[i2] >> 12;
1718
+ }
1719
+ }
1720
+ } else {
1721
+ lm = flm, ll = flt, dm = fdm, dl = fdt;
1722
+ }
1723
+ for (var i2 = 0; i2 < li; ++i2) {
1724
+ var sym = syms[i2];
1725
+ if (sym > 255) {
1726
+ var len = sym >> 18 & 31;
1727
+ wbits16(out, p, lm[len + 257]), p += ll[len + 257];
1728
+ if (len > 7)
1729
+ wbits(out, p, sym >> 23 & 31), p += fleb[len];
1730
+ var dst = sym & 31;
1731
+ wbits16(out, p, dm[dst]), p += dl[dst];
1732
+ if (dst > 3)
1733
+ wbits16(out, p, sym >> 5 & 8191), p += fdeb[dst];
1734
+ } else {
1735
+ wbits16(out, p, lm[sym]), p += ll[sym];
1736
+ }
1737
+ }
1738
+ wbits16(out, p, lm[256]);
1739
+ return p + ll[256];
1740
+ };
1741
+ var deo = /* @__PURE__ */ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
1742
+ var et = /* @__PURE__ */ new u8(0);
1743
+ var dflt = function(dat, lvl, plvl, pre, post, st) {
1744
+ var s = st.z || dat.length;
1745
+ var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7e3)) + post);
1746
+ var w = o.subarray(pre, o.length - post);
1747
+ var lst = st.l;
1748
+ var pos = (st.r || 0) & 7;
1749
+ if (lvl) {
1750
+ if (pos)
1751
+ w[0] = st.r >> 3;
1752
+ var opt = deo[lvl - 1];
1753
+ var n = opt >> 13, c = opt & 8191;
1754
+ var msk_1 = (1 << plvl) - 1;
1755
+ var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
1756
+ var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
1757
+ var hsh = function(i3) {
1758
+ return (dat[i3] ^ dat[i3 + 1] << bs1_1 ^ dat[i3 + 2] << bs2_1) & msk_1;
1759
+ };
1760
+ var syms = new i32(25e3);
1761
+ var lf = new u16(288), df = new u16(32);
1762
+ var lc_1 = 0, eb = 0, i2 = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
1763
+ for (; i2 + 2 < s; ++i2) {
1764
+ var hv = hsh(i2);
1765
+ var imod = i2 & 32767, pimod = head[hv];
1766
+ prev[imod] = pimod;
1767
+ head[hv] = imod;
1768
+ if (wi <= i2) {
1769
+ var rem = s - i2;
1770
+ if ((lc_1 > 7e3 || li > 24576) && (rem > 423 || !lst)) {
1771
+ pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i2 - bs, pos);
1772
+ li = lc_1 = eb = 0, bs = i2;
1773
+ for (var j = 0; j < 286; ++j)
1774
+ lf[j] = 0;
1775
+ for (var j = 0; j < 30; ++j)
1776
+ df[j] = 0;
1777
+ }
1778
+ var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
1779
+ if (rem > 2 && hv == hsh(i2 - dif)) {
1780
+ var maxn = Math.min(n, rem) - 1;
1781
+ var maxd = Math.min(32767, i2);
1782
+ var ml = Math.min(258, rem);
1783
+ while (dif <= maxd && --ch_1 && imod != pimod) {
1784
+ if (dat[i2 + l] == dat[i2 + l - dif]) {
1785
+ var nl = 0;
1786
+ for (; nl < ml && dat[i2 + nl] == dat[i2 + nl - dif]; ++nl)
1787
+ ;
1788
+ if (nl > l) {
1789
+ l = nl, d = dif;
1790
+ if (nl > maxn)
1791
+ break;
1792
+ var mmd = Math.min(dif, nl - 2);
1793
+ var md = 0;
1794
+ for (var j = 0; j < mmd; ++j) {
1795
+ var ti = i2 - dif + j & 32767;
1796
+ var pti = prev[ti];
1797
+ var cd = ti - pti & 32767;
1798
+ if (cd > md)
1799
+ md = cd, pimod = ti;
1800
+ }
1801
+ }
1802
+ }
1803
+ imod = pimod, pimod = prev[imod];
1804
+ dif += imod - pimod & 32767;
1805
+ }
1806
+ }
1807
+ if (d) {
1808
+ syms[li++] = 268435456 | revfl[l] << 18 | revfd[d];
1809
+ var lin = revfl[l] & 31, din = revfd[d] & 31;
1810
+ eb += fleb[lin] + fdeb[din];
1811
+ ++lf[257 + lin];
1812
+ ++df[din];
1813
+ wi = i2 + l;
1814
+ ++lc_1;
1815
+ } else {
1816
+ syms[li++] = dat[i2];
1817
+ ++lf[dat[i2]];
1818
+ }
1819
+ }
1820
+ }
1821
+ for (i2 = Math.max(i2, wi); i2 < s; ++i2) {
1822
+ syms[li++] = dat[i2];
1823
+ ++lf[dat[i2]];
1824
+ }
1825
+ pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i2 - bs, pos);
1826
+ if (!lst) {
1827
+ st.r = pos & 7 | w[pos / 8 | 0] << 3;
1828
+ pos -= 7;
1829
+ st.h = head, st.p = prev, st.i = i2, st.w = wi;
1830
+ }
1831
+ } else {
1832
+ for (var i2 = st.w || 0; i2 < s + lst; i2 += 65535) {
1833
+ var e = i2 + 65535;
1834
+ if (e >= s) {
1835
+ w[pos / 8 | 0] = lst;
1836
+ e = s;
1837
+ }
1838
+ pos = wfblk(w, pos + 1, dat.subarray(i2, e));
1839
+ }
1840
+ st.i = s;
1841
+ }
1842
+ return slc(o, 0, pre + shft(pos) + post);
1843
+ };
1844
+ var crct = /* @__PURE__ */ (function() {
1845
+ var t = new Int32Array(256);
1846
+ for (var i2 = 0; i2 < 256; ++i2) {
1847
+ var c = i2, k = 9;
1848
+ while (--k)
1849
+ c = (c & 1 && -306674912) ^ c >>> 1;
1850
+ t[i2] = c;
1851
+ }
1852
+ return t;
1853
+ })();
1854
+ var crc = function() {
1855
+ var c = -1;
1856
+ return {
1857
+ p: function(d) {
1858
+ var cr = c;
1859
+ for (var i2 = 0; i2 < d.length; ++i2)
1860
+ cr = crct[cr & 255 ^ d[i2]] ^ cr >>> 8;
1861
+ c = cr;
1862
+ },
1863
+ d: function() {
1864
+ return ~c;
1865
+ }
1866
+ };
1867
+ };
1868
+ var dopt = function(dat, opt, pre, post, st) {
1869
+ if (!st) {
1870
+ st = { l: 1 };
1871
+ if (opt.dictionary) {
1872
+ var dict = opt.dictionary.subarray(-32768);
1873
+ var newDat = new u8(dict.length + dat.length);
1874
+ newDat.set(dict);
1875
+ newDat.set(dat, dict.length);
1876
+ dat = newDat;
1877
+ st.w = dict.length;
1878
+ }
1879
+ }
1880
+ return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20 : 12 + opt.mem, pre, post, st);
1881
+ };
1882
+ var wbytes = function(d, b, v) {
1883
+ for (; v; ++b)
1884
+ d[b] = v, v >>>= 8;
1885
+ };
1886
+ var gzh = function(c, o) {
1887
+ var fn = o.filename;
1888
+ c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 : 0, c[9] = 3;
1889
+ if (o.mtime != 0)
1890
+ wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1e3));
1891
+ if (fn) {
1892
+ c[3] = 8;
1893
+ for (var i2 = 0; i2 <= fn.length; ++i2)
1894
+ c[i2 + 10] = fn.charCodeAt(i2);
1895
+ }
1896
+ };
1897
+ var gzhl = function(o) {
1898
+ return 10 + (o.filename ? o.filename.length + 1 : 0);
1899
+ };
1900
+ function gzipSync(data, opts) {
1901
+ if (!opts)
1902
+ opts = {};
1903
+ var c = crc(), l = data.length;
1904
+ c.p(data);
1905
+ var d = dopt(data, opts, gzhl(opts), 8), s = d.length;
1906
+ return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;
1907
+ }
1908
+ var te = typeof TextEncoder != "undefined" && /* @__PURE__ */ new TextEncoder();
1909
+ var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
1910
+ var tds = 0;
1911
+ try {
1912
+ td.decode(et, { stream: true });
1913
+ tds = 1;
1914
+ } catch (e) {
1915
+ }
1916
+ function strToU8(str, latin1) {
1917
+ if (latin1) {
1918
+ var ar_1 = new u8(str.length);
1919
+ for (var i2 = 0; i2 < str.length; ++i2)
1920
+ ar_1[i2] = str.charCodeAt(i2);
1921
+ return ar_1;
1922
+ }
1923
+ if (te)
1924
+ return te.encode(str);
1925
+ var l = str.length;
1926
+ var ar = new u8(str.length + (str.length >> 1));
1927
+ var ai = 0;
1928
+ var w = function(v) {
1929
+ ar[ai++] = v;
1930
+ };
1931
+ for (var i2 = 0; i2 < l; ++i2) {
1932
+ if (ai + 5 > ar.length) {
1933
+ var n = new u8(ai + 8 + (l - i2 << 1));
1934
+ n.set(ar);
1935
+ ar = n;
1936
+ }
1937
+ var c = str.charCodeAt(i2);
1938
+ if (c < 128 || latin1)
1939
+ w(c);
1940
+ else if (c < 2048)
1941
+ w(192 | c >> 6), w(128 | c & 63);
1942
+ else if (c > 55295 && c < 57344)
1943
+ c = 65536 + (c & 1023 << 10) | str.charCodeAt(++i2) & 1023, w(240 | c >> 18), w(128 | c >> 12 & 63), w(128 | c >> 6 & 63), w(128 | c & 63);
1944
+ else
1945
+ w(224 | c >> 12), w(128 | c >> 6 & 63), w(128 | c & 63);
1946
+ }
1947
+ return slc(ar, 0, ai);
1948
+ }
1949
+
1950
+ // src/Transport.ts
1951
+ var MAX_ATTEMPTS_PER_FRAME = 3;
1952
+ var DEFAULT_RETRY_MS = 15e3;
1953
+ var MAX_RETRY_MS = 5 * 6e4;
1954
+ var CHUNK_POST_TIMEOUT_MS = 1e4;
1955
+ var DIRECTIVE_REASON_PATTERN = /^[A-Za-z0-9_.:-]{1,100}$/u;
1956
+ function encodeReplayFrame(originalEnvelope, payload) {
1957
+ const compressed = gzipSync(strToU8(payload));
1958
+ const envelope = {
1959
+ ...originalEnvelope,
1960
+ payloadEncoding: "gzip",
1961
+ payloadBytes: compressed.byteLength,
1962
+ clientSendUnixMs: Date.now()
1963
+ };
1964
+ const prefix = strToU8(`${JSON.stringify(envelope)}
1965
+ `);
1966
+ const body = new Uint8Array(
1967
+ prefix.byteLength + compressed.byteLength
1968
+ );
1969
+ body.set(prefix, 0);
1970
+ body.set(compressed, prefix.byteLength);
1971
+ return { envelope, body };
1972
+ }
1973
+ function safeReason(value) {
1974
+ return typeof value === "string" && DIRECTIVE_REASON_PATTERN.test(value) ? value : null;
1975
+ }
1976
+ function parseDirectiveValue(value) {
1977
+ return value === "continue" || value === "stop" || value === "throttle" ? value : null;
1978
+ }
1979
+ async function readDirective(response) {
1980
+ if (response.status === 204) {
1981
+ return {
1982
+ directive: parseDirectiveValue(
1983
+ response.headers?.get("x-oneuptime-replay-directive")
1984
+ ),
1985
+ reason: safeReason(response.headers?.get("x-oneuptime-replay-reason")),
1986
+ retryAfterSeconds: parseRetryAfter(
1987
+ response.headers?.get("retry-after") ?? null
1988
+ )
1989
+ };
1990
+ }
1991
+ try {
1992
+ const value = await response.json();
1993
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1994
+ return { directive: null, reason: null, retryAfterSeconds: null };
1995
+ }
1996
+ const body = value;
1997
+ return {
1998
+ directive: parseDirectiveValue(body.directive),
1999
+ reason: safeReason(body.reason) ?? safeReason(body.error),
2000
+ retryAfterSeconds: typeof body.retryAfterSeconds === "number" && Number.isFinite(body.retryAfterSeconds) ? Math.max(0, Math.min(300, body.retryAfterSeconds)) : parseRetryAfter(response.headers?.get("retry-after") ?? null)
2001
+ };
2002
+ } catch {
2003
+ return {
2004
+ directive: null,
2005
+ reason: null,
2006
+ retryAfterSeconds: parseRetryAfter(
2007
+ response.headers?.get("retry-after") ?? null
2008
+ )
2009
+ };
2010
+ }
2011
+ }
2012
+ function parseRetryAfter(value) {
2013
+ if (!value) {
2014
+ return null;
2015
+ }
2016
+ const seconds = Number(value);
2017
+ return Number.isFinite(seconds) && seconds >= 0 ? Math.min(300, seconds) : null;
2018
+ }
2019
+ var ReplayTransport = class {
2020
+ constructor(options) {
2021
+ __publicField(this, "options");
2022
+ __publicField(this, "fetch");
2023
+ __publicField(this, "drainPromise", null);
2024
+ __publicField(this, "retryTimer", null);
2025
+ __publicField(this, "stopped", false);
2026
+ __publicField(this, "flushFailures", 0);
2027
+ __publicField(this, "retryAfterDrainMs", null);
2028
+ __publicField(this, "activeAbortController", null);
2029
+ __publicField(this, "abortCurrentRequest", null);
2030
+ this.options = options;
2031
+ this.fetch = options.startOptions.fetch;
2032
+ }
2033
+ async enqueue(envelope, payload) {
2034
+ if (this.stopped) {
2035
+ return;
2036
+ }
2037
+ const mutation = await this.options.outbox.enqueue({
2038
+ id: frameId(envelope),
2039
+ envelope,
2040
+ payload,
2041
+ attempts: 0,
2042
+ createdAtUnixMs: Date.now()
2043
+ });
2044
+ if (mutation.dropped > 0) {
2045
+ this.options.onDiagnostic("outbox-overflow", {
2046
+ dropped: mutation.dropped
2047
+ });
2048
+ }
2049
+ await this.drain();
2050
+ }
2051
+ async restore() {
2052
+ await this.drain();
2053
+ }
2054
+ async drain() {
2055
+ if (this.stopped) {
2056
+ return;
2057
+ }
2058
+ if (this.drainPromise) {
2059
+ return await this.drainPromise;
2060
+ }
2061
+ this.drainPromise = this.drainLoop().finally(() => {
2062
+ this.drainPromise = null;
2063
+ if (this.retryAfterDrainMs !== null) {
2064
+ const delayMs = this.retryAfterDrainMs;
2065
+ this.retryAfterDrainMs = null;
2066
+ this.scheduleRetry(delayMs);
2067
+ }
2068
+ });
2069
+ return await this.drainPromise;
2070
+ }
2071
+ destroy() {
2072
+ this.stopped = true;
2073
+ this.activeAbortController?.abort();
2074
+ this.abortCurrentRequest?.();
2075
+ if (this.retryTimer) {
2076
+ clearTimeout(this.retryTimer);
2077
+ this.retryTimer = null;
2078
+ }
2079
+ }
2080
+ getFlushFailures() {
2081
+ return this.flushFailures;
2082
+ }
2083
+ async drainLoop() {
2084
+ const frames = await this.options.outbox.list();
2085
+ for (const frame of frames) {
2086
+ if (this.stopped) {
2087
+ return;
2088
+ }
2089
+ const outcome = await this.post(frame);
2090
+ if (outcome === "remove") {
2091
+ await this.options.outbox.remove(frame.id);
2092
+ continue;
2093
+ }
2094
+ if (outcome === "remove-and-halt") {
2095
+ await this.options.outbox.remove(frame.id);
2096
+ return;
2097
+ }
2098
+ if (outcome === "stop") {
2099
+ return;
2100
+ }
2101
+ const attempts = await this.options.outbox.incrementAttempts(
2102
+ frame.id
2103
+ );
2104
+ this.flushFailures += 1;
2105
+ if (attempts >= MAX_ATTEMPTS_PER_FRAME) {
2106
+ await this.options.outbox.remove(frame.id);
2107
+ this.options.onDiagnostic("chunk-retry-exhausted", {
2108
+ chunkIndex: frame.envelope.chunkIndex
2109
+ });
2110
+ continue;
2111
+ }
2112
+ if (this.retryAfterDrainMs === null) {
2113
+ this.retryAfterDrainMs = DEFAULT_RETRY_MS;
2114
+ }
2115
+ return;
2116
+ }
2117
+ }
2118
+ async post(frame) {
2119
+ const encoded = encodeReplayFrame(
2120
+ frame.envelope,
2121
+ frame.payload
2122
+ );
2123
+ if (encoded.body.byteLength > MAX_SESSION_REPLAY_CHUNK_BYTES) {
2124
+ this.options.onDiagnostic("chunk-too-large", {
2125
+ chunkIndex: frame.envelope.chunkIndex,
2126
+ bytes: encoded.body.byteLength
2127
+ });
2128
+ return "remove";
2129
+ }
2130
+ let response;
2131
+ let said;
2132
+ const controller = typeof AbortController === "undefined" ? null : new AbortController();
2133
+ this.activeAbortController = controller;
2134
+ let timedOut = false;
2135
+ let timeout = null;
2136
+ let abortDeadline = null;
2137
+ try {
2138
+ const request = (async () => {
2139
+ const requestInit = {
2140
+ method: "POST",
2141
+ headers: {
2142
+ ...mobileRequestHeaders(this.options.startOptions),
2143
+ "Content-Type": SESSION_REPLAY_CONTENT_TYPE
2144
+ },
2145
+ body: encoded.body
2146
+ };
2147
+ if (controller) {
2148
+ requestInit.signal = controller.signal;
2149
+ }
2150
+ const fetched = await this.fetch(
2151
+ `${this.options.startOptions.host}${CHUNK_PATH}`,
2152
+ requestInit
2153
+ );
2154
+ return { response: fetched, said: await readDirective(fetched) };
2155
+ })();
2156
+ const deadline = new Promise(
2157
+ (_resolve, reject) => {
2158
+ abortDeadline = () => {
2159
+ controller?.abort();
2160
+ reject(new Error("chunk-post-aborted"));
2161
+ };
2162
+ this.abortCurrentRequest = abortDeadline;
2163
+ timeout = setTimeout(() => {
2164
+ timedOut = true;
2165
+ abortDeadline?.();
2166
+ }, CHUNK_POST_TIMEOUT_MS);
2167
+ }
2168
+ );
2169
+ ({ response, said } = await Promise.race([request, deadline]));
2170
+ } catch {
2171
+ if (this.stopped) {
2172
+ return "stop";
2173
+ }
2174
+ this.options.onDiagnostic(
2175
+ timedOut ? "chunk-post-timeout" : "chunk-network-failure",
2176
+ {
2177
+ chunkIndex: frame.envelope.chunkIndex
2178
+ }
2179
+ );
2180
+ return "retry";
2181
+ } finally {
2182
+ if (timeout) {
2183
+ clearTimeout(timeout);
2184
+ }
2185
+ if (this.activeAbortController === controller) {
2186
+ this.activeAbortController = null;
2187
+ }
2188
+ if (this.abortCurrentRequest === abortDeadline) {
2189
+ this.abortCurrentRequest = null;
2190
+ }
2191
+ }
2192
+ if (said.directive) {
2193
+ this.options.onDirective(said.directive, said.reason);
2194
+ }
2195
+ if (said.directive === "stop") {
2196
+ this.stopped = true;
2197
+ await this.options.outbox.clear();
2198
+ return "stop";
2199
+ }
2200
+ if (response.status >= 200 && response.status < 300) {
2201
+ if (said.directive === "throttle") {
2202
+ this.retryAfterDrainMs = Math.min(
2203
+ MAX_RETRY_MS,
2204
+ (said.retryAfterSeconds ?? DEFAULT_RETRY_MS / 1e3) * 1e3
2205
+ );
2206
+ return "remove-and-halt";
2207
+ }
2208
+ return "remove";
2209
+ }
2210
+ if (response.status === 400 || response.status === 413 || response.status === 422) {
2211
+ this.options.onDiagnostic("chunk-refused", {
2212
+ status: response.status,
2213
+ chunkIndex: frame.envelope.chunkIndex,
2214
+ reason: said.reason
2215
+ });
2216
+ return "remove";
2217
+ }
2218
+ if (response.status === 401 || response.status === 403 || response.status === 404) {
2219
+ this.options.onDiagnostic("transport-disabled", {
2220
+ status: response.status,
2221
+ reason: said.reason
2222
+ });
2223
+ this.options.onDirective("stop", said.reason ?? "transport-disabled");
2224
+ this.stopped = true;
2225
+ await this.options.outbox.clear();
2226
+ return "stop";
2227
+ }
2228
+ const retryMs = Math.min(
2229
+ MAX_RETRY_MS,
2230
+ (said.retryAfterSeconds ?? DEFAULT_RETRY_MS / 1e3) * 1e3
2231
+ );
2232
+ this.retryAfterDrainMs = retryMs;
2233
+ return "retry";
2234
+ }
2235
+ scheduleRetry(delayMs) {
2236
+ if (this.retryTimer || this.stopped) {
2237
+ return;
2238
+ }
2239
+ this.retryTimer = setTimeout(
2240
+ () => {
2241
+ this.retryTimer = null;
2242
+ void this.drain();
2243
+ },
2244
+ Math.max(0, delayMs)
2245
+ );
2246
+ }
2247
+ };
2248
+
2249
+ // src/TouchPrivacy.ts
2250
+ var MAX_PRIVACY_TREE_NODES = 5e3;
2251
+ var OPAQUE_KINDS = /* @__PURE__ */ new Set([
2252
+ "image",
2253
+ "webview",
2254
+ "canvas",
2255
+ "masked"
2256
+ ]);
2257
+ var CONSERVATIVE_TOUCH_PRIVACY_MAP = {
2258
+ regions: [],
2259
+ suppressAll: true
2260
+ };
2261
+ function finite(value) {
2262
+ return typeof value === "number" && Number.isFinite(value);
2263
+ }
2264
+ function deriveTouchPrivacyMap(root) {
2265
+ const regions = [];
2266
+ const hasRootOrigin = finite(root.touchOriginX) && finite(root.touchOriginY);
2267
+ const rootX = hasRootOrigin ? root.touchOriginX : 0;
2268
+ const rootY = hasRootOrigin ? root.touchOriginY : 0;
2269
+ let suppressAll = finite(root.truncatedNodes) && root.truncatedNodes > 0;
2270
+ let visited = 0;
2271
+ const visit = (node, parentX, parentY, alignedPath, isRoot) => {
2272
+ if (visited >= MAX_PRIVACY_TREE_NODES) {
2273
+ suppressAll = true;
2274
+ return;
2275
+ }
2276
+ visited += 1;
2277
+ const hasGeometry = finite(node.x) && finite(node.y) && finite(node.width) && finite(node.height);
2278
+ const left = isRoot ? parentX : parentX + (finite(node.x) ? node.x : 0);
2279
+ const top = isRoot ? parentY : parentY + (finite(node.y) ? node.y : 0);
2280
+ const pathIsAligned = alignedPath && hasGeometry;
2281
+ const opaque = node.masked === true || node.opaque === true || OPAQUE_KINDS.has(typeof node.kind === "string" ? node.kind : "");
2282
+ if (opaque) {
2283
+ if (!pathIsAligned || !hasRootOrigin) {
2284
+ suppressAll = true;
2285
+ return;
2286
+ }
2287
+ if (node.width > 0 && node.height > 0) {
2288
+ regions.push({
2289
+ left,
2290
+ top,
2291
+ right: left + node.width,
2292
+ bottom: top + node.height
2293
+ });
2294
+ }
2295
+ return;
2296
+ }
2297
+ if (Array.isArray(node.children)) {
2298
+ for (const child of node.children) {
2299
+ if (child && typeof child === "object") {
2300
+ visit(child, left, top, pathIsAligned, false);
2301
+ }
2302
+ }
2303
+ }
2304
+ };
2305
+ visit(root, rootX, rootY, hasRootOrigin, true);
2306
+ return { regions, suppressAll };
2307
+ }
2308
+ function isTouchPrivate(privacyMap, x2, y) {
2309
+ if (privacyMap.suppressAll || !finite(x2) || !finite(y)) {
2310
+ return true;
2311
+ }
2312
+ return privacyMap.regions.some((region) => {
2313
+ return x2 >= region.left && x2 <= region.right && y >= region.top && y <= region.bottom;
2314
+ });
2315
+ }
2316
+
2317
+ // src/MobileReplayRecorder.ts
2318
+ var MAX_DIAGNOSTIC_EVENTS = 100;
2319
+ var TOUCH_MOVE_SAMPLE_MS = 50;
2320
+ var MOBILE_POLICY_REFRESH_INTERVAL_MS = 5 * 6e4;
2321
+ var SAFE_ERROR_NAMES = /* @__PURE__ */ new Set([
2322
+ "AggregateError",
2323
+ "Error",
2324
+ "EvalError",
2325
+ "RangeError",
2326
+ "ReferenceError",
2327
+ "SyntaxError",
2328
+ "TypeError",
2329
+ "URIError"
2330
+ ]);
2331
+ function defaultNow() {
2332
+ return Date.now();
2333
+ }
2334
+ function deterministicSample(sessionId, percentage) {
2335
+ if (percentage >= 100) {
2336
+ return true;
2337
+ }
2338
+ if (percentage <= 0) {
2339
+ return false;
2340
+ }
2341
+ let hash = 2166136261;
2342
+ for (let index = 0; index < sessionId.length; index += 1) {
2343
+ hash ^= sessionId.charCodeAt(index);
2344
+ hash = Math.imul(hash, 16777619);
2345
+ }
2346
+ return (hash >>> 0) % 1e4 < Math.round(percentage * 100);
2347
+ }
2348
+ function hashNamespaceSecret(value) {
2349
+ const seeds = [
2350
+ 2166136261,
2351
+ 2654435761,
2352
+ 1013904223,
2353
+ 3747611933
2354
+ ];
2355
+ return seeds.map((seed) => {
2356
+ let hash = seed;
2357
+ for (let index = 0; index < value.length; index += 1) {
2358
+ hash ^= value.charCodeAt(index);
2359
+ hash = Math.imul(hash, 16777619);
2360
+ }
2361
+ return (hash >>> 0).toString(16).padStart(8, "0");
2362
+ }).join("");
2363
+ }
2364
+ function getReplayStorageNamespace(options) {
2365
+ const scope = [
2366
+ options.host,
2367
+ options.appIdentifier,
2368
+ options.mobileAppIdentifier,
2369
+ hashNamespaceSecret(options.token)
2370
+ ].join("\0");
2371
+ return `v1-${hashNamespaceSecret(scope)}`;
2372
+ }
2373
+ function safeMetadataValue(value, fallback, maximumLength = 100) {
2374
+ return typeof value === "string" && value.trim() ? truncate(value.trim(), maximumLength) : fallback;
2375
+ }
2376
+ function isRetryablePolicyFailure(config) {
2377
+ if (config.disabledReason === "config-fetch-failed") {
2378
+ return true;
2379
+ }
2380
+ const match = config.disabledReason?.match(/^config-http-(\d{3})$/u) ?? null;
2381
+ if (!match) {
2382
+ return false;
2383
+ }
2384
+ const status = Number(match[1]);
2385
+ return status === 408 || status === 425 || status === 429 || status >= 500;
2386
+ }
2387
+ var MobileReplayRecorder = class {
2388
+ constructor(dependencies = {}) {
2389
+ __publicField(this, "storage");
2390
+ __publicField(this, "nativeViewTree");
2391
+ __publicField(this, "appState");
2392
+ __publicField(this, "now");
2393
+ __publicField(this, "serializer", new ViewTreeSerializer());
2394
+ __publicField(this, "chunkBuffer", new ReplayChunkBuffer());
2395
+ __publicField(this, "rollingBuffer", new RollingReplayBuffer());
2396
+ __publicField(this, "diagnosticsStatus", "idle");
2397
+ __publicField(this, "diagnosticsEvents", []);
2398
+ __publicField(this, "options", null);
2399
+ __publicField(this, "config", null);
2400
+ __publicField(this, "sessionStore", null);
2401
+ __publicField(this, "identity", null);
2402
+ __publicField(this, "outbox", null);
2403
+ __publicField(this, "transport", null);
2404
+ __publicField(this, "metadata", {
2405
+ appName: "React Native",
2406
+ appVersion: "unknown",
2407
+ osName: "unknown",
2408
+ osVersion: "unknown"
2409
+ });
2410
+ __publicField(this, "rootTag", null);
2411
+ __publicField(this, "route", "/");
2412
+ __publicField(this, "entryUrl", "");
2413
+ __publicField(this, "captureTimer", null);
2414
+ __publicField(this, "flushTimer", null);
2415
+ __publicField(this, "appStateSubscription", null);
2416
+ __publicField(this, "captureGeneration", null);
2417
+ __publicField(this, "running", false);
2418
+ __publicField(this, "foreground", true);
2419
+ __publicField(this, "forceFullSnapshot", true);
2420
+ __publicField(this, "lastFullSnapshotAtUnixMs", 0);
2421
+ __publicField(this, "sampled", false);
2422
+ __publicField(this, "triggered", false);
2423
+ __publicField(this, "uploadActive", false);
2424
+ __publicField(this, "consentGranted", false);
2425
+ __publicField(this, "triggerReason", "sampled" /* Sampled */);
2426
+ __publicField(this, "userRef", null);
2427
+ __publicField(this, "traits", {});
2428
+ __publicField(this, "tags", {});
2429
+ __publicField(this, "metaDirty", true);
2430
+ __publicField(this, "droppedEvents", 0);
2431
+ __publicField(this, "closeQueue", Promise.resolve());
2432
+ __publicField(this, "lastTouchMoveAtUnixMs", 0);
2433
+ __publicField(this, "previousGlobalErrorHandler", null);
2434
+ __publicField(this, "installedGlobalErrorHandler", null);
2435
+ __publicField(this, "globalErrorUtils", null);
2436
+ __publicField(this, "lifecycleGeneration", 0);
2437
+ __publicField(this, "lifecycleCancellationEpoch", 0);
2438
+ __publicField(this, "lifecycleQueue", Promise.resolve());
2439
+ __publicField(this, "pendingStart", null);
2440
+ __publicField(this, "pendingStartCancellation", null);
2441
+ __publicField(this, "identityPersisted", false);
2442
+ __publicField(this, "sessionLastActivityUnixMs", 0);
2443
+ __publicField(this, "rotatingSession", false);
2444
+ __publicField(this, "rotationPromise", null);
2445
+ __publicField(this, "appStateQueue", Promise.resolve());
2446
+ __publicField(this, "acceptingEvents", false);
2447
+ __publicField(this, "lastPolicyRefreshAtUnixMs", 0);
2448
+ __publicField(this, "policyRefreshPromise", null);
2449
+ __publicField(this, "identityQueue", Promise.resolve());
2450
+ __publicField(this, "identityCancellationEpoch", 0);
2451
+ __publicField(this, "pendingIdentityOperations", 0);
2452
+ __publicField(this, "sessionChangeListeners", /* @__PURE__ */ new Set());
2453
+ __publicField(this, "lastNotifiedSessionId", null);
2454
+ __publicField(this, "touchPrivacyMap", CONSERVATIVE_TOUCH_PRIVACY_MAP);
2455
+ __publicField(this, "touchQueue", Promise.resolve());
2456
+ __publicField(this, "suppressedTouchPointers", /* @__PURE__ */ new Set());
2457
+ __publicField(this, "unidentifiedTouchCount", 0);
2458
+ __publicField(this, "unidentifiedTouchPrivate", false);
2459
+ this.storage = dependencies.storage ?? defaultReplayStorage;
2460
+ this.nativeViewTree = dependencies.nativeViewTree ?? defaultNativeViewTreeAdapter;
2461
+ this.appState = dependencies.appState ?? import_react_native2.AppState;
2462
+ this.now = dependencies.now ?? defaultNow;
2463
+ }
2464
+ start(options) {
2465
+ if (this.pendingStart && this.pendingStart.generation === this.lifecycleCancellationEpoch) {
2466
+ return this.pendingStart.promise;
2467
+ }
2468
+ const requestGeneration = this.lifecycleCancellationEpoch;
2469
+ const identityBarrier = this.identityQueue;
2470
+ const promise = this.enqueueLifecycleOperation(
2471
+ async () => {
2472
+ await identityBarrier;
2473
+ if (requestGeneration !== this.lifecycleCancellationEpoch) {
2474
+ return false;
2475
+ }
2476
+ return await this.startNow(options);
2477
+ }
2478
+ );
2479
+ this.pendingStart = { generation: requestGeneration, promise };
2480
+ void promise.then(
2481
+ () => {
2482
+ if (this.pendingStart?.promise === promise) {
2483
+ this.pendingStart = null;
2484
+ }
2485
+ },
2486
+ () => {
2487
+ if (this.pendingStart?.promise === promise) {
2488
+ this.pendingStart = null;
2489
+ }
2490
+ }
2491
+ );
2492
+ return promise;
2493
+ }
2494
+ async startNow(options) {
2495
+ if (this.running) {
2496
+ return true;
2497
+ }
2498
+ const generation = this.lifecycleGeneration + 1;
2499
+ this.lifecycleGeneration = generation;
2500
+ let cancelStart = () => {
2501
+ return void 0;
2502
+ };
2503
+ const cancellation = new Promise(
2504
+ (resolve) => {
2505
+ cancelStart = () => {
2506
+ resolve(false);
2507
+ };
2508
+ }
2509
+ );
2510
+ this.pendingStartCancellation = { generation, cancel: cancelStart };
2511
+ const work = this.startInternal(options, generation);
2512
+ try {
2513
+ return await Promise.race([work, cancellation]);
2514
+ } finally {
2515
+ if (this.pendingStartCancellation?.generation === generation) {
2516
+ this.pendingStartCancellation = null;
2517
+ }
2518
+ }
2519
+ }
2520
+ async startInternal(options, generation) {
2521
+ this.acceptingEvents = false;
2522
+ this.diagnosticsStatus = "starting";
2523
+ this.serializer.reset();
2524
+ this.chunkBuffer.clear();
2525
+ this.rollingBuffer.clear();
2526
+ this.closeQueue = Promise.resolve();
2527
+ this.appStateQueue = Promise.resolve();
2528
+ this.rotationPromise = null;
2529
+ this.rotatingSession = false;
2530
+ this.policyRefreshPromise = null;
2531
+ this.lastPolicyRefreshAtUnixMs = 0;
2532
+ this.forceFullSnapshot = true;
2533
+ this.lastFullSnapshotAtUnixMs = 0;
2534
+ this.touchPrivacyMap = CONSERVATIVE_TOUCH_PRIVACY_MAP;
2535
+ this.touchQueue = Promise.resolve();
2536
+ this.resetTouchSequences();
2537
+ const validated = validateStartOptions(options);
2538
+ if (!validated) {
2539
+ this.disable("invalid-start-options");
2540
+ return false;
2541
+ }
2542
+ if (validated.userRef) {
2543
+ this.userRef = truncate(validated.userRef, MAX_USER_REFERENCE_LENGTH);
2544
+ }
2545
+ this.options = this.userRef ? { ...validated, userRef: this.userRef } : validated;
2546
+ if (this.nativeViewTree.isAvailable?.() === false) {
2547
+ this.disable("native-module-unavailable", {
2548
+ expoGoUnsupported: true
2549
+ });
2550
+ return false;
2551
+ }
2552
+ let config = await fetchReplayConfig(this.options);
2553
+ if (!this.isCurrentGeneration(generation)) {
2554
+ return false;
2555
+ }
2556
+ if (config.enabled && this.userRef && (config.consentMode === "NotRequired" /* NotRequired */ || this.consentGranted)) {
2557
+ config = await this.refreshTargetingConfig(config);
2558
+ if (!this.isCurrentGeneration(generation)) {
2559
+ return false;
2560
+ }
2561
+ }
2562
+ this.config = config;
2563
+ if (!config.enabled) {
2564
+ this.disable("config-disabled", {
2565
+ reason: config.disabledReason ?? "not-reported"
2566
+ });
2567
+ return false;
2568
+ }
2569
+ this.lastPolicyRefreshAtUnixMs = this.now();
2570
+ const namespace = getReplayStorageNamespace(validated);
2571
+ const sessionStore = new ReplaySessionStore(
2572
+ this.storage,
2573
+ namespace
2574
+ );
2575
+ this.sessionStore = sessionStore;
2576
+ const nowUnixMs = this.now();
2577
+ const awaitingConsent = config.consentMode === "RequireExplicit" /* RequireExplicit */ && !this.consentGranted;
2578
+ if (awaitingConsent) {
2579
+ this.identity = this.ephemeralIdentity(nowUnixMs);
2580
+ this.identityPersisted = false;
2581
+ this.sampled = false;
2582
+ } else {
2583
+ const identity = await sessionStore.resolve(nowUnixMs);
2584
+ if (!this.isCurrentGeneration(generation) || this.sessionStore !== sessionStore) {
2585
+ return false;
2586
+ }
2587
+ this.identity = identity;
2588
+ this.identityPersisted = true;
2589
+ this.sampled = deterministicSample(
2590
+ this.identity.sessionId,
2591
+ config.samplePercentage
2592
+ );
2593
+ }
2594
+ this.sessionLastActivityUnixMs = nowUnixMs;
2595
+ if (!awaitingConsent && config.captureTrigger === "Always" /* Always */ && !this.sampled && !config.isTargeted) {
2596
+ this.disable("not-sampled");
2597
+ return false;
2598
+ }
2599
+ this.outbox = new ReplayOutbox(this.storage, namespace);
2600
+ this.transport = this.createTransport(validated, this.outbox);
2601
+ try {
2602
+ const nativeMetadata = await this.nativeViewTree.getAppMetadata();
2603
+ if (!this.isCurrentGeneration(generation)) {
2604
+ return false;
2605
+ }
2606
+ this.metadata = {
2607
+ appName: safeMetadataValue(
2608
+ validated.appName,
2609
+ safeMetadataValue(nativeMetadata?.appName, "React Native")
2610
+ ),
2611
+ appVersion: safeMetadataValue(
2612
+ validated.appVersion,
2613
+ safeMetadataValue(nativeMetadata?.appVersion, "unknown")
2614
+ ),
2615
+ osName: safeMetadataValue(nativeMetadata?.osName, "unknown"),
2616
+ osVersion: safeMetadataValue(nativeMetadata?.osVersion, "unknown")
2617
+ };
2618
+ } catch {
2619
+ if (!this.isCurrentGeneration(generation)) {
2620
+ return false;
2621
+ }
2622
+ this.diagnostic("native-metadata-unavailable");
2623
+ }
2624
+ this.route = "/";
2625
+ this.entryUrl = toAppUrl(validated.mobileAppIdentifier, this.route);
2626
+ this.running = true;
2627
+ this.acceptingEvents = true;
2628
+ this.triggered = config.isTargeted;
2629
+ this.triggerReason = config.isTargeted ? "manual" /* Manual */ : "sampled" /* Sampled */;
2630
+ const hasConsent = config.consentMode === "NotRequired" /* NotRequired */ || this.consentGranted;
2631
+ this.uploadActive = hasConsent && (this.sampled || config.isTargeted || this.triggered);
2632
+ this.foreground = this.appState.currentState === "active";
2633
+ this.diagnosticsStatus = hasConsent ? this.foreground ? "recording" : "background" : "consent-required";
2634
+ this.notifySessionChange();
2635
+ this.installLifecycle();
2636
+ this.installGlobalErrorHandler();
2637
+ if (this.foreground) {
2638
+ this.startTimers();
2639
+ await this.captureTick(true);
2640
+ }
2641
+ if (!this.running) {
2642
+ return false;
2643
+ }
2644
+ if (this.uploadActive) {
2645
+ await this.transport.restore();
2646
+ }
2647
+ if (!this.isCurrentGeneration(generation) || !this.running) {
2648
+ return false;
2649
+ }
2650
+ this.diagnostic("recorder-started", {
2651
+ sampled: this.sampled,
2652
+ consentState: this.consentState()
2653
+ });
2654
+ return true;
2655
+ }
2656
+ stop() {
2657
+ const identityBarrier = this.identityQueue;
2658
+ this.lifecycleGeneration += 1;
2659
+ this.lifecycleCancellationEpoch += 1;
2660
+ this.pendingStartCancellation?.cancel();
2661
+ this.acceptingEvents = false;
2662
+ this.quiesceCapture();
2663
+ return this.enqueueLifecycleOperation(async () => {
2664
+ await identityBarrier;
2665
+ await this.stopInternal();
2666
+ });
2667
+ }
2668
+ async stopInternal() {
2669
+ if (!this.running) {
2670
+ this.diagnosticsStatus = "stopped";
2671
+ return;
2672
+ }
2673
+ if (this.rotationPromise) {
2674
+ await this.rotationPromise;
2675
+ }
2676
+ if (!this.running) {
2677
+ this.diagnosticsStatus = "stopped";
2678
+ return;
2679
+ }
2680
+ this.appendCustom(
2681
+ VISIBILITY_CUSTOM_EVENT_TAG,
2682
+ { state: "stopped" },
2683
+ void 0,
2684
+ true
2685
+ );
2686
+ this.haltCapture();
2687
+ await this.closeCurrent(true);
2688
+ if (this.uploadActive) {
2689
+ await this.transport?.drain();
2690
+ } else {
2691
+ this.rollingBuffer.clear();
2692
+ }
2693
+ this.transport?.destroy();
2694
+ this.diagnosticsStatus = "stopped";
2695
+ this.diagnostic("recorder-stopped");
2696
+ }
2697
+ grantConsent() {
2698
+ const identityBarrier = this.identityQueue;
2699
+ return this.enqueueLifecycleOperation(async () => {
2700
+ await identityBarrier;
2701
+ await this.grantConsentInternal();
2702
+ });
2703
+ }
2704
+ async grantConsentInternal() {
2705
+ this.consentGranted = true;
2706
+ if (!this.running && this.options) {
2707
+ const options = this.options;
2708
+ await this.startNow(options);
2709
+ return;
2710
+ }
2711
+ if (!this.running || !this.config) {
2712
+ return;
2713
+ }
2714
+ const generation = this.lifecycleGeneration;
2715
+ const refreshed = await this.refreshRuntimePolicy(
2716
+ generation,
2717
+ true,
2718
+ "consent"
2719
+ );
2720
+ if (!refreshed || !this.running || !this.consentGranted || !this.isCurrentGeneration(generation)) {
2721
+ return;
2722
+ }
2723
+ this.diagnosticsStatus = this.foreground ? "recording" : "background";
2724
+ this.diagnostic("consent-granted");
2725
+ }
2726
+ revokeConsent() {
2727
+ this.lifecycleGeneration += 1;
2728
+ this.lifecycleCancellationEpoch += 1;
2729
+ this.identityCancellationEpoch += 1;
2730
+ this.pendingStartCancellation?.cancel();
2731
+ this.consentGranted = false;
2732
+ this.acceptingEvents = false;
2733
+ this.haltCapture();
2734
+ this.transport?.destroy();
2735
+ return this.enqueueLifecycleOperation(async () => {
2736
+ return this.revokeConsentInternal();
2737
+ });
2738
+ }
2739
+ async revokeConsentInternal() {
2740
+ this.haltCapture();
2741
+ this.transport?.destroy();
2742
+ this.chunkBuffer.clear();
2743
+ this.rollingBuffer.clear();
2744
+ this.serializer.reset();
2745
+ await this.closeQueue.catch(() => {
2746
+ return void 0;
2747
+ });
2748
+ await this.outbox?.clear();
2749
+ await this.sessionStore?.clear();
2750
+ this.identity = null;
2751
+ this.userRef = null;
2752
+ this.traits = {};
2753
+ this.tags = {};
2754
+ this.uploadActive = false;
2755
+ this.triggered = false;
2756
+ this.diagnosticsStatus = "consent-revoked";
2757
+ this.diagnostic("consent-revoked");
2758
+ }
2759
+ identify(userRef, traits) {
2760
+ const trimmedRef = typeof userRef === "string" ? userRef.trim() : "";
2761
+ const ref = trimmedRef ? truncate(trimmedRef, MAX_USER_REFERENCE_LENGTH) : null;
2762
+ const sanitizedTraits = traits === void 0 ? null : maskStringMapValues(sanitizeTraits(traits));
2763
+ const identityEpoch = this.identityCancellationEpoch;
2764
+ const lifecycleBarrier = this.lifecycleQueue;
2765
+ this.pendingIdentityOperations += 1;
2766
+ const operation = this.identityQueue.then(
2767
+ async () => {
2768
+ await lifecycleBarrier;
2769
+ await this.applyIdentityChange(ref, sanitizedTraits, identityEpoch);
2770
+ }
2771
+ );
2772
+ this.identityQueue = operation.then(
2773
+ () => {
2774
+ this.pendingIdentityOperations = Math.max(
2775
+ 0,
2776
+ this.pendingIdentityOperations - 1
2777
+ );
2778
+ },
2779
+ () => {
2780
+ this.pendingIdentityOperations = Math.max(
2781
+ 0,
2782
+ this.pendingIdentityOperations - 1
2783
+ );
2784
+ this.diagnostic("identity-change-failed");
2785
+ }
2786
+ );
2787
+ }
2788
+ setTags(tags) {
2789
+ const sanitizedTags = sanitizeTags(tags);
2790
+ this.runExternalActivity(this.now(), () => {
2791
+ this.tags = sanitizedTags;
2792
+ this.metaDirty = true;
2793
+ this.appendCustom(TAGS_CUSTOM_EVENT_TAG, { ...this.tags });
2794
+ });
2795
+ }
2796
+ addTag(key, value) {
2797
+ if (typeof key !== "string") {
2798
+ return;
2799
+ }
2800
+ const safeKey = truncate(key.trim(), MAX_TAG_KEY_LENGTH);
2801
+ if (!safeKey) {
2802
+ return;
2803
+ }
2804
+ const safeValue = truncate(String(value), MAX_TAG_VALUE_LENGTH);
2805
+ this.runExternalActivity(this.now(), () => {
2806
+ this.tags = sanitizeTags({
2807
+ ...this.tags,
2808
+ [safeKey]: safeValue
2809
+ });
2810
+ this.metaDirty = true;
2811
+ this.appendCustom(TAGS_CUSTOM_EVENT_TAG, { ...this.tags });
2812
+ });
2813
+ }
2814
+ track(name, properties) {
2815
+ const eventName = typeof name === "string" ? truncate(name.trim(), MAX_CUSTOM_EVENT_NAME_LENGTH) : "";
2816
+ if (!eventName) {
2817
+ return;
2818
+ }
2819
+ const sanitizedProperties = maskStringMapValues(
2820
+ sanitizeEventProperties(properties)
2821
+ );
2822
+ this.runExternalActivity(this.now(), () => {
2823
+ this.appendCustom(
2824
+ CUSTOM_EVENT_TAG,
2825
+ { name: eventName, properties: sanitizedProperties },
2826
+ "customEventCount"
2827
+ );
2828
+ });
2829
+ }
2830
+ async setRoute(route) {
2831
+ await this.identityQueue;
2832
+ if (this.running && !this.acceptingEvents) {
2833
+ this.route = sanitizeRoute(route);
2834
+ return;
2835
+ }
2836
+ await this.maybeRotateSession(this.now());
2837
+ if (!this.options) {
2838
+ this.route = sanitizeRoute(route);
2839
+ return;
2840
+ }
2841
+ const previous = this.route;
2842
+ const next = sanitizeRoute(route);
2843
+ if (previous === next) {
2844
+ return;
2845
+ }
2846
+ this.route = next;
2847
+ this.appendCustom(
2848
+ ROUTE_CUSTOM_EVENT_TAG,
2849
+ {
2850
+ from: toAppUrl(this.options.mobileAppIdentifier, previous),
2851
+ to: toAppUrl(this.options.mobileAppIdentifier, next),
2852
+ kind: "manual"
2853
+ },
2854
+ "routeCount"
2855
+ );
2856
+ await this.closeCurrent(false);
2857
+ this.forceFullSnapshot = true;
2858
+ await this.captureTick(true);
2859
+ }
2860
+ async captureSession(reason = "manual") {
2861
+ await this.identityQueue;
2862
+ if (!this.running || !this.acceptingEvents) {
2863
+ return;
2864
+ }
2865
+ await this.maybeRotateSession(this.now());
2866
+ const safeReason2 = truncate(
2867
+ (typeof reason === "string" ? reason.trim() : "") || "manual",
2868
+ MAX_CAPTURE_REASON_LENGTH
2869
+ );
2870
+ this.appendCustom(CUSTOM_EVENT_TAG, {
2871
+ name: "oneuptime.capture",
2872
+ properties: { reason: maskTextValue(safeReason2) }
2873
+ });
2874
+ await this.trigger("manual" /* Manual */);
2875
+ }
2876
+ async captureError(error) {
2877
+ await this.identityQueue;
2878
+ if (!this.running || !this.acceptingEvents) {
2879
+ return;
2880
+ }
2881
+ await this.maybeRotateSession(this.now());
2882
+ const rawName = error instanceof Error && typeof error.name === "string" ? error.name.trim() : "";
2883
+ const name = SAFE_ERROR_NAMES.has(rawName) ? rawName : "Error";
2884
+ this.appendCustom(
2885
+ ERROR_CUSTOM_EVENT_TAG,
2886
+ { kind: "error", name, message: "[masked]" },
2887
+ "errorCount"
2888
+ );
2889
+ await this.trigger("error" /* Error */);
2890
+ }
2891
+ recordTouch(touch) {
2892
+ const sanitizedTouch = sanitizeRecordedTouch(
2893
+ touch,
2894
+ this.now()
2895
+ );
2896
+ if (!sanitizedTouch) {
2897
+ this.diagnostic("invalid-touch-event");
2898
+ return;
2899
+ }
2900
+ if (!this.running || !this.acceptingEvents || !this.foreground) {
2901
+ return;
2902
+ }
2903
+ const generation = this.lifecycleGeneration;
2904
+ const operation = this.touchQueue.then(
2905
+ async () => {
2906
+ await this.recordTouchAfterPrivacyCheck(sanitizedTouch, generation);
2907
+ }
2908
+ );
2909
+ this.touchQueue = operation.catch(() => {
2910
+ this.diagnostic("touch-privacy-check-failed");
2911
+ });
2912
+ }
2913
+ async recordTouchAfterPrivacyCheck(touch, generation) {
2914
+ if (!this.running || !this.acceptingEvents || !this.foreground || !this.isCurrentGeneration(generation)) {
2915
+ return;
2916
+ }
2917
+ const pointIsPrivate = await this.isCurrentTouchPrivate(touch);
2918
+ if (!this.running || !this.acceptingEvents || !this.foreground || !this.isCurrentGeneration(generation)) {
2919
+ return;
2920
+ }
2921
+ if (this.updateTouchSuppression(touch, pointIsPrivate)) {
2922
+ this.diagnostic("touch-suppressed-private-region");
2923
+ return;
2924
+ }
2925
+ if (touch.phase === "move" && touch.timestamp - this.lastTouchMoveAtUnixMs < TOUCH_MOVE_SAMPLE_MS) {
2926
+ return;
2927
+ }
2928
+ if (touch.phase === "move") {
2929
+ this.lastTouchMoveAtUnixMs = touch.timestamp;
2930
+ }
2931
+ const nowUnixMs = this.now();
2932
+ if (this.rotatingSession || this.rotationReason(nowUnixMs)) {
2933
+ this.diagnostic("touch-dropped-session-rotation");
2934
+ void this.maybeRotateSession(nowUnixMs);
2935
+ return;
2936
+ }
2937
+ this.appendEvent(
2938
+ createTouchEvent(touch),
2939
+ touch.phase === "start" ? "clickCount" : void 0
2940
+ );
2941
+ if (touch.phase === "start") {
2942
+ this.appendCustom(TOUCH_CUSTOM_EVENT_TAG, {
2943
+ phase: "start",
2944
+ x: touch.x,
2945
+ y: touch.y
2946
+ });
2947
+ }
2948
+ }
2949
+ setRootTag(rootTag) {
2950
+ this.rootTag = typeof rootTag === "number" && Number.isSafeInteger(rootTag) && rootTag > 0 ? rootTag : null;
2951
+ this.forceFullSnapshot = true;
2952
+ this.touchPrivacyMap = CONSERVATIVE_TOUCH_PRIVACY_MAP;
2953
+ this.resetTouchSequences();
2954
+ if (this.rootTag && this.running && this.acceptingEvents && this.foreground) {
2955
+ void this.captureTick(true);
2956
+ }
2957
+ }
2958
+ getDiagnostics() {
2959
+ return {
2960
+ status: this.diagnosticsStatus,
2961
+ sessionId: this.identity?.sessionId ?? null,
2962
+ recorderKind: MOBILE_RECORDER_KIND,
2963
+ configEpoch: this.config?.configEpoch ?? 0,
2964
+ sampled: this.sampled,
2965
+ triggered: this.triggered,
2966
+ consentState: this.consentState(),
2967
+ pendingEvents: this.rollingBuffer.length + (this.chunkBuffer.isEmpty() ? 0 : 1),
2968
+ events: this.diagnosticsEvents.map(
2969
+ (event) => {
2970
+ const copied = {
2971
+ code: event.code,
2972
+ atUnixMs: event.atUnixMs
2973
+ };
2974
+ if (event.details) {
2975
+ copied.details = { ...event.details };
2976
+ }
2977
+ return copied;
2978
+ }
2979
+ )
2980
+ };
2981
+ }
2982
+ /** Current consented replay session for host trace/log correlation. */
2983
+ getSessionId() {
2984
+ return this.running && this.identityPersisted ? this.identity?.sessionId ?? null : null;
2985
+ }
2986
+ /** Fires immediately when a session exists and again on rotate/clear. */
2987
+ onSessionChange(listener) {
2988
+ if (typeof listener !== "function") {
2989
+ return () => {
2990
+ return void 0;
2991
+ };
2992
+ }
2993
+ this.sessionChangeListeners.add(listener);
2994
+ const sessionId = this.getSessionId();
2995
+ if (sessionId) {
2996
+ try {
2997
+ listener(sessionId);
2998
+ } catch {
2999
+ }
3000
+ }
3001
+ return () => {
3002
+ this.sessionChangeListeners.delete(listener);
3003
+ };
3004
+ }
3005
+ async trigger(reason) {
3006
+ if (!this.running || !this.acceptingEvents || this.triggered || this.uploadActive || this.sampled || this.config?.isTargeted) {
3007
+ return;
3008
+ }
3009
+ this.triggered = true;
3010
+ this.triggerReason = reason;
3011
+ this.diagnostic("capture-triggered", { reason });
3012
+ if (this.consentState() !== "Unknown") {
3013
+ await this.activateUpload(this.lifecycleGeneration);
3014
+ }
3015
+ }
3016
+ async activateUpload(generation) {
3017
+ if (!this.running || !this.acceptingEvents || this.uploadActive || !this.isCurrentGeneration(generation)) {
3018
+ return;
3019
+ }
3020
+ await this.closeCurrent(false);
3021
+ if (!this.running || !this.acceptingEvents || !this.isCurrentGeneration(generation)) {
3022
+ return;
3023
+ }
3024
+ this.uploadActive = true;
3025
+ const buffered = this.rollingBuffer.drain();
3026
+ for (const segment of buffered) {
3027
+ if (!this.running || !this.acceptingEvents || !this.isCurrentGeneration(generation)) {
3028
+ return;
3029
+ }
3030
+ await this.uploadSegment(segment, false);
3031
+ }
3032
+ if (!this.running || !this.acceptingEvents || !this.isCurrentGeneration(generation)) {
3033
+ return;
3034
+ }
3035
+ await this.transport?.restore();
3036
+ }
3037
+ async captureTick(force = false) {
3038
+ const generation = this.lifecycleGeneration;
3039
+ if (!this.running || !this.acceptingEvents || !this.foreground || !this.rootTag || this.captureGeneration === generation || !this.options) {
3040
+ return;
3041
+ }
3042
+ this.captureGeneration = generation;
3043
+ try {
3044
+ const nowUnixMs = this.now();
3045
+ const policyReady = await this.refreshRuntimePolicy(
3046
+ generation,
3047
+ false,
3048
+ "periodic"
3049
+ );
3050
+ if (!policyReady) {
3051
+ return;
3052
+ }
3053
+ await this.maybeRotateSession(nowUnixMs);
3054
+ if (!this.running || !this.acceptingEvents || !this.foreground || !this.isCurrentGeneration(generation)) {
3055
+ return;
3056
+ }
3057
+ const capturedSessionId = this.identity?.sessionId;
3058
+ const tree = await this.nativeViewTree.captureViewTree(this.rootTag);
3059
+ if (!this.running || !this.acceptingEvents || !this.foreground || !this.isCurrentGeneration(generation) || this.rotatingSession || !capturedSessionId || this.identity?.sessionId !== capturedSessionId) {
3060
+ return;
3061
+ }
3062
+ this.touchPrivacyMap = deriveTouchPrivacyMap(tree);
3063
+ const size = getViewport();
3064
+ const shouldForce = force || this.forceFullSnapshot || nowUnixMs - this.lastFullSnapshotAtUnixMs >= SESSION_REPLAY_CHECKOUT_INTERVAL_MS;
3065
+ if (shouldForce && !this.chunkBuffer.isEmpty()) {
3066
+ await this.closeCurrent(false);
3067
+ }
3068
+ if (!this.running || !this.acceptingEvents || !this.foreground || !this.isCurrentGeneration(generation) || this.identity?.sessionId !== capturedSessionId) {
3069
+ return;
3070
+ }
3071
+ const capture = this.serializer.capture(tree, {
3072
+ timestamp: nowUnixMs,
3073
+ url: toAppUrl(this.options.mobileAppIdentifier, this.route),
3074
+ viewportWidth: size.width,
3075
+ viewportHeight: size.height,
3076
+ forceFullSnapshot: shouldForce
3077
+ });
3078
+ this.droppedEvents += capture.droppedNodes;
3079
+ this.chunkBuffer.appendMany(capture.events, {
3080
+ route: toAppUrl(this.options.mobileAppIdentifier, this.route),
3081
+ fidelityNotices: capture.fidelityNotices
3082
+ });
3083
+ if (capture.hasFullSnapshot) {
3084
+ this.lastFullSnapshotAtUnixMs = nowUnixMs;
3085
+ this.forceFullSnapshot = false;
3086
+ }
3087
+ if (this.chunkBuffer.shouldFlush()) {
3088
+ await this.closeCurrent(false);
3089
+ }
3090
+ } catch (error) {
3091
+ if (this.running && this.isCurrentGeneration(generation)) {
3092
+ this.disable("native-capture-failed", {
3093
+ name: error instanceof Error ? error.name : "Error"
3094
+ });
3095
+ this.haltCapture();
3096
+ }
3097
+ } finally {
3098
+ if (this.captureGeneration === generation) {
3099
+ this.captureGeneration = null;
3100
+ }
3101
+ }
3102
+ }
3103
+ appendCustom(tag, payload, signal, allowWhenQuiesced = false) {
3104
+ this.appendEvent(
3105
+ createCustomEvent(tag, payload, this.now()),
3106
+ signal,
3107
+ allowWhenQuiesced
3108
+ );
3109
+ }
3110
+ appendEvent(event, signal, allowWhenQuiesced = false) {
3111
+ if (!this.running || !this.acceptingEvents && !allowWhenQuiesced) {
3112
+ return;
3113
+ }
3114
+ if (!Number.isFinite(event.timestamp)) {
3115
+ this.droppedEvents += 1;
3116
+ this.diagnostic("invalid-event-timestamp");
3117
+ return;
3118
+ }
3119
+ this.sessionLastActivityUnixMs = Math.max(
3120
+ this.sessionLastActivityUnixMs,
3121
+ event.timestamp
3122
+ );
3123
+ const appendOptions = {};
3124
+ if (this.options) {
3125
+ appendOptions.route = toAppUrl(
3126
+ this.options.mobileAppIdentifier,
3127
+ this.route
3128
+ );
3129
+ }
3130
+ if (signal) {
3131
+ appendOptions.signal = signal;
3132
+ }
3133
+ const accepted = this.chunkBuffer.append(event, appendOptions);
3134
+ if (!accepted) {
3135
+ this.droppedEvents += 1;
3136
+ }
3137
+ if (this.chunkBuffer.shouldFlush()) {
3138
+ void this.closeCurrent(false);
3139
+ }
3140
+ }
3141
+ async closeCurrent(isFinal) {
3142
+ const closesFinalChunkSlot = !isFinal && !this.rotatingSession && this.uploadActive && this.sessionStore?.getNextChunkIndex() === MAX_SESSION_REPLAY_CHUNKS_PER_SESSION - 1;
3143
+ if (closesFinalChunkSlot) {
3144
+ this.rotatingSession = true;
3145
+ const rotationOptions = {};
3146
+ if (this.options) {
3147
+ rotationOptions.route = toAppUrl(
3148
+ this.options.mobileAppIdentifier,
3149
+ this.route
3150
+ );
3151
+ }
3152
+ this.chunkBuffer.append(
3153
+ createCustomEvent(
3154
+ "oneuptime.session-rotated",
3155
+ { reason: "chunk-cap" },
3156
+ this.now()
3157
+ ),
3158
+ rotationOptions
3159
+ );
3160
+ }
3161
+ const segment = this.chunkBuffer.take();
3162
+ if (!segment) {
3163
+ if (closesFinalChunkSlot) {
3164
+ this.rotatingSession = false;
3165
+ }
3166
+ return;
3167
+ }
3168
+ const operation = this.closeQueue.then(
3169
+ async () => {
3170
+ return this.processSegment(segment, isFinal || closesFinalChunkSlot);
3171
+ }
3172
+ );
3173
+ if (closesFinalChunkSlot) {
3174
+ const rotation = operation.then(
3175
+ async () => {
3176
+ if (this.running) {
3177
+ await this.completeSessionIdentityRotation(this.now(), "chunk-cap");
3178
+ }
3179
+ }
3180
+ );
3181
+ this.rotationPromise = rotation;
3182
+ this.closeQueue = rotation.catch(() => {
3183
+ this.diagnostic("chunk-close-failed");
3184
+ });
3185
+ try {
3186
+ await rotation;
3187
+ } finally {
3188
+ if (this.rotationPromise === rotation) {
3189
+ this.rotationPromise = null;
3190
+ }
3191
+ this.rotatingSession = false;
3192
+ }
3193
+ return;
3194
+ }
3195
+ this.closeQueue = operation.catch(() => {
3196
+ this.diagnostic("chunk-close-failed");
3197
+ });
3198
+ return await operation;
3199
+ }
3200
+ async processSegment(segment, isFinal) {
3201
+ if (!this.uploadActive) {
3202
+ this.rollingBuffer.push(segment, this.now());
3203
+ return;
3204
+ }
3205
+ await this.uploadSegment(segment, isFinal);
3206
+ }
3207
+ async uploadSegment(segment, isFinal) {
3208
+ if (!this.options || !this.config || !this.identity || !this.sessionStore || !this.transport || !this.identityPersisted) {
3209
+ return;
3210
+ }
3211
+ const chunkIndex = await this.sessionStore.takeChunkIndex(
3212
+ this.now()
3213
+ );
3214
+ if (chunkIndex >= MAX_SESSION_REPLAY_CHUNKS_PER_SESSION) {
3215
+ this.diagnostic("session-chunk-cap-race", { chunkIndex });
3216
+ if (this.running) {
3217
+ await this.completeSessionIdentityRotation(this.now(), "chunk-cap");
3218
+ await this.uploadSegment(segment, isFinal);
3219
+ }
3220
+ return;
3221
+ }
3222
+ const includeMeta = chunkIndex === 0 || isFinal || this.metaDirty;
3223
+ const envelope = {
3224
+ v: SESSION_REPLAY_WIRE_VERSION,
3225
+ appIdentifier: this.options.appIdentifier,
3226
+ sessionId: this.identity.sessionId,
3227
+ tabId: this.identity.tabId,
3228
+ chunkIndex,
3229
+ sessionStartUnixMs: this.identity.sessionStartUnixMs,
3230
+ clientSendUnixMs: this.now(),
3231
+ chunkStartOffsetMs: Math.max(
3232
+ 0,
3233
+ segment.startUnixMs - this.identity.sessionStartUnixMs
3234
+ ),
3235
+ chunkEndOffsetMs: Math.max(
3236
+ 0,
3237
+ segment.endUnixMs - this.identity.sessionStartUnixMs
3238
+ ),
3239
+ eventCount: segment.eventCount,
3240
+ hasFullSnapshot: segment.hasFullSnapshot,
3241
+ isFinal,
3242
+ recorderKind: MOBILE_RECORDER_KIND,
3243
+ schemaVersion: SESSION_REPLAY_SCHEMA_VERSION,
3244
+ rrwebVersion: SYNTHETIC_RRWEB_VERSION,
3245
+ recorderVersion: RECORDER_VERSION,
3246
+ maskingMode: this.config.maskingMode,
3247
+ consentState: this.consentState(),
3248
+ triggerReason: this.triggerReason,
3249
+ payloadEncoding: "gzip",
3250
+ payloadBytes: 0,
3251
+ url: toAppUrl(this.options.mobileAppIdentifier, this.route),
3252
+ routes: segment.routes,
3253
+ signals: segment.signals,
3254
+ fidelityNotices: segment.fidelityNotices,
3255
+ droppedEvents: segment.droppedEvents + this.droppedEvents,
3256
+ flushFailures: this.transport.getFlushFailures()
3257
+ };
3258
+ if (includeMeta) {
3259
+ envelope.meta = this.buildMeta();
3260
+ }
3261
+ if (chunkIndex === 0) {
3262
+ envelope.capabilities = Array.from(
3263
+ SESSION_REPLAY_MOBILE_RECORDER_CAPABILITIES
3264
+ );
3265
+ }
3266
+ this.metaDirty = false;
3267
+ this.droppedEvents = 0;
3268
+ await this.transport.enqueue(envelope, segment.payload);
3269
+ }
3270
+ buildMeta() {
3271
+ const size = getViewport();
3272
+ const captureIdentity = this.config?.captureUserIdentity === true;
3273
+ const meta = {
3274
+ entryUrl: this.entryUrl,
3275
+ browserName: this.metadata.appName,
3276
+ browserVersion: this.metadata.appVersion,
3277
+ osName: `${this.metadata.osName} ${this.metadata.osVersion}`.trim(),
3278
+ deviceType: this.metadata.osName.toLowerCase().includes("ios") ? "ios" : "android",
3279
+ viewportWidth: size.width,
3280
+ viewportHeight: size.height
3281
+ };
3282
+ if (this.identity?.visitorId) {
3283
+ meta.visitorId = this.identity.visitorId;
3284
+ }
3285
+ if (captureIdentity && this.userRef) {
3286
+ meta.identifiedUserRef = this.userRef;
3287
+ }
3288
+ if (captureIdentity && Object.keys(this.traits).length > 0) {
3289
+ meta.identifiedUserTraits = { ...this.traits };
3290
+ }
3291
+ if (Object.keys(this.tags).length > 0) {
3292
+ meta.tags = { ...this.tags };
3293
+ }
3294
+ return meta;
3295
+ }
3296
+ consentState() {
3297
+ if (this.config?.consentMode === "NotRequired" /* NotRequired */) {
3298
+ return "NotRequired";
3299
+ }
3300
+ return this.consentGranted ? "Granted" : "Unknown";
3301
+ }
3302
+ installLifecycle() {
3303
+ this.appStateSubscription?.remove();
3304
+ this.appStateSubscription = this.appState.addEventListener(
3305
+ "change",
3306
+ (state) => {
3307
+ const generation = this.lifecycleGeneration;
3308
+ const operation = this.appStateQueue.then(
3309
+ async () => {
3310
+ await this.onAppStateChange(state, generation);
3311
+ }
3312
+ );
3313
+ this.appStateQueue = operation.catch(() => {
3314
+ this.diagnostic("app-state-transition-failed");
3315
+ });
3316
+ }
3317
+ );
3318
+ }
3319
+ async onAppStateChange(state, generation) {
3320
+ if (!this.running || !this.isCurrentGeneration(generation)) {
3321
+ return;
3322
+ }
3323
+ const isActive = state === "active";
3324
+ if (!isActive && this.foreground) {
3325
+ this.foreground = false;
3326
+ this.clearTimers();
3327
+ this.diagnosticsStatus = "background";
3328
+ if (this.rotationPromise) {
3329
+ await this.rotationPromise;
3330
+ }
3331
+ if (this.policyRefreshPromise) {
3332
+ await this.policyRefreshPromise;
3333
+ }
3334
+ if (!this.running || !this.isCurrentGeneration(generation)) {
3335
+ return;
3336
+ }
3337
+ this.appendCustom(VISIBILITY_CUSTOM_EVENT_TAG, { state: "hidden" });
3338
+ await this.closeCurrent(false);
3339
+ if (this.uploadActive) {
3340
+ await this.transport?.drain();
3341
+ }
3342
+ return;
3343
+ }
3344
+ if (isActive && !this.foreground) {
3345
+ this.foreground = true;
3346
+ const policyReady = await this.refreshRuntimePolicy(
3347
+ generation,
3348
+ true,
3349
+ "foreground"
3350
+ );
3351
+ if (!policyReady) {
3352
+ return;
3353
+ }
3354
+ await this.maybeRotateSession(this.now());
3355
+ if (!this.running || !this.isCurrentGeneration(generation)) {
3356
+ return;
3357
+ }
3358
+ this.appendCustom(VISIBILITY_CUSTOM_EVENT_TAG, { state: "visible" });
3359
+ this.forceFullSnapshot = true;
3360
+ this.startTimers();
3361
+ this.diagnosticsStatus = this.consentState() === "Unknown" ? "consent-required" : "recording";
3362
+ await this.captureTick(true);
3363
+ }
3364
+ }
3365
+ startTimers() {
3366
+ this.clearTimers();
3367
+ const intervalMs = Math.max(
3368
+ 250,
3369
+ Math.min(
3370
+ 5e3,
3371
+ typeof this.options?.captureIntervalMs === "number" && Number.isFinite(this.options.captureIntervalMs) ? this.options.captureIntervalMs : MOBILE_CAPTURE_INTERVAL_MS
3372
+ )
3373
+ );
3374
+ this.captureTimer = setInterval(() => {
3375
+ void this.captureTick();
3376
+ }, intervalMs);
3377
+ this.flushTimer = setInterval(() => {
3378
+ if (this.acceptingEvents) {
3379
+ void this.closeCurrent(false);
3380
+ }
3381
+ }, SESSION_REPLAY_FLUSH_INTERVAL_MS);
3382
+ }
3383
+ clearTimers() {
3384
+ if (this.captureTimer) {
3385
+ clearInterval(this.captureTimer);
3386
+ this.captureTimer = null;
3387
+ }
3388
+ if (this.flushTimer) {
3389
+ clearInterval(this.flushTimer);
3390
+ this.flushTimer = null;
3391
+ }
3392
+ }
3393
+ installGlobalErrorHandler() {
3394
+ const globals = globalThis;
3395
+ const errorUtils = globals["ErrorUtils"];
3396
+ const previous = errorUtils?.getGlobalHandler?.();
3397
+ if (!errorUtils?.setGlobalHandler || typeof previous !== "function") {
3398
+ return;
3399
+ }
3400
+ this.globalErrorUtils = errorUtils;
3401
+ this.previousGlobalErrorHandler = previous;
3402
+ const handlerGeneration = this.lifecycleGeneration;
3403
+ const installedHandler = (error, isFatal) => {
3404
+ if (this.installedGlobalErrorHandler === installedHandler && this.lifecycleGeneration === handlerGeneration) {
3405
+ void this.captureError(error);
3406
+ }
3407
+ try {
3408
+ previous?.(error, isFatal);
3409
+ } catch {
3410
+ }
3411
+ };
3412
+ this.installedGlobalErrorHandler = installedHandler;
3413
+ errorUtils.setGlobalHandler(installedHandler);
3414
+ }
3415
+ restoreGlobalErrorHandler() {
3416
+ if (this.globalErrorUtils?.setGlobalHandler && this.globalErrorUtils.getGlobalHandler?.() === this.installedGlobalErrorHandler && this.previousGlobalErrorHandler) {
3417
+ this.globalErrorUtils.setGlobalHandler(this.previousGlobalErrorHandler);
3418
+ }
3419
+ this.globalErrorUtils = null;
3420
+ this.previousGlobalErrorHandler = null;
3421
+ this.installedGlobalErrorHandler = null;
3422
+ }
3423
+ createTransport(startOptions, outbox) {
3424
+ return new ReplayTransport({
3425
+ startOptions,
3426
+ outbox,
3427
+ onDirective: (directive, reason) => {
3428
+ this.onDirective(directive, reason);
3429
+ },
3430
+ onDiagnostic: (code, details) => {
3431
+ this.diagnostic(code, details);
3432
+ }
3433
+ });
3434
+ }
3435
+ onDirective(directive, reason) {
3436
+ this.diagnostic("server-directive", { directive, reason });
3437
+ if (directive === "stop") {
3438
+ this.disable("server-stopped", { reason });
3439
+ this.haltCapture();
3440
+ this.chunkBuffer.clear();
3441
+ this.rollingBuffer.clear();
3442
+ this.serializer.reset();
3443
+ this.touchPrivacyMap = CONSERVATIVE_TOUCH_PRIVACY_MAP;
3444
+ this.resetTouchSequences();
3445
+ this.uploadActive = false;
3446
+ this.transport?.destroy();
3447
+ }
3448
+ }
3449
+ haltCapture() {
3450
+ this.running = false;
3451
+ this.acceptingEvents = false;
3452
+ this.quiesceCapture();
3453
+ this.notifySessionChange();
3454
+ }
3455
+ notifySessionChange() {
3456
+ const sessionId = this.getSessionId();
3457
+ if (sessionId === this.lastNotifiedSessionId) {
3458
+ return;
3459
+ }
3460
+ this.lastNotifiedSessionId = sessionId;
3461
+ for (const listener of this.sessionChangeListeners) {
3462
+ try {
3463
+ listener(sessionId);
3464
+ } catch {
3465
+ }
3466
+ }
3467
+ }
3468
+ quiesceCapture() {
3469
+ this.clearTimers();
3470
+ this.appStateSubscription?.remove();
3471
+ this.appStateSubscription = null;
3472
+ this.restoreGlobalErrorHandler();
3473
+ }
3474
+ isCurrentGeneration(generation) {
3475
+ return generation === this.lifecycleGeneration;
3476
+ }
3477
+ enqueueLifecycleOperation(action) {
3478
+ const operation = this.lifecycleQueue.then(action, action);
3479
+ this.lifecycleQueue = operation.then(
3480
+ () => {
3481
+ return void 0;
3482
+ },
3483
+ () => {
3484
+ this.diagnostic("lifecycle-operation-failed");
3485
+ }
3486
+ );
3487
+ return operation;
3488
+ }
3489
+ async isCurrentTouchPrivate(touch) {
3490
+ if (!this.rootTag || !touch.targetTag || typeof this.nativeViewTree.isTouchTargetPrivate !== "function") {
3491
+ return true;
3492
+ }
3493
+ try {
3494
+ const privateAncestry = await this.nativeViewTree.isTouchTargetPrivate(
3495
+ touch.targetTag,
3496
+ this.rootTag
3497
+ );
3498
+ if (privateAncestry) {
3499
+ return true;
3500
+ }
3501
+ return isTouchPrivate(this.touchPrivacyMap, touch.x, touch.y);
3502
+ } catch {
3503
+ return true;
3504
+ }
3505
+ }
3506
+ updateTouchSuppression(touch, pointIsPrivate) {
3507
+ if (touch.pointerId !== void 0) {
3508
+ const pointerKey = `${typeof touch.pointerId}:${String(
3509
+ touch.pointerId
3510
+ )}`;
3511
+ if (pointIsPrivate) {
3512
+ this.suppressedTouchPointers.add(pointerKey);
3513
+ }
3514
+ const suppressed2 = this.suppressedTouchPointers.has(pointerKey);
3515
+ if (touch.phase === "end") {
3516
+ this.suppressedTouchPointers.delete(pointerKey);
3517
+ }
3518
+ return suppressed2;
3519
+ }
3520
+ if (touch.phase === "start") {
3521
+ this.unidentifiedTouchCount += 1;
3522
+ }
3523
+ if (pointIsPrivate) {
3524
+ this.unidentifiedTouchPrivate = true;
3525
+ }
3526
+ const suppressed = this.unidentifiedTouchPrivate;
3527
+ if (touch.phase === "end") {
3528
+ this.unidentifiedTouchCount = Math.max(
3529
+ 0,
3530
+ this.unidentifiedTouchCount - 1
3531
+ );
3532
+ if (this.unidentifiedTouchCount === 0) {
3533
+ this.unidentifiedTouchPrivate = false;
3534
+ }
3535
+ }
3536
+ return suppressed;
3537
+ }
3538
+ resetTouchSequences() {
3539
+ this.suppressedTouchPointers.clear();
3540
+ this.unidentifiedTouchCount = 0;
3541
+ this.unidentifiedTouchPrivate = false;
3542
+ }
3543
+ async applyIdentityChange(ref, traits, identityEpoch) {
3544
+ if (identityEpoch !== this.identityCancellationEpoch) {
3545
+ return;
3546
+ }
3547
+ if (!this.running) {
3548
+ this.applyIdentityValues(ref, traits, true);
3549
+ return;
3550
+ }
3551
+ if (ref === this.userRef) {
3552
+ this.applyIdentityValues(ref, traits, false);
3553
+ return;
3554
+ }
3555
+ const wasAcceptingEvents = this.acceptingEvents;
3556
+ this.acceptingEvents = false;
3557
+ try {
3558
+ if (this.policyRefreshPromise) {
3559
+ await this.policyRefreshPromise;
3560
+ }
3561
+ if (!this.running || identityEpoch !== this.identityCancellationEpoch) {
3562
+ return;
3563
+ }
3564
+ if (this.rotationPromise) {
3565
+ await this.rotationPromise;
3566
+ } else {
3567
+ this.rotatingSession = true;
3568
+ const rotation = this.performSessionRotation(
3569
+ this.now(),
3570
+ "identity"
3571
+ );
3572
+ this.rotationPromise = rotation;
3573
+ try {
3574
+ await rotation;
3575
+ } finally {
3576
+ if (this.rotationPromise === rotation) {
3577
+ this.rotationPromise = null;
3578
+ }
3579
+ this.rotatingSession = false;
3580
+ }
3581
+ }
3582
+ if (identityEpoch !== this.identityCancellationEpoch) {
3583
+ return;
3584
+ }
3585
+ this.acceptingEvents = this.running && wasAcceptingEvents;
3586
+ this.triggered = false;
3587
+ this.triggerReason = "sampled" /* Sampled */;
3588
+ this.uploadActive = this.consentState() !== "Unknown" && this.sampled;
3589
+ this.applyIdentityValues(ref, traits, true);
3590
+ const generation = this.lifecycleGeneration;
3591
+ const policyReady = await this.refreshRuntimePolicy(
3592
+ generation,
3593
+ true,
3594
+ "identity"
3595
+ );
3596
+ if (!policyReady || !this.running || !this.isCurrentGeneration(generation) || identityEpoch !== this.identityCancellationEpoch) {
3597
+ return;
3598
+ }
3599
+ if (this.running && this.foreground) {
3600
+ this.forceFullSnapshot = true;
3601
+ void this.captureTick(true);
3602
+ }
3603
+ } finally {
3604
+ if (this.running && identityEpoch === this.identityCancellationEpoch) {
3605
+ this.acceptingEvents = wasAcceptingEvents;
3606
+ }
3607
+ }
3608
+ }
3609
+ applyIdentityValues(ref, traits, clearTraitsWhenMissing) {
3610
+ const changed = ref !== this.userRef;
3611
+ this.userRef = ref;
3612
+ if (traits) {
3613
+ this.traits = traits;
3614
+ } else if (clearTraitsWhenMissing && changed) {
3615
+ this.traits = {};
3616
+ }
3617
+ if (this.options) {
3618
+ const currentOptions = { ...this.options };
3619
+ if (ref) {
3620
+ currentOptions.userRef = ref;
3621
+ } else {
3622
+ delete currentOptions.userRef;
3623
+ }
3624
+ this.options = currentOptions;
3625
+ }
3626
+ this.metaDirty = true;
3627
+ this.appendCustom(IDENTIFY_CUSTOM_EVENT_TAG, {
3628
+ identified: ref !== null,
3629
+ traitKeys: Object.keys(this.traits)
3630
+ });
3631
+ }
3632
+ async refreshTargetingConfig(baseConfig, forceRefresh = false) {
3633
+ if (!this.options || !forceRefresh && !this.userRef) {
3634
+ return baseConfig;
3635
+ }
3636
+ const currentOptions = { ...this.options };
3637
+ if (this.userRef) {
3638
+ currentOptions.userRef = this.userRef;
3639
+ } else {
3640
+ delete currentOptions.userRef;
3641
+ }
3642
+ const targetedConfig = await fetchReplayConfig(
3643
+ currentOptions,
3644
+ Boolean(this.userRef),
3645
+ forceRefresh
3646
+ );
3647
+ return targetedConfig;
3648
+ }
3649
+ async refreshRuntimePolicy(generation, forceRefresh, reason) {
3650
+ if (!this.running || !this.config || !this.options || !this.isCurrentGeneration(generation)) {
3651
+ return false;
3652
+ }
3653
+ if (this.policyRefreshPromise) {
3654
+ const existingResult = await this.policyRefreshPromise;
3655
+ if (!forceRefresh || !existingResult) {
3656
+ return existingResult;
3657
+ }
3658
+ }
3659
+ const nowUnixMs = this.now();
3660
+ if (!forceRefresh && nowUnixMs - this.lastPolicyRefreshAtUnixMs < MOBILE_POLICY_REFRESH_INTERVAL_MS) {
3661
+ return true;
3662
+ }
3663
+ const operation = this.performRuntimePolicyRefresh(
3664
+ generation,
3665
+ reason,
3666
+ nowUnixMs
3667
+ );
3668
+ this.policyRefreshPromise = operation;
3669
+ try {
3670
+ return await operation;
3671
+ } finally {
3672
+ if (this.policyRefreshPromise === operation) {
3673
+ this.policyRefreshPromise = null;
3674
+ }
3675
+ }
3676
+ }
3677
+ async performRuntimePolicyRefresh(generation, reason, nowUnixMs) {
3678
+ if (!this.options || !this.config) {
3679
+ return false;
3680
+ }
3681
+ const previousConfig = this.config;
3682
+ const previousConsentState = this.consentState();
3683
+ const wasAcceptingEvents = this.acceptingEvents;
3684
+ this.acceptingEvents = false;
3685
+ try {
3686
+ let currentOptions = this.currentStartOptions();
3687
+ const maySendUserRef = previousConsentState !== "Unknown" && Boolean(currentOptions.userRef);
3688
+ let freshConfig = await fetchReplayConfig(
3689
+ currentOptions,
3690
+ maySendUserRef,
3691
+ true
3692
+ );
3693
+ if (!this.running || !this.isCurrentGeneration(generation)) {
3694
+ return false;
3695
+ }
3696
+ if (freshConfig.enabled && !maySendUserRef && freshConfig.consentMode === "NotRequired" /* NotRequired */ && this.userRef) {
3697
+ currentOptions = this.currentStartOptions();
3698
+ freshConfig = await fetchReplayConfig(currentOptions, true, true);
3699
+ if (!this.running || !this.isCurrentGeneration(generation)) {
3700
+ return false;
3701
+ }
3702
+ }
3703
+ this.lastPolicyRefreshAtUnixMs = nowUnixMs;
3704
+ if (!freshConfig.enabled) {
3705
+ if (reason !== "consent" && isRetryablePolicyFailure(freshConfig)) {
3706
+ this.diagnostic("policy-refresh-failed-using-last-config", {
3707
+ reason
3708
+ });
3709
+ return true;
3710
+ }
3711
+ this.config = freshConfig;
3712
+ await this.discardForPolicyStop(
3713
+ reason === "consent" ? "config-disabled-after-consent" : "config-disabled-after-refresh",
3714
+ freshConfig.disabledReason ?? "not-reported"
3715
+ );
3716
+ return false;
3717
+ }
3718
+ return await this.applyRuntimePolicy(
3719
+ previousConfig,
3720
+ freshConfig,
3721
+ previousConsentState,
3722
+ wasAcceptingEvents,
3723
+ generation
3724
+ );
3725
+ } catch (error) {
3726
+ if (!this.running || !this.isCurrentGeneration(generation)) {
3727
+ return false;
3728
+ }
3729
+ this.lastPolicyRefreshAtUnixMs = nowUnixMs;
3730
+ if (reason !== "consent") {
3731
+ this.diagnostic("policy-refresh-failed-using-last-config", {
3732
+ reason,
3733
+ name: error instanceof Error ? error.name : "Error"
3734
+ });
3735
+ return true;
3736
+ }
3737
+ await this.discardForPolicyStop(
3738
+ "config-disabled-after-consent",
3739
+ "config-fetch-failed"
3740
+ );
3741
+ return false;
3742
+ } finally {
3743
+ if (this.running && this.isCurrentGeneration(generation)) {
3744
+ this.acceptingEvents = wasAcceptingEvents;
3745
+ }
3746
+ }
3747
+ }
3748
+ async applyRuntimePolicy(_previousConfig, freshConfig, previousConsentState, wasAcceptingEvents, generation) {
3749
+ this.config = freshConfig;
3750
+ this.metaDirty = true;
3751
+ const hasConsent = freshConfig.consentMode === "NotRequired" /* NotRequired */ || this.consentGranted;
3752
+ if (!hasConsent) {
3753
+ const mustWithdrawPersistedState = this.identityPersisted || this.uploadActive || previousConsentState !== "Unknown";
3754
+ if (mustWithdrawPersistedState) {
3755
+ this.transport?.destroy();
3756
+ this.chunkBuffer.clear();
3757
+ this.rollingBuffer.clear();
3758
+ this.serializer.reset();
3759
+ await this.closeQueue.catch(() => {
3760
+ return void 0;
3761
+ });
3762
+ await this.outbox?.clear();
3763
+ await this.sessionStore?.clear();
3764
+ if (!this.running || !this.isCurrentGeneration(generation)) {
3765
+ return false;
3766
+ }
3767
+ const nowUnixMs = this.now();
3768
+ this.identity = this.ephemeralIdentity(nowUnixMs);
3769
+ this.identityPersisted = false;
3770
+ this.notifySessionChange();
3771
+ this.sessionLastActivityUnixMs = nowUnixMs;
3772
+ this.sampled = false;
3773
+ this.uploadActive = false;
3774
+ this.forceFullSnapshot = true;
3775
+ this.touchPrivacyMap = CONSERVATIVE_TOUCH_PRIVACY_MAP;
3776
+ this.resetTouchSequences();
3777
+ this.recreateTransport();
3778
+ }
3779
+ this.triggered = freshConfig.isTargeted;
3780
+ this.triggerReason = freshConfig.isTargeted ? "manual" /* Manual */ : "sampled" /* Sampled */;
3781
+ this.diagnosticsStatus = "consent-required";
3782
+ this.acceptingEvents = wasAcceptingEvents;
3783
+ this.diagnostic("policy-now-requires-consent");
3784
+ return true;
3785
+ }
3786
+ if (!this.identityPersisted && this.sessionStore) {
3787
+ const sessionStore = this.sessionStore;
3788
+ const identity = await sessionStore.resolve(
3789
+ this.now()
3790
+ );
3791
+ if (!this.running || !this.isCurrentGeneration(generation) || this.sessionStore !== sessionStore) {
3792
+ return false;
3793
+ }
3794
+ this.identity = identity;
3795
+ this.identityPersisted = true;
3796
+ this.notifySessionChange();
3797
+ this.sessionLastActivityUnixMs = this.now();
3798
+ }
3799
+ if (!this.identity) {
3800
+ return false;
3801
+ }
3802
+ this.sampled = deterministicSample(
3803
+ this.identity.sessionId,
3804
+ freshConfig.samplePercentage
3805
+ );
3806
+ if (freshConfig.isTargeted) {
3807
+ this.triggered = true;
3808
+ this.triggerReason = "manual" /* Manual */;
3809
+ } else if (!this.triggered) {
3810
+ this.triggerReason = "sampled" /* Sampled */;
3811
+ }
3812
+ if (freshConfig.captureTrigger === "Always" /* Always */ && !this.sampled && !freshConfig.isTargeted) {
3813
+ await this.discardForPolicyStop(
3814
+ "not-sampled-after-config-refresh",
3815
+ "not-sampled"
3816
+ );
3817
+ return false;
3818
+ }
3819
+ const shouldUpload = this.sampled || this.triggered || freshConfig.isTargeted;
3820
+ if (!shouldUpload && this.uploadActive) {
3821
+ this.transport?.destroy();
3822
+ this.chunkBuffer.clear();
3823
+ this.rollingBuffer.clear();
3824
+ await this.closeQueue.catch(() => {
3825
+ return void 0;
3826
+ });
3827
+ await this.outbox?.clear();
3828
+ if (!this.running || !this.isCurrentGeneration(generation)) {
3829
+ return false;
3830
+ }
3831
+ this.uploadActive = false;
3832
+ this.recreateTransport();
3833
+ }
3834
+ this.acceptingEvents = wasAcceptingEvents;
3835
+ if (shouldUpload && !this.uploadActive) {
3836
+ await this.activateUpload(generation);
3837
+ }
3838
+ if (!this.running || !this.isCurrentGeneration(generation)) {
3839
+ return false;
3840
+ }
3841
+ this.diagnosticsStatus = this.foreground ? "recording" : "background";
3842
+ this.diagnostic("policy-refreshed", {
3843
+ configEpoch: freshConfig.configEpoch
3844
+ });
3845
+ return true;
3846
+ }
3847
+ currentStartOptions() {
3848
+ const currentOptions = {
3849
+ ...this.options
3850
+ };
3851
+ if (this.userRef) {
3852
+ currentOptions.userRef = this.userRef;
3853
+ } else {
3854
+ delete currentOptions.userRef;
3855
+ }
3856
+ return currentOptions;
3857
+ }
3858
+ recreateTransport() {
3859
+ if (this.options && this.outbox) {
3860
+ this.transport = this.createTransport(this.options, this.outbox);
3861
+ }
3862
+ }
3863
+ async discardForPolicyStop(code, reason) {
3864
+ this.disable(code, { reason });
3865
+ this.haltCapture();
3866
+ this.transport?.destroy();
3867
+ this.chunkBuffer.clear();
3868
+ this.rollingBuffer.clear();
3869
+ this.serializer.reset();
3870
+ this.touchPrivacyMap = CONSERVATIVE_TOUCH_PRIVACY_MAP;
3871
+ this.resetTouchSequences();
3872
+ this.uploadActive = false;
3873
+ await this.closeQueue.catch(() => {
3874
+ return void 0;
3875
+ });
3876
+ await this.outbox?.clear();
3877
+ }
3878
+ ephemeralIdentity(nowUnixMs) {
3879
+ return {
3880
+ sessionId: generateReplayId(),
3881
+ tabId: generateReplayId(),
3882
+ visitorId: generateReplayId(),
3883
+ sessionStartUnixMs: nowUnixMs,
3884
+ chunkIndex: 0
3885
+ };
3886
+ }
3887
+ rotationReason(nowUnixMs) {
3888
+ if (!this.identity) {
3889
+ return null;
3890
+ }
3891
+ if (nowUnixMs - this.identity.sessionStartUnixMs >= SESSION_REPLAY_MAX_SESSION_MS) {
3892
+ return "duration";
3893
+ }
3894
+ if (nowUnixMs - this.sessionLastActivityUnixMs >= SESSION_REPLAY_IDLE_ROLLOVER_MS) {
3895
+ return "idle";
3896
+ }
3897
+ return null;
3898
+ }
3899
+ runExternalActivity(nowUnixMs, action) {
3900
+ if (this.pendingIdentityOperations > 0) {
3901
+ const identityBarrier = this.identityQueue;
3902
+ const lifecycleEpoch = this.lifecycleCancellationEpoch;
3903
+ const identityEpoch = this.identityCancellationEpoch;
3904
+ const wasRunning = this.running;
3905
+ void identityBarrier.then(() => {
3906
+ if (lifecycleEpoch !== this.lifecycleCancellationEpoch || identityEpoch !== this.identityCancellationEpoch) {
3907
+ return;
3908
+ }
3909
+ if (this.running && this.acceptingEvents || !wasRunning && this.diagnosticsStatus === "idle") {
3910
+ action();
3911
+ }
3912
+ });
3913
+ return;
3914
+ }
3915
+ if (!this.running) {
3916
+ action();
3917
+ return;
3918
+ }
3919
+ const shouldDefer = this.rotatingSession || this.rotationReason(nowUnixMs) !== null;
3920
+ if (!shouldDefer) {
3921
+ action();
3922
+ return;
3923
+ }
3924
+ const generation = this.lifecycleGeneration;
3925
+ void this.maybeRotateSession(nowUnixMs).then(() => {
3926
+ if (this.running && this.isCurrentGeneration(generation)) {
3927
+ action();
3928
+ }
3929
+ });
3930
+ }
3931
+ async maybeRotateSession(nowUnixMs) {
3932
+ if (this.rotationPromise) {
3933
+ await this.rotationPromise;
3934
+ return;
3935
+ }
3936
+ if (!this.running || this.rotatingSession || !this.identity || !this.config || !this.sessionStore || !this.options) {
3937
+ return;
3938
+ }
3939
+ const reason = this.rotationReason(nowUnixMs);
3940
+ if (!reason) {
3941
+ return;
3942
+ }
3943
+ this.rotatingSession = true;
3944
+ const operation = this.performSessionRotation(
3945
+ nowUnixMs,
3946
+ reason
3947
+ );
3948
+ this.rotationPromise = operation;
3949
+ try {
3950
+ await operation;
3951
+ } finally {
3952
+ if (this.rotationPromise === operation) {
3953
+ this.rotationPromise = null;
3954
+ }
3955
+ this.rotatingSession = false;
3956
+ }
3957
+ }
3958
+ async performSessionRotation(nowUnixMs, reason) {
3959
+ this.appendCustom("oneuptime.session-rotated", { reason }, void 0, true);
3960
+ await this.closeCurrent(true);
3961
+ await this.closeQueue;
3962
+ if (!this.running) {
3963
+ return;
3964
+ }
3965
+ if (this.uploadActive) {
3966
+ await this.transport?.drain();
3967
+ } else {
3968
+ this.rollingBuffer.clear();
3969
+ }
3970
+ if (!this.running) {
3971
+ return;
3972
+ }
3973
+ await this.completeSessionIdentityRotation(nowUnixMs, reason);
3974
+ }
3975
+ async completeSessionIdentityRotation(nowUnixMs, reason) {
3976
+ if (!this.sessionStore || !this.config || !this.options) {
3977
+ return;
3978
+ }
3979
+ this.identity = this.identityPersisted ? await this.sessionStore.rotate(nowUnixMs) : this.ephemeralIdentity(nowUnixMs);
3980
+ this.notifySessionChange();
3981
+ this.sampled = this.identityPersisted ? deterministicSample(
3982
+ this.identity.sessionId,
3983
+ this.config.samplePercentage
3984
+ ) : false;
3985
+ this.triggered = this.config.isTargeted;
3986
+ this.triggerReason = this.config.isTargeted ? "manual" /* Manual */ : "sampled" /* Sampled */;
3987
+ this.uploadActive = this.consentState() !== "Unknown" && (this.sampled || this.config.isTargeted);
3988
+ this.serializer.reset();
3989
+ this.chunkBuffer.clear();
3990
+ this.rollingBuffer.clear();
3991
+ this.forceFullSnapshot = true;
3992
+ this.lastFullSnapshotAtUnixMs = 0;
3993
+ this.sessionLastActivityUnixMs = nowUnixMs;
3994
+ this.metaDirty = true;
3995
+ this.entryUrl = toAppUrl(this.options.mobileAppIdentifier, this.route);
3996
+ this.diagnostic("session-rotated", { reason });
3997
+ if (this.config.captureTrigger === "Always" /* Always */ && this.identityPersisted && !this.sampled && !this.config.isTargeted) {
3998
+ this.disable("not-sampled");
3999
+ this.haltCapture();
4000
+ }
4001
+ }
4002
+ disable(code, details) {
4003
+ this.diagnosticsStatus = "disabled";
4004
+ this.diagnostic(code, details);
4005
+ }
4006
+ diagnostic(code, details) {
4007
+ const event = {
4008
+ code,
4009
+ atUnixMs: this.now()
4010
+ };
4011
+ if (details) {
4012
+ event.details = details;
4013
+ }
4014
+ this.diagnosticsEvents.push(event);
4015
+ if (this.diagnosticsEvents.length > MAX_DIAGNOSTIC_EVENTS) {
4016
+ this.diagnosticsEvents.shift();
4017
+ }
4018
+ if (this.options?.debug) {
4019
+ console.info(`[OneUptime Replay] ${code}`, details ?? {});
4020
+ }
4021
+ }
4022
+ };
4023
+
4024
+ // src/ReplayProvider.tsx
4025
+ var import_jsx_runtime = require("react/jsx-runtime");
4026
+ var defaultMobileReplayRecorder = new MobileReplayRecorder();
4027
+ var ReplayContext = (0, import_react.createContext)(defaultMobileReplayRecorder);
4028
+ function touchPointFromEvent(event) {
4029
+ const nativeEvent = event.nativeEvent;
4030
+ const x2 = typeof nativeEvent.pageX === "number" ? nativeEvent.pageX : typeof nativeEvent.locationX === "number" ? nativeEvent.locationX : 0;
4031
+ const y = typeof nativeEvent.pageY === "number" ? nativeEvent.pageY : typeof nativeEvent.locationY === "number" ? nativeEvent.locationY : 0;
4032
+ return {
4033
+ x: Number.isFinite(x2) ? x2 : 0,
4034
+ y: Number.isFinite(y) ? y : 0
4035
+ };
4036
+ }
4037
+ function OneUptimeReplayProvider(props) {
4038
+ const recorder = props.recorder ?? defaultMobileReplayRecorder;
4039
+ const rootRef = (0, import_react.useRef)(null);
4040
+ const initialOptions = (0, import_react.useRef)(props.options);
4041
+ (0, import_react.useEffect)(() => {
4042
+ const tag = (0, import_react_native3.findNodeHandle)(rootRef.current);
4043
+ recorder.setRootTag(tag);
4044
+ if (initialOptions.current) {
4045
+ void recorder.start(initialOptions.current);
4046
+ }
4047
+ return () => {
4048
+ recorder.setRootTag(null);
4049
+ if (initialOptions.current && props.stopOnUnmount !== false) {
4050
+ void recorder.stop();
4051
+ }
4052
+ };
4053
+ }, [props.stopOnUnmount, recorder]);
4054
+ const record = (phase, event) => {
4055
+ const point = touchPointFromEvent(event);
4056
+ const targetTag = event.nativeEvent.target;
4057
+ const pointerId = event.nativeEvent["identifier"];
4058
+ const touch = {
4059
+ phase,
4060
+ x: point.x,
4061
+ y: point.y,
4062
+ timestamp: Date.now()
4063
+ };
4064
+ if (typeof targetTag === "number" && Number.isSafeInteger(targetTag)) {
4065
+ touch.targetTag = targetTag;
4066
+ }
4067
+ if (typeof pointerId === "number" && Number.isSafeInteger(pointerId) || typeof pointerId === "string") {
4068
+ touch.pointerId = pointerId;
4069
+ }
4070
+ recorder.recordTouch(touch);
4071
+ };
4072
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReplayContext.Provider, { value: recorder, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4073
+ import_react_native3.View,
4074
+ {
4075
+ ref: rootRef,
4076
+ collapsable: false,
4077
+ style: [{ flex: 1 }, props.style],
4078
+ onTouchStart: (event) => {
4079
+ record("start", event);
4080
+ },
4081
+ onTouchMove: (event) => {
4082
+ record("move", event);
4083
+ },
4084
+ onTouchEnd: (event) => {
4085
+ record("end", event);
4086
+ },
4087
+ children: props.children
4088
+ }
4089
+ ) });
4090
+ }
4091
+ function useOneUptimeReplay() {
4092
+ return (0, import_react.useContext)(ReplayContext);
4093
+ }
4094
+ function ReplayMask(props) {
4095
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4096
+ import_react_native3.View,
4097
+ {
4098
+ collapsable: false,
4099
+ nativeID: "oneuptime-replay-mask",
4100
+ testID: "oneuptime-replay-mask",
4101
+ style: props.style,
4102
+ children: props.children
4103
+ }
4104
+ );
4105
+ }
4106
+ var ReplayProvider = OneUptimeReplayProvider;
4107
+
4108
+ // src/index.ts
4109
+ var OneUptimeReplay = defaultMobileReplayRecorder;
4110
+ var index_default = OneUptimeReplay;
4111
+ //# sourceMappingURL=index.cjs.map