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