@guidelytech/react-native 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -3
- package/dist/index.cjs +586 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +235 -4
- package/dist/index.d.ts +235 -4
- package/dist/index.js +584 -8
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
4
4
|
|
|
5
5
|
// src/index.tsx
|
|
6
6
|
import { useContext as useContext3, useEffect as useEffect9, useMemo as useMemo6, useRef as useRef7, useState as useState8 } from "react";
|
|
7
|
-
import { AppState as AppState2, Dimensions, Linking, Platform as Platform6, StyleSheet as StyleSheet10, View as View12 } from "react-native";
|
|
7
|
+
import { AppState as AppState2, Dimensions, Linking, NativeModules, Platform as Platform6, StyleSheet as StyleSheet10, View as View12 } from "react-native";
|
|
8
8
|
import { createStaticFlags as createStaticFlags2, stableStringify as stableStringify3 } from "@guidelytech/flags";
|
|
9
9
|
|
|
10
10
|
// src/flags.tsx
|
|
@@ -2428,6 +2428,425 @@ var Messenger = class {
|
|
|
2428
2428
|
}
|
|
2429
2429
|
};
|
|
2430
2430
|
|
|
2431
|
+
// src/core/replay.ts
|
|
2432
|
+
var RrwebEvents = {
|
|
2433
|
+
IMG_ID: 8,
|
|
2434
|
+
TOUCH_START: 7,
|
|
2435
|
+
TOUCH_END: 9,
|
|
2436
|
+
CLICK: 2,
|
|
2437
|
+
CSS: "html,body{margin:0;padding:0;width:100%;height:100%;overflow:hidden;background:#000}img{display:block;width:100%;height:100%;object-fit:contain}",
|
|
2438
|
+
dataUrl(base64) {
|
|
2439
|
+
return `data:image/jpeg;base64,${base64}`;
|
|
2440
|
+
},
|
|
2441
|
+
meta(timestamp, href, width, height) {
|
|
2442
|
+
return JSON.stringify({ type: 4, data: { href, width, height }, timestamp });
|
|
2443
|
+
},
|
|
2444
|
+
fullSnapshot(timestamp, dataUrl, width, height) {
|
|
2445
|
+
const style = { type: 2, id: 5, tagName: "style", attributes: {}, childNodes: [{ type: 3, id: 6, textContent: RrwebEvents.CSS, isStyle: true }] };
|
|
2446
|
+
const img = { type: 2, id: 8, tagName: "img", attributes: { src: dataUrl, width, height, alt: "" }, childNodes: [] };
|
|
2447
|
+
const html = { type: 2, id: 3, tagName: "html", attributes: {}, childNodes: [{ type: 2, id: 4, tagName: "head", attributes: {}, childNodes: [style] }, { type: 2, id: 7, tagName: "body", attributes: {}, childNodes: [img] }] };
|
|
2448
|
+
const node = { type: 0, id: 1, childNodes: [{ type: 1, id: 2, name: "html", publicId: "", systemId: "" }, html] };
|
|
2449
|
+
return JSON.stringify({ type: 2, data: { node, initialOffset: { left: 0, top: 0 } }, timestamp });
|
|
2450
|
+
},
|
|
2451
|
+
frame(timestamp, dataUrl) {
|
|
2452
|
+
return JSON.stringify({ type: 3, data: { source: 0, texts: [], attributes: [{ id: 8, attributes: { src: dataUrl } }], removes: [], adds: [] }, timestamp });
|
|
2453
|
+
},
|
|
2454
|
+
/** MouseInteraction (source 2): 7 TouchStart, 9 TouchEnd, 2 Click. */
|
|
2455
|
+
interaction(timestamp, type, x, y) {
|
|
2456
|
+
return JSON.stringify({ type: 3, data: { source: 2, type, id: 8, x: round1(x), y: round1(y) }, timestamp });
|
|
2457
|
+
},
|
|
2458
|
+
touchMove(timestamp, x, y) {
|
|
2459
|
+
return JSON.stringify({ type: 3, data: { source: 6, positions: [{ x: round1(x), y: round1(y), id: 8, timeOffset: 0 }] }, timestamp });
|
|
2460
|
+
},
|
|
2461
|
+
custom(timestamp, tag, payload) {
|
|
2462
|
+
return JSON.stringify({ type: 5, data: { tag, payload }, timestamp });
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
var round1 = (v) => Math.round(v * 10) / 10;
|
|
2466
|
+
var UPLOAD_TIMEOUT_MS = 3e4;
|
|
2467
|
+
var ReplayUploader = class {
|
|
2468
|
+
constructor(opts) {
|
|
2469
|
+
__publicField(this, "opts", opts);
|
|
2470
|
+
__publicField(this, "windowId", randomId(16));
|
|
2471
|
+
__publicField(this, "buffer", null);
|
|
2472
|
+
__publicField(this, "queue", []);
|
|
2473
|
+
__publicField(this, "queuedBytes", 0);
|
|
2474
|
+
__publicField(this, "seqs", /* @__PURE__ */ new Map());
|
|
2475
|
+
__publicField(this, "inflight", null);
|
|
2476
|
+
__publicField(this, "flushTimer", null);
|
|
2477
|
+
__publicField(this, "retryTimer", null);
|
|
2478
|
+
__publicField(this, "attempt", 0);
|
|
2479
|
+
__publicField(this, "destroyed", false);
|
|
2480
|
+
__publicField(this, "offRecover");
|
|
2481
|
+
this.offRecover = opts.health?.onRecover?.(() => {
|
|
2482
|
+
this.clearRetry();
|
|
2483
|
+
this.attempt = 0;
|
|
2484
|
+
this.flush();
|
|
2485
|
+
}) ?? null;
|
|
2486
|
+
}
|
|
2487
|
+
get pendingChunks() {
|
|
2488
|
+
return this.queue.length;
|
|
2489
|
+
}
|
|
2490
|
+
get bufferedEvents() {
|
|
2491
|
+
return this.buffer?.events.length ?? 0;
|
|
2492
|
+
}
|
|
2493
|
+
push(sessionId, json) {
|
|
2494
|
+
if (this.destroyed) return;
|
|
2495
|
+
if (this.buffer && this.buffer.sessionId !== sessionId) this.seal();
|
|
2496
|
+
const chunk = this.buffer ?? (this.buffer = { sessionId, meta: this.opts.meta(), events: [], bytes: 0 });
|
|
2497
|
+
chunk.events.push(json);
|
|
2498
|
+
chunk.bytes += json.length;
|
|
2499
|
+
if (chunk.bytes > (this.opts.maxBufferBytes ?? 1e6)) this.flush();
|
|
2500
|
+
else if (!this.flushTimer) {
|
|
2501
|
+
this.flushTimer = setTimeout(() => {
|
|
2502
|
+
this.flushTimer = null;
|
|
2503
|
+
this.flush();
|
|
2504
|
+
}, this.opts.flushIntervalMs ?? 1e4);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
/** Seals the buffer and uploads the backlog (one chunk at a time). */
|
|
2508
|
+
flush() {
|
|
2509
|
+
if (this.destroyed) return;
|
|
2510
|
+
this.seal();
|
|
2511
|
+
if (this.retryTimer || this.inflight || !this.queue.length) return;
|
|
2512
|
+
void this.sendNext();
|
|
2513
|
+
}
|
|
2514
|
+
/** Drops everything buffered or queued (consent withdrawn, server disabled replay). */
|
|
2515
|
+
discard() {
|
|
2516
|
+
this.buffer = null;
|
|
2517
|
+
this.queue = [];
|
|
2518
|
+
this.queuedBytes = 0;
|
|
2519
|
+
this.clearFlushTimer();
|
|
2520
|
+
this.clearRetry();
|
|
2521
|
+
this.attempt = 0;
|
|
2522
|
+
}
|
|
2523
|
+
/** Uploads what is left once; no retries afterwards. */
|
|
2524
|
+
destroy() {
|
|
2525
|
+
this.flush();
|
|
2526
|
+
this.destroyed = true;
|
|
2527
|
+
this.offRecover?.();
|
|
2528
|
+
this.clearFlushTimer();
|
|
2529
|
+
this.clearRetry();
|
|
2530
|
+
}
|
|
2531
|
+
clearFlushTimer() {
|
|
2532
|
+
if (this.flushTimer) clearTimeout(this.flushTimer);
|
|
2533
|
+
this.flushTimer = null;
|
|
2534
|
+
}
|
|
2535
|
+
clearRetry() {
|
|
2536
|
+
if (this.retryTimer) clearTimeout(this.retryTimer);
|
|
2537
|
+
this.retryTimer = null;
|
|
2538
|
+
}
|
|
2539
|
+
scheduleRetry(ms) {
|
|
2540
|
+
this.retryTimer = setTimeout(() => {
|
|
2541
|
+
this.retryTimer = null;
|
|
2542
|
+
this.flush();
|
|
2543
|
+
}, ms);
|
|
2544
|
+
}
|
|
2545
|
+
seal() {
|
|
2546
|
+
this.clearFlushTimer();
|
|
2547
|
+
const chunk = this.buffer;
|
|
2548
|
+
this.buffer = null;
|
|
2549
|
+
if (!chunk || !chunk.events.length) return;
|
|
2550
|
+
this.queue.push(chunk);
|
|
2551
|
+
this.queuedBytes += chunk.bytes;
|
|
2552
|
+
const max = this.opts.maxQueuedBytes ?? 5e6;
|
|
2553
|
+
while (this.queuedBytes > max && this.queue.length > 1) {
|
|
2554
|
+
const i = this.inflight && this.queue[0] === this.inflight ? 1 : 0;
|
|
2555
|
+
const [dropped] = this.queue.splice(i, 1);
|
|
2556
|
+
if (!dropped) break;
|
|
2557
|
+
this.queuedBytes -= dropped.bytes;
|
|
2558
|
+
this.opts.log?.(`replay backlog full: dropped a chunk of ${dropped.events.length} events`);
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
nextSeq(chunk) {
|
|
2562
|
+
if (chunk.seq === void 0) {
|
|
2563
|
+
const seq = this.seqs.get(chunk.sessionId) ?? 0;
|
|
2564
|
+
this.seqs.set(chunk.sessionId, seq + 1);
|
|
2565
|
+
chunk.seq = seq;
|
|
2566
|
+
}
|
|
2567
|
+
return chunk.seq;
|
|
2568
|
+
}
|
|
2569
|
+
body(chunk) {
|
|
2570
|
+
const head = JSON.stringify({ sessionId: chunk.sessionId, windowId: this.windowId, seq: this.nextSeq(chunk), ...chunk.meta });
|
|
2571
|
+
return `${head.slice(0, -1)},"events":[${chunk.events.join(",")}]}`;
|
|
2572
|
+
}
|
|
2573
|
+
async sendNext() {
|
|
2574
|
+
const chunk = this.queue[0];
|
|
2575
|
+
if (!chunk) return;
|
|
2576
|
+
const wait = this.destroyed ? 0 : this.opts.health?.waitMs() ?? 0;
|
|
2577
|
+
if (wait > 0) return this.scheduleRetry(wait);
|
|
2578
|
+
this.inflight = chunk;
|
|
2579
|
+
const result = await this.upload(this.body(chunk));
|
|
2580
|
+
this.onResult(chunk, result);
|
|
2581
|
+
}
|
|
2582
|
+
async upload(json) {
|
|
2583
|
+
const f = this.opts.fetch();
|
|
2584
|
+
if (!f) return "drop";
|
|
2585
|
+
const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
2586
|
+
const timer = ac ? setTimeout(() => ac.abort(), UPLOAD_TIMEOUT_MS) : null;
|
|
2587
|
+
try {
|
|
2588
|
+
let res;
|
|
2589
|
+
try {
|
|
2590
|
+
res = await f(this.opts.url(), { method: "POST", body: json, headers: { "Content-Type": "application/json" }, signal: ac?.signal });
|
|
2591
|
+
} catch {
|
|
2592
|
+
this.opts.health?.failure();
|
|
2593
|
+
return "retry";
|
|
2594
|
+
}
|
|
2595
|
+
if (res.status === 429 || res.status >= 500) this.opts.health?.failure();
|
|
2596
|
+
else this.opts.health?.success();
|
|
2597
|
+
if (res.ok) {
|
|
2598
|
+
const data = await res.json().catch(() => null);
|
|
2599
|
+
return data?.disabled ? "disabled" : "ok";
|
|
2600
|
+
}
|
|
2601
|
+
return res.status >= 400 && res.status < 500 && res.status !== 429 ? "drop" : "retry";
|
|
2602
|
+
} finally {
|
|
2603
|
+
if (timer) clearTimeout(timer);
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
onResult(chunk, result) {
|
|
2607
|
+
if (this.inflight === chunk) this.inflight = null;
|
|
2608
|
+
if (result === "retry" && this.queue.includes(chunk)) {
|
|
2609
|
+
if (!this.destroyed) {
|
|
2610
|
+
const max = this.opts.maxAttempts ?? 5;
|
|
2611
|
+
this.attempt = Math.min(this.attempt + 1, max);
|
|
2612
|
+
const delay = this.attempt >= max ? this.opts.slowRetryMs ?? 6e4 : backoffMs(this.attempt - 1, 1e3, 8e3);
|
|
2613
|
+
return this.scheduleRetry(Math.max(delay, this.opts.health?.waitMs() ?? 0));
|
|
2614
|
+
}
|
|
2615
|
+
this.opts.log?.("replay upload failed after the recorder stopped; dropping the chunk");
|
|
2616
|
+
}
|
|
2617
|
+
this.attempt = 0;
|
|
2618
|
+
this.remove(chunk);
|
|
2619
|
+
if (result === "disabled") {
|
|
2620
|
+
for (const c of this.queue.slice()) if (c.sessionId === chunk.sessionId) this.remove(c);
|
|
2621
|
+
if (this.buffer?.sessionId === chunk.sessionId) this.buffer = null;
|
|
2622
|
+
this.opts.onDisabled(chunk.sessionId);
|
|
2623
|
+
}
|
|
2624
|
+
if (this.queue.length && !this.retryTimer && !this.inflight) void this.sendNext();
|
|
2625
|
+
}
|
|
2626
|
+
remove(chunk) {
|
|
2627
|
+
const i = this.queue.indexOf(chunk);
|
|
2628
|
+
if (i >= 0) {
|
|
2629
|
+
this.queue.splice(i, 1);
|
|
2630
|
+
this.queuedBytes -= chunk.bytes;
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
var TOUCH_SETTLE_MS = 200;
|
|
2635
|
+
var IDLE_FRAMES = 3;
|
|
2636
|
+
var MAX_IDLE_INTERVAL_MS = 5e3;
|
|
2637
|
+
var SessionRecorder = class {
|
|
2638
|
+
constructor(opts) {
|
|
2639
|
+
__publicField(this, "opts", opts);
|
|
2640
|
+
__publicField(this, "intervalMs");
|
|
2641
|
+
__publicField(this, "now");
|
|
2642
|
+
__publicField(this, "sid", null);
|
|
2643
|
+
__publicField(this, "foreground", true);
|
|
2644
|
+
__publicField(this, "generation", 0);
|
|
2645
|
+
__publicField(this, "timer", null);
|
|
2646
|
+
__publicField(this, "timerDue", 0);
|
|
2647
|
+
__publicField(this, "capturing", false);
|
|
2648
|
+
__publicField(this, "needKeyframe", true);
|
|
2649
|
+
__publicField(this, "hasKeyframe", false);
|
|
2650
|
+
__publicField(this, "lastFrame", null);
|
|
2651
|
+
__publicField(this, "unchanged", 0);
|
|
2652
|
+
__publicField(this, "captureSoon", false);
|
|
2653
|
+
__publicField(this, "pendingCustom", []);
|
|
2654
|
+
__publicField(this, "touchDown", false);
|
|
2655
|
+
__publicField(this, "downX", 0);
|
|
2656
|
+
__publicField(this, "downY", 0);
|
|
2657
|
+
__publicField(this, "downAt", 0);
|
|
2658
|
+
__publicField(this, "maxDistance", 0);
|
|
2659
|
+
__publicField(this, "lastMoveAt", 0);
|
|
2660
|
+
__publicField(this, "originX", 0);
|
|
2661
|
+
__publicField(this, "originY", 0);
|
|
2662
|
+
/** Viewport of the last frame (points), for chunk metadata. */
|
|
2663
|
+
__publicField(this, "viewportWidth", 0);
|
|
2664
|
+
__publicField(this, "viewportHeight", 0);
|
|
2665
|
+
this.intervalMs = Math.max(500, opts.intervalMs);
|
|
2666
|
+
this.now = opts.now ?? Date.now;
|
|
2667
|
+
}
|
|
2668
|
+
get sessionId() {
|
|
2669
|
+
return this.sid;
|
|
2670
|
+
}
|
|
2671
|
+
get isRecording() {
|
|
2672
|
+
return this.sid !== null;
|
|
2673
|
+
}
|
|
2674
|
+
get currentIntervalMs() {
|
|
2675
|
+
return this.unchanged >= IDLE_FRAMES ? Math.max(this.intervalMs, Math.min(4 * this.intervalMs, MAX_IDLE_INTERVAL_MS)) : this.intervalMs;
|
|
2676
|
+
}
|
|
2677
|
+
/** Starts (or keeps) recording `sessionId`; another session stops the current one first (uploading it). */
|
|
2678
|
+
start(sessionId) {
|
|
2679
|
+
if (this.sid === sessionId) return;
|
|
2680
|
+
if (this.sid !== null) this.stop(false);
|
|
2681
|
+
this.sid = sessionId;
|
|
2682
|
+
this.generation++;
|
|
2683
|
+
this.needKeyframe = true;
|
|
2684
|
+
this.hasKeyframe = false;
|
|
2685
|
+
this.lastFrame = null;
|
|
2686
|
+
this.unchanged = 0;
|
|
2687
|
+
this.captureSoon = false;
|
|
2688
|
+
this.pendingCustom = [];
|
|
2689
|
+
this.touchDown = false;
|
|
2690
|
+
if (this.foreground) this.schedule(0);
|
|
2691
|
+
}
|
|
2692
|
+
/** Stops recording; buffered events are uploaded unless `discard`. */
|
|
2693
|
+
stop(discard) {
|
|
2694
|
+
if (this.sid === null) return;
|
|
2695
|
+
this.sid = null;
|
|
2696
|
+
this.generation++;
|
|
2697
|
+
this.clearTimer();
|
|
2698
|
+
this.capturing = false;
|
|
2699
|
+
this.pendingCustom = [];
|
|
2700
|
+
this.touchDown = false;
|
|
2701
|
+
if (discard) this.opts.uploader.discard();
|
|
2702
|
+
else this.opts.uploader.flush();
|
|
2703
|
+
}
|
|
2704
|
+
/** App backgrounded: no captures; uploads what is buffered. */
|
|
2705
|
+
pause() {
|
|
2706
|
+
this.foreground = false;
|
|
2707
|
+
this.clearTimer();
|
|
2708
|
+
this.generation++;
|
|
2709
|
+
this.capturing = false;
|
|
2710
|
+
this.touchDown = false;
|
|
2711
|
+
this.opts.uploader.flush();
|
|
2712
|
+
}
|
|
2713
|
+
/** App foregrounded: resume with a keyframe. */
|
|
2714
|
+
resume() {
|
|
2715
|
+
if (this.foreground) return;
|
|
2716
|
+
this.foreground = true;
|
|
2717
|
+
this.needKeyframe = true;
|
|
2718
|
+
this.unchanged = 0;
|
|
2719
|
+
if (this.sid !== null) this.schedule(0);
|
|
2720
|
+
}
|
|
2721
|
+
/** A new screen: the next frame is a keyframe with the new href. */
|
|
2722
|
+
onScreen() {
|
|
2723
|
+
if (this.sid === null) return;
|
|
2724
|
+
this.needKeyframe = true;
|
|
2725
|
+
this.activity();
|
|
2726
|
+
}
|
|
2727
|
+
activity() {
|
|
2728
|
+
const wasIdle = this.unchanged >= IDLE_FRAMES;
|
|
2729
|
+
this.unchanged = 0;
|
|
2730
|
+
if (wasIdle && this.sid !== null && this.foreground && !this.capturing && !this.touchDown) this.schedule(this.intervalMs, true);
|
|
2731
|
+
}
|
|
2732
|
+
/** Adds the `guidely` marker for an analytics event. */
|
|
2733
|
+
addGuidelyEvent(name, customName, guideId) {
|
|
2734
|
+
const id = this.sid;
|
|
2735
|
+
if (id === null) return;
|
|
2736
|
+
const payload = { name };
|
|
2737
|
+
if (customName) payload.customName = customName;
|
|
2738
|
+
if (guideId) payload.guideId = guideId;
|
|
2739
|
+
this.activity();
|
|
2740
|
+
const now = this.now();
|
|
2741
|
+
if (!this.hasKeyframe) {
|
|
2742
|
+
if (this.pendingCustom.length < 50) this.pendingCustom.push([now, payload]);
|
|
2743
|
+
return;
|
|
2744
|
+
}
|
|
2745
|
+
this.opts.uploader.push(id, RrwebEvents.custom(now, "guidely", payload));
|
|
2746
|
+
}
|
|
2747
|
+
/** A primary-pointer touch at (x, y) points from the React root view. */
|
|
2748
|
+
touch(phase, rootX, rootY) {
|
|
2749
|
+
const id = this.sid;
|
|
2750
|
+
if (id === null || !this.foreground) return;
|
|
2751
|
+
const x = rootX + this.originX;
|
|
2752
|
+
const y = rootY + this.originY;
|
|
2753
|
+
const now = this.now();
|
|
2754
|
+
const emit = this.hasKeyframe;
|
|
2755
|
+
const push = (json) => emit && this.opts.uploader.push(id, json);
|
|
2756
|
+
switch (phase) {
|
|
2757
|
+
case "down":
|
|
2758
|
+
this.touchDown = true;
|
|
2759
|
+
this.unchanged = 0;
|
|
2760
|
+
this.downX = x;
|
|
2761
|
+
this.downY = y;
|
|
2762
|
+
this.downAt = now;
|
|
2763
|
+
this.maxDistance = 0;
|
|
2764
|
+
this.lastMoveAt = now;
|
|
2765
|
+
push(RrwebEvents.interaction(now, RrwebEvents.TOUCH_START, x, y));
|
|
2766
|
+
return;
|
|
2767
|
+
case "move":
|
|
2768
|
+
if (!this.touchDown) return;
|
|
2769
|
+
this.maxDistance = Math.max(this.maxDistance, Math.hypot(x - this.downX, y - this.downY));
|
|
2770
|
+
if (now - this.lastMoveAt < 50) return;
|
|
2771
|
+
this.lastMoveAt = now;
|
|
2772
|
+
push(RrwebEvents.touchMove(now, x, y));
|
|
2773
|
+
return;
|
|
2774
|
+
default:
|
|
2775
|
+
if (!this.touchDown) return;
|
|
2776
|
+
this.touchDown = false;
|
|
2777
|
+
this.unchanged = 0;
|
|
2778
|
+
this.maxDistance = Math.max(this.maxDistance, Math.hypot(x - this.downX, y - this.downY));
|
|
2779
|
+
push(RrwebEvents.interaction(now, RrwebEvents.TOUCH_END, x, y));
|
|
2780
|
+
if (phase === "up" && this.maxDistance <= 10 && now - this.downAt < 1e3) push(RrwebEvents.interaction(now, RrwebEvents.CLICK, x, y));
|
|
2781
|
+
if (this.capturing) this.captureSoon = true;
|
|
2782
|
+
else this.schedule(TOUCH_SETTLE_MS, true);
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
clearTimer() {
|
|
2786
|
+
if (this.timer) clearTimeout(this.timer);
|
|
2787
|
+
this.timer = null;
|
|
2788
|
+
}
|
|
2789
|
+
/** Schedules the next capture `delay` ms from now, or keeps an earlier one when `ifSooner`. */
|
|
2790
|
+
schedule(delay, ifSooner = false) {
|
|
2791
|
+
const due = this.now() + delay;
|
|
2792
|
+
if (ifSooner && this.timer && this.timerDue <= due) return;
|
|
2793
|
+
this.clearTimer();
|
|
2794
|
+
this.timerDue = due;
|
|
2795
|
+
const gen = this.generation;
|
|
2796
|
+
this.timer = setTimeout(() => {
|
|
2797
|
+
this.timer = null;
|
|
2798
|
+
if (gen === this.generation) void this.tick();
|
|
2799
|
+
}, delay);
|
|
2800
|
+
}
|
|
2801
|
+
async tick() {
|
|
2802
|
+
if (this.sid === null || !this.foreground || this.capturing) return;
|
|
2803
|
+
if (this.touchDown) {
|
|
2804
|
+
this.schedule(this.intervalMs);
|
|
2805
|
+
return;
|
|
2806
|
+
}
|
|
2807
|
+
this.capturing = true;
|
|
2808
|
+
this.captureSoon = false;
|
|
2809
|
+
const gen = this.generation;
|
|
2810
|
+
let frame = null;
|
|
2811
|
+
try {
|
|
2812
|
+
frame = await this.opts.capturer(await this.opts.captureOptions());
|
|
2813
|
+
} catch (err) {
|
|
2814
|
+
this.opts.onError?.(err);
|
|
2815
|
+
}
|
|
2816
|
+
if (gen !== this.generation) return;
|
|
2817
|
+
this.capturing = false;
|
|
2818
|
+
if (frame) this.onFrame(frame, this.now());
|
|
2819
|
+
if (this.sid !== null && this.foreground) this.schedule(this.captureSoon ? TOUCH_SETTLE_MS : this.currentIntervalMs);
|
|
2820
|
+
}
|
|
2821
|
+
onFrame(frame, at) {
|
|
2822
|
+
const id = this.sid;
|
|
2823
|
+
if (id === null || !(frame.width > 0) || !(frame.height > 0) || !frame.base64) return;
|
|
2824
|
+
const sizeChanged = frame.width !== this.viewportWidth || frame.height !== this.viewportHeight;
|
|
2825
|
+
const keyframe = this.needKeyframe || !this.hasKeyframe || sizeChanged;
|
|
2826
|
+
if (!keyframe && this.lastFrame === frame.base64) {
|
|
2827
|
+
this.unchanged++;
|
|
2828
|
+
return;
|
|
2829
|
+
}
|
|
2830
|
+
this.unchanged = 0;
|
|
2831
|
+
this.lastFrame = frame.base64;
|
|
2832
|
+
this.originX = frame.originX ?? 0;
|
|
2833
|
+
this.originY = frame.originY ?? 0;
|
|
2834
|
+
this.viewportWidth = frame.width;
|
|
2835
|
+
this.viewportHeight = frame.height;
|
|
2836
|
+
const url = RrwebEvents.dataUrl(frame.base64);
|
|
2837
|
+
const up = this.opts.uploader;
|
|
2838
|
+
if (!keyframe) return up.push(id, RrwebEvents.frame(at, url));
|
|
2839
|
+
this.needKeyframe = false;
|
|
2840
|
+
up.push(id, RrwebEvents.meta(at, this.opts.href(), frame.width, frame.height));
|
|
2841
|
+
up.push(id, RrwebEvents.fullSnapshot(at, url, frame.width, frame.height));
|
|
2842
|
+
if (!this.hasKeyframe) {
|
|
2843
|
+
this.hasKeyframe = true;
|
|
2844
|
+
for (const [ts, payload] of this.pendingCustom) up.push(id, RrwebEvents.custom(Math.max(ts, at), "guidely", payload));
|
|
2845
|
+
this.pendingCustom = [];
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
};
|
|
2849
|
+
|
|
2431
2850
|
// src/core/storage.ts
|
|
2432
2851
|
var PersistentStore = class {
|
|
2433
2852
|
constructor(backing, onError = () => {
|
|
@@ -2481,7 +2900,7 @@ var PersistentStore = class {
|
|
|
2481
2900
|
|
|
2482
2901
|
// src/core/client.ts
|
|
2483
2902
|
var SDK_NAME = "@guidelytech/react-native";
|
|
2484
|
-
var SDK_VERSION = "0.
|
|
2903
|
+
var SDK_VERSION = "0.13.0";
|
|
2485
2904
|
var DEFAULT_API_URL = "https://guidelytech.com";
|
|
2486
2905
|
var PAGE_SCOPED = /* @__PURE__ */ new Set(["page_load", "url_match", "element_click", "element_visible", "delay"]);
|
|
2487
2906
|
var CACHED_START_MS = 1500;
|
|
@@ -2542,6 +2961,10 @@ var GuidelyClient = class {
|
|
|
2542
2961
|
__publicField(this, "lastAudiences", /* @__PURE__ */ new Set());
|
|
2543
2962
|
__publicField(this, "backgroundedAt", null);
|
|
2544
2963
|
__publicField(this, "messenger", null);
|
|
2964
|
+
__publicField(this, "replayUploader", null);
|
|
2965
|
+
__publicField(this, "recorder", null);
|
|
2966
|
+
/** Replay decision for the current session (sampled once; forced by start/stopSessionRecording). */
|
|
2967
|
+
__publicField(this, "replayDecision", null);
|
|
2545
2968
|
__publicField(this, "ui", { steps: [], checklists: [], survey: null, resourceCenterOpen: false, theme: {}, messenger: false });
|
|
2546
2969
|
__publicField(this, "uiListeners", /* @__PURE__ */ new Set());
|
|
2547
2970
|
/** Feature flags for this environment: `guidely.flags.isEnabled("new-checkout")`. */
|
|
@@ -2693,6 +3116,7 @@ var GuidelyClient = class {
|
|
|
2693
3116
|
if (this.destroyed) return;
|
|
2694
3117
|
this.isReady = true;
|
|
2695
3118
|
if (this.config) this.emitter.emit("ready", this.config);
|
|
3119
|
+
this.syncReplay();
|
|
2696
3120
|
this.notify();
|
|
2697
3121
|
this.evaluate("ready");
|
|
2698
3122
|
} catch (err) {
|
|
@@ -2719,6 +3143,7 @@ var GuidelyClient = class {
|
|
|
2719
3143
|
this.syncMessenger();
|
|
2720
3144
|
if (this.hydrated) this.checkLaunch();
|
|
2721
3145
|
this.emitter.emit("config", this.config);
|
|
3146
|
+
this.syncReplay();
|
|
2722
3147
|
this.log("config loaded", this.config.configVersion, `${this.config.guides.length} guides`);
|
|
2723
3148
|
return true;
|
|
2724
3149
|
}
|
|
@@ -2771,6 +3196,7 @@ var GuidelyClient = class {
|
|
|
2771
3196
|
void this.outbox?.flush();
|
|
2772
3197
|
void this.store.flush();
|
|
2773
3198
|
void this.flags.flush();
|
|
3199
|
+
this.recorder?.pause();
|
|
2774
3200
|
this.messenger?.setForeground(false);
|
|
2775
3201
|
} else if (state === "active" && this.backgroundedAt !== null) {
|
|
2776
3202
|
this.messenger?.setForeground(true);
|
|
@@ -2780,7 +3206,10 @@ var GuidelyClient = class {
|
|
|
2780
3206
|
this.session = newSession();
|
|
2781
3207
|
this.persistSession();
|
|
2782
3208
|
this.firedDelays.clear();
|
|
3209
|
+
this.recorder?.stop(false);
|
|
2783
3210
|
}
|
|
3211
|
+
this.recorder?.resume();
|
|
3212
|
+
this.syncReplay();
|
|
2784
3213
|
this.cameToForeground();
|
|
2785
3214
|
void this.flags.refresh();
|
|
2786
3215
|
if (Date.now() - this.configFetchedAt > this.options.configRefreshMs && this.api.health.canSend()) void this.refresh();
|
|
@@ -2803,6 +3232,9 @@ var GuidelyClient = class {
|
|
|
2803
3232
|
this.queued = [];
|
|
2804
3233
|
this.messenger?.destroy();
|
|
2805
3234
|
this.messenger = null;
|
|
3235
|
+
this.recorder?.stop(!this.analyticsEnabled());
|
|
3236
|
+
this.recorder = null;
|
|
3237
|
+
this.replayUploader?.destroy();
|
|
2806
3238
|
this.visibilityWatchers.forEach((stop) => stop());
|
|
2807
3239
|
this.visibilityWatchers.clear();
|
|
2808
3240
|
this.timers.forEach((t) => clearTimeout(t));
|
|
@@ -2917,6 +3349,7 @@ var GuidelyClient = class {
|
|
|
2917
3349
|
this.store.set(KEYS.anon, this.anonymousId);
|
|
2918
3350
|
this.state = emptyState();
|
|
2919
3351
|
this.persistState();
|
|
3352
|
+
this.recorder?.stop(false);
|
|
2920
3353
|
this.session = newSession();
|
|
2921
3354
|
this.persistSession();
|
|
2922
3355
|
this.lastAudiences = /* @__PURE__ */ new Set();
|
|
@@ -2924,6 +3357,7 @@ var GuidelyClient = class {
|
|
|
2924
3357
|
this.firstVisit = true;
|
|
2925
3358
|
this.exposures.clear();
|
|
2926
3359
|
this.syncFlagAttributes();
|
|
3360
|
+
this.syncReplay();
|
|
2927
3361
|
this.emitter.emit("identify", { userId: null, anonymousId: this.anonymousId });
|
|
2928
3362
|
this.notify();
|
|
2929
3363
|
if (this.isReady) this.evaluate("identify");
|
|
@@ -2938,6 +3372,7 @@ var GuidelyClient = class {
|
|
|
2938
3372
|
this.whenHydrated(() => {
|
|
2939
3373
|
if (consent === "denied") this.queue?.clear();
|
|
2940
3374
|
else if (consent === "granted") void this.queue?.flush();
|
|
3375
|
+
this.syncReplay();
|
|
2941
3376
|
});
|
|
2942
3377
|
}
|
|
2943
3378
|
analyticsEnabled() {
|
|
@@ -2966,6 +3401,7 @@ var GuidelyClient = class {
|
|
|
2966
3401
|
const locale = extra.guideId ? this.guideLocales.get(extra.guideId) : void 0;
|
|
2967
3402
|
if (locale) event.properties = { ...event.properties ?? {}, locale };
|
|
2968
3403
|
this.queue.enqueue(event);
|
|
3404
|
+
this.recorder?.addGuidelyEvent(name, event.customName, event.guideId);
|
|
2969
3405
|
this.emitter.emit("track", event);
|
|
2970
3406
|
return event;
|
|
2971
3407
|
}
|
|
@@ -3003,6 +3439,7 @@ var GuidelyClient = class {
|
|
|
3003
3439
|
}
|
|
3004
3440
|
this.screenName = name;
|
|
3005
3441
|
this.targets.setScreen(name);
|
|
3442
|
+
if (changed) this.recorder?.onScreen();
|
|
3006
3443
|
if (changed) this.syncFlagAttributes();
|
|
3007
3444
|
const url = this.currentUrl();
|
|
3008
3445
|
if (changed) {
|
|
@@ -3037,6 +3474,112 @@ var GuidelyClient = class {
|
|
|
3037
3474
|
if (this.isReady) this.evaluate("click", { targetId });
|
|
3038
3475
|
}
|
|
3039
3476
|
// ---------------------------------------------------------------------------
|
|
3477
|
+
// Session replay
|
|
3478
|
+
// ---------------------------------------------------------------------------
|
|
3479
|
+
replaySettings() {
|
|
3480
|
+
const r = this.config?.settings?.replay;
|
|
3481
|
+
if (!r?.enabled || !this.options.sessionReplay?.enabled || !this.bridge.capturer) return null;
|
|
3482
|
+
return r;
|
|
3483
|
+
}
|
|
3484
|
+
/** Reconciles recording with config, consent, readiness, the session and its sample decision. Idempotent. */
|
|
3485
|
+
syncReplay() {
|
|
3486
|
+
if (this.destroyed || !this.hydrated) return;
|
|
3487
|
+
const r = this.isReady && this.analyticsEnabled() ? this.replaySettings() : null;
|
|
3488
|
+
const id = this.session.id;
|
|
3489
|
+
if (r && this.replayDecision?.sessionId !== id) {
|
|
3490
|
+
this.replayDecision = { sessionId: id, record: rolloutBucket(id, "replay") < (r.sampleRate ?? 100) };
|
|
3491
|
+
this.log(`replay ${this.replayDecision.record ? "sampled" : "not sampled"} for session ${id}`);
|
|
3492
|
+
}
|
|
3493
|
+
if (!r || !this.replayDecision?.record) {
|
|
3494
|
+
if (this.recorder?.isRecording) this.recorder.stop(!this.analyticsEnabled());
|
|
3495
|
+
return;
|
|
3496
|
+
}
|
|
3497
|
+
const rec = this.recorder ?? this.createRecorder();
|
|
3498
|
+
if (rec && rec.sessionId !== id) {
|
|
3499
|
+
this.log(`recording session ${id}`);
|
|
3500
|
+
rec.start(id);
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
createRecorder() {
|
|
3504
|
+
const capturer = this.bridge.capturer;
|
|
3505
|
+
if (!capturer) return null;
|
|
3506
|
+
const uploader = this.replayUploader ?? (this.replayUploader = new ReplayUploader({
|
|
3507
|
+
url: () => this.api.url("/replay"),
|
|
3508
|
+
fetch: () => this.options.fetch ?? (typeof fetch === "function" ? fetch : null),
|
|
3509
|
+
health: this.api.health,
|
|
3510
|
+
meta: () => {
|
|
3511
|
+
const rec2 = this.recorder;
|
|
3512
|
+
return {
|
|
3513
|
+
anonymousId: this.anonymousId,
|
|
3514
|
+
userId: this.user?.id || null,
|
|
3515
|
+
...this.collectUrls() && this.screenName ? { url: this.currentUrl() } : {},
|
|
3516
|
+
device: { type: this.bridge.device.deviceType, os: this.bridge.device.platform },
|
|
3517
|
+
...rec2 && rec2.viewportWidth > 0 && rec2.viewportHeight > 0 ? { viewport: { width: rec2.viewportWidth, height: rec2.viewportHeight } } : {},
|
|
3518
|
+
sdk: { name: SDK_NAME, version: SDK_VERSION }
|
|
3519
|
+
};
|
|
3520
|
+
},
|
|
3521
|
+
onDisabled: (sid) => {
|
|
3522
|
+
if (sid !== this.session.id) return;
|
|
3523
|
+
this.log(`replay disabled by the server for session ${sid}`);
|
|
3524
|
+
this.replayDecision = { sessionId: sid, record: false };
|
|
3525
|
+
this.recorder?.stop(true);
|
|
3526
|
+
},
|
|
3527
|
+
log: (m) => this.log(m)
|
|
3528
|
+
}));
|
|
3529
|
+
const opts = this.options.sessionReplay ?? {};
|
|
3530
|
+
const rec = new SessionRecorder({
|
|
3531
|
+
capturer,
|
|
3532
|
+
uploader,
|
|
3533
|
+
intervalMs: opts.captureIntervalMs ?? 1e3,
|
|
3534
|
+
captureOptions: () => this.replayCaptureOptions(),
|
|
3535
|
+
href: () => this.currentUrl(),
|
|
3536
|
+
onError: (err) => this.reportError(err)
|
|
3537
|
+
});
|
|
3538
|
+
if (this.backgroundedAt !== null) rec.pause();
|
|
3539
|
+
this.recorder = rec;
|
|
3540
|
+
return rec;
|
|
3541
|
+
}
|
|
3542
|
+
async replayCaptureOptions() {
|
|
3543
|
+
const r = this.config?.settings?.replay;
|
|
3544
|
+
const opts = this.options.sessionReplay ?? {};
|
|
3545
|
+
const ids = new Set((r?.blockSelectors ?? []).map((sel) => guidelyIdFromSelector(sel)).filter((id) => !!id));
|
|
3546
|
+
const rects = await Promise.all(Array.from(ids, (id) => this.targets.measure(id)));
|
|
3547
|
+
return { maskAllText: !!opts.maskAllText || !!r?.maskAllText, maskAllImages: !!opts.maskAllImages, blockRects: rects.filter((rect) => !!rect) };
|
|
3548
|
+
}
|
|
3549
|
+
/**
|
|
3550
|
+
* Records this session even when it was not sampled (still needs `sessionReplay.enabled`, replay
|
|
3551
|
+
* enabled for the project, analytics consent and the native replay module). Sticks until the session ends.
|
|
3552
|
+
*/
|
|
3553
|
+
startSessionRecording() {
|
|
3554
|
+
this.whenHydrated(() => {
|
|
3555
|
+
this.replayDecision = { sessionId: this.session.id, record: true };
|
|
3556
|
+
this.syncReplay();
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
/** Stops recording for the rest of this session; what was recorded is uploaded. */
|
|
3560
|
+
stopSessionRecording() {
|
|
3561
|
+
this.whenHydrated(() => {
|
|
3562
|
+
this.replayDecision = { sessionId: this.session.id, record: false };
|
|
3563
|
+
this.recorder?.stop(false);
|
|
3564
|
+
});
|
|
3565
|
+
}
|
|
3566
|
+
/** True while this session is being recorded. */
|
|
3567
|
+
isRecording() {
|
|
3568
|
+
return !!this.recorder?.isRecording;
|
|
3569
|
+
}
|
|
3570
|
+
/** The current analytics session id (also the replay id). */
|
|
3571
|
+
getSessionId() {
|
|
3572
|
+
return this.session.id;
|
|
3573
|
+
}
|
|
3574
|
+
/** True when touches should be passed to {@link recordTouch}. */
|
|
3575
|
+
isRecordingTouches() {
|
|
3576
|
+
return !!this.recorder?.isRecording;
|
|
3577
|
+
}
|
|
3578
|
+
/** A primary-pointer touch on the app, in window points (the provider's touch observer calls this). */
|
|
3579
|
+
recordTouch(phase, x, y) {
|
|
3580
|
+
this.recorder?.touch(phase, x, y);
|
|
3581
|
+
}
|
|
3582
|
+
// ---------------------------------------------------------------------------
|
|
3040
3583
|
// Product analytics (autocapture)
|
|
3041
3584
|
// ---------------------------------------------------------------------------
|
|
3042
3585
|
captureAllowed() {
|
|
@@ -3083,7 +3626,7 @@ var GuidelyClient = class {
|
|
|
3083
3626
|
}
|
|
3084
3627
|
if (this.destroyed) return;
|
|
3085
3628
|
const platform = this.bridge.device.platform;
|
|
3086
|
-
const maskAll = !!this.config?.settings?.replay?.maskAllText;
|
|
3629
|
+
const maskAll = !!this.config?.settings?.replay?.maskAllText || !!this.options.sessionReplay?.maskAllText;
|
|
3087
3630
|
const eventUrl = this.collectUrls() && url ? url : void 0;
|
|
3088
3631
|
if (on.clicks && el) this.enqueue("click", { url: eventUrl, properties: { ...tapProperties(el, tap.x, tap.y, platform, maskAll) } });
|
|
3089
3632
|
const count = this.rage.add(tap.x, tap.y, t);
|
|
@@ -6052,8 +6595,9 @@ function rcLauncherLabel(client) {
|
|
|
6052
6595
|
|
|
6053
6596
|
// src/ui/Autocapture.tsx
|
|
6054
6597
|
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
6598
|
+
var GUIDELY_MASK_NATIVE_ID = "guidely-mask";
|
|
6055
6599
|
function GuidelyMask({ children, ...rest }) {
|
|
6056
|
-
return /* @__PURE__ */ jsx11(View10, { ...rest, children });
|
|
6600
|
+
return /* @__PURE__ */ jsx11(View10, { collapsable: false, ...rest, nativeID: GUIDELY_MASK_NATIVE_ID, children });
|
|
6057
6601
|
}
|
|
6058
6602
|
var RESOLVE = {
|
|
6059
6603
|
isIgnored: (type) => type === GuidelyOverlay,
|
|
@@ -6064,6 +6608,18 @@ function useTapObserver(client) {
|
|
|
6064
6608
|
clientRef.current = client;
|
|
6065
6609
|
return useMemo5(() => {
|
|
6066
6610
|
const tracker = new TapTracker();
|
|
6611
|
+
let replayPointer = null;
|
|
6612
|
+
const replay = (phase, e) => {
|
|
6613
|
+
const c = clientRef.current;
|
|
6614
|
+
const n = e.nativeEvent;
|
|
6615
|
+
if (!c?.isRecordingTouches()) return void (replayPointer = null);
|
|
6616
|
+
if (phase === "down") {
|
|
6617
|
+
if (replayPointer !== null) return;
|
|
6618
|
+
replayPointer = n.identifier;
|
|
6619
|
+
} else if (n.identifier !== replayPointer) return;
|
|
6620
|
+
if (phase === "up" || phase === "cancel") replayPointer = null;
|
|
6621
|
+
c.recordTouch(phase, n.pageX, n.pageY);
|
|
6622
|
+
};
|
|
6067
6623
|
const guard = (fn) => (e) => {
|
|
6068
6624
|
try {
|
|
6069
6625
|
fn(e);
|
|
@@ -6073,12 +6629,17 @@ function useTapObserver(client) {
|
|
|
6073
6629
|
};
|
|
6074
6630
|
return {
|
|
6075
6631
|
onTouchStartCapture: guard((e) => {
|
|
6632
|
+
replay("down", e);
|
|
6076
6633
|
const n = e.nativeEvent;
|
|
6077
6634
|
if (!clientRef.current?.isTapCaptureOn()) return tracker.cancel();
|
|
6078
6635
|
tracker.begin(n.pageX, n.pageY, Date.now(), Array.isArray(n.touches) ? n.touches.length : 1, n.identifier);
|
|
6079
6636
|
}),
|
|
6080
|
-
onTouchMoveCapture: guard((e) =>
|
|
6637
|
+
onTouchMoveCapture: guard((e) => {
|
|
6638
|
+
replay("move", e);
|
|
6639
|
+
tracker.move(e.nativeEvent.pageX, e.nativeEvent.pageY, e.nativeEvent.identifier);
|
|
6640
|
+
}),
|
|
6081
6641
|
onTouchEndCapture: guard((e) => {
|
|
6642
|
+
replay("up", e);
|
|
6082
6643
|
const n = e.nativeEvent;
|
|
6083
6644
|
const tap = tracker.end(n.pageX, n.pageY, Date.now(), n.identifier);
|
|
6084
6645
|
const c = clientRef.current;
|
|
@@ -6086,7 +6647,10 @@ function useTapObserver(client) {
|
|
|
6086
6647
|
const r = resolveTapTarget(e, RESOLVE);
|
|
6087
6648
|
if (!r.ignored) c.recordTap({ x: tap.x, y: tap.y, t: Date.now(), element: r.element, hit: r.hit });
|
|
6088
6649
|
}),
|
|
6089
|
-
onTouchCancelCapture: guard(() =>
|
|
6650
|
+
onTouchCancelCapture: guard((e) => {
|
|
6651
|
+
replay("cancel", e);
|
|
6652
|
+
tracker.cancel();
|
|
6653
|
+
})
|
|
6090
6654
|
};
|
|
6091
6655
|
}, []);
|
|
6092
6656
|
}
|
|
@@ -6180,9 +6744,15 @@ function defaultBridge(appVersion) {
|
|
|
6180
6744
|
},
|
|
6181
6745
|
openExternal: (url) => void Linking.openURL(url).catch(() => {
|
|
6182
6746
|
}),
|
|
6183
|
-
windowSize: () => Dimensions.get("window")
|
|
6747
|
+
windowSize: () => Dimensions.get("window"),
|
|
6748
|
+
capturer: nativeCapturer()
|
|
6184
6749
|
};
|
|
6185
6750
|
}
|
|
6751
|
+
function nativeCapturer() {
|
|
6752
|
+
const mod = NativeModules.GuidelyReplay;
|
|
6753
|
+
if (!mod || typeof mod.capture !== "function") return void 0;
|
|
6754
|
+
return (options) => mod.capture(options);
|
|
6755
|
+
}
|
|
6186
6756
|
function GuidelyProvider({ children, user, insets, launcherOffset = 72, flagsPayload, flagAttributes, ...options }) {
|
|
6187
6757
|
const [client, setClient] = useState8(null);
|
|
6188
6758
|
const optionsRef = useRef7(options);
|
|
@@ -6252,7 +6822,7 @@ function GuidelyProvider({ children, user, insets, launcherOffset = 72, flagsPay
|
|
|
6252
6822
|
const flags = useMemo6(() => ({ client: client?.flags ?? null, bootstrap }), [client, bootstrap]);
|
|
6253
6823
|
const tapObserver = useTapObserver(client);
|
|
6254
6824
|
return /* @__PURE__ */ jsx13(GuidelyContext.Provider, { value: client, children: /* @__PURE__ */ jsxs9(FlagsContext.Provider, { value: flags, children: [
|
|
6255
|
-
options.autocapture === false ? children : /* @__PURE__ */ jsx13(View12, { style: styles10.root, ...tapObserver, children }),
|
|
6825
|
+
options.autocapture === false && !options.sessionReplay?.enabled ? children : /* @__PURE__ */ jsx13(View12, { style: styles10.root, ...tapObserver, children }),
|
|
6256
6826
|
client ? /* @__PURE__ */ jsx13(GuidelyOverlay, { client, insets, launcherOffset }) : null
|
|
6257
6827
|
] }) });
|
|
6258
6828
|
}
|
|
@@ -6272,6 +6842,9 @@ var NOOP_RESULTS = {
|
|
|
6272
6842
|
getMessenger: null,
|
|
6273
6843
|
isGuideCompleted: false,
|
|
6274
6844
|
isClientReady: false,
|
|
6845
|
+
isRecording: false,
|
|
6846
|
+
isRecordingTouches: false,
|
|
6847
|
+
getSessionId: "",
|
|
6275
6848
|
getScreen: "",
|
|
6276
6849
|
on: () => {
|
|
6277
6850
|
},
|
|
@@ -6405,8 +6978,11 @@ export {
|
|
|
6405
6978
|
InMemoryStickyBucketStore,
|
|
6406
6979
|
Messenger,
|
|
6407
6980
|
RageClickDetector,
|
|
6981
|
+
ReplayUploader,
|
|
6982
|
+
RrwebEvents,
|
|
6408
6983
|
SDK_NAME,
|
|
6409
6984
|
SDK_VERSION,
|
|
6985
|
+
SessionRecorder,
|
|
6410
6986
|
TapTracker,
|
|
6411
6987
|
TargetRegistry,
|
|
6412
6988
|
createFlagsClient3 as createFlagsClient,
|