fast-replay 0.1.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/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/api.d.ts +65 -0
- package/dist/api.js +78 -0
- package/dist/api.js.map +1 -0
- package/dist/browser.d.ts +67 -0
- package/dist/browser.js +77 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli/format.d.ts +12 -0
- package/dist/cli/format.js +55 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +199 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/compiler/compile.d.ts +18 -0
- package/dist/compiler/compile.js +226 -0
- package/dist/compiler/compile.js.map +1 -0
- package/dist/compiler/normalize.d.ts +16 -0
- package/dist/compiler/normalize.js +75 -0
- package/dist/compiler/normalize.js.map +1 -0
- package/dist/compiler/waits.d.ts +31 -0
- package/dist/compiler/waits.js +69 -0
- package/dist/compiler/waits.js.map +1 -0
- package/dist/ir/io.d.ts +47 -0
- package/dist/ir/io.js +122 -0
- package/dist/ir/io.js.map +1 -0
- package/dist/ir/schema.d.ts +236 -0
- package/dist/ir/schema.js +153 -0
- package/dist/ir/schema.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +22 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +268 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/noise.d.ts +20 -0
- package/dist/noise.js +70 -0
- package/dist/noise.js.map +1 -0
- package/dist/recorder/agent/capture.d.ts +15 -0
- package/dist/recorder/agent/capture.js +310 -0
- package/dist/recorder/agent/capture.js.map +1 -0
- package/dist/recorder/agent/config.d.ts +25 -0
- package/dist/recorder/agent/config.js +23 -0
- package/dist/recorder/agent/config.js.map +1 -0
- package/dist/recorder/agent/dom-reaction.d.ts +13 -0
- package/dist/recorder/agent/dom-reaction.js +166 -0
- package/dist/recorder/agent/dom-reaction.js.map +1 -0
- package/dist/recorder/agent/main.d.ts +1 -0
- package/dist/recorder/agent/main.js +14 -0
- package/dist/recorder/agent/main.js.map +1 -0
- package/dist/recorder/agent/page-agent.d.ts +12 -0
- package/dist/recorder/agent/page-agent.js +40 -0
- package/dist/recorder/agent/page-agent.js.map +1 -0
- package/dist/recorder/agent/reveal-tracker.d.ts +26 -0
- package/dist/recorder/agent/reveal-tracker.js +41 -0
- package/dist/recorder/agent/reveal-tracker.js.map +1 -0
- package/dist/recorder/agent/roles.d.ts +11 -0
- package/dist/recorder/agent/roles.js +148 -0
- package/dist/recorder/agent/roles.js.map +1 -0
- package/dist/recorder/agent/selectors.d.ts +26 -0
- package/dist/recorder/agent/selectors.js +256 -0
- package/dist/recorder/agent/selectors.js.map +1 -0
- package/dist/recorder/agent/text.d.ts +19 -0
- package/dist/recorder/agent/text.js +82 -0
- package/dist/recorder/agent/text.js.map +1 -0
- package/dist/recorder/agent/transport.d.ts +14 -0
- package/dist/recorder/agent/transport.js +43 -0
- package/dist/recorder/agent/transport.js.map +1 -0
- package/dist/recorder/agent/visibility.d.ts +8 -0
- package/dist/recorder/agent/visibility.js +20 -0
- package/dist/recorder/agent/visibility.js.map +1 -0
- package/dist/recorder/agent-bundle.generated.d.ts +2 -0
- package/dist/recorder/agent-bundle.generated.js +6 -0
- package/dist/recorder/agent-bundle.generated.js.map +1 -0
- package/dist/recorder/attach.d.ts +52 -0
- package/dist/recorder/attach.js +159 -0
- package/dist/recorder/attach.js.map +1 -0
- package/dist/recorder/instrument.d.ts +17 -0
- package/dist/recorder/instrument.js +24 -0
- package/dist/recorder/instrument.js.map +1 -0
- package/dist/recorder/launch.d.ts +38 -0
- package/dist/recorder/launch.js +72 -0
- package/dist/recorder/launch.js.map +1 -0
- package/dist/recorder/reaction.d.ts +13 -0
- package/dist/recorder/reaction.js +65 -0
- package/dist/recorder/reaction.js.map +1 -0
- package/dist/recorder/types.d.ts +62 -0
- package/dist/recorder/types.js +2 -0
- package/dist/recorder/types.js.map +1 -0
- package/dist/replayer/artifacts.d.ts +25 -0
- package/dist/replayer/artifacts.js +58 -0
- package/dist/replayer/artifacts.js.map +1 -0
- package/dist/replayer/invariants.d.ts +26 -0
- package/dist/replayer/invariants.js +109 -0
- package/dist/replayer/invariants.js.map +1 -0
- package/dist/replayer/resolve.d.ts +35 -0
- package/dist/replayer/resolve.js +50 -0
- package/dist/replayer/resolve.js.map +1 -0
- package/dist/replayer/run.d.ts +104 -0
- package/dist/replayer/run.js +467 -0
- package/dist/replayer/run.js.map +1 -0
- package/dist/replayer/values.d.ts +13 -0
- package/dist/replayer/values.js +76 -0
- package/dist/replayer/values.js.map +1 -0
- package/dist/replayer/waits.d.ts +25 -0
- package/dist/replayer/waits.js +90 -0
- package/dist/replayer/waits.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { isEditable } from './roles.js';
|
|
2
|
+
import { describe } from './selectors.js';
|
|
3
|
+
import { clean } from './text.js';
|
|
4
|
+
/**
|
|
5
|
+
* Turns raw DOM events into recorded actions.
|
|
6
|
+
*
|
|
7
|
+
* Every listener is registered in the capturing phase on `document`, so an app
|
|
8
|
+
* that calls `stopPropagation` cannot hide a user's action from the recorder.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* How close two click events must be to be one physical click. A label
|
|
12
|
+
* forwarding to its control dispatches synchronously; a human clicking twice
|
|
13
|
+
* cannot get near this.
|
|
14
|
+
*/
|
|
15
|
+
const ECHO_WINDOW_MS = 30;
|
|
16
|
+
/**
|
|
17
|
+
* How long a `pointerdown` stays eligible as the aim point of the click that
|
|
18
|
+
* follows it. A click is dispatched milliseconds after its pointerdown; this is
|
|
19
|
+
* generous enough to cover a slow frame and far too short to pair up two
|
|
20
|
+
* separate gestures.
|
|
21
|
+
*/
|
|
22
|
+
const GESTURE_WINDOW_MS = 1_000;
|
|
23
|
+
/** Keys that carry meaning outside a text field. */
|
|
24
|
+
const MEANINGFUL_KEYS = [
|
|
25
|
+
'Enter',
|
|
26
|
+
'Escape',
|
|
27
|
+
'Tab',
|
|
28
|
+
'ArrowUp',
|
|
29
|
+
'ArrowDown',
|
|
30
|
+
'ArrowLeft',
|
|
31
|
+
'ArrowRight',
|
|
32
|
+
'Backspace',
|
|
33
|
+
'Delete',
|
|
34
|
+
'Home',
|
|
35
|
+
'End',
|
|
36
|
+
'PageUp',
|
|
37
|
+
'PageDown',
|
|
38
|
+
' ',
|
|
39
|
+
];
|
|
40
|
+
function targetOf(e) {
|
|
41
|
+
const t = e.target;
|
|
42
|
+
return t && t.nodeType === 1 ? t : null;
|
|
43
|
+
}
|
|
44
|
+
function playwrightKey(e) {
|
|
45
|
+
const mods = [];
|
|
46
|
+
if (e.ctrlKey)
|
|
47
|
+
mods.push('Control');
|
|
48
|
+
if (e.altKey)
|
|
49
|
+
mods.push('Alt');
|
|
50
|
+
if (e.metaKey)
|
|
51
|
+
mods.push('Meta');
|
|
52
|
+
if (e.shiftKey && e.key.length > 1)
|
|
53
|
+
mods.push('Shift');
|
|
54
|
+
const key = e.key === ' ' ? 'Space' : e.key;
|
|
55
|
+
return [...mods, key].join('+');
|
|
56
|
+
}
|
|
57
|
+
function isStopHotkey(e) {
|
|
58
|
+
return (e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === 'x';
|
|
59
|
+
}
|
|
60
|
+
export function installCapture(ctx) {
|
|
61
|
+
const { config, transport, reveals } = ctx;
|
|
62
|
+
// fill: committed on change/blur, never per keystroke
|
|
63
|
+
const focusValue = new WeakMap();
|
|
64
|
+
const committed = new WeakMap();
|
|
65
|
+
/**
|
|
66
|
+
* One physical click can dispatch more than once. A `<label>` forwards to its
|
|
67
|
+
* control; menu and dialog primitives re-dispatch a synthetic click on their
|
|
68
|
+
* trigger. Replaying every copy runs a different flow than the one recorded —
|
|
69
|
+
* two toggles instead of one, a menu opened and immediately closed.
|
|
70
|
+
*
|
|
71
|
+
* Tracked against the last click we actually RECORDED, never merely the last
|
|
72
|
+
* one seen. If a click is dropped because its target has no usable selector,
|
|
73
|
+
* the copy that follows must still be free to record: suppressing it too
|
|
74
|
+
* would leave the gesture with no step at all, which is the one outcome worse
|
|
75
|
+
* than a duplicate, because nothing in the artifact shows it happened.
|
|
76
|
+
*/
|
|
77
|
+
let lastRecorded = null;
|
|
78
|
+
let lastPointerDown = null;
|
|
79
|
+
/**
|
|
80
|
+
* What the user actually aimed at.
|
|
81
|
+
*
|
|
82
|
+
* When a menu or dialog opens on `pointerdown` and portals content over the
|
|
83
|
+
* cursor, `pointerup` lands on that new content and the browser dispatches
|
|
84
|
+
* the click on the nearest common ancestor of the two — `<body>`, or `<html>`
|
|
85
|
+
* when the portal is a sibling of the trigger. The click event then names an
|
|
86
|
+
* element the user never touched, and for `<html>` there is no usable
|
|
87
|
+
* selector at all, so the gesture is dropped and the recording is silently
|
|
88
|
+
* missing a step.
|
|
89
|
+
*
|
|
90
|
+
* `pointerdown` fires before any of that reshuffling, so its target is the
|
|
91
|
+
* one thing in the sequence that reliably identifies the control.
|
|
92
|
+
*/
|
|
93
|
+
function resolveGestureTarget(clicked) {
|
|
94
|
+
const pd = lastPointerDown;
|
|
95
|
+
if (!pd || pd.el === clicked)
|
|
96
|
+
return clicked;
|
|
97
|
+
if (Date.now() - pd.t > GESTURE_WINDOW_MS)
|
|
98
|
+
return clicked;
|
|
99
|
+
const retargeted = clicked === document.documentElement || clicked === document.body;
|
|
100
|
+
// Only trust the aim point if it is still addressable; a control that
|
|
101
|
+
// unmounted on open tells us nothing replay could act on.
|
|
102
|
+
if (retargeted && pd.el.isConnected && describe(pd.el))
|
|
103
|
+
return pd.el;
|
|
104
|
+
// The click target may also simply have no selector of its own.
|
|
105
|
+
if (!describe(clicked) && pd.el.isConnected && describe(pd.el))
|
|
106
|
+
return pd.el;
|
|
107
|
+
return clicked;
|
|
108
|
+
}
|
|
109
|
+
function isEchoOfRecordedClick(el) {
|
|
110
|
+
const prev = lastRecorded;
|
|
111
|
+
if (!prev)
|
|
112
|
+
return false;
|
|
113
|
+
// A forwarded or synthesized click lands in the same task as the one that
|
|
114
|
+
// caused it. Anything slower is a person clicking again.
|
|
115
|
+
if (Date.now() - prev.t > ECHO_WINDOW_MS)
|
|
116
|
+
return false;
|
|
117
|
+
if (prev.el === el)
|
|
118
|
+
return true;
|
|
119
|
+
if (prev.el.contains(el) || el.contains(prev.el))
|
|
120
|
+
return true;
|
|
121
|
+
const label = el.closest('label');
|
|
122
|
+
return Boolean(label && label === prev.el.closest('label'));
|
|
123
|
+
}
|
|
124
|
+
/** Returns false when the step was dropped for having no addressable target. */
|
|
125
|
+
function emitAction(action, el, value) {
|
|
126
|
+
ctx.beforeAction?.();
|
|
127
|
+
const target = el ? describe(el) : null;
|
|
128
|
+
if (el && !target)
|
|
129
|
+
return false; // nothing addressable — a step we could never replay
|
|
130
|
+
const t = Date.now();
|
|
131
|
+
reveals.noteAction(t);
|
|
132
|
+
const ev = { kind: 'action', action, value, target, t, author: 'human' };
|
|
133
|
+
transport.emit(ev);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
/** Emit the preceding hover first, when that hover is what revealed this target. */
|
|
137
|
+
function flushRevealingHover(actionTarget) {
|
|
138
|
+
const hovered = reveals.takeLoadBearingHover(actionTarget);
|
|
139
|
+
if (hovered)
|
|
140
|
+
emitAction('hover', hovered, null);
|
|
141
|
+
}
|
|
142
|
+
function valueOf(el) {
|
|
143
|
+
if (el.isContentEditable)
|
|
144
|
+
return clean(el.innerText, 500);
|
|
145
|
+
return el.value ?? '';
|
|
146
|
+
}
|
|
147
|
+
function commitFill(el) {
|
|
148
|
+
if (!isEditable(el))
|
|
149
|
+
return;
|
|
150
|
+
const value = valueOf(el);
|
|
151
|
+
if (committed.get(el) === value)
|
|
152
|
+
return;
|
|
153
|
+
if (focusValue.get(el) === value && committed.get(el) === undefined)
|
|
154
|
+
return;
|
|
155
|
+
committed.set(el, value);
|
|
156
|
+
flushRevealingHover(el);
|
|
157
|
+
emitAction('fill', el, value);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Commit an edit that is still sitting in the focused field before recording
|
|
161
|
+
* whatever the user just did instead.
|
|
162
|
+
*
|
|
163
|
+
* `change`/`blur` are the commit triggers, but they are not guaranteed to
|
|
164
|
+
* fire *before* the next action: programmatic drivers frequently move on
|
|
165
|
+
* without blurring, which would emit the fill after the action it preceded
|
|
166
|
+
* and put the IR out of order. This only fixes ordering — it never commits a
|
|
167
|
+
* value that change/blur would not have committed anyway.
|
|
168
|
+
*/
|
|
169
|
+
function flushPendingFill() {
|
|
170
|
+
const active = document.activeElement;
|
|
171
|
+
if (active && isEditable(active))
|
|
172
|
+
commitFill(active);
|
|
173
|
+
}
|
|
174
|
+
const on = (type, handler) => {
|
|
175
|
+
document.addEventListener(type, handler, true);
|
|
176
|
+
};
|
|
177
|
+
on('focusin', (e) => {
|
|
178
|
+
const el = targetOf(e);
|
|
179
|
+
if (el && isEditable(el))
|
|
180
|
+
focusValue.set(el, valueOf(el));
|
|
181
|
+
});
|
|
182
|
+
on('mouseover', (e) => {
|
|
183
|
+
const el = targetOf(e);
|
|
184
|
+
if (el)
|
|
185
|
+
reveals.noteHover(el);
|
|
186
|
+
});
|
|
187
|
+
on('pointerdown', (e) => {
|
|
188
|
+
const el = targetOf(e);
|
|
189
|
+
if (el)
|
|
190
|
+
lastPointerDown = { el, t: Date.now() };
|
|
191
|
+
});
|
|
192
|
+
on('click', (e) => {
|
|
193
|
+
const clicked = targetOf(e);
|
|
194
|
+
if (!clicked)
|
|
195
|
+
return;
|
|
196
|
+
const el = resolveGestureTarget(clicked);
|
|
197
|
+
if (isEchoOfRecordedClick(el))
|
|
198
|
+
return;
|
|
199
|
+
flushPendingFill();
|
|
200
|
+
flushRevealingHover(el);
|
|
201
|
+
if (emitAction('click', el, null))
|
|
202
|
+
lastRecorded = { el, t: Date.now() };
|
|
203
|
+
});
|
|
204
|
+
on('dblclick', (e) => {
|
|
205
|
+
const el = targetOf(e);
|
|
206
|
+
if (!el)
|
|
207
|
+
return;
|
|
208
|
+
flushPendingFill();
|
|
209
|
+
emitAction('dblclick', el, null);
|
|
210
|
+
});
|
|
211
|
+
on('change', (e) => {
|
|
212
|
+
const el = targetOf(e);
|
|
213
|
+
if (!el)
|
|
214
|
+
return;
|
|
215
|
+
if (el.tagName.toLowerCase() === 'select') {
|
|
216
|
+
const sel = el;
|
|
217
|
+
const opt = sel.selectedOptions[0];
|
|
218
|
+
flushPendingFill();
|
|
219
|
+
flushRevealingHover(el);
|
|
220
|
+
emitAction('select', el, opt ? opt.value || clean(opt.textContent) : sel.value);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// Checkbox/radio state changes are already represented by their click.
|
|
224
|
+
if (isEditable(el))
|
|
225
|
+
commitFill(el);
|
|
226
|
+
});
|
|
227
|
+
on('blur', (e) => {
|
|
228
|
+
const el = targetOf(e);
|
|
229
|
+
if (el && isEditable(el))
|
|
230
|
+
commitFill(el);
|
|
231
|
+
});
|
|
232
|
+
on('keydown', (e) => {
|
|
233
|
+
// Recording hotkey: swallowed entirely so it never lands in the IR.
|
|
234
|
+
if (isStopHotkey(e)) {
|
|
235
|
+
e.preventDefault();
|
|
236
|
+
e.stopPropagation();
|
|
237
|
+
transport.binding(config.stopBinding)?.({ reason: 'hotkey' });
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const el = targetOf(e);
|
|
241
|
+
if (isEditable(el)) {
|
|
242
|
+
// Inside a field only Enter and Escape carry meaning (submit / dismiss).
|
|
243
|
+
// Typing itself is captured as a single `fill` on commit.
|
|
244
|
+
if (e.key !== 'Enter' && e.key !== 'Escape')
|
|
245
|
+
return;
|
|
246
|
+
commitFill(el);
|
|
247
|
+
}
|
|
248
|
+
else if (!MEANINGFUL_KEYS.includes(e.key) && !(e.ctrlKey || e.metaKey || e.altKey)) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
flushPendingFill();
|
|
253
|
+
}
|
|
254
|
+
emitAction('press', el, playwrightKey(e));
|
|
255
|
+
});
|
|
256
|
+
installScrollCapture(ctx, emitAction);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Offscreen probe elements that libraries scroll programmatically to detect
|
|
260
|
+
* resizes — element-resize-detector, react-virtualized, ResizeSensor. They fire
|
|
261
|
+
* a scroll during layout that no user performed, and recording it produces a
|
|
262
|
+
* step whose selector can never resolve.
|
|
263
|
+
*/
|
|
264
|
+
const RESIZE_PROBE = [
|
|
265
|
+
'.erd_scroll_detection_container',
|
|
266
|
+
'.resize-sensor',
|
|
267
|
+
'.resize-triggers',
|
|
268
|
+
'[data-resize-sensor]',
|
|
269
|
+
];
|
|
270
|
+
function isResizeProbe(el) {
|
|
271
|
+
if (!el)
|
|
272
|
+
return false;
|
|
273
|
+
return RESIZE_PROBE.some((sel) => {
|
|
274
|
+
try {
|
|
275
|
+
return el.matches(sel) || el.closest(sel) !== null;
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/** Scroll is debounced and only recorded when the position actually moved. */
|
|
283
|
+
function installScrollCapture(ctx, emitAction) {
|
|
284
|
+
const { config } = ctx;
|
|
285
|
+
const lastPosition = new WeakMap();
|
|
286
|
+
const timers = new WeakMap();
|
|
287
|
+
document.addEventListener('scroll', (e) => {
|
|
288
|
+
const raw = e.target;
|
|
289
|
+
if (raw && raw.nodeType === 1 && isResizeProbe(raw))
|
|
290
|
+
return;
|
|
291
|
+
const isDocument = !raw || raw === document || raw === document.documentElement || raw === document.body;
|
|
292
|
+
const key = isDocument ? document : raw;
|
|
293
|
+
const el = isDocument ? null : raw;
|
|
294
|
+
const x = el ? el.scrollLeft : window.scrollX;
|
|
295
|
+
const y = el ? el.scrollTop : window.scrollY;
|
|
296
|
+
const existing = timers.get(key);
|
|
297
|
+
if (existing)
|
|
298
|
+
clearTimeout(existing);
|
|
299
|
+
timers.set(key, setTimeout(() => {
|
|
300
|
+
const last = lastPosition.get(key) ?? { x: 0, y: 0 };
|
|
301
|
+
const moved = Math.abs(x - last.x) >= config.scrollMinDeltaPx ||
|
|
302
|
+
Math.abs(y - last.y) >= config.scrollMinDeltaPx;
|
|
303
|
+
if (!moved)
|
|
304
|
+
return;
|
|
305
|
+
lastPosition.set(key, { x, y });
|
|
306
|
+
emitAction('scroll', el, JSON.stringify({ x, y }));
|
|
307
|
+
}, config.scrollDebounceMs));
|
|
308
|
+
}, true);
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../../src/recorder/agent/capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared between the bundled in-page agent and the Node side that injects it.
|
|
3
|
+
* Kept in its own module so neither direction pulls in the other's code.
|
|
4
|
+
*/
|
|
5
|
+
export interface AgentConfig {
|
|
6
|
+
emitBinding: string;
|
|
7
|
+
stopBinding: string;
|
|
8
|
+
scrollDebounceMs: number;
|
|
9
|
+
scrollMinDeltaPx: number;
|
|
10
|
+
/** Cap on selectors harvested from a single mutation record, to bound cost. */
|
|
11
|
+
maxSelectorsPerMutation: number;
|
|
12
|
+
/** A hover only becomes a step if it revealed content this recently. */
|
|
13
|
+
hoverRevealWindowMs: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const DEFAULT_AGENT_CONFIG: AgentConfig;
|
|
16
|
+
/** The bundle exposes the installer here; the injected tail calls it. */
|
|
17
|
+
export declare const INSTALL_GLOBAL = "__replayInstall";
|
|
18
|
+
/** Set once every listener is installed; see `verifyInstrumentation`. */
|
|
19
|
+
export declare const AGENT_READY_FLAG = "__replayAgentReady";
|
|
20
|
+
/**
|
|
21
|
+
* Settles any pending DOM reaction immediately. The host calls this when the
|
|
22
|
+
* recording stops, so the final action's reaction is not lost to a confirmation
|
|
23
|
+
* timer that would have fired after teardown.
|
|
24
|
+
*/
|
|
25
|
+
export declare const FLUSH_GLOBAL = "__replayFlush";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared between the bundled in-page agent and the Node side that injects it.
|
|
3
|
+
* Kept in its own module so neither direction pulls in the other's code.
|
|
4
|
+
*/
|
|
5
|
+
export const DEFAULT_AGENT_CONFIG = {
|
|
6
|
+
emitBinding: '__replayEmit',
|
|
7
|
+
stopBinding: '__replayStop',
|
|
8
|
+
scrollDebounceMs: 150,
|
|
9
|
+
scrollMinDeltaPx: 50,
|
|
10
|
+
maxSelectorsPerMutation: 12,
|
|
11
|
+
hoverRevealWindowMs: 400,
|
|
12
|
+
};
|
|
13
|
+
/** The bundle exposes the installer here; the injected tail calls it. */
|
|
14
|
+
export const INSTALL_GLOBAL = '__replayInstall';
|
|
15
|
+
/** Set once every listener is installed; see `verifyInstrumentation`. */
|
|
16
|
+
export const AGENT_READY_FLAG = '__replayAgentReady';
|
|
17
|
+
/**
|
|
18
|
+
* Settles any pending DOM reaction immediately. The host calls this when the
|
|
19
|
+
* recording stops, so the final action's reaction is not lost to a confirmation
|
|
20
|
+
* timer that would have fired after teardown.
|
|
21
|
+
*/
|
|
22
|
+
export const FLUSH_GLOBAL = '__replayFlush';
|
|
23
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/recorder/agent/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,MAAM,CAAC,MAAM,oBAAoB,GAAgB;IAC/C,WAAW,EAAE,cAAc;IAC3B,WAAW,EAAE,cAAc;IAC3B,gBAAgB,EAAE,GAAG;IACrB,gBAAgB,EAAE,EAAE;IACpB,uBAAuB,EAAE,EAAE;IAC3B,mBAAmB,EAAE,GAAG;CACzB,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAEhD,yEAAyE;AACzE,MAAM,CAAC,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AgentConfig } from './config.js';
|
|
2
|
+
import type { RevealTracker } from './reveal-tracker.js';
|
|
3
|
+
import type { Transport } from './transport.js';
|
|
4
|
+
export interface DomReactionContext {
|
|
5
|
+
config: AgentConfig;
|
|
6
|
+
transport: Transport;
|
|
7
|
+
reveals: RevealTracker;
|
|
8
|
+
}
|
|
9
|
+
export interface DomReactionHandle {
|
|
10
|
+
/** Confirm and emit any pending appearances now. */
|
|
11
|
+
flushAppearances(): void;
|
|
12
|
+
}
|
|
13
|
+
export declare function observeDomReactions(ctx: DomReactionContext): DomReactionHandle;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { isTransientIndicator } from '../../noise.js';
|
|
2
|
+
import { accessibleName, getRole } from './roles.js';
|
|
3
|
+
import { appearedSelector, goneSelector } from './selectors.js';
|
|
4
|
+
import { isVisible } from './visibility.js';
|
|
5
|
+
/**
|
|
6
|
+
* Summarizes DOM mutations into the appeared/gone selectors that become a
|
|
7
|
+
* step's `waitAfter`. This is what lets replay wait on a real signal instead of
|
|
8
|
+
* a guessed sleep.
|
|
9
|
+
*/
|
|
10
|
+
/** Descendants worth checking inside an added subtree. */
|
|
11
|
+
const INTERESTING = '[data-testid], [data-test], [data-test-id], [data-cy], [id], [role], button, a[href]';
|
|
12
|
+
const OBSERVED_ATTRS = ['class', 'style', 'hidden', 'aria-hidden'];
|
|
13
|
+
/**
|
|
14
|
+
* Longest an appearance waits for confirmation before being recorded.
|
|
15
|
+
*
|
|
16
|
+
* On a busy React app most DOM churn after an action is unrelated to it —
|
|
17
|
+
* portals, context menus, tooltips and virtualised rows mount and unmount
|
|
18
|
+
* constantly. Recording all of it produces waits the app will never satisfy,
|
|
19
|
+
* and the replay then fails on a perfectly healthy build.
|
|
20
|
+
*
|
|
21
|
+
* The confirmation also flushes early, the moment the next action is captured,
|
|
22
|
+
* because that is exactly when replay stops waiting and moves on. Confirming on
|
|
23
|
+
* a fixed timer alone would discard real signals in a fast or agent-driven
|
|
24
|
+
* flow, where the next action lands well inside this window.
|
|
25
|
+
*
|
|
26
|
+
* `gone` needs no such check: a removed node stays removed.
|
|
27
|
+
*/
|
|
28
|
+
const APPEAR_CONFIRM_MS = 600;
|
|
29
|
+
/** Playwright selector -> something document.querySelector can evaluate. */
|
|
30
|
+
function toCssSelector(selector) {
|
|
31
|
+
if (selector.startsWith('role=') || selector.startsWith('text=')) {
|
|
32
|
+
throw new Error('not a CSS selector');
|
|
33
|
+
}
|
|
34
|
+
return selector;
|
|
35
|
+
}
|
|
36
|
+
function harvest(nodes, into, pick, revealed, max) {
|
|
37
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
38
|
+
const node = nodes[i];
|
|
39
|
+
if (!node || node.nodeType !== 1)
|
|
40
|
+
continue;
|
|
41
|
+
const el = node;
|
|
42
|
+
revealed.push(el);
|
|
43
|
+
const own = pick(el);
|
|
44
|
+
if (own && into.size < max)
|
|
45
|
+
into.add(own);
|
|
46
|
+
const descendants = el.querySelectorAll(INTERESTING);
|
|
47
|
+
for (let j = 0; j < descendants.length && into.size < max; j++) {
|
|
48
|
+
const d = descendants[j];
|
|
49
|
+
if (!d)
|
|
50
|
+
continue;
|
|
51
|
+
const sel = pick(d);
|
|
52
|
+
if (sel)
|
|
53
|
+
into.add(sel);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function observeDomReactions(ctx) {
|
|
58
|
+
const { config, transport, reveals } = ctx;
|
|
59
|
+
const visibility = new WeakMap();
|
|
60
|
+
/** Appearances awaiting confirmation, keyed by their original timestamp. */
|
|
61
|
+
let pending = [];
|
|
62
|
+
const stillVisible = (sel) => {
|
|
63
|
+
try {
|
|
64
|
+
const el = document.querySelector(toCssSelector(sel));
|
|
65
|
+
return el ? isVisible(el) : false;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// role=/text= selectors are not queryable here. Keep them: they came from
|
|
69
|
+
// a high-priority path, and dropping one on a syntax technicality would
|
|
70
|
+
// lose real signal.
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const confirm = (entry) => {
|
|
75
|
+
const survived = entry.selectors.filter(stillVisible);
|
|
76
|
+
// Carries the ORIGINAL timestamp so the compiler still attributes it to the
|
|
77
|
+
// action that caused it, not to the confirmation delay.
|
|
78
|
+
if (survived.length) {
|
|
79
|
+
transport.emit({ kind: 'dom', appeared: survived, gone: [], t: entry.at });
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const flushAppearances = () => {
|
|
83
|
+
const due = pending;
|
|
84
|
+
pending = [];
|
|
85
|
+
for (const entry of due) {
|
|
86
|
+
clearTimeout(entry.timer);
|
|
87
|
+
confirm(entry);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// Only rendered elements make usable appear-signals; see visibility.ts.
|
|
91
|
+
// Loading indicators are excluded by what they are, not how long they last:
|
|
92
|
+
// a spinner genuinely survives to the next action on a cold run, then never
|
|
93
|
+
// renders at all once the data is cached — so a healthy replay would fail.
|
|
94
|
+
const visibleAppearedSelector = (el) => {
|
|
95
|
+
if (!isVisible(el))
|
|
96
|
+
return null;
|
|
97
|
+
if (isTransientIndicator(getRole(el), accessibleName(el)))
|
|
98
|
+
return null;
|
|
99
|
+
return appearedSelector(el);
|
|
100
|
+
};
|
|
101
|
+
const start = () => {
|
|
102
|
+
const observer = new MutationObserver((records) => {
|
|
103
|
+
const appeared = new Set();
|
|
104
|
+
const gone = new Set();
|
|
105
|
+
const revealed = [];
|
|
106
|
+
for (const rec of records) {
|
|
107
|
+
if (rec.type === 'childList') {
|
|
108
|
+
harvest(rec.addedNodes, appeared, visibleAppearedSelector, revealed, config.maxSelectorsPerMutation);
|
|
109
|
+
harvest(rec.removedNodes, gone, goneSelector, [], config.maxSelectorsPerMutation);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
// An attribute change can flip visibility without touching the tree —
|
|
113
|
+
// the CSS-hidden toast case, which childList alone would miss.
|
|
114
|
+
const el = rec.target;
|
|
115
|
+
const sel = appearedSelector(el);
|
|
116
|
+
if (!sel)
|
|
117
|
+
continue;
|
|
118
|
+
const now = isVisible(el);
|
|
119
|
+
const was = visibility.get(el);
|
|
120
|
+
visibility.set(el, now);
|
|
121
|
+
// First sighting only seeds the cache; it is not a transition.
|
|
122
|
+
if (was === undefined || was === now)
|
|
123
|
+
continue;
|
|
124
|
+
if (now) {
|
|
125
|
+
appeared.add(sel);
|
|
126
|
+
revealed.push(el);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const g = goneSelector(el);
|
|
130
|
+
if (g)
|
|
131
|
+
gone.add(g);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const at = Date.now();
|
|
135
|
+
reveals.noteRevealed(revealed, at);
|
|
136
|
+
// Emitted immediately — a removal is already final.
|
|
137
|
+
if (gone.size) {
|
|
138
|
+
transport.emit({ kind: 'dom', appeared: [], gone: Array.from(gone), t: at });
|
|
139
|
+
}
|
|
140
|
+
if (appeared.size) {
|
|
141
|
+
const entry = {
|
|
142
|
+
selectors: Array.from(appeared),
|
|
143
|
+
at,
|
|
144
|
+
timer: setTimeout(() => {
|
|
145
|
+
pending = pending.filter((e) => e !== entry);
|
|
146
|
+
confirm(entry);
|
|
147
|
+
}, APPEAR_CONFIRM_MS),
|
|
148
|
+
};
|
|
149
|
+
pending.push(entry);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
observer.observe(document.documentElement, {
|
|
153
|
+
childList: true,
|
|
154
|
+
subtree: true,
|
|
155
|
+
attributes: true,
|
|
156
|
+
attributeFilter: OBSERVED_ATTRS,
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
// The init script can run before <html> exists.
|
|
160
|
+
if (document.documentElement)
|
|
161
|
+
start();
|
|
162
|
+
else
|
|
163
|
+
document.addEventListener('DOMContentLoaded', start, { once: true });
|
|
164
|
+
return { flushAppearances };
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=dom-reaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-reaction.js","sourceRoot":"","sources":["../../../src/recorder/agent/dom-reaction.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;GAIG;AAEH,0DAA0D;AAC1D,MAAM,WAAW,GAAG,sFAAsF,CAAC;AAE3G,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAa9B,4EAA4E;AAC5E,SAAS,aAAa,CAAC,QAAgB;IACrC,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,OAAO,CACd,KAAe,EACf,IAAiB,EACjB,IAAoC,EACpC,QAAmB,EACnB,GAAW;IAEX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;YAAE,SAAS;QAC3C,MAAM,EAAE,GAAG,IAAe,CAAC;QAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAElB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;QACrB,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,GAAG,GAAG;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE1C,MAAM,WAAW,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/D,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,CAAC,CAAC;gBAAE,SAAS;YACjB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,GAAG;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAuB;IACzD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;IAC3C,MAAM,UAAU,GAAG,IAAI,OAAO,EAAoB,CAAC;IAEnD,4EAA4E;IAC5E,IAAI,OAAO,GAAgF,EAAE,CAAC;IAE9F,MAAM,YAAY,GAAG,CAAC,GAAW,EAAW,EAAE;QAC5C,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;YACtD,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;YAC1E,wEAAwE;YACxE,oBAAoB;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,KAA0C,EAAQ,EAAE;QACnE,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACtD,4EAA4E;QAC5E,wDAAwD;QACxD,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;YACpB,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,GAAS,EAAE;QAClC,MAAM,GAAG,GAAG,OAAO,CAAC;QACpB,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;YACxB,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IACH,CAAC,CAAC;IAEF,wEAAwE;IACxE,4EAA4E;IAC5E,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,uBAAuB,GAAG,CAAC,EAAW,EAAiB,EAAE;QAC7D,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;QAChC,IAAI,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACvE,OAAO,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,CAAC,OAAO,EAAE,EAAE;YAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;YACnC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,MAAM,QAAQ,GAAc,EAAE,CAAC;YAE/B,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;gBAC1B,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBAC7B,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,uBAAuB,EAAE,QAAQ,EAAE,MAAM,CAAC,uBAAuB,CAAC,CAAC;oBACrG,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,CAAC,uBAAuB,CAAC,CAAC;oBAClF,SAAS;gBACX,CAAC;gBAED,sEAAsE;gBACtE,+DAA+D;gBAC/D,MAAM,EAAE,GAAG,GAAG,CAAC,MAAiB,CAAC;gBACjC,MAAM,GAAG,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;gBACjC,IAAI,CAAC,GAAG;oBAAE,SAAS;gBACnB,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;gBAC1B,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC/B,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBACxB,+DAA+D;gBAC/D,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,GAAG;oBAAE,SAAS;gBAC/C,IAAI,GAAG,EAAE,CAAC;oBACR,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBAClB,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;oBAC3B,IAAI,CAAC;wBAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACrB,CAAC;YACH,CAAC;YAED,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACtB,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAEnC,oDAAoD;YACpD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC/E,CAAC;YAED,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAClB,MAAM,KAAK,GAAG;oBACZ,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAC/B,EAAE;oBACF,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE;wBACrB,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;wBAC7C,OAAO,CAAC,KAAK,CAAC,CAAC;oBACjB,CAAC,EAAE,iBAAiB,CAAC;iBACtB,CAAC;gBACF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE;YACzC,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,IAAI;YAChB,eAAe,EAAE,cAAc;SAChC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,gDAAgD;IAChD,IAAI,QAAQ,CAAC,eAAe;QAAE,KAAK,EAAE,CAAC;;QACjC,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1E,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { INSTALL_GLOBAL } from './config.js';
|
|
2
|
+
import { pageAgent } from './page-agent.js';
|
|
3
|
+
/**
|
|
4
|
+
* Bundle entry point. esbuild compiles this and everything it imports into a
|
|
5
|
+
* single IIFE, which is injected into the page as source text.
|
|
6
|
+
*
|
|
7
|
+
* The bundle only *publishes* the installer — it does not run it. Config is
|
|
8
|
+
* applied by a short call appended after the bundle, so the bundle itself is a
|
|
9
|
+
* fixed, cacheable string.
|
|
10
|
+
*/
|
|
11
|
+
window[INSTALL_GLOBAL] = (config) => {
|
|
12
|
+
pageAgent(config);
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/recorder/agent/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAoB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;GAOG;AACF,MAA6C,CAAC,cAAc,CAAC,GAAG,CAAC,MAAmB,EAAQ,EAAE;IAC7F,SAAS,CAAC,MAAM,CAAC,CAAC;AACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type AgentConfig } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* The in-page capture agent.
|
|
4
|
+
*
|
|
5
|
+
* Bundled by `scripts/build-agent.ts` into a standalone IIFE and injected as
|
|
6
|
+
* source text. It is ordinary module code — it may import freely.
|
|
7
|
+
*
|
|
8
|
+
* Only the top frame records. Selectors generated in a subframe would not
|
|
9
|
+
* resolve against the main frame at replay time, and Phase 0 has no frame
|
|
10
|
+
* addressing in the IR.
|
|
11
|
+
*/
|
|
12
|
+
export declare function pageAgent(config: AgentConfig): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { installCapture } from './capture.js';
|
|
2
|
+
import { AGENT_READY_FLAG, FLUSH_GLOBAL } from './config.js';
|
|
3
|
+
import { observeDomReactions } from './dom-reaction.js';
|
|
4
|
+
import { createRevealTracker } from './reveal-tracker.js';
|
|
5
|
+
import { createTransport } from './transport.js';
|
|
6
|
+
const INSTALLED_FLAG = '__replayAgentInstalled';
|
|
7
|
+
/**
|
|
8
|
+
* The in-page capture agent.
|
|
9
|
+
*
|
|
10
|
+
* Bundled by `scripts/build-agent.ts` into a standalone IIFE and injected as
|
|
11
|
+
* source text. It is ordinary module code — it may import freely.
|
|
12
|
+
*
|
|
13
|
+
* Only the top frame records. Selectors generated in a subframe would not
|
|
14
|
+
* resolve against the main frame at replay time, and Phase 0 has no frame
|
|
15
|
+
* addressing in the IR.
|
|
16
|
+
*/
|
|
17
|
+
export function pageAgent(config) {
|
|
18
|
+
const globals = window;
|
|
19
|
+
if (globals[INSTALLED_FLAG])
|
|
20
|
+
return;
|
|
21
|
+
if (window.top !== window)
|
|
22
|
+
return;
|
|
23
|
+
globals[INSTALLED_FLAG] = true;
|
|
24
|
+
const transport = createTransport(config);
|
|
25
|
+
// Shared by both halves: the observer reports what appeared, the capture
|
|
26
|
+
// listeners report hovers and actions, and this decides when a hover mattered.
|
|
27
|
+
const reveals = createRevealTracker(config);
|
|
28
|
+
const dom = observeDomReactions({ config, transport, reveals });
|
|
29
|
+
// An action closes the previous action's reaction window, so pending
|
|
30
|
+
// appearances are confirmed at exactly the point replay would give up on them.
|
|
31
|
+
installCapture({ config, transport, reveals, beforeAction: () => dom.flushAppearances() });
|
|
32
|
+
// The last action has no successor to close its window, so the host settles
|
|
33
|
+
// it explicitly at teardown.
|
|
34
|
+
globals[FLUSH_GLOBAL] = () => dom.flushAppearances();
|
|
35
|
+
// Last line on purpose: reaching it proves every listener above installed.
|
|
36
|
+
// A recording that silently captures nothing is the worst failure this tool
|
|
37
|
+
// could have, so the host asserts on this flag.
|
|
38
|
+
globals[AGENT_READY_FLAG] = true;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=page-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-agent.js","sourceRoot":"","sources":["../../../src/recorder/agent/page-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAoB,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEhD;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAAC,MAAmB;IAC3C,MAAM,OAAO,GAAG,MAA4C,CAAC;IAC7D,IAAI,OAAO,CAAC,cAAc,CAAC;QAAE,OAAO;IACpC,IAAI,MAAM,CAAC,GAAG,KAAK,MAAM;QAAE,OAAO;IAClC,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;IAE/B,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC1C,yEAAyE;IACzE,+EAA+E;IAC/E,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE5C,MAAM,GAAG,GAAG,mBAAmB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;IAChE,qEAAqE;IACrE,+EAA+E;IAC/E,cAAc,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAE3F,4EAA4E;IAC5E,6BAA6B;IAC7B,OAAO,CAAC,YAAY,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAErD,2EAA2E;IAC3E,4EAA4E;IAC5E,gDAAgD;IAChD,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;AACnC,CAAC"}
|