@reticlehq/browser 2.13.0 → 2.14.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/dist/actions/actions-dom.d.ts +0 -20
- package/dist/actions/actions-dom.js +0 -66
- package/dist/actions/actions-press.d.ts +10 -0
- package/dist/actions/actions-press.js +77 -0
- package/dist/actions/actions.d.ts +0 -89
- package/dist/actions/actions.js +24 -358
- package/dist/actions/appeared-text.d.ts +0 -20
- package/dist/actions/appeared-text.js +0 -36
- package/dist/actions/danger-context.d.ts +3 -0
- package/dist/actions/danger-context.js +39 -0
- package/dist/actions/scroll.d.ts +0 -9
- package/dist/actions/scroll.js +0 -7
- package/dist/actions/synthetic-input.d.ts +0 -7
- package/dist/actions/synthetic-input.js +0 -27
- package/dist/actions/value-input.d.ts +0 -30
- package/dist/actions/value-input.js +0 -33
- package/dist/commands/commands.d.ts +0 -2
- package/dist/commands/commands.js +0 -67
- package/dist/connect-options.d.ts +2 -96
- package/dist/dom/a11y.d.ts +0 -29
- package/dist/dom/a11y.js +1 -88
- package/dist/dom/auto-anchor.d.ts +0 -40
- package/dist/dom/auto-anchor.js +0 -33
- package/dist/dom/chart.d.ts +0 -61
- package/dist/dom/chart.js +0 -60
- package/dist/dom/desktop-capture.d.ts +0 -30
- package/dist/dom/desktop-capture.js +0 -72
- package/dist/dom/dom-ignore.d.ts +0 -30
- package/dist/dom/dom-ignore.js +0 -47
- package/dist/dom/occlusion.d.ts +0 -7
- package/dist/dom/occlusion.js +0 -7
- package/dist/dom/query.d.ts +0 -14
- package/dist/dom/query.js +48 -214
- package/dist/dom/realm.d.ts +0 -20
- package/dist/dom/realm.js +0 -28
- package/dist/dom/refs.d.ts +0 -76
- package/dist/dom/refs.js +2 -133
- package/dist/dom/shadow-registry.d.ts +0 -33
- package/dist/dom/shadow-registry.js +2 -72
- package/dist/dom/snapshot.d.ts +2 -43
- package/dist/dom/snapshot.js +26 -151
- package/dist/dom/source.d.ts +0 -44
- package/dist/dom/source.js +0 -45
- package/dist/dom/theme.d.ts +0 -37
- package/dist/dom/theme.js +2 -52
- package/dist/dom/virtualized.d.ts +0 -17
- package/dist/dom/virtualized.js +0 -27
- package/dist/edit-epoch.d.ts +0 -22
- package/dist/edit-epoch.js +0 -52
- package/dist/index.js +1 -23
- package/dist/observers/animation.d.ts +0 -1
- package/dist/observers/animation.js +0 -3
- package/dist/observers/blind-spots.d.ts +0 -26
- package/dist/observers/blind-spots.js +2 -61
- package/dist/observers/console.d.ts +1 -2
- package/dist/observers/console.js +41 -30
- package/dist/observers/context-open.d.ts +1 -11
- package/dist/observers/context-open.js +10 -14
- package/dist/observers/departure.d.ts +3 -0
- package/dist/observers/departure.js +52 -0
- package/dist/observers/dialogs.d.ts +2 -0
- package/dist/observers/dialogs.js +39 -0
- package/dist/observers/dom.js +0 -44
- package/dist/observers/download.d.ts +1 -2
- package/dist/observers/download.js +38 -55
- package/dist/observers/focus.d.ts +0 -6
- package/dist/observers/focus.js +0 -7
- package/dist/observers/frames.d.ts +0 -9
- package/dist/observers/frames.js +1 -28
- package/dist/observers/health.d.ts +0 -5
- package/dist/observers/health.js +1 -33
- package/dist/observers/install-all.d.ts +2 -0
- package/dist/observers/install-all.js +26 -27
- package/dist/observers/ipc.d.ts +0 -25
- package/dist/observers/ipc.js +0 -65
- package/dist/observers/navigation.d.ts +2 -0
- package/dist/observers/navigation.js +33 -0
- package/dist/observers/network-body.d.ts +5 -28
- package/dist/observers/network-body.js +11 -108
- package/dist/observers/network-redact.d.ts +1 -20
- package/dist/observers/network-redact.js +1 -80
- package/dist/observers/network-stream.d.ts +0 -21
- package/dist/observers/network-stream.js +1 -44
- package/dist/observers/network.d.ts +2 -26
- package/dist/observers/network.js +177 -294
- package/dist/observers/perf.d.ts +0 -10
- package/dist/observers/perf.js +0 -16
- package/dist/observers/route.d.ts +1 -2
- package/dist/observers/route.js +13 -19
- package/dist/observers/scroll.d.ts +0 -1
- package/dist/observers/scroll.js +0 -9
- package/dist/observers/sdk-failure.d.ts +1 -9
- package/dist/observers/sdk-failure.js +1 -30
- package/dist/observers/state.d.ts +1 -11
- package/dist/observers/state.js +13 -41
- package/dist/observers/storage.d.ts +0 -14
- package/dist/observers/storage.js +10 -34
- package/dist/observers/types.d.ts +0 -15
- package/dist/observers/types.js +0 -14
- package/dist/patching/capture-method.d.ts +0 -24
- package/dist/patching/capture-method.js +0 -26
- package/dist/presenter/overlay.d.ts +0 -1
- package/dist/presenter/overlay.js +0 -1
- package/dist/presenter/presenter-brand.d.ts +0 -19
- package/dist/presenter/presenter-brand.js +0 -21
- package/dist/presenter/presenter-config.d.ts +1 -105
- package/dist/presenter/presenter-config.js +2 -91
- package/dist/presenter/presenter-controls.d.ts +0 -29
- package/dist/presenter/presenter-controls.js +3 -131
- package/dist/presenter/presenter-dock-layout.d.ts +0 -3
- package/dist/presenter/presenter-dock-layout.js +0 -4
- package/dist/presenter/presenter-drag.d.ts +2 -24
- package/dist/presenter/presenter-drag.js +0 -26
- package/dist/presenter/presenter-effects.d.ts +0 -9
- package/dist/presenter/presenter-effects.js +0 -9
- package/dist/presenter/presenter-glow.d.ts +0 -21
- package/dist/presenter/presenter-glow.js +1 -27
- package/dist/presenter/presenter-heroicons-data.d.ts +0 -8
- package/dist/presenter/presenter-heroicons-data.js +0 -8
- package/dist/presenter/presenter-hud-chrome.d.ts +0 -10
- package/dist/presenter/presenter-hud-chrome.js +0 -10
- package/dist/presenter/presenter-icons.d.ts +0 -6
- package/dist/presenter/presenter-icons.js +0 -6
- package/dist/presenter/presenter-log.d.ts +0 -38
- package/dist/presenter/presenter-log.js +1 -45
- package/dist/presenter/presenter-report-copy.d.ts +5 -66
- package/dist/presenter/presenter-report-copy.js +5 -66
- package/dist/presenter/presenter-report-styles.d.ts +0 -6
- package/dist/presenter/presenter-report-styles.js +6 -6
- package/dist/presenter/presenter-report.d.ts +4 -7
- package/dist/presenter/presenter-report.js +18 -76
- package/dist/presenter/presenter-run-state.d.ts +0 -7
- package/dist/presenter/presenter-run-state.js +0 -5
- package/dist/presenter/presenter-settings-styles.d.ts +0 -1
- package/dist/presenter/presenter-settings-styles.js +0 -1
- package/dist/presenter/presenter-settings.d.ts +1 -23
- package/dist/presenter/presenter-settings.js +1 -31
- package/dist/presenter/presenter-shell-styles.js +2 -5
- package/dist/presenter/presenter-shell.d.ts +2 -18
- package/dist/presenter/presenter-shell.js +1 -73
- package/dist/presenter/presenter-styles.d.ts +0 -9
- package/dist/presenter/presenter-styles.js +0 -9
- package/dist/presenter/presenter-tally.d.ts +0 -5
- package/dist/presenter/presenter-tally.js +0 -4
- package/dist/presenter/presenter-test-helpers.d.ts +0 -2
- package/dist/presenter/presenter-test-helpers.js +0 -2
- package/dist/presenter/presenter-verbs.js +0 -7
- package/dist/presenter/presenter-workspace.d.ts +0 -4
- package/dist/presenter/presenter-workspace.js +1 -8
- package/dist/presenter/presenter.d.ts +0 -60
- package/dist/presenter/presenter.js +4 -118
- package/dist/recorder/recorder-styles.d.ts +0 -6
- package/dist/recorder/recorder-styles.js +0 -6
- package/dist/recorder/recorder.d.ts +0 -34
- package/dist/recorder/recorder.js +0 -49
- package/dist/registry/adapters.d.ts +0 -10
- package/dist/registry/adapters.js +0 -10
- package/dist/registry/auto-testids.d.ts +0 -19
- package/dist/registry/auto-testids.js +1 -21
- package/dist/registry/capabilities.d.ts +0 -31
- package/dist/registry/capabilities.js +1 -46
- package/dist/registry/commit-and-signal.d.ts +0 -6
- package/dist/registry/commit-and-signal.js +1 -7
- package/dist/registry/domains.d.ts +0 -8
- package/dist/registry/domains.js +0 -7
- package/dist/registry/emitter.d.ts +0 -14
- package/dist/registry/emitter.js +0 -10
- package/dist/registry/store-adapters.d.ts +0 -186
- package/dist/registry/store-adapters.js +0 -163
- package/dist/registry/stores.d.ts +0 -33
- package/dist/registry/stores.js +0 -59
- package/dist/reticle-presenter-helpers.d.ts +0 -13
- package/dist/reticle-presenter-helpers.js +0 -14
- package/dist/reticle.d.ts +1 -52
- package/dist/reticle.js +20 -145
- package/dist/review/annotator-styles.js +0 -7
- package/dist/review/annotator.d.ts +2 -13
- package/dist/review/annotator.js +0 -40
- package/dist/review/mark-anchor.d.ts +0 -11
- package/dist/review/mark-anchor.js +0 -8
- package/dist/security/serialization.d.ts +0 -20
- package/dist/security/serialization.js +1 -66
- package/dist/session-continuity.d.ts +0 -16
- package/dist/session-continuity.js +1 -20
- package/dist/timers/clock.d.ts +1 -14
- package/dist/timers/clock.js +59 -60
- package/dist/timers/native-console.js +0 -4
- package/dist/timers/native-timers.d.ts +0 -25
- package/dist/timers/native-timers.js +3 -33
- package/dist/transport/transport.d.ts +0 -51
- package/dist/transport/transport.js +3 -129
- package/dist/transport/unreachable-message.d.ts +0 -17
- package/dist/transport/unreachable-message.js +0 -17
- package/dist/util/captured-method.d.ts +0 -35
- package/dist/util/captured-method.js +0 -35
- package/package.json +2 -2
|
@@ -1,26 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gathers the text an action put on the page, from mutation records the observer already receives.
|
|
3
|
-
*
|
|
4
|
-
* `domMutatedWithin` counts records and throws their content away, so a failed login reports
|
|
5
|
-
* `ok / settled / mutated` and reads exactly like a successful one. The message the app rendered
|
|
6
|
-
* was passing through this callback the whole time.
|
|
7
|
-
*
|
|
8
|
-
* Deliberately reports what appeared, never what it means: no error/success classification, no
|
|
9
|
-
* guessing which fragment matters. Truncated, because a route change can add a whole page.
|
|
10
|
-
*/
|
|
11
1
|
export declare class AppearedText {
|
|
12
2
|
#private;
|
|
13
3
|
collect(records: readonly MutationRecord[]): void;
|
|
14
|
-
/**
|
|
15
|
-
* `{ appeared }` when the APP added text, `{}` otherwise — an absent key means it added none.
|
|
16
|
-
*
|
|
17
|
-
* `wrote` is the value the action itself just set, and is excluded: a textarea carries its value
|
|
18
|
-
* in a child text node, so a controlled one re-rendering after the write mutates characterData
|
|
19
|
-
* with the caller's own string. Handing that back is noise wearing the name of evidence, and
|
|
20
|
-
* `valueChanged` already reports that the write landed. Exact-match only, so an app that quotes
|
|
21
|
-
* your input inside a sentence of its own ("No results for zzz") is still reported — that is
|
|
22
|
-
* the app talking.
|
|
23
|
-
*/
|
|
24
4
|
effect(wrote?: string): {
|
|
25
5
|
appeared?: string;
|
|
26
6
|
};
|
|
@@ -1,34 +1,11 @@
|
|
|
1
1
|
import { isIgnored } from '../dom/dom-ignore.js';
|
|
2
|
-
/** Cap on the reported text. Enough for an error message, far short of a re-rendered page. */
|
|
3
2
|
const APPEARED_MAX = 200;
|
|
4
|
-
/** Separator between distinct fragments that appeared in the same window. */
|
|
5
3
|
const JOIN = ' | ';
|
|
6
4
|
const TEXT_NODE = 3;
|
|
7
|
-
/** Any letter, in any script — Latin, Cyrillic, CJK, Arabic. */
|
|
8
5
|
const HAS_LETTER = /\p{L}/u;
|
|
9
|
-
/**
|
|
10
|
-
* A fragment with no letters at all is not the app saying something.
|
|
11
|
-
*
|
|
12
|
-
* The Hostile fixture mutates a counter every 16ms, and clicking a button on that page reported
|
|
13
|
-
* `appeared: "409"` — the ticker, not the action's effect. A count-up animation is exactly what
|
|
14
|
-
* emits a bare number into the settle window, and a bare number carries no message a reader can
|
|
15
|
-
* act on. Deliberately conservative: it drops only fragments with NO letters, so "status 500",
|
|
16
|
-
* "3 items deleted" and "Could not save" all survive. An app whose only feedback is a naked
|
|
17
|
-
* numeral loses it here — the snapshot still shows it, and that is the cheaper mistake.
|
|
18
|
-
*/
|
|
19
6
|
function saysSomething(text) {
|
|
20
7
|
return HAS_LETTER.test(text);
|
|
21
8
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Gathers the text an action put on the page, from mutation records the observer already receives.
|
|
24
|
-
*
|
|
25
|
-
* `domMutatedWithin` counts records and throws their content away, so a failed login reports
|
|
26
|
-
* `ok / settled / mutated` and reads exactly like a successful one. The message the app rendered
|
|
27
|
-
* was passing through this callback the whole time.
|
|
28
|
-
*
|
|
29
|
-
* Deliberately reports what appeared, never what it means: no error/success classification, no
|
|
30
|
-
* guessing which fragment matters. Truncated, because a route change can add a whole page.
|
|
31
|
-
*/
|
|
32
9
|
export class AppearedText {
|
|
33
10
|
#seen = new Set();
|
|
34
11
|
#length = 0;
|
|
@@ -37,7 +14,6 @@ export class AppearedText {
|
|
|
37
14
|
if (this.#full())
|
|
38
15
|
return;
|
|
39
16
|
if ('characterData' === record.type) {
|
|
40
|
-
// The NEW value; oldValue is not requested, so this is what the reader now sees.
|
|
41
17
|
this.#add(record.target.textContent, record.target.parentElement);
|
|
42
18
|
continue;
|
|
43
19
|
}
|
|
@@ -49,16 +25,6 @@ export class AppearedText {
|
|
|
49
25
|
}
|
|
50
26
|
}
|
|
51
27
|
}
|
|
52
|
-
/**
|
|
53
|
-
* `{ appeared }` when the APP added text, `{}` otherwise — an absent key means it added none.
|
|
54
|
-
*
|
|
55
|
-
* `wrote` is the value the action itself just set, and is excluded: a textarea carries its value
|
|
56
|
-
* in a child text node, so a controlled one re-rendering after the write mutates characterData
|
|
57
|
-
* with the caller's own string. Handing that back is noise wearing the name of evidence, and
|
|
58
|
-
* `valueChanged` already reports that the write landed. Exact-match only, so an app that quotes
|
|
59
|
-
* your input inside a sentence of its own ("No results for zzz") is still reported — that is
|
|
60
|
-
* the app talking.
|
|
61
|
-
*/
|
|
62
28
|
effect(wrote) {
|
|
63
29
|
const said = [...this.#seen].filter((text) => text !== wrote);
|
|
64
30
|
if (0 === said.length)
|
|
@@ -72,8 +38,6 @@ export class AppearedText {
|
|
|
72
38
|
return this.#length > APPEARED_MAX;
|
|
73
39
|
}
|
|
74
40
|
#add(raw, owner) {
|
|
75
|
-
// Reticle's own overlay mutates constantly (the "live" panel); reporting it as the app's
|
|
76
|
-
// response would be worse than reporting nothing.
|
|
77
41
|
if (null !== owner && isIgnored(owner))
|
|
78
42
|
return;
|
|
79
43
|
const text = (raw ?? '').replace(/\s+/g, ' ').trim();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isDangerousActionText } from '@reticlehq/core';
|
|
2
|
+
import { getAccessibleName } from '../dom/a11y.js';
|
|
3
|
+
const LABEL_VALUED_INPUT_TYPES = new Set([
|
|
4
|
+
'submit',
|
|
5
|
+
'button',
|
|
6
|
+
'reset',
|
|
7
|
+
'image',
|
|
8
|
+
]);
|
|
9
|
+
function holdsUserText(el) {
|
|
10
|
+
if (el.isContentEditable)
|
|
11
|
+
return true;
|
|
12
|
+
if (el instanceof HTMLTextAreaElement)
|
|
13
|
+
return true;
|
|
14
|
+
if (el instanceof HTMLInputElement)
|
|
15
|
+
return !LABEL_VALUED_INPUT_TYPES.has(el.type.toLowerCase());
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
export function dangerousActionContext(el) {
|
|
19
|
+
const form = el.closest('form');
|
|
20
|
+
const ownText = holdsUserText(el)
|
|
21
|
+
? ['', '']
|
|
22
|
+
: [el.textContent ?? '', el.getAttribute('value') ?? ''];
|
|
23
|
+
return [
|
|
24
|
+
getAccessibleName(el),
|
|
25
|
+
...ownText,
|
|
26
|
+
el.getAttribute('title') ?? '',
|
|
27
|
+
el.getAttribute('aria-label') ?? '',
|
|
28
|
+
el.getAttribute('href') ?? '',
|
|
29
|
+
form?.getAttribute('action') ?? '',
|
|
30
|
+
].join(' ');
|
|
31
|
+
}
|
|
32
|
+
const SUBMIT_CONTROL_SELECTOR = 'button[type="submit"], input[type="submit"], button:not([type]):not([type=""])';
|
|
33
|
+
export function submitControlFor(el) {
|
|
34
|
+
const found = el.closest('form')?.querySelector(SUBMIT_CONTROL_SELECTOR);
|
|
35
|
+
return found instanceof HTMLElement ? found : null;
|
|
36
|
+
}
|
|
37
|
+
export function requiresDangerousConfirmation(text, role) {
|
|
38
|
+
return isDangerousActionText(text, role);
|
|
39
|
+
}
|
package/dist/actions/scroll.d.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
/** Outcome of one container scroll — enough for the server to drive a find loop. */
|
|
2
1
|
interface ScrollResult {
|
|
3
|
-
/** scrollTop actually moved (false ⇒ already at the end / not scrollable). */
|
|
4
2
|
scrolled: boolean;
|
|
5
3
|
scrollTop: number;
|
|
6
4
|
scrollHeight: number;
|
|
7
5
|
clientHeight: number;
|
|
8
|
-
/** At (or within 1px of) the bottom — no more rows to reveal by scrolling down. */
|
|
9
6
|
atEnd: boolean;
|
|
10
7
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Scroll the container of `ref` (or the document) down by `dy` (default ~one viewport) so a
|
|
13
|
-
* virtualized list mounts its next window of rows. If `fraction` (0–1) is supplied, jumps
|
|
14
|
-
* directly to that fraction of scrollHeight instead of stepping — enables bisection for large
|
|
15
|
-
* lists where a specific row index is known (targetIndex / totalCount = fraction).
|
|
16
|
-
*/
|
|
17
8
|
export declare function scrollContainer(ref: string | undefined, dy: number | undefined, fraction?: number): ScrollResult;
|
|
18
9
|
export {};
|
package/dist/actions/scroll.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { refs } from '../dom/refs.js';
|
|
2
2
|
const FALLBACK_STEP_PX = 400;
|
|
3
3
|
const VIEWPORT_FRACTION = 0.8;
|
|
4
|
-
/** The nearest scrollable ancestor of `el` (inclusive), else the document scrolling element. */
|
|
5
4
|
function nearestScrollable(el) {
|
|
6
5
|
let cur = el;
|
|
7
6
|
while (cur !== null && cur !== document.body) {
|
|
@@ -12,12 +11,6 @@ function nearestScrollable(el) {
|
|
|
12
11
|
}
|
|
13
12
|
return document.scrollingElement ?? document.documentElement;
|
|
14
13
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Scroll the container of `ref` (or the document) down by `dy` (default ~one viewport) so a
|
|
17
|
-
* virtualized list mounts its next window of rows. If `fraction` (0–1) is supplied, jumps
|
|
18
|
-
* directly to that fraction of scrollHeight instead of stepping — enables bisection for large
|
|
19
|
-
* lists where a specific row index is known (targetIndex / totalCount = fraction).
|
|
20
|
-
*/
|
|
21
14
|
export function scrollContainer(ref, dy, fraction) {
|
|
22
15
|
const base = ref !== undefined ? refs.resolve(ref) : null;
|
|
23
16
|
const target = base instanceof Element
|
|
@@ -1,9 +1,2 @@
|
|
|
1
|
-
/** True while a Reticle-dispatched input event is being delivered. */
|
|
2
1
|
export declare function isSyntheticInput(): boolean;
|
|
3
|
-
/**
|
|
4
|
-
* Run `dispatch` marked as Reticle's own.
|
|
5
|
-
*
|
|
6
|
-
* `finally` rather than a trailing decrement: a listener that throws must not leave the flag stuck
|
|
7
|
-
* on, which would silently disable annotation for the rest of the page's life.
|
|
8
|
-
*/
|
|
9
2
|
export declare function asSyntheticInput<T>(dispatch: () => T): T;
|
|
@@ -1,34 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Is the input event currently being dispatched one that RETICLE made?
|
|
3
|
-
*
|
|
4
|
-
* The annotator listens for clicks in the capture phase and cancels them, because a click in
|
|
5
|
-
* annotate mode is a request to place a mark rather than a request to press a button. That is right
|
|
6
|
-
* for a person and wrong for the agent: `reticle_act` dispatches its clicks into the same document,
|
|
7
|
-
* so with the HUD expanded every agent click was swallowed while the action still reported
|
|
8
|
-
* `dispatched: true`. Measured on this repo's own fixture — the same click that produced
|
|
9
|
-
* `POST /api/login -> 200` with annotate off produced no network at all with it on.
|
|
10
|
-
*
|
|
11
|
-
* `isTrusted` looks like the answer and is not usable: it is false for everything `dispatchEvent`
|
|
12
|
-
* creates, which is also how every test in this package drives the annotator, so gating on it would
|
|
13
|
-
* make annotation untestable and would still not distinguish Reticle's clicks from the page's own
|
|
14
|
-
* synthetic ones.
|
|
15
|
-
*
|
|
16
|
-
* So Reticle marks its own. Dispatch is synchronous, so a plain depth counter held across the call
|
|
17
|
-
* is exact: any listener running inside it — capture phase included — is running because of us.
|
|
18
|
-
* Nested actions are counted rather than flagged, so an inner action finishing does not clear the
|
|
19
|
-
* mark for the outer one still in flight.
|
|
20
|
-
*/
|
|
21
1
|
let depth = 0;
|
|
22
|
-
/** True while a Reticle-dispatched input event is being delivered. */
|
|
23
2
|
export function isSyntheticInput() {
|
|
24
3
|
return depth > 0;
|
|
25
4
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Run `dispatch` marked as Reticle's own.
|
|
28
|
-
*
|
|
29
|
-
* `finally` rather than a trailing decrement: a listener that throws must not leave the flag stuck
|
|
30
|
-
* on, which would silently disable annotation for the rest of the page's life.
|
|
31
|
-
*/
|
|
32
5
|
export function asSyntheticInput(dispatch) {
|
|
33
6
|
depth += 1;
|
|
34
7
|
try {
|
|
@@ -1,33 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Writing a value into a field, and the two preconditions that make it honest.
|
|
3
|
-
*
|
|
4
|
-
* Lifted out of actions.ts when that file hit the 600-line cap. It is a cohesive unit: every export
|
|
5
|
-
* here exists so that a value write either lands as a real user's edit would, or is refused with a
|
|
6
|
-
* reason — never forced through while reporting success.
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Set a value on a controlled input the way React expects (native setter + input event).
|
|
10
|
-
* Returns the `input` event's dispatchEvent result (defaultPrevented of the primary event).
|
|
11
|
-
*/
|
|
12
1
|
export declare function setNativeValue(el: HTMLInputElement | HTMLTextAreaElement, value: string): boolean;
|
|
13
|
-
/**
|
|
14
|
-
* Refuse to write to a field a person could not edit.
|
|
15
|
-
*
|
|
16
|
-
* `readonly` and `disabled` block USER input, not scripted assignment, so the prototype value setter
|
|
17
|
-
* writes straight through both. Measured: filling a readonly input reported `ok:true` and
|
|
18
|
-
* `valueChanged:true` with nothing in the effect block marking it read-only. That leaves the app in a
|
|
19
|
-
* state no user could reach, and every conclusion drawn from what follows is about software nobody
|
|
20
|
-
* can operate — the same reason the click path reports `occluded`, and the same choice Playwright
|
|
21
|
-
* makes when it refuses to fill a non-editable element.
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* A rich-text target is UNSUPPORTED, not a mistake by the caller.
|
|
25
|
-
*
|
|
26
|
-
* Every rich-text editor (TipTap, Quill, ProseMirror, Slate, Lexical) is `[contenteditable]`, and
|
|
27
|
-
* these actions handle only input/textarea. `cannot fill a <div>` reads as "you picked the wrong
|
|
28
|
-
* element" and sends the reader hunting for a better selector that does not exist. Support is not
|
|
29
|
-
* faked: assigning textContent would update the DOM while the editor's own model kept the old value,
|
|
30
|
-
* so the tool would report success for content the app will never submit.
|
|
31
|
-
*/
|
|
32
2
|
export declare function assertNotRichText(el: HTMLElement, action: string): void;
|
|
33
3
|
export declare function assertEditable(el: HTMLInputElement | HTMLTextAreaElement, action: string): void;
|
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
import { captureValueSetter } from '../patching/capture-method.js';
|
|
2
2
|
import { valuePrototypeOf } from '../dom/realm.js';
|
|
3
|
-
/**
|
|
4
|
-
* Writing a value into a field, and the two preconditions that make it honest.
|
|
5
|
-
*
|
|
6
|
-
* Lifted out of actions.ts when that file hit the 600-line cap. It is a cohesive unit: every export
|
|
7
|
-
* here exists so that a value write either lands as a real user's edit would, or is refused with a
|
|
8
|
-
* reason — never forced through while reporting success.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Set a value on a controlled input the way React expects (native setter + input event).
|
|
12
|
-
* Returns the `input` event's dispatchEvent result (defaultPrevented of the primary event).
|
|
13
|
-
*/
|
|
14
3
|
export function setNativeValue(el, value) {
|
|
15
|
-
// Realm-aware: a controlled input inside a same-origin frame has its own prototype.
|
|
16
4
|
const proto = valuePrototypeOf(el);
|
|
17
5
|
const setter = proto === undefined ? undefined : captureValueSetter(proto);
|
|
18
6
|
if (setter !== undefined) {
|
|
@@ -25,28 +13,7 @@ export function setNativeValue(el, value) {
|
|
|
25
13
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
26
14
|
return !notPrevented;
|
|
27
15
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Refuse to write to a field a person could not edit.
|
|
30
|
-
*
|
|
31
|
-
* `readonly` and `disabled` block USER input, not scripted assignment, so the prototype value setter
|
|
32
|
-
* writes straight through both. Measured: filling a readonly input reported `ok:true` and
|
|
33
|
-
* `valueChanged:true` with nothing in the effect block marking it read-only. That leaves the app in a
|
|
34
|
-
* state no user could reach, and every conclusion drawn from what follows is about software nobody
|
|
35
|
-
* can operate — the same reason the click path reports `occluded`, and the same choice Playwright
|
|
36
|
-
* makes when it refuses to fill a non-editable element.
|
|
37
|
-
*/
|
|
38
|
-
/**
|
|
39
|
-
* A rich-text target is UNSUPPORTED, not a mistake by the caller.
|
|
40
|
-
*
|
|
41
|
-
* Every rich-text editor (TipTap, Quill, ProseMirror, Slate, Lexical) is `[contenteditable]`, and
|
|
42
|
-
* these actions handle only input/textarea. `cannot fill a <div>` reads as "you picked the wrong
|
|
43
|
-
* element" and sends the reader hunting for a better selector that does not exist. Support is not
|
|
44
|
-
* faked: assigning textContent would update the DOM while the editor's own model kept the old value,
|
|
45
|
-
* so the tool would report success for content the app will never submit.
|
|
46
|
-
*/
|
|
47
16
|
export function assertNotRichText(el, action) {
|
|
48
|
-
// The ATTRIBUTE, not `isContentEditable`: jsdom does not implement the property, so a test would
|
|
49
|
-
// pass against a guard that never fires in the environment the unit suite runs in.
|
|
50
17
|
const flag = el.getAttribute('contenteditable');
|
|
51
18
|
if (el.isContentEditable || (flag !== null && flag !== 'false')) {
|
|
52
19
|
throw new Error(`cannot ${action} a contenteditable element — rich-text editors keep their own document model, ` +
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export type CommandHandler = (args: Record<string, unknown>) => unknown;
|
|
2
|
-
/** Query param appended on a hard reload to bypass the browser cache. */
|
|
3
2
|
export declare const RELOAD_CACHE_BUST_PARAM = "_reticle_reload";
|
|
4
3
|
export declare function resolveNavigationUrl(rawUrl: string, baseUrl: string): string | null;
|
|
5
|
-
/** Map browser command names to handlers. Used by the transport on each COMMAND. */
|
|
6
4
|
export declare function createCommandRegistry(): Map<string, CommandHandler>;
|
|
@@ -17,7 +17,6 @@ import { sanitizeWithReport } from '../security/serialization.js';
|
|
|
17
17
|
import { getCapabilities } from '../registry/capabilities.js';
|
|
18
18
|
import { freezeClock, advanceClock, resetClock, isClockFrozen } from '../timers/clock.js';
|
|
19
19
|
import { scrollContainer } from '../actions/scroll.js';
|
|
20
|
-
/** Query param appended on a hard reload to bypass the browser cache. */
|
|
21
20
|
export const RELOAD_CACHE_BUST_PARAM = '_reticle_reload';
|
|
22
21
|
function str(value) {
|
|
23
22
|
return 'string' === typeof value ? value : undefined;
|
|
@@ -49,10 +48,6 @@ function queryFromArgs(args) {
|
|
|
49
48
|
alt: str(args['alt']),
|
|
50
49
|
component: str(args['component']),
|
|
51
50
|
scope: str(args['scope']),
|
|
52
|
-
// The THIRD allowlist a query input has to appear in — tool schema, server forward, and here.
|
|
53
|
-
// `self` was in the first two and missing from this one, so a live call returned zero matches on
|
|
54
|
-
// an element that was plainly on the page, with no error to explain it. Same shape as the
|
|
55
|
-
// `attrs` drop below it.
|
|
56
51
|
self: true === args['self'] ? true : undefined,
|
|
57
52
|
attrs: Array.isArray(args['attrs'])
|
|
58
53
|
? args['attrs'].filter((a) => 'string' === typeof a)
|
|
@@ -62,20 +57,12 @@ function queryFromArgs(args) {
|
|
|
62
57
|
}
|
|
63
58
|
function inspect(ref) {
|
|
64
59
|
const el = refs.resolve(ref);
|
|
65
|
-
// THROW, exactly as executeAction does. Returning an error payload made it a SUCCESSFUL command
|
|
66
|
-
// result, which the server then handed to reticle_inspect's outputSchema (ref/role/name/states/
|
|
67
|
-
// visible all required) — so the MCP layer answered -32602 Output validation error for the most
|
|
68
|
-
// ordinary thing that follows a click. The wording matches actions.ts because the server's
|
|
69
|
-
// recovery table keys off /no longer resolves to an element/ to attach the stale-ref recovery.
|
|
70
60
|
if (null === el)
|
|
71
61
|
throw new Error(editEpoch.staleRefMessage(ref));
|
|
72
62
|
const rect = el.getBoundingClientRect();
|
|
73
63
|
const component = identifyComponent(el);
|
|
74
64
|
const view = el.ownerDocument.defaultView;
|
|
75
65
|
const cs = view !== null ? view.getComputedStyle(el) : null;
|
|
76
|
-
// Computed style the a11y tree is blind to — `cursor` (does it look interactive?), display/
|
|
77
|
-
// visibility, and color/opacity — so a UI bug that leaves the element "present but unusable"
|
|
78
|
-
// (dead cursor, invisible, recolored) is observable in one inspect call.
|
|
79
66
|
const styles = cs !== null
|
|
80
67
|
? {
|
|
81
68
|
color: cs.color,
|
|
@@ -86,16 +73,7 @@ function inspect(ref) {
|
|
|
86
73
|
visibility: cs.visibility,
|
|
87
74
|
}
|
|
88
75
|
: null;
|
|
89
|
-
// `describe()` carries the CHEAP source (the nearest babel-stamped ancestor), because it runs per
|
|
90
|
-
// element on paths that describe hundreds at once. inspect is a single-element path, so it takes
|
|
91
|
-
// the adapter's answer first — the component that actually RENDERED this element, not the nearest
|
|
92
|
-
// stamped host. `act` already did this; inspect did not, so the two tools disagreed about the same
|
|
93
|
-
// ref and inspect — the tool you reach for to ask where something lives — was the one saying null.
|
|
94
76
|
const source = formatSource(sourceFor(el, component?.source));
|
|
95
|
-
// Say WHY the source is missing rather than omitting the field and leaving the caller to guess.
|
|
96
|
-
// No stamp anywhere in the document is decisive: the loader is not running, so no element will
|
|
97
|
-
// have one this session, and the fix is a build-config change rather than anything about this
|
|
98
|
-
// element. Only computed when `source` is already absent, so the ordinary path pays nothing.
|
|
99
77
|
const sourceUnavailable = source !== undefined
|
|
100
78
|
? undefined
|
|
101
79
|
: documentHasSourceStamps(el.ownerDocument)
|
|
@@ -116,21 +94,16 @@ function inspect(ref) {
|
|
|
116
94
|
formAction: isButton(el) || isInput(el) ? (el.form?.getAttribute('action') ?? undefined) : undefined,
|
|
117
95
|
formText: isButton(el) || isInput(el) ? (el.form?.textContent ?? undefined) : undefined,
|
|
118
96
|
box: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
119
|
-
// True when another element sits over this one's center point — the click would hit the overlay,
|
|
120
|
-
// not this control (a z-index/overlay bug the DOM tree cannot show).
|
|
121
97
|
occluded: isOccluded(el, rect),
|
|
122
98
|
styles,
|
|
123
99
|
scroll,
|
|
124
|
-
// Theme compliance vs the app's design tokens (off-theme colors a DOM tool can't judge).
|
|
125
100
|
theme: cs !== null ? themeReport(cs) : null,
|
|
126
101
|
component,
|
|
127
102
|
};
|
|
128
103
|
}
|
|
129
|
-
/** Whether a NON-Reticle element covers this one's center point (a transparent overlay / z-index bug). */
|
|
130
104
|
function isOccluded(el, rect) {
|
|
131
105
|
return hitTestOccluder(el, rect) !== null;
|
|
132
106
|
}
|
|
133
|
-
/** Narrowing guard: an adapter returned a ComponentStateResult (has a boolean `ok`). */
|
|
134
107
|
function isComponentStateResult(value) {
|
|
135
108
|
return ('object' === typeof value && value !== null && 'ok' in value && 'boolean' === typeof value.ok);
|
|
136
109
|
}
|
|
@@ -138,21 +111,8 @@ const COMPONENT_UNAVAILABLE = {
|
|
|
138
111
|
ok: false,
|
|
139
112
|
reason: ComponentStateReason.UNAVAILABLE,
|
|
140
113
|
};
|
|
141
|
-
/**
|
|
142
|
-
* Stores are the reliable path. The `ref` component read is bounded: a stale ref, no
|
|
143
|
-
* adapter, or an adapter returning a non-conforming value all collapse to a structured
|
|
144
|
-
* `{ ok: false, reason }` — never a raw (possibly circular) object that could hang serialization.
|
|
145
|
-
*
|
|
146
|
-
* `path`/`depth` are applied HERE, in the page, BEFORE the value crosses the transport — so a scoped
|
|
147
|
-
* read of a huge store (e.g. `deployments.0.status` on a 500-row store) returns only the small
|
|
148
|
-
* sub-tree and is never truncated. (Previously selection ran server-side, AFTER the whole store had
|
|
149
|
-
* already been size-capped in transit, which silently lost any field after a large array.)
|
|
150
|
-
*/
|
|
151
114
|
function readState(ref, store, path, depth) {
|
|
152
115
|
const names = storeNames();
|
|
153
|
-
// Scoped read: walk `path` into the RAW (uncapped) store, then sanitize only the selected sub-tree.
|
|
154
|
-
// Selecting before the transport cap is what lets a deep/large path (row 250 of a 500-row array)
|
|
155
|
-
// resolve — capping first would truncate the store before selection ever reached the row.
|
|
156
116
|
if (path !== undefined || depth !== undefined) {
|
|
157
117
|
const rawStores = readStoresRaw(store);
|
|
158
118
|
const base = store !== undefined ? rawStores[store] : { stores: rawStores, storeNames: names };
|
|
@@ -164,13 +124,8 @@ function readState(ref, store, path, depth) {
|
|
|
164
124
|
path,
|
|
165
125
|
found: selection.found,
|
|
166
126
|
value: projected.value,
|
|
167
|
-
// Even a SCOPED read can hit the caps when the selected sub-tree is itself large; saying so is
|
|
168
|
-
// what keeps "the list is short" distinguishable from "I shortened the list".
|
|
169
127
|
...(projected.truncation === undefined ? {} : { truncation: projected.truncation }),
|
|
170
128
|
...('availableKeys' in selection ? { availableKeys: selection.availableKeys } : {}),
|
|
171
|
-
// How many keys there REALLY were, when the near-miss list is a sample. Without it, 50 names
|
|
172
|
-
// and no marker reads as "the key you asked for does not exist" — the strongest negative
|
|
173
|
-
// signal there is, and a false one when the key is simply number 51.
|
|
174
129
|
...('totalKeys' in selection ? { totalKeys: selection.totalKeys } : {}),
|
|
175
130
|
storeNames: names,
|
|
176
131
|
};
|
|
@@ -180,8 +135,6 @@ function readState(ref, store, path, depth) {
|
|
|
180
135
|
stores,
|
|
181
136
|
storeNames: names,
|
|
182
137
|
};
|
|
183
|
-
// The whole-store read is where a large store silently became a small one. Present only when a cap
|
|
184
|
-
// actually fired, so an intact read is unchanged and the field's presence is the warning.
|
|
185
138
|
if (truncation !== undefined)
|
|
186
139
|
result.truncation = truncation;
|
|
187
140
|
if (ref !== undefined && ref.length > 0) {
|
|
@@ -196,19 +149,6 @@ function readState(ref, store, path, depth) {
|
|
|
196
149
|
}
|
|
197
150
|
return result;
|
|
198
151
|
}
|
|
199
|
-
/**
|
|
200
|
-
* Every animation, and WHICH element it drives.
|
|
201
|
-
*
|
|
202
|
-
* Reported from the field: a page with nine concurrent animations returned nine indistinguishable
|
|
203
|
-
* `{playState, currentTime, duration}` rows. `a.effect` was read only for `getTiming()`, never for
|
|
204
|
-
* its `target` — so the tool advertised "targets/timing" and shipped timing alone. A list you cannot
|
|
205
|
-
* index is not a list, and the question this tool exists to answer is "is THAT thing still
|
|
206
|
-
* animating".
|
|
207
|
-
*
|
|
208
|
-
* `describe()` is reused rather than inventing a target shape: it is what `reticle_query` returns,
|
|
209
|
-
* so the descriptor carries a `ref` the agent can pass straight to `reticle_act` — a target you
|
|
210
|
-
* cannot act on would be half an answer.
|
|
211
|
-
*/
|
|
212
152
|
function listAnimations() {
|
|
213
153
|
const doc = document;
|
|
214
154
|
if (typeof doc.getAnimations !== 'function')
|
|
@@ -216,9 +156,6 @@ function listAnimations() {
|
|
|
216
156
|
const animations = doc.getAnimations().map((a) => {
|
|
217
157
|
const effect = a.effect;
|
|
218
158
|
const timing = effect?.getTiming();
|
|
219
|
-
// A KeyframeEffect's target is legitimately nullable, and only KeyframeEffect has one at all.
|
|
220
|
-
// `null` is reported explicitly: omitting the key would make "this animation has no element"
|
|
221
|
-
// indistinguishable from the bug above, which is the distinction the fix exists to draw.
|
|
222
159
|
const target = effect?.target ?? null;
|
|
223
160
|
return {
|
|
224
161
|
playState: a.playState,
|
|
@@ -240,14 +177,11 @@ export function resolveNavigationUrl(rawUrl, baseUrl) {
|
|
|
240
177
|
return null;
|
|
241
178
|
}
|
|
242
179
|
}
|
|
243
|
-
/** Map browser command names to handlers. Used by the transport on each COMMAND. */
|
|
244
180
|
export function createCommandRegistry() {
|
|
245
181
|
const reg = new Map();
|
|
246
182
|
reg.set(ReticleCommand.SNAPSHOT, (args) => buildSnapshot({
|
|
247
183
|
scope: str(args['scope']),
|
|
248
184
|
mode: str(args['mode']) ?? SnapshotMode.FULL,
|
|
249
|
-
// Only the completion re-read sets this: it re-reads a branch the truncated walk stopped
|
|
250
|
-
// BEFORE emitting, so that branch's own line has to come back with its descendants.
|
|
251
185
|
includeRoot: true === args['includeRoot'],
|
|
252
186
|
}));
|
|
253
187
|
reg.set(ReticleCommand.QUERY, (args) => {
|
|
@@ -300,7 +234,6 @@ export function createCommandRegistry() {
|
|
|
300
234
|
});
|
|
301
235
|
reg.set(ReticleCommand.REFRESH, (args) => {
|
|
302
236
|
if (true === args['hard']) {
|
|
303
|
-
// Hard reload: navigate to self with a cache-busting param then replace history.
|
|
304
237
|
const url = new URL(window.location.href);
|
|
305
238
|
url.searchParams.set(RELOAD_CACHE_BUST_PARAM, String(Date.now()));
|
|
306
239
|
window.location.replace(url.toString());
|
|
@@ -1,120 +1,26 @@
|
|
|
1
1
|
import type { RedactionConfig } from '@reticlehq/core';
|
|
2
|
-
/**
|
|
3
|
-
* What `reticle.connect()` accepts.
|
|
4
|
-
*
|
|
5
|
-
* Split out of reticle.ts when that file crossed the 600-line cap - the same move `session-info.ts`
|
|
6
|
-
* made for the server's Session. A pure description of the SDK's public entry contract has no reason
|
|
7
|
-
* to live inside the class that happens to consume it, and separating them keeps the surface every
|
|
8
|
-
* integrating app actually reads reviewable on its own.
|
|
9
|
-
*/
|
|
10
2
|
export interface ReticleConnectOptions {
|
|
11
|
-
/** WS endpoint of the local bridge. Defaults to ws://localhost:<port><path>. */
|
|
12
3
|
url?: string;
|
|
13
|
-
/** Human-friendly session label so the agent can target the right tab. */
|
|
14
4
|
session?: string;
|
|
15
|
-
/**
|
|
16
|
-
* Stable project identity, normally stamped by the build plugin (e.g. "acme-web-9f3c1d"). Lets the
|
|
17
|
-
* agent scope to the right app even when its dev server boots on an unexpected port. Optional.
|
|
18
|
-
*/
|
|
19
5
|
projectId?: string;
|
|
20
|
-
/**
|
|
21
|
-
* Browser/bridge pairing token. Required whenever the daemon has one, which is the ordinary case:
|
|
22
|
-
* it auto-provisions `~/.reticle/pairing-token` on first start and then refuses a hello without it,
|
|
23
|
-
* including on localhost. Non-localhost additionally needs `allowNonLocalhost`.
|
|
24
|
-
*/
|
|
25
6
|
token?: string;
|
|
26
|
-
/** Explicitly allow Reticle on a non-localhost page or bridge. Requires token. */
|
|
27
7
|
allowNonLocalhost?: boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Escape hatch for the production backstop. Reticle is dev-only and refuses to connect when the
|
|
30
|
-
* build reports NODE_ENV=production (an SSR healthcheck or a prod bundle opened locally would
|
|
31
|
-
* otherwise activate). The real fix is to gate the import behind `import.meta.env.DEV` so it's
|
|
32
|
-
* tree-shaken out; this flag only exists for the rare intentional prod diagnostic.
|
|
33
|
-
*/
|
|
34
8
|
allowInProduction?: boolean;
|
|
35
|
-
/** Show a small in-page status chip (connection + event count). */
|
|
36
9
|
overlay?: boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Capture request/response bodies on net.request events (dev-only; text-like content only,
|
|
39
|
-
* sensitive keys redacted, per-body capped). Off by default - bodies cost tokens and can carry PII.
|
|
40
|
-
*/
|
|
41
10
|
captureNetworkBodies?: boolean;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*
|
|
45
|
-
* The presenter is hidden from every tool by design, for a good reason: an agent that can drive
|
|
46
|
-
* Reticle's own interface can fabricate its own impact report, and Reticle chrome in a snapshot is
|
|
47
|
-
* noise in every other app on earth.
|
|
48
|
-
*
|
|
49
|
-
* The cost of that rule is that a HUD change is the only kind of change Reticle cannot be used to
|
|
50
|
-
* check — the panel rendering it is invisible to everything that could look at it. This hatch
|
|
51
|
-
* exists for exactly that case: the app under test IS Reticle. It reports itself in the app's
|
|
52
|
-
* capabilities, so a verdict drawn with it open can never be mistaken for an ordinary one.
|
|
53
|
-
*/
|
|
11
|
+
sourceMapping?: boolean;
|
|
12
|
+
captureErrorBodies?: boolean;
|
|
54
13
|
exposePresenter?: boolean;
|
|
55
|
-
/**
|
|
56
|
-
* The project root, so source paths report repo-relative instead of absolute.
|
|
57
|
-
*
|
|
58
|
-
* React's `_debugSource.fileName` is absolute; the babel stamp is repo-relative. Build plugins that
|
|
59
|
-
* can `define` a global set it themselves; frameworks that cannot (Next passes it through env,
|
|
60
|
-
* Astro through its own config) hand it here instead. Taking it as a connect OPTION is what keeps
|
|
61
|
-
* the generated app-side code plain JavaScript - the previous shape made the caller assign a global,
|
|
62
|
-
* and the TypeScript cast that needed shipped into a `.jsx` file and broke the build.
|
|
63
|
-
*/
|
|
64
14
|
root?: string;
|
|
65
|
-
/**
|
|
66
|
-
* The SDK's own package version, so a version-skewed pair against the daemon can name itself
|
|
67
|
-
* rather than surfacing as a bare `-32000`. Build plugins supply it automatically; pass it here
|
|
68
|
-
* only for a hand-wired connect that has no plugin.
|
|
69
|
-
*/
|
|
70
15
|
sdkVersion?: string;
|
|
71
|
-
/** Presenter mode: glow border, animated cursor, click/hover effects, narration HUD. */
|
|
72
16
|
present?: boolean;
|
|
73
|
-
/** Per-action pacing (ms) in presenter mode so a human can follow. Default 450. */
|
|
74
17
|
pace?: number;
|
|
75
|
-
/** Min ms each narration line stays visible before the next replaces it (presenter). Default 3000. */
|
|
76
18
|
narrationDwellMs?: number;
|
|
77
|
-
/**
|
|
78
|
-
* Border behavior in presenter mode: 'session' (default) persists the border for the whole
|
|
79
|
-
* session; 'busy' restores the fade-after-idle behavior.
|
|
80
|
-
*/
|
|
81
19
|
border?: 'session' | 'busy';
|
|
82
|
-
/** Max accumulated activity-log rows before the oldest are pruned (presenter). Default 50. */
|
|
83
20
|
logMax?: number;
|
|
84
|
-
/**
|
|
85
|
-
* Mount the floating human-recorder toolbar (Record/Stop/Annotate).
|
|
86
|
-
* Default off - purely additive, dev-only.
|
|
87
|
-
*/
|
|
88
21
|
recorder?: boolean;
|
|
89
|
-
/**
|
|
90
|
-
* Mount the page annotator: expanding the HUD enters click-to-annotate mode, and Reticle emits
|
|
91
|
-
* a HUMAN_MARK the agent drains via reticle_review. Defaults to ON with the presenter; pass
|
|
92
|
-
* `annotate: false` to suppress.
|
|
93
|
-
*/
|
|
94
22
|
annotate?: boolean;
|
|
95
|
-
/** Live-control: overridable ended-border fade delay (native timer). Default 4000. */
|
|
96
23
|
endedFadeMs?: number;
|
|
97
|
-
/** Session auto-end after this much agent idle (presenter). Default 5min; agent-tunable via reticle_session. */
|
|
98
24
|
idleEndMs?: number;
|
|
99
|
-
/**
|
|
100
|
-
* Extend the redaction rules with your app's own vocabulary.
|
|
101
|
-
*
|
|
102
|
-
* ```ts
|
|
103
|
-
* reticle.connect({
|
|
104
|
-
* redact: {
|
|
105
|
-
* keys: ['deviceSecretRef', /^partner[-_]?code$/i], // also redact these
|
|
106
|
-
* allow: ['designToken'], // stop redacting this false positive
|
|
107
|
-
* },
|
|
108
|
-
* });
|
|
109
|
-
* ```
|
|
110
|
-
*
|
|
111
|
-
* Additive only - there is no way to replace the built-in rule, because a config that could would
|
|
112
|
-
* eventually ship an app that leaks. `keys` strings match a key name exactly (case-insensitively);
|
|
113
|
-
* a RegExp is tested against it. `allow` exempts a key from the DEFAULT rule and loses to `keys`.
|
|
114
|
-
*
|
|
115
|
-
* Literal `keys` strings also cross the bridge, so the daemon redacts them on the driven path,
|
|
116
|
-
* where request bodies are captured raw from the network stack and never pass through this SDK.
|
|
117
|
-
* RegExp entries and `allow` do NOT cross - see docs/usage.md ("Extending the redaction rules") for why, and what that means.
|
|
118
|
-
*/
|
|
119
25
|
redact?: RedactionConfig;
|
|
120
26
|
}
|
package/dist/dom/a11y.d.ts
CHANGED
|
@@ -1,38 +1,9 @@
|
|
|
1
1
|
import { ElementState, type ElementDescriptor } from '@reticlehq/core';
|
|
2
|
-
/** Compute the ARIA role (explicit wins, else implicit from the tag). */
|
|
3
2
|
export declare function getRole(el: Element): string;
|
|
4
3
|
export declare function getAccessibleName(el: Element): string;
|
|
5
|
-
/**
|
|
6
|
-
* The set of states relevant to assertions. `visible` is an O(depth) forced-style walk; callers that
|
|
7
|
-
* already computed it (describe) pass it in so it isn't resolved twice per element.
|
|
8
|
-
*/
|
|
9
4
|
export declare function getStates(el: Element, visible?: boolean): ElementState[];
|
|
10
|
-
/**
|
|
11
|
-
* True when a form field holds a secret the SDK must never capture verbatim — a password input, a
|
|
12
|
-
* sensitive `autocomplete` (cc-number, one-time-code, …), or a name/id/testid/aria-label that trips
|
|
13
|
-
* `isSensitiveKey`. The single source of truth for both live-snapshot redaction (`getValue`) and the
|
|
14
|
-
* flow recorder's fill-value redaction, so recorded flows never persist typed passwords/OTPs/keys.
|
|
15
|
-
*/
|
|
16
5
|
export declare function isSensitiveField(el: Element): boolean;
|
|
17
6
|
export declare function getValue(el: Element): string | undefined;
|
|
18
|
-
/**
|
|
19
|
-
* Whether the element is actually visible (not display:none/hidden/aria-hidden/opacity:0), walking to
|
|
20
|
-
* root. This is an O(depth) forced-style walk PER node; `memo` (optional, scoped to ONE synchronous
|
|
21
|
-
* query pass) caches the full inherited result per element so a broad state-filtered query stops
|
|
22
|
-
* re-resolving getComputedStyle up the same ancestor chain for every sibling. Sound because the DOM is
|
|
23
|
-
* static for the pass's duration — the cache MUST be a per-call Map, never module-level (that would go
|
|
24
|
-
* stale the instant the app mutates, the same trap the shadow-root note in query.ts documents).
|
|
25
|
-
*/
|
|
26
|
-
/**
|
|
27
|
-
* True when the element is inside the viewport right now: visible AND its bounding box intersects
|
|
28
|
-
* the window. This is what makes a scroll assertable (#398) — content below the fold of a scrolling
|
|
29
|
-
* container is `visible`/`present` before any scroll, so only a viewport-intersection check can tell
|
|
30
|
-
* "scrolled into view" from "was always in the DOM". Uses getBoundingClientRect (viewport-relative,
|
|
31
|
-
* already accounts for scroll position), not an IntersectionObserver, so it stays synchronous inside
|
|
32
|
-
* the predicate pass.
|
|
33
|
-
*/
|
|
34
7
|
export declare function isInViewport(el: Element, memo?: Map<Element, boolean>): boolean;
|
|
35
8
|
export declare function isVisible(el: Element, memo?: Map<Element, boolean>): boolean;
|
|
36
|
-
/** Build the compact descriptor surfaced to the agent. `memo` (optional) shares the per-call
|
|
37
|
-
* visibility cache with the query's state filter so ancestors aren't re-walked per element. */
|
|
38
9
|
export declare function describe(el: Element, memo?: Map<Element, boolean>): ElementDescriptor;
|