@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.cjs
CHANGED
|
@@ -37,8 +37,11 @@ __export(index_exports, {
|
|
|
37
37
|
InMemoryStickyBucketStore: () => import_flags6.InMemoryStickyBucketStore,
|
|
38
38
|
Messenger: () => Messenger,
|
|
39
39
|
RageClickDetector: () => RageClickDetector,
|
|
40
|
+
ReplayUploader: () => ReplayUploader,
|
|
41
|
+
RrwebEvents: () => RrwebEvents,
|
|
40
42
|
SDK_NAME: () => SDK_NAME,
|
|
41
43
|
SDK_VERSION: () => SDK_VERSION,
|
|
44
|
+
SessionRecorder: () => SessionRecorder,
|
|
42
45
|
TapTracker: () => TapTracker,
|
|
43
46
|
TargetRegistry: () => TargetRegistry,
|
|
44
47
|
createFlagsClient: () => import_flags6.createFlagsClient,
|
|
@@ -2492,6 +2495,425 @@ var Messenger = class {
|
|
|
2492
2495
|
}
|
|
2493
2496
|
};
|
|
2494
2497
|
|
|
2498
|
+
// src/core/replay.ts
|
|
2499
|
+
var RrwebEvents = {
|
|
2500
|
+
IMG_ID: 8,
|
|
2501
|
+
TOUCH_START: 7,
|
|
2502
|
+
TOUCH_END: 9,
|
|
2503
|
+
CLICK: 2,
|
|
2504
|
+
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}",
|
|
2505
|
+
dataUrl(base64) {
|
|
2506
|
+
return `data:image/jpeg;base64,${base64}`;
|
|
2507
|
+
},
|
|
2508
|
+
meta(timestamp, href, width, height) {
|
|
2509
|
+
return JSON.stringify({ type: 4, data: { href, width, height }, timestamp });
|
|
2510
|
+
},
|
|
2511
|
+
fullSnapshot(timestamp, dataUrl, width, height) {
|
|
2512
|
+
const style = { type: 2, id: 5, tagName: "style", attributes: {}, childNodes: [{ type: 3, id: 6, textContent: RrwebEvents.CSS, isStyle: true }] };
|
|
2513
|
+
const img = { type: 2, id: 8, tagName: "img", attributes: { src: dataUrl, width, height, alt: "" }, childNodes: [] };
|
|
2514
|
+
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] }] };
|
|
2515
|
+
const node = { type: 0, id: 1, childNodes: [{ type: 1, id: 2, name: "html", publicId: "", systemId: "" }, html] };
|
|
2516
|
+
return JSON.stringify({ type: 2, data: { node, initialOffset: { left: 0, top: 0 } }, timestamp });
|
|
2517
|
+
},
|
|
2518
|
+
frame(timestamp, dataUrl) {
|
|
2519
|
+
return JSON.stringify({ type: 3, data: { source: 0, texts: [], attributes: [{ id: 8, attributes: { src: dataUrl } }], removes: [], adds: [] }, timestamp });
|
|
2520
|
+
},
|
|
2521
|
+
/** MouseInteraction (source 2): 7 TouchStart, 9 TouchEnd, 2 Click. */
|
|
2522
|
+
interaction(timestamp, type, x, y) {
|
|
2523
|
+
return JSON.stringify({ type: 3, data: { source: 2, type, id: 8, x: round1(x), y: round1(y) }, timestamp });
|
|
2524
|
+
},
|
|
2525
|
+
touchMove(timestamp, x, y) {
|
|
2526
|
+
return JSON.stringify({ type: 3, data: { source: 6, positions: [{ x: round1(x), y: round1(y), id: 8, timeOffset: 0 }] }, timestamp });
|
|
2527
|
+
},
|
|
2528
|
+
custom(timestamp, tag, payload) {
|
|
2529
|
+
return JSON.stringify({ type: 5, data: { tag, payload }, timestamp });
|
|
2530
|
+
}
|
|
2531
|
+
};
|
|
2532
|
+
var round1 = (v) => Math.round(v * 10) / 10;
|
|
2533
|
+
var UPLOAD_TIMEOUT_MS = 3e4;
|
|
2534
|
+
var ReplayUploader = class {
|
|
2535
|
+
constructor(opts) {
|
|
2536
|
+
__publicField(this, "opts", opts);
|
|
2537
|
+
__publicField(this, "windowId", randomId(16));
|
|
2538
|
+
__publicField(this, "buffer", null);
|
|
2539
|
+
__publicField(this, "queue", []);
|
|
2540
|
+
__publicField(this, "queuedBytes", 0);
|
|
2541
|
+
__publicField(this, "seqs", /* @__PURE__ */ new Map());
|
|
2542
|
+
__publicField(this, "inflight", null);
|
|
2543
|
+
__publicField(this, "flushTimer", null);
|
|
2544
|
+
__publicField(this, "retryTimer", null);
|
|
2545
|
+
__publicField(this, "attempt", 0);
|
|
2546
|
+
__publicField(this, "destroyed", false);
|
|
2547
|
+
__publicField(this, "offRecover");
|
|
2548
|
+
this.offRecover = opts.health?.onRecover?.(() => {
|
|
2549
|
+
this.clearRetry();
|
|
2550
|
+
this.attempt = 0;
|
|
2551
|
+
this.flush();
|
|
2552
|
+
}) ?? null;
|
|
2553
|
+
}
|
|
2554
|
+
get pendingChunks() {
|
|
2555
|
+
return this.queue.length;
|
|
2556
|
+
}
|
|
2557
|
+
get bufferedEvents() {
|
|
2558
|
+
return this.buffer?.events.length ?? 0;
|
|
2559
|
+
}
|
|
2560
|
+
push(sessionId, json) {
|
|
2561
|
+
if (this.destroyed) return;
|
|
2562
|
+
if (this.buffer && this.buffer.sessionId !== sessionId) this.seal();
|
|
2563
|
+
const chunk = this.buffer ?? (this.buffer = { sessionId, meta: this.opts.meta(), events: [], bytes: 0 });
|
|
2564
|
+
chunk.events.push(json);
|
|
2565
|
+
chunk.bytes += json.length;
|
|
2566
|
+
if (chunk.bytes > (this.opts.maxBufferBytes ?? 1e6)) this.flush();
|
|
2567
|
+
else if (!this.flushTimer) {
|
|
2568
|
+
this.flushTimer = setTimeout(() => {
|
|
2569
|
+
this.flushTimer = null;
|
|
2570
|
+
this.flush();
|
|
2571
|
+
}, this.opts.flushIntervalMs ?? 1e4);
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
/** Seals the buffer and uploads the backlog (one chunk at a time). */
|
|
2575
|
+
flush() {
|
|
2576
|
+
if (this.destroyed) return;
|
|
2577
|
+
this.seal();
|
|
2578
|
+
if (this.retryTimer || this.inflight || !this.queue.length) return;
|
|
2579
|
+
void this.sendNext();
|
|
2580
|
+
}
|
|
2581
|
+
/** Drops everything buffered or queued (consent withdrawn, server disabled replay). */
|
|
2582
|
+
discard() {
|
|
2583
|
+
this.buffer = null;
|
|
2584
|
+
this.queue = [];
|
|
2585
|
+
this.queuedBytes = 0;
|
|
2586
|
+
this.clearFlushTimer();
|
|
2587
|
+
this.clearRetry();
|
|
2588
|
+
this.attempt = 0;
|
|
2589
|
+
}
|
|
2590
|
+
/** Uploads what is left once; no retries afterwards. */
|
|
2591
|
+
destroy() {
|
|
2592
|
+
this.flush();
|
|
2593
|
+
this.destroyed = true;
|
|
2594
|
+
this.offRecover?.();
|
|
2595
|
+
this.clearFlushTimer();
|
|
2596
|
+
this.clearRetry();
|
|
2597
|
+
}
|
|
2598
|
+
clearFlushTimer() {
|
|
2599
|
+
if (this.flushTimer) clearTimeout(this.flushTimer);
|
|
2600
|
+
this.flushTimer = null;
|
|
2601
|
+
}
|
|
2602
|
+
clearRetry() {
|
|
2603
|
+
if (this.retryTimer) clearTimeout(this.retryTimer);
|
|
2604
|
+
this.retryTimer = null;
|
|
2605
|
+
}
|
|
2606
|
+
scheduleRetry(ms) {
|
|
2607
|
+
this.retryTimer = setTimeout(() => {
|
|
2608
|
+
this.retryTimer = null;
|
|
2609
|
+
this.flush();
|
|
2610
|
+
}, ms);
|
|
2611
|
+
}
|
|
2612
|
+
seal() {
|
|
2613
|
+
this.clearFlushTimer();
|
|
2614
|
+
const chunk = this.buffer;
|
|
2615
|
+
this.buffer = null;
|
|
2616
|
+
if (!chunk || !chunk.events.length) return;
|
|
2617
|
+
this.queue.push(chunk);
|
|
2618
|
+
this.queuedBytes += chunk.bytes;
|
|
2619
|
+
const max = this.opts.maxQueuedBytes ?? 5e6;
|
|
2620
|
+
while (this.queuedBytes > max && this.queue.length > 1) {
|
|
2621
|
+
const i = this.inflight && this.queue[0] === this.inflight ? 1 : 0;
|
|
2622
|
+
const [dropped] = this.queue.splice(i, 1);
|
|
2623
|
+
if (!dropped) break;
|
|
2624
|
+
this.queuedBytes -= dropped.bytes;
|
|
2625
|
+
this.opts.log?.(`replay backlog full: dropped a chunk of ${dropped.events.length} events`);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
nextSeq(chunk) {
|
|
2629
|
+
if (chunk.seq === void 0) {
|
|
2630
|
+
const seq = this.seqs.get(chunk.sessionId) ?? 0;
|
|
2631
|
+
this.seqs.set(chunk.sessionId, seq + 1);
|
|
2632
|
+
chunk.seq = seq;
|
|
2633
|
+
}
|
|
2634
|
+
return chunk.seq;
|
|
2635
|
+
}
|
|
2636
|
+
body(chunk) {
|
|
2637
|
+
const head = JSON.stringify({ sessionId: chunk.sessionId, windowId: this.windowId, seq: this.nextSeq(chunk), ...chunk.meta });
|
|
2638
|
+
return `${head.slice(0, -1)},"events":[${chunk.events.join(",")}]}`;
|
|
2639
|
+
}
|
|
2640
|
+
async sendNext() {
|
|
2641
|
+
const chunk = this.queue[0];
|
|
2642
|
+
if (!chunk) return;
|
|
2643
|
+
const wait = this.destroyed ? 0 : this.opts.health?.waitMs() ?? 0;
|
|
2644
|
+
if (wait > 0) return this.scheduleRetry(wait);
|
|
2645
|
+
this.inflight = chunk;
|
|
2646
|
+
const result = await this.upload(this.body(chunk));
|
|
2647
|
+
this.onResult(chunk, result);
|
|
2648
|
+
}
|
|
2649
|
+
async upload(json) {
|
|
2650
|
+
const f = this.opts.fetch();
|
|
2651
|
+
if (!f) return "drop";
|
|
2652
|
+
const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
2653
|
+
const timer = ac ? setTimeout(() => ac.abort(), UPLOAD_TIMEOUT_MS) : null;
|
|
2654
|
+
try {
|
|
2655
|
+
let res;
|
|
2656
|
+
try {
|
|
2657
|
+
res = await f(this.opts.url(), { method: "POST", body: json, headers: { "Content-Type": "application/json" }, signal: ac?.signal });
|
|
2658
|
+
} catch {
|
|
2659
|
+
this.opts.health?.failure();
|
|
2660
|
+
return "retry";
|
|
2661
|
+
}
|
|
2662
|
+
if (res.status === 429 || res.status >= 500) this.opts.health?.failure();
|
|
2663
|
+
else this.opts.health?.success();
|
|
2664
|
+
if (res.ok) {
|
|
2665
|
+
const data = await res.json().catch(() => null);
|
|
2666
|
+
return data?.disabled ? "disabled" : "ok";
|
|
2667
|
+
}
|
|
2668
|
+
return res.status >= 400 && res.status < 500 && res.status !== 429 ? "drop" : "retry";
|
|
2669
|
+
} finally {
|
|
2670
|
+
if (timer) clearTimeout(timer);
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
onResult(chunk, result) {
|
|
2674
|
+
if (this.inflight === chunk) this.inflight = null;
|
|
2675
|
+
if (result === "retry" && this.queue.includes(chunk)) {
|
|
2676
|
+
if (!this.destroyed) {
|
|
2677
|
+
const max = this.opts.maxAttempts ?? 5;
|
|
2678
|
+
this.attempt = Math.min(this.attempt + 1, max);
|
|
2679
|
+
const delay = this.attempt >= max ? this.opts.slowRetryMs ?? 6e4 : backoffMs(this.attempt - 1, 1e3, 8e3);
|
|
2680
|
+
return this.scheduleRetry(Math.max(delay, this.opts.health?.waitMs() ?? 0));
|
|
2681
|
+
}
|
|
2682
|
+
this.opts.log?.("replay upload failed after the recorder stopped; dropping the chunk");
|
|
2683
|
+
}
|
|
2684
|
+
this.attempt = 0;
|
|
2685
|
+
this.remove(chunk);
|
|
2686
|
+
if (result === "disabled") {
|
|
2687
|
+
for (const c of this.queue.slice()) if (c.sessionId === chunk.sessionId) this.remove(c);
|
|
2688
|
+
if (this.buffer?.sessionId === chunk.sessionId) this.buffer = null;
|
|
2689
|
+
this.opts.onDisabled(chunk.sessionId);
|
|
2690
|
+
}
|
|
2691
|
+
if (this.queue.length && !this.retryTimer && !this.inflight) void this.sendNext();
|
|
2692
|
+
}
|
|
2693
|
+
remove(chunk) {
|
|
2694
|
+
const i = this.queue.indexOf(chunk);
|
|
2695
|
+
if (i >= 0) {
|
|
2696
|
+
this.queue.splice(i, 1);
|
|
2697
|
+
this.queuedBytes -= chunk.bytes;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
};
|
|
2701
|
+
var TOUCH_SETTLE_MS = 200;
|
|
2702
|
+
var IDLE_FRAMES = 3;
|
|
2703
|
+
var MAX_IDLE_INTERVAL_MS = 5e3;
|
|
2704
|
+
var SessionRecorder = class {
|
|
2705
|
+
constructor(opts) {
|
|
2706
|
+
__publicField(this, "opts", opts);
|
|
2707
|
+
__publicField(this, "intervalMs");
|
|
2708
|
+
__publicField(this, "now");
|
|
2709
|
+
__publicField(this, "sid", null);
|
|
2710
|
+
__publicField(this, "foreground", true);
|
|
2711
|
+
__publicField(this, "generation", 0);
|
|
2712
|
+
__publicField(this, "timer", null);
|
|
2713
|
+
__publicField(this, "timerDue", 0);
|
|
2714
|
+
__publicField(this, "capturing", false);
|
|
2715
|
+
__publicField(this, "needKeyframe", true);
|
|
2716
|
+
__publicField(this, "hasKeyframe", false);
|
|
2717
|
+
__publicField(this, "lastFrame", null);
|
|
2718
|
+
__publicField(this, "unchanged", 0);
|
|
2719
|
+
__publicField(this, "captureSoon", false);
|
|
2720
|
+
__publicField(this, "pendingCustom", []);
|
|
2721
|
+
__publicField(this, "touchDown", false);
|
|
2722
|
+
__publicField(this, "downX", 0);
|
|
2723
|
+
__publicField(this, "downY", 0);
|
|
2724
|
+
__publicField(this, "downAt", 0);
|
|
2725
|
+
__publicField(this, "maxDistance", 0);
|
|
2726
|
+
__publicField(this, "lastMoveAt", 0);
|
|
2727
|
+
__publicField(this, "originX", 0);
|
|
2728
|
+
__publicField(this, "originY", 0);
|
|
2729
|
+
/** Viewport of the last frame (points), for chunk metadata. */
|
|
2730
|
+
__publicField(this, "viewportWidth", 0);
|
|
2731
|
+
__publicField(this, "viewportHeight", 0);
|
|
2732
|
+
this.intervalMs = Math.max(500, opts.intervalMs);
|
|
2733
|
+
this.now = opts.now ?? Date.now;
|
|
2734
|
+
}
|
|
2735
|
+
get sessionId() {
|
|
2736
|
+
return this.sid;
|
|
2737
|
+
}
|
|
2738
|
+
get isRecording() {
|
|
2739
|
+
return this.sid !== null;
|
|
2740
|
+
}
|
|
2741
|
+
get currentIntervalMs() {
|
|
2742
|
+
return this.unchanged >= IDLE_FRAMES ? Math.max(this.intervalMs, Math.min(4 * this.intervalMs, MAX_IDLE_INTERVAL_MS)) : this.intervalMs;
|
|
2743
|
+
}
|
|
2744
|
+
/** Starts (or keeps) recording `sessionId`; another session stops the current one first (uploading it). */
|
|
2745
|
+
start(sessionId) {
|
|
2746
|
+
if (this.sid === sessionId) return;
|
|
2747
|
+
if (this.sid !== null) this.stop(false);
|
|
2748
|
+
this.sid = sessionId;
|
|
2749
|
+
this.generation++;
|
|
2750
|
+
this.needKeyframe = true;
|
|
2751
|
+
this.hasKeyframe = false;
|
|
2752
|
+
this.lastFrame = null;
|
|
2753
|
+
this.unchanged = 0;
|
|
2754
|
+
this.captureSoon = false;
|
|
2755
|
+
this.pendingCustom = [];
|
|
2756
|
+
this.touchDown = false;
|
|
2757
|
+
if (this.foreground) this.schedule(0);
|
|
2758
|
+
}
|
|
2759
|
+
/** Stops recording; buffered events are uploaded unless `discard`. */
|
|
2760
|
+
stop(discard) {
|
|
2761
|
+
if (this.sid === null) return;
|
|
2762
|
+
this.sid = null;
|
|
2763
|
+
this.generation++;
|
|
2764
|
+
this.clearTimer();
|
|
2765
|
+
this.capturing = false;
|
|
2766
|
+
this.pendingCustom = [];
|
|
2767
|
+
this.touchDown = false;
|
|
2768
|
+
if (discard) this.opts.uploader.discard();
|
|
2769
|
+
else this.opts.uploader.flush();
|
|
2770
|
+
}
|
|
2771
|
+
/** App backgrounded: no captures; uploads what is buffered. */
|
|
2772
|
+
pause() {
|
|
2773
|
+
this.foreground = false;
|
|
2774
|
+
this.clearTimer();
|
|
2775
|
+
this.generation++;
|
|
2776
|
+
this.capturing = false;
|
|
2777
|
+
this.touchDown = false;
|
|
2778
|
+
this.opts.uploader.flush();
|
|
2779
|
+
}
|
|
2780
|
+
/** App foregrounded: resume with a keyframe. */
|
|
2781
|
+
resume() {
|
|
2782
|
+
if (this.foreground) return;
|
|
2783
|
+
this.foreground = true;
|
|
2784
|
+
this.needKeyframe = true;
|
|
2785
|
+
this.unchanged = 0;
|
|
2786
|
+
if (this.sid !== null) this.schedule(0);
|
|
2787
|
+
}
|
|
2788
|
+
/** A new screen: the next frame is a keyframe with the new href. */
|
|
2789
|
+
onScreen() {
|
|
2790
|
+
if (this.sid === null) return;
|
|
2791
|
+
this.needKeyframe = true;
|
|
2792
|
+
this.activity();
|
|
2793
|
+
}
|
|
2794
|
+
activity() {
|
|
2795
|
+
const wasIdle = this.unchanged >= IDLE_FRAMES;
|
|
2796
|
+
this.unchanged = 0;
|
|
2797
|
+
if (wasIdle && this.sid !== null && this.foreground && !this.capturing && !this.touchDown) this.schedule(this.intervalMs, true);
|
|
2798
|
+
}
|
|
2799
|
+
/** Adds the `guidely` marker for an analytics event. */
|
|
2800
|
+
addGuidelyEvent(name, customName, guideId) {
|
|
2801
|
+
const id = this.sid;
|
|
2802
|
+
if (id === null) return;
|
|
2803
|
+
const payload = { name };
|
|
2804
|
+
if (customName) payload.customName = customName;
|
|
2805
|
+
if (guideId) payload.guideId = guideId;
|
|
2806
|
+
this.activity();
|
|
2807
|
+
const now = this.now();
|
|
2808
|
+
if (!this.hasKeyframe) {
|
|
2809
|
+
if (this.pendingCustom.length < 50) this.pendingCustom.push([now, payload]);
|
|
2810
|
+
return;
|
|
2811
|
+
}
|
|
2812
|
+
this.opts.uploader.push(id, RrwebEvents.custom(now, "guidely", payload));
|
|
2813
|
+
}
|
|
2814
|
+
/** A primary-pointer touch at (x, y) points from the React root view. */
|
|
2815
|
+
touch(phase, rootX, rootY) {
|
|
2816
|
+
const id = this.sid;
|
|
2817
|
+
if (id === null || !this.foreground) return;
|
|
2818
|
+
const x = rootX + this.originX;
|
|
2819
|
+
const y = rootY + this.originY;
|
|
2820
|
+
const now = this.now();
|
|
2821
|
+
const emit = this.hasKeyframe;
|
|
2822
|
+
const push = (json) => emit && this.opts.uploader.push(id, json);
|
|
2823
|
+
switch (phase) {
|
|
2824
|
+
case "down":
|
|
2825
|
+
this.touchDown = true;
|
|
2826
|
+
this.unchanged = 0;
|
|
2827
|
+
this.downX = x;
|
|
2828
|
+
this.downY = y;
|
|
2829
|
+
this.downAt = now;
|
|
2830
|
+
this.maxDistance = 0;
|
|
2831
|
+
this.lastMoveAt = now;
|
|
2832
|
+
push(RrwebEvents.interaction(now, RrwebEvents.TOUCH_START, x, y));
|
|
2833
|
+
return;
|
|
2834
|
+
case "move":
|
|
2835
|
+
if (!this.touchDown) return;
|
|
2836
|
+
this.maxDistance = Math.max(this.maxDistance, Math.hypot(x - this.downX, y - this.downY));
|
|
2837
|
+
if (now - this.lastMoveAt < 50) return;
|
|
2838
|
+
this.lastMoveAt = now;
|
|
2839
|
+
push(RrwebEvents.touchMove(now, x, y));
|
|
2840
|
+
return;
|
|
2841
|
+
default:
|
|
2842
|
+
if (!this.touchDown) return;
|
|
2843
|
+
this.touchDown = false;
|
|
2844
|
+
this.unchanged = 0;
|
|
2845
|
+
this.maxDistance = Math.max(this.maxDistance, Math.hypot(x - this.downX, y - this.downY));
|
|
2846
|
+
push(RrwebEvents.interaction(now, RrwebEvents.TOUCH_END, x, y));
|
|
2847
|
+
if (phase === "up" && this.maxDistance <= 10 && now - this.downAt < 1e3) push(RrwebEvents.interaction(now, RrwebEvents.CLICK, x, y));
|
|
2848
|
+
if (this.capturing) this.captureSoon = true;
|
|
2849
|
+
else this.schedule(TOUCH_SETTLE_MS, true);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
clearTimer() {
|
|
2853
|
+
if (this.timer) clearTimeout(this.timer);
|
|
2854
|
+
this.timer = null;
|
|
2855
|
+
}
|
|
2856
|
+
/** Schedules the next capture `delay` ms from now, or keeps an earlier one when `ifSooner`. */
|
|
2857
|
+
schedule(delay, ifSooner = false) {
|
|
2858
|
+
const due = this.now() + delay;
|
|
2859
|
+
if (ifSooner && this.timer && this.timerDue <= due) return;
|
|
2860
|
+
this.clearTimer();
|
|
2861
|
+
this.timerDue = due;
|
|
2862
|
+
const gen = this.generation;
|
|
2863
|
+
this.timer = setTimeout(() => {
|
|
2864
|
+
this.timer = null;
|
|
2865
|
+
if (gen === this.generation) void this.tick();
|
|
2866
|
+
}, delay);
|
|
2867
|
+
}
|
|
2868
|
+
async tick() {
|
|
2869
|
+
if (this.sid === null || !this.foreground || this.capturing) return;
|
|
2870
|
+
if (this.touchDown) {
|
|
2871
|
+
this.schedule(this.intervalMs);
|
|
2872
|
+
return;
|
|
2873
|
+
}
|
|
2874
|
+
this.capturing = true;
|
|
2875
|
+
this.captureSoon = false;
|
|
2876
|
+
const gen = this.generation;
|
|
2877
|
+
let frame = null;
|
|
2878
|
+
try {
|
|
2879
|
+
frame = await this.opts.capturer(await this.opts.captureOptions());
|
|
2880
|
+
} catch (err) {
|
|
2881
|
+
this.opts.onError?.(err);
|
|
2882
|
+
}
|
|
2883
|
+
if (gen !== this.generation) return;
|
|
2884
|
+
this.capturing = false;
|
|
2885
|
+
if (frame) this.onFrame(frame, this.now());
|
|
2886
|
+
if (this.sid !== null && this.foreground) this.schedule(this.captureSoon ? TOUCH_SETTLE_MS : this.currentIntervalMs);
|
|
2887
|
+
}
|
|
2888
|
+
onFrame(frame, at) {
|
|
2889
|
+
const id = this.sid;
|
|
2890
|
+
if (id === null || !(frame.width > 0) || !(frame.height > 0) || !frame.base64) return;
|
|
2891
|
+
const sizeChanged = frame.width !== this.viewportWidth || frame.height !== this.viewportHeight;
|
|
2892
|
+
const keyframe = this.needKeyframe || !this.hasKeyframe || sizeChanged;
|
|
2893
|
+
if (!keyframe && this.lastFrame === frame.base64) {
|
|
2894
|
+
this.unchanged++;
|
|
2895
|
+
return;
|
|
2896
|
+
}
|
|
2897
|
+
this.unchanged = 0;
|
|
2898
|
+
this.lastFrame = frame.base64;
|
|
2899
|
+
this.originX = frame.originX ?? 0;
|
|
2900
|
+
this.originY = frame.originY ?? 0;
|
|
2901
|
+
this.viewportWidth = frame.width;
|
|
2902
|
+
this.viewportHeight = frame.height;
|
|
2903
|
+
const url = RrwebEvents.dataUrl(frame.base64);
|
|
2904
|
+
const up = this.opts.uploader;
|
|
2905
|
+
if (!keyframe) return up.push(id, RrwebEvents.frame(at, url));
|
|
2906
|
+
this.needKeyframe = false;
|
|
2907
|
+
up.push(id, RrwebEvents.meta(at, this.opts.href(), frame.width, frame.height));
|
|
2908
|
+
up.push(id, RrwebEvents.fullSnapshot(at, url, frame.width, frame.height));
|
|
2909
|
+
if (!this.hasKeyframe) {
|
|
2910
|
+
this.hasKeyframe = true;
|
|
2911
|
+
for (const [ts, payload] of this.pendingCustom) up.push(id, RrwebEvents.custom(Math.max(ts, at), "guidely", payload));
|
|
2912
|
+
this.pendingCustom = [];
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
};
|
|
2916
|
+
|
|
2495
2917
|
// src/core/storage.ts
|
|
2496
2918
|
var PersistentStore = class {
|
|
2497
2919
|
constructor(backing, onError = () => {
|
|
@@ -2545,7 +2967,7 @@ var PersistentStore = class {
|
|
|
2545
2967
|
|
|
2546
2968
|
// src/core/client.ts
|
|
2547
2969
|
var SDK_NAME = "@guidelytech/react-native";
|
|
2548
|
-
var SDK_VERSION = "0.
|
|
2970
|
+
var SDK_VERSION = "0.13.0";
|
|
2549
2971
|
var DEFAULT_API_URL = "https://guidelytech.com";
|
|
2550
2972
|
var PAGE_SCOPED = /* @__PURE__ */ new Set(["page_load", "url_match", "element_click", "element_visible", "delay"]);
|
|
2551
2973
|
var CACHED_START_MS = 1500;
|
|
@@ -2606,6 +3028,10 @@ var GuidelyClient = class {
|
|
|
2606
3028
|
__publicField(this, "lastAudiences", /* @__PURE__ */ new Set());
|
|
2607
3029
|
__publicField(this, "backgroundedAt", null);
|
|
2608
3030
|
__publicField(this, "messenger", null);
|
|
3031
|
+
__publicField(this, "replayUploader", null);
|
|
3032
|
+
__publicField(this, "recorder", null);
|
|
3033
|
+
/** Replay decision for the current session (sampled once; forced by start/stopSessionRecording). */
|
|
3034
|
+
__publicField(this, "replayDecision", null);
|
|
2609
3035
|
__publicField(this, "ui", { steps: [], checklists: [], survey: null, resourceCenterOpen: false, theme: {}, messenger: false });
|
|
2610
3036
|
__publicField(this, "uiListeners", /* @__PURE__ */ new Set());
|
|
2611
3037
|
/** Feature flags for this environment: `guidely.flags.isEnabled("new-checkout")`. */
|
|
@@ -2757,6 +3183,7 @@ var GuidelyClient = class {
|
|
|
2757
3183
|
if (this.destroyed) return;
|
|
2758
3184
|
this.isReady = true;
|
|
2759
3185
|
if (this.config) this.emitter.emit("ready", this.config);
|
|
3186
|
+
this.syncReplay();
|
|
2760
3187
|
this.notify();
|
|
2761
3188
|
this.evaluate("ready");
|
|
2762
3189
|
} catch (err) {
|
|
@@ -2783,6 +3210,7 @@ var GuidelyClient = class {
|
|
|
2783
3210
|
this.syncMessenger();
|
|
2784
3211
|
if (this.hydrated) this.checkLaunch();
|
|
2785
3212
|
this.emitter.emit("config", this.config);
|
|
3213
|
+
this.syncReplay();
|
|
2786
3214
|
this.log("config loaded", this.config.configVersion, `${this.config.guides.length} guides`);
|
|
2787
3215
|
return true;
|
|
2788
3216
|
}
|
|
@@ -2835,6 +3263,7 @@ var GuidelyClient = class {
|
|
|
2835
3263
|
void this.outbox?.flush();
|
|
2836
3264
|
void this.store.flush();
|
|
2837
3265
|
void this.flags.flush();
|
|
3266
|
+
this.recorder?.pause();
|
|
2838
3267
|
this.messenger?.setForeground(false);
|
|
2839
3268
|
} else if (state === "active" && this.backgroundedAt !== null) {
|
|
2840
3269
|
this.messenger?.setForeground(true);
|
|
@@ -2844,7 +3273,10 @@ var GuidelyClient = class {
|
|
|
2844
3273
|
this.session = newSession();
|
|
2845
3274
|
this.persistSession();
|
|
2846
3275
|
this.firedDelays.clear();
|
|
3276
|
+
this.recorder?.stop(false);
|
|
2847
3277
|
}
|
|
3278
|
+
this.recorder?.resume();
|
|
3279
|
+
this.syncReplay();
|
|
2848
3280
|
this.cameToForeground();
|
|
2849
3281
|
void this.flags.refresh();
|
|
2850
3282
|
if (Date.now() - this.configFetchedAt > this.options.configRefreshMs && this.api.health.canSend()) void this.refresh();
|
|
@@ -2867,6 +3299,9 @@ var GuidelyClient = class {
|
|
|
2867
3299
|
this.queued = [];
|
|
2868
3300
|
this.messenger?.destroy();
|
|
2869
3301
|
this.messenger = null;
|
|
3302
|
+
this.recorder?.stop(!this.analyticsEnabled());
|
|
3303
|
+
this.recorder = null;
|
|
3304
|
+
this.replayUploader?.destroy();
|
|
2870
3305
|
this.visibilityWatchers.forEach((stop) => stop());
|
|
2871
3306
|
this.visibilityWatchers.clear();
|
|
2872
3307
|
this.timers.forEach((t) => clearTimeout(t));
|
|
@@ -2981,6 +3416,7 @@ var GuidelyClient = class {
|
|
|
2981
3416
|
this.store.set(KEYS.anon, this.anonymousId);
|
|
2982
3417
|
this.state = emptyState();
|
|
2983
3418
|
this.persistState();
|
|
3419
|
+
this.recorder?.stop(false);
|
|
2984
3420
|
this.session = newSession();
|
|
2985
3421
|
this.persistSession();
|
|
2986
3422
|
this.lastAudiences = /* @__PURE__ */ new Set();
|
|
@@ -2988,6 +3424,7 @@ var GuidelyClient = class {
|
|
|
2988
3424
|
this.firstVisit = true;
|
|
2989
3425
|
this.exposures.clear();
|
|
2990
3426
|
this.syncFlagAttributes();
|
|
3427
|
+
this.syncReplay();
|
|
2991
3428
|
this.emitter.emit("identify", { userId: null, anonymousId: this.anonymousId });
|
|
2992
3429
|
this.notify();
|
|
2993
3430
|
if (this.isReady) this.evaluate("identify");
|
|
@@ -3002,6 +3439,7 @@ var GuidelyClient = class {
|
|
|
3002
3439
|
this.whenHydrated(() => {
|
|
3003
3440
|
if (consent === "denied") this.queue?.clear();
|
|
3004
3441
|
else if (consent === "granted") void this.queue?.flush();
|
|
3442
|
+
this.syncReplay();
|
|
3005
3443
|
});
|
|
3006
3444
|
}
|
|
3007
3445
|
analyticsEnabled() {
|
|
@@ -3030,6 +3468,7 @@ var GuidelyClient = class {
|
|
|
3030
3468
|
const locale = extra.guideId ? this.guideLocales.get(extra.guideId) : void 0;
|
|
3031
3469
|
if (locale) event.properties = { ...event.properties ?? {}, locale };
|
|
3032
3470
|
this.queue.enqueue(event);
|
|
3471
|
+
this.recorder?.addGuidelyEvent(name, event.customName, event.guideId);
|
|
3033
3472
|
this.emitter.emit("track", event);
|
|
3034
3473
|
return event;
|
|
3035
3474
|
}
|
|
@@ -3067,6 +3506,7 @@ var GuidelyClient = class {
|
|
|
3067
3506
|
}
|
|
3068
3507
|
this.screenName = name;
|
|
3069
3508
|
this.targets.setScreen(name);
|
|
3509
|
+
if (changed) this.recorder?.onScreen();
|
|
3070
3510
|
if (changed) this.syncFlagAttributes();
|
|
3071
3511
|
const url = this.currentUrl();
|
|
3072
3512
|
if (changed) {
|
|
@@ -3101,6 +3541,112 @@ var GuidelyClient = class {
|
|
|
3101
3541
|
if (this.isReady) this.evaluate("click", { targetId });
|
|
3102
3542
|
}
|
|
3103
3543
|
// ---------------------------------------------------------------------------
|
|
3544
|
+
// Session replay
|
|
3545
|
+
// ---------------------------------------------------------------------------
|
|
3546
|
+
replaySettings() {
|
|
3547
|
+
const r = this.config?.settings?.replay;
|
|
3548
|
+
if (!r?.enabled || !this.options.sessionReplay?.enabled || !this.bridge.capturer) return null;
|
|
3549
|
+
return r;
|
|
3550
|
+
}
|
|
3551
|
+
/** Reconciles recording with config, consent, readiness, the session and its sample decision. Idempotent. */
|
|
3552
|
+
syncReplay() {
|
|
3553
|
+
if (this.destroyed || !this.hydrated) return;
|
|
3554
|
+
const r = this.isReady && this.analyticsEnabled() ? this.replaySettings() : null;
|
|
3555
|
+
const id = this.session.id;
|
|
3556
|
+
if (r && this.replayDecision?.sessionId !== id) {
|
|
3557
|
+
this.replayDecision = { sessionId: id, record: rolloutBucket(id, "replay") < (r.sampleRate ?? 100) };
|
|
3558
|
+
this.log(`replay ${this.replayDecision.record ? "sampled" : "not sampled"} for session ${id}`);
|
|
3559
|
+
}
|
|
3560
|
+
if (!r || !this.replayDecision?.record) {
|
|
3561
|
+
if (this.recorder?.isRecording) this.recorder.stop(!this.analyticsEnabled());
|
|
3562
|
+
return;
|
|
3563
|
+
}
|
|
3564
|
+
const rec = this.recorder ?? this.createRecorder();
|
|
3565
|
+
if (rec && rec.sessionId !== id) {
|
|
3566
|
+
this.log(`recording session ${id}`);
|
|
3567
|
+
rec.start(id);
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
createRecorder() {
|
|
3571
|
+
const capturer = this.bridge.capturer;
|
|
3572
|
+
if (!capturer) return null;
|
|
3573
|
+
const uploader = this.replayUploader ?? (this.replayUploader = new ReplayUploader({
|
|
3574
|
+
url: () => this.api.url("/replay"),
|
|
3575
|
+
fetch: () => this.options.fetch ?? (typeof fetch === "function" ? fetch : null),
|
|
3576
|
+
health: this.api.health,
|
|
3577
|
+
meta: () => {
|
|
3578
|
+
const rec2 = this.recorder;
|
|
3579
|
+
return {
|
|
3580
|
+
anonymousId: this.anonymousId,
|
|
3581
|
+
userId: this.user?.id || null,
|
|
3582
|
+
...this.collectUrls() && this.screenName ? { url: this.currentUrl() } : {},
|
|
3583
|
+
device: { type: this.bridge.device.deviceType, os: this.bridge.device.platform },
|
|
3584
|
+
...rec2 && rec2.viewportWidth > 0 && rec2.viewportHeight > 0 ? { viewport: { width: rec2.viewportWidth, height: rec2.viewportHeight } } : {},
|
|
3585
|
+
sdk: { name: SDK_NAME, version: SDK_VERSION }
|
|
3586
|
+
};
|
|
3587
|
+
},
|
|
3588
|
+
onDisabled: (sid) => {
|
|
3589
|
+
if (sid !== this.session.id) return;
|
|
3590
|
+
this.log(`replay disabled by the server for session ${sid}`);
|
|
3591
|
+
this.replayDecision = { sessionId: sid, record: false };
|
|
3592
|
+
this.recorder?.stop(true);
|
|
3593
|
+
},
|
|
3594
|
+
log: (m) => this.log(m)
|
|
3595
|
+
}));
|
|
3596
|
+
const opts = this.options.sessionReplay ?? {};
|
|
3597
|
+
const rec = new SessionRecorder({
|
|
3598
|
+
capturer,
|
|
3599
|
+
uploader,
|
|
3600
|
+
intervalMs: opts.captureIntervalMs ?? 1e3,
|
|
3601
|
+
captureOptions: () => this.replayCaptureOptions(),
|
|
3602
|
+
href: () => this.currentUrl(),
|
|
3603
|
+
onError: (err) => this.reportError(err)
|
|
3604
|
+
});
|
|
3605
|
+
if (this.backgroundedAt !== null) rec.pause();
|
|
3606
|
+
this.recorder = rec;
|
|
3607
|
+
return rec;
|
|
3608
|
+
}
|
|
3609
|
+
async replayCaptureOptions() {
|
|
3610
|
+
const r = this.config?.settings?.replay;
|
|
3611
|
+
const opts = this.options.sessionReplay ?? {};
|
|
3612
|
+
const ids = new Set((r?.blockSelectors ?? []).map((sel) => guidelyIdFromSelector(sel)).filter((id) => !!id));
|
|
3613
|
+
const rects = await Promise.all(Array.from(ids, (id) => this.targets.measure(id)));
|
|
3614
|
+
return { maskAllText: !!opts.maskAllText || !!r?.maskAllText, maskAllImages: !!opts.maskAllImages, blockRects: rects.filter((rect) => !!rect) };
|
|
3615
|
+
}
|
|
3616
|
+
/**
|
|
3617
|
+
* Records this session even when it was not sampled (still needs `sessionReplay.enabled`, replay
|
|
3618
|
+
* enabled for the project, analytics consent and the native replay module). Sticks until the session ends.
|
|
3619
|
+
*/
|
|
3620
|
+
startSessionRecording() {
|
|
3621
|
+
this.whenHydrated(() => {
|
|
3622
|
+
this.replayDecision = { sessionId: this.session.id, record: true };
|
|
3623
|
+
this.syncReplay();
|
|
3624
|
+
});
|
|
3625
|
+
}
|
|
3626
|
+
/** Stops recording for the rest of this session; what was recorded is uploaded. */
|
|
3627
|
+
stopSessionRecording() {
|
|
3628
|
+
this.whenHydrated(() => {
|
|
3629
|
+
this.replayDecision = { sessionId: this.session.id, record: false };
|
|
3630
|
+
this.recorder?.stop(false);
|
|
3631
|
+
});
|
|
3632
|
+
}
|
|
3633
|
+
/** True while this session is being recorded. */
|
|
3634
|
+
isRecording() {
|
|
3635
|
+
return !!this.recorder?.isRecording;
|
|
3636
|
+
}
|
|
3637
|
+
/** The current analytics session id (also the replay id). */
|
|
3638
|
+
getSessionId() {
|
|
3639
|
+
return this.session.id;
|
|
3640
|
+
}
|
|
3641
|
+
/** True when touches should be passed to {@link recordTouch}. */
|
|
3642
|
+
isRecordingTouches() {
|
|
3643
|
+
return !!this.recorder?.isRecording;
|
|
3644
|
+
}
|
|
3645
|
+
/** A primary-pointer touch on the app, in window points (the provider's touch observer calls this). */
|
|
3646
|
+
recordTouch(phase, x, y) {
|
|
3647
|
+
this.recorder?.touch(phase, x, y);
|
|
3648
|
+
}
|
|
3649
|
+
// ---------------------------------------------------------------------------
|
|
3104
3650
|
// Product analytics (autocapture)
|
|
3105
3651
|
// ---------------------------------------------------------------------------
|
|
3106
3652
|
captureAllowed() {
|
|
@@ -3147,7 +3693,7 @@ var GuidelyClient = class {
|
|
|
3147
3693
|
}
|
|
3148
3694
|
if (this.destroyed) return;
|
|
3149
3695
|
const platform = this.bridge.device.platform;
|
|
3150
|
-
const maskAll = !!this.config?.settings?.replay?.maskAllText;
|
|
3696
|
+
const maskAll = !!this.config?.settings?.replay?.maskAllText || !!this.options.sessionReplay?.maskAllText;
|
|
3151
3697
|
const eventUrl = this.collectUrls() && url ? url : void 0;
|
|
3152
3698
|
if (on.clicks && el) this.enqueue("click", { url: eventUrl, properties: { ...tapProperties(el, tap.x, tap.y, platform, maskAll) } });
|
|
3153
3699
|
const count = this.rage.add(tap.x, tap.y, t);
|
|
@@ -6106,8 +6652,9 @@ function rcLauncherLabel(client) {
|
|
|
6106
6652
|
|
|
6107
6653
|
// src/ui/Autocapture.tsx
|
|
6108
6654
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
6655
|
+
var GUIDELY_MASK_NATIVE_ID = "guidely-mask";
|
|
6109
6656
|
function GuidelyMask({ children, ...rest }) {
|
|
6110
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native11.View, { ...rest, children });
|
|
6657
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native11.View, { collapsable: false, ...rest, nativeID: GUIDELY_MASK_NATIVE_ID, children });
|
|
6111
6658
|
}
|
|
6112
6659
|
var RESOLVE = {
|
|
6113
6660
|
isIgnored: (type) => type === GuidelyOverlay,
|
|
@@ -6118,6 +6665,18 @@ function useTapObserver(client) {
|
|
|
6118
6665
|
clientRef.current = client;
|
|
6119
6666
|
return (0, import_react8.useMemo)(() => {
|
|
6120
6667
|
const tracker = new TapTracker();
|
|
6668
|
+
let replayPointer = null;
|
|
6669
|
+
const replay = (phase, e) => {
|
|
6670
|
+
const c = clientRef.current;
|
|
6671
|
+
const n = e.nativeEvent;
|
|
6672
|
+
if (!c?.isRecordingTouches()) return void (replayPointer = null);
|
|
6673
|
+
if (phase === "down") {
|
|
6674
|
+
if (replayPointer !== null) return;
|
|
6675
|
+
replayPointer = n.identifier;
|
|
6676
|
+
} else if (n.identifier !== replayPointer) return;
|
|
6677
|
+
if (phase === "up" || phase === "cancel") replayPointer = null;
|
|
6678
|
+
c.recordTouch(phase, n.pageX, n.pageY);
|
|
6679
|
+
};
|
|
6121
6680
|
const guard = (fn) => (e) => {
|
|
6122
6681
|
try {
|
|
6123
6682
|
fn(e);
|
|
@@ -6127,12 +6686,17 @@ function useTapObserver(client) {
|
|
|
6127
6686
|
};
|
|
6128
6687
|
return {
|
|
6129
6688
|
onTouchStartCapture: guard((e) => {
|
|
6689
|
+
replay("down", e);
|
|
6130
6690
|
const n = e.nativeEvent;
|
|
6131
6691
|
if (!clientRef.current?.isTapCaptureOn()) return tracker.cancel();
|
|
6132
6692
|
tracker.begin(n.pageX, n.pageY, Date.now(), Array.isArray(n.touches) ? n.touches.length : 1, n.identifier);
|
|
6133
6693
|
}),
|
|
6134
|
-
onTouchMoveCapture: guard((e) =>
|
|
6694
|
+
onTouchMoveCapture: guard((e) => {
|
|
6695
|
+
replay("move", e);
|
|
6696
|
+
tracker.move(e.nativeEvent.pageX, e.nativeEvent.pageY, e.nativeEvent.identifier);
|
|
6697
|
+
}),
|
|
6135
6698
|
onTouchEndCapture: guard((e) => {
|
|
6699
|
+
replay("up", e);
|
|
6136
6700
|
const n = e.nativeEvent;
|
|
6137
6701
|
const tap = tracker.end(n.pageX, n.pageY, Date.now(), n.identifier);
|
|
6138
6702
|
const c = clientRef.current;
|
|
@@ -6140,7 +6704,10 @@ function useTapObserver(client) {
|
|
|
6140
6704
|
const r = resolveTapTarget(e, RESOLVE);
|
|
6141
6705
|
if (!r.ignored) c.recordTap({ x: tap.x, y: tap.y, t: Date.now(), element: r.element, hit: r.hit });
|
|
6142
6706
|
}),
|
|
6143
|
-
onTouchCancelCapture: guard(() =>
|
|
6707
|
+
onTouchCancelCapture: guard((e) => {
|
|
6708
|
+
replay("cancel", e);
|
|
6709
|
+
tracker.cancel();
|
|
6710
|
+
})
|
|
6144
6711
|
};
|
|
6145
6712
|
}, []);
|
|
6146
6713
|
}
|
|
@@ -6234,9 +6801,15 @@ function defaultBridge(appVersion) {
|
|
|
6234
6801
|
},
|
|
6235
6802
|
openExternal: (url) => void import_react_native13.Linking.openURL(url).catch(() => {
|
|
6236
6803
|
}),
|
|
6237
|
-
windowSize: () => import_react_native13.Dimensions.get("window")
|
|
6804
|
+
windowSize: () => import_react_native13.Dimensions.get("window"),
|
|
6805
|
+
capturer: nativeCapturer()
|
|
6238
6806
|
};
|
|
6239
6807
|
}
|
|
6808
|
+
function nativeCapturer() {
|
|
6809
|
+
const mod = import_react_native13.NativeModules.GuidelyReplay;
|
|
6810
|
+
if (!mod || typeof mod.capture !== "function") return void 0;
|
|
6811
|
+
return (options) => mod.capture(options);
|
|
6812
|
+
}
|
|
6240
6813
|
function GuidelyProvider({ children, user, insets, launcherOffset = 72, flagsPayload, flagAttributes, ...options }) {
|
|
6241
6814
|
const [client, setClient] = (0, import_react10.useState)(null);
|
|
6242
6815
|
const optionsRef = (0, import_react10.useRef)(options);
|
|
@@ -6306,7 +6879,7 @@ function GuidelyProvider({ children, user, insets, launcherOffset = 72, flagsPay
|
|
|
6306
6879
|
const flags = (0, import_react10.useMemo)(() => ({ client: client?.flags ?? null, bootstrap }), [client, bootstrap]);
|
|
6307
6880
|
const tapObserver = useTapObserver(client);
|
|
6308
6881
|
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(GuidelyContext.Provider, { value: client, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(FlagsContext.Provider, { value: flags, children: [
|
|
6309
|
-
options.autocapture === false ? children : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native13.View, { style: styles10.root, ...tapObserver, children }),
|
|
6882
|
+
options.autocapture === false && !options.sessionReplay?.enabled ? children : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native13.View, { style: styles10.root, ...tapObserver, children }),
|
|
6310
6883
|
client ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(GuidelyOverlay, { client, insets, launcherOffset }) : null
|
|
6311
6884
|
] }) });
|
|
6312
6885
|
}
|
|
@@ -6326,6 +6899,9 @@ var NOOP_RESULTS = {
|
|
|
6326
6899
|
getMessenger: null,
|
|
6327
6900
|
isGuideCompleted: false,
|
|
6328
6901
|
isClientReady: false,
|
|
6902
|
+
isRecording: false,
|
|
6903
|
+
isRecordingTouches: false,
|
|
6904
|
+
getSessionId: "",
|
|
6329
6905
|
getScreen: "",
|
|
6330
6906
|
on: () => {
|
|
6331
6907
|
},
|
|
@@ -6460,8 +7036,11 @@ function useActiveGuide() {
|
|
|
6460
7036
|
InMemoryStickyBucketStore,
|
|
6461
7037
|
Messenger,
|
|
6462
7038
|
RageClickDetector,
|
|
7039
|
+
ReplayUploader,
|
|
7040
|
+
RrwebEvents,
|
|
6463
7041
|
SDK_NAME,
|
|
6464
7042
|
SDK_VERSION,
|
|
7043
|
+
SessionRecorder,
|
|
6465
7044
|
TapTracker,
|
|
6466
7045
|
TargetRegistry,
|
|
6467
7046
|
createFlagsClient,
|