@jini-ai/agentic 0.1.2 → 0.3.6
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 +92 -0
- package/dist/a2ui/agent-to-renderer.d.ts +36 -2
- package/dist/a2ui/agent-to-renderer.d.ts.map +1 -1
- package/dist/a2ui/agent-to-renderer.js +23 -1
- package/dist/a2ui/agent-to-renderer.js.map +1 -1
- package/dist/a2ui/interpreter.d.ts +24 -1
- package/dist/a2ui/interpreter.d.ts.map +1 -1
- package/dist/a2ui/interpreter.js +115 -65
- package/dist/a2ui/interpreter.js.map +1 -1
- package/dist/a2ui/json-pointer.d.ts.map +1 -1
- package/dist/a2ui/json-pointer.js +28 -3
- package/dist/a2ui/json-pointer.js.map +1 -1
- package/dist/a2ui/renderer-to-agent.d.ts +35 -11
- package/dist/a2ui/renderer-to-agent.d.ts.map +1 -1
- package/dist/a2ui/renderer-to-agent.js +15 -2
- package/dist/a2ui/renderer-to-agent.js.map +1 -1
- package/dist/a2ui/resolve.d.ts +1 -1
- package/dist/a2ui/resolve.d.ts.map +1 -1
- package/dist/a2ui/resolve.js +29 -3
- package/dist/a2ui/resolve.js.map +1 -1
- package/dist/a2ui/tree.d.ts +27 -2
- package/dist/a2ui/tree.d.ts.map +1 -1
- package/dist/a2ui/tree.js +48 -9
- package/dist/a2ui/tree.js.map +1 -1
- package/dist/ag-ui.d.ts.map +1 -1
- package/dist/ag-ui.js +30 -1
- package/dist/ag-ui.js.map +1 -1
- package/dist/core/a2ui/agent-to-renderer.d.ts +613 -0
- package/dist/core/a2ui/agent-to-renderer.d.ts.map +1 -0
- package/dist/core/a2ui/agent-to-renderer.js +191 -0
- package/dist/core/a2ui/agent-to-renderer.js.map +1 -0
- package/dist/core/a2ui/catalog.d.ts +73 -0
- package/dist/core/a2ui/catalog.d.ts.map +1 -0
- package/dist/core/a2ui/catalog.js +304 -0
- package/dist/core/a2ui/catalog.js.map +1 -0
- package/dist/core/a2ui/common-types.d.ts +401 -0
- package/dist/core/a2ui/common-types.d.ts.map +1 -0
- package/dist/core/a2ui/common-types.js +130 -0
- package/dist/core/a2ui/common-types.js.map +1 -0
- package/dist/core/a2ui/index.d.ts +16 -0
- package/dist/core/a2ui/index.d.ts.map +1 -0
- package/dist/core/a2ui/index.js +16 -0
- package/dist/core/a2ui/index.js.map +1 -0
- package/dist/core/a2ui/interpreter.d.ts +71 -0
- package/dist/core/a2ui/interpreter.d.ts.map +1 -0
- package/dist/core/a2ui/interpreter.js +341 -0
- package/dist/core/a2ui/interpreter.js.map +1 -0
- package/dist/core/a2ui/json-pointer.d.ts +48 -0
- package/dist/core/a2ui/json-pointer.d.ts.map +1 -0
- package/dist/core/a2ui/json-pointer.js +174 -0
- package/dist/core/a2ui/json-pointer.js.map +1 -0
- package/dist/core/a2ui/renderer-to-agent.d.ts +342 -0
- package/dist/core/a2ui/renderer-to-agent.d.ts.map +1 -0
- package/dist/core/a2ui/renderer-to-agent.js +111 -0
- package/dist/core/a2ui/renderer-to-agent.js.map +1 -0
- package/dist/core/a2ui/resolve.d.ts +28 -0
- package/dist/core/a2ui/resolve.d.ts.map +1 -0
- package/dist/core/a2ui/resolve.js +144 -0
- package/dist/core/a2ui/resolve.js.map +1 -0
- package/dist/core/a2ui/tree.d.ts +66 -0
- package/dist/core/a2ui/tree.d.ts.map +1 -0
- package/dist/core/a2ui/tree.js +74 -0
- package/dist/core/a2ui/tree.js.map +1 -0
- package/dist/core/ag-ui.d.ts +82 -0
- package/dist/core/ag-ui.d.ts.map +1 -0
- package/dist/core/ag-ui.js +85 -0
- package/dist/core/ag-ui.js.map +1 -0
- package/dist/core/capability.d.ts +81 -0
- package/dist/core/capability.d.ts.map +1 -0
- package/dist/core/capability.js +79 -0
- package/dist/core/capability.js.map +1 -0
- package/dist/core/dom/dom-page-driver.d.ts +36 -0
- package/dist/core/dom/dom-page-driver.d.ts.map +1 -0
- package/dist/core/dom/dom-page-driver.js +601 -0
- package/dist/core/dom/dom-page-driver.js.map +1 -0
- package/dist/core/dom/index.d.ts +17 -0
- package/dist/core/dom/index.d.ts.map +1 -0
- package/dist/core/dom/index.js +17 -0
- package/dist/core/dom/index.js.map +1 -0
- package/dist/core/dom/model-context.d.ts +69 -0
- package/dist/core/dom/model-context.d.ts.map +1 -0
- package/dist/core/dom/model-context.js +44 -0
- package/dist/core/dom/model-context.js.map +1 -0
- package/dist/core/element-handles.d.ts +120 -0
- package/dist/core/element-handles.d.ts.map +1 -0
- package/dist/core/element-handles.js +39 -0
- package/dist/core/element-handles.js.map +1 -0
- package/dist/core/gen-ui/encoder.d.ts +50 -0
- package/dist/core/gen-ui/encoder.d.ts.map +1 -0
- package/dist/core/gen-ui/encoder.js +97 -0
- package/dist/core/gen-ui/encoder.js.map +1 -0
- package/dist/core/gen-ui/events.d.ts +59 -0
- package/dist/core/gen-ui/events.d.ts.map +1 -0
- package/dist/core/gen-ui/events.js +15 -0
- package/dist/core/gen-ui/events.js.map +1 -0
- package/dist/core/gen-ui/index.d.ts +18 -0
- package/dist/core/gen-ui/index.d.ts.map +1 -0
- package/dist/core/gen-ui/index.js +17 -0
- package/dist/core/gen-ui/index.js.map +1 -0
- package/dist/core/guards.d.ts +96 -0
- package/dist/core/guards.d.ts.map +1 -0
- package/dist/core/guards.js +186 -0
- package/dist/core/guards.js.map +1 -0
- package/dist/core/handle.d.ts +89 -0
- package/dist/core/handle.d.ts.map +1 -0
- package/dist/core/handle.js +88 -0
- package/dist/core/handle.js.map +1 -0
- package/dist/core/index.d.ts +75 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +74 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/list-handles.d.ts +43 -0
- package/dist/core/list-handles.d.ts.map +1 -0
- package/dist/core/list-handles.js +57 -0
- package/dist/core/list-handles.js.map +1 -0
- package/dist/core/mcp-ui-apps.d.ts +223 -0
- package/dist/core/mcp-ui-apps.d.ts.map +1 -0
- package/dist/core/mcp-ui-apps.js +252 -0
- package/dist/core/mcp-ui-apps.js.map +1 -0
- package/dist/core/page-capabilities.d.ts +14 -0
- package/dist/core/page-capabilities.d.ts.map +1 -0
- package/dist/core/page-capabilities.js +129 -0
- package/dist/core/page-capabilities.js.map +1 -0
- package/dist/core/page-driver.d.ts +120 -0
- package/dist/core/page-driver.d.ts.map +1 -0
- package/dist/core/page-driver.js +2 -0
- package/dist/core/page-driver.js.map +1 -0
- package/dist/core/page-executor.d.ts +94 -0
- package/dist/core/page-executor.d.ts.map +1 -0
- package/dist/core/page-executor.js +320 -0
- package/dist/core/page-executor.js.map +1 -0
- package/dist/core/webmcp.d.ts +202 -0
- package/dist/core/webmcp.d.ts.map +1 -0
- package/dist/core/webmcp.js +173 -0
- package/dist/core/webmcp.js.map +1 -0
- package/dist/dom/dom-page-driver.d.ts +9 -2
- package/dist/dom/dom-page-driver.d.ts.map +1 -1
- package/dist/dom/dom-page-driver.js +84 -40
- package/dist/dom/dom-page-driver.js.map +1 -1
- package/dist/dom/index.d.ts +1 -1
- package/dist/dom/index.d.ts.map +1 -1
- package/dist/dom/index.js +1 -1
- package/dist/dom/index.js.map +1 -1
- package/dist/element-handles.d.ts +22 -1
- package/dist/element-handles.d.ts.map +1 -1
- package/dist/gen-ui/encoder.d.ts +8 -8
- package/dist/gen-ui/encoder.d.ts.map +1 -1
- package/dist/gen-ui/encoder.js +2 -2
- package/dist/gen-ui/encoder.js.map +1 -1
- package/dist/gen-ui/events.d.ts +10 -10
- package/dist/gen-ui/events.d.ts.map +1 -1
- package/dist/gen-ui/index.d.ts +3 -3
- package/dist/gen-ui/index.d.ts.map +1 -1
- package/dist/gen-ui/index.js +2 -2
- package/dist/gen-ui/index.js.map +1 -1
- package/dist/guards.d.ts +8 -3
- package/dist/guards.d.ts.map +1 -1
- package/dist/guards.js +6 -1
- package/dist/guards.js.map +1 -1
- package/dist/index.d.ts +4 -62
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -61
- package/dist/index.js.map +1 -1
- package/dist/mcp-ui-apps.d.ts +19 -0
- package/dist/mcp-ui-apps.d.ts.map +1 -1
- package/dist/mcp-ui-apps.js +50 -5
- package/dist/mcp-ui-apps.js.map +1 -1
- package/dist/page-capabilities.js +6 -6
- package/dist/page-capabilities.js.map +1 -1
- package/dist/page-driver.d.ts +15 -2
- package/dist/page-driver.d.ts.map +1 -1
- package/dist/page-executor.d.ts +11 -4
- package/dist/page-executor.d.ts.map +1 -1
- package/dist/page-executor.js +47 -15
- package/dist/page-executor.js.map +1 -1
- package/package.json +16 -9
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
import { AGENT_ELEMENT_ATTRIBUTE, AGENT_LABEL_ATTRIBUTE, AGENT_PAGE_ATTRIBUTE, AGENT_ROLE_ATTRIBUTE, AGENT_ELEMENT_ROLES, resolveHandleSelector, } from '../element-handles.js';
|
|
2
|
+
import { normalizeAgentLabel } from '../guards.js';
|
|
3
|
+
/**
|
|
4
|
+
* A {@link PageDriver} over a real DOM subtree.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately mechanical: every refusal — handle validation, the credential-field guard, the
|
|
7
|
+
* page allowlist, highlight clamping — already ran in `executePageCapability` before anything
|
|
8
|
+
* here is called. Re-checking would duplicate policy in a place that will drift from it.
|
|
9
|
+
*
|
|
10
|
+
* Scoped to a `root` the host names, never `document`. Scanning the whole document would make
|
|
11
|
+
* any markup anywhere — including rendered user content — an authorization decision, which
|
|
12
|
+
* inverts the point of an explicit allowlist.
|
|
13
|
+
*/
|
|
14
|
+
/** How the marker is drawn. Inline styles, so no stylesheet or CSP allowance is needed. */
|
|
15
|
+
const HIGHLIGHT_STYLE = {
|
|
16
|
+
outline: '3px solid #e11d48',
|
|
17
|
+
outlineOffset: '2px',
|
|
18
|
+
borderRadius: '4px',
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Upper bound on {@link createDomPageDriver}'s `settle`.
|
|
22
|
+
*
|
|
23
|
+
* A hidden tab stops delivering animation frames, so waiting for one never returns — and a
|
|
24
|
+
* background tab is exactly where an agent-driven page ends up while the user works elsewhere.
|
|
25
|
+
* This is the ceiling that keeps every write bounded regardless.
|
|
26
|
+
*/
|
|
27
|
+
const SETTLE_TIMEOUT_MS = 120;
|
|
28
|
+
function isAgentElementRole(value) {
|
|
29
|
+
return value !== null && AGENT_ELEMENT_ROLES.includes(value);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Whether the element is a rich-text surface — a `contenteditable` region rather than a form
|
|
33
|
+
* control. Rich-text editors are built this way, so treating one as unfillable makes every
|
|
34
|
+
* comment box, description field and document editor unreachable.
|
|
35
|
+
*/
|
|
36
|
+
function isEditableRegion(element) {
|
|
37
|
+
if (!(element instanceof HTMLElement))
|
|
38
|
+
return false;
|
|
39
|
+
// A control embedded in a rich-text document is still that control. Without this, a real
|
|
40
|
+
// browser — which, unlike jsdom, propagates `isContentEditable` to every descendant — reports
|
|
41
|
+
// the Save button inside an editor as a fillable text region, and `fill` overwrites its label
|
|
42
|
+
// instead of refusing. The type-based refusals in `findFieldFillRefusal` cannot catch that
|
|
43
|
+
// either, because the descriptor would say `contenteditable` rather than `button`.
|
|
44
|
+
if (element.matches('input, textarea, select, button, a[href], summary, option'))
|
|
45
|
+
return false;
|
|
46
|
+
// The attribute is checked before `isContentEditable` because a headless DOM does not compute
|
|
47
|
+
// the latter — relying on it alone makes every one of these tests pass vacuously while the real
|
|
48
|
+
// browser behaves differently. `contenteditable=""` and `"plaintext-only"` are both editable;
|
|
49
|
+
// only an explicit `"false"` is not.
|
|
50
|
+
const attribute = element.getAttribute('contenteditable');
|
|
51
|
+
if (attribute !== null)
|
|
52
|
+
return attribute !== 'false';
|
|
53
|
+
// Editability inherited from an ancestor, which only a real layout engine can tell us about.
|
|
54
|
+
return element.isContentEditable === true;
|
|
55
|
+
}
|
|
56
|
+
/** The text of every `<label>` associated with `control`, by any of the relationships that link one. */
|
|
57
|
+
function labelTextsOf(control) {
|
|
58
|
+
const texts = [];
|
|
59
|
+
const add = (text) => {
|
|
60
|
+
const trimmed = text?.trim();
|
|
61
|
+
if (trimmed)
|
|
62
|
+
texts.push(trimmed);
|
|
63
|
+
};
|
|
64
|
+
// Real form controls carry `.labels` — every associated <label>, both for-linked and wrapping.
|
|
65
|
+
const associated = 'labels' in control
|
|
66
|
+
? Array.from(control.labels ?? [])
|
|
67
|
+
: [];
|
|
68
|
+
for (const label of associated)
|
|
69
|
+
add(label.textContent);
|
|
70
|
+
// A contenteditable region is not a form control and has no `.labels`; resolve the same two
|
|
71
|
+
// relationships by hand — an ancestor <label> (implicit wrapping) and a for-linked one (explicit).
|
|
72
|
+
const wrapping = control.closest('label');
|
|
73
|
+
if (wrapping)
|
|
74
|
+
add(wrapping.textContent);
|
|
75
|
+
if (control.id) {
|
|
76
|
+
for (const label of Array.from(control.ownerDocument.querySelectorAll('label'))) {
|
|
77
|
+
if (label.htmlFor === control.id)
|
|
78
|
+
add(label.textContent);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return texts;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* **Every** human-visible naming of the field, for the guard's benefit: `name`/`id` are the machine
|
|
85
|
+
* names, and a CMS emitting `name="field_47"` next to a visibly-labelled "Card number" leaves every
|
|
86
|
+
* guard here nothing to judge unless this is populated. Sources, in the order a human would read
|
|
87
|
+
* them: `aria-label`, `placeholder`, then the text of each associated `<label>` (for-linked or
|
|
88
|
+
* wrapping).
|
|
89
|
+
*
|
|
90
|
+
* All of them, not the first that resolves. Stopping at the first made the *page* the one choosing
|
|
91
|
+
* which of its own labels the guard is allowed to see, which is the opposite of what a guard is
|
|
92
|
+
* for: `<label for=f>Card number</label><input id=f name=field_47 placeholder="Enter value">` was
|
|
93
|
+
* described to the guard as "Enter value" alone, and its value read back in full.
|
|
94
|
+
*
|
|
95
|
+
* De-duplicated because one label is often reachable twice (a wrapping `<label>` is also in
|
|
96
|
+
* `.labels`), and a repeated entry is noise in every refusal built from these.
|
|
97
|
+
*/
|
|
98
|
+
function accessibleLabelsOf(control) {
|
|
99
|
+
const candidates = [
|
|
100
|
+
control.getAttribute('aria-label')?.trim(),
|
|
101
|
+
control.getAttribute('placeholder')?.trim(),
|
|
102
|
+
...labelTextsOf(control),
|
|
103
|
+
];
|
|
104
|
+
return [...new Set(candidates.filter((text) => !!text))];
|
|
105
|
+
}
|
|
106
|
+
/** The field attributes the guards need, or `null` when the control is not a text-bearing input. */
|
|
107
|
+
function fieldDescriptorOf(control) {
|
|
108
|
+
if (isEditableRegion(control)) {
|
|
109
|
+
// Reported as a field so the same guards run: a contenteditable div named `card-number` is
|
|
110
|
+
// no more fillable than an input would be. `type` is not a real HTML input type, and is not
|
|
111
|
+
// in any denied set, which is correct — the guard should judge it by name and attributes.
|
|
112
|
+
return {
|
|
113
|
+
type: 'contenteditable',
|
|
114
|
+
// Read for the same reason `name` is: neither attribute is standard on a div, but a page
|
|
115
|
+
// that bothers to set one is telling the guard what the field holds, and honouring `name`
|
|
116
|
+
// while ignoring `autocomplete` would refuse `name="card_number"` and accept the
|
|
117
|
+
// `autocomplete="cc-number"` spelling of the same field.
|
|
118
|
+
autocomplete: control.getAttribute('autocomplete')?.toLowerCase() || undefined,
|
|
119
|
+
name: control.getAttribute('name') || undefined,
|
|
120
|
+
id: control.id || undefined,
|
|
121
|
+
accessibleLabels: accessibleLabelsOf(control),
|
|
122
|
+
readOnly: control.getAttribute('aria-readonly') === 'true',
|
|
123
|
+
disabled: control.getAttribute('aria-disabled') === 'true',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (!(control instanceof HTMLInputElement) && !(control instanceof HTMLTextAreaElement)) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
type: control instanceof HTMLInputElement ? control.type.toLowerCase() : 'textarea',
|
|
131
|
+
autocomplete: control.getAttribute('autocomplete')?.toLowerCase() ?? undefined,
|
|
132
|
+
name: control.name || undefined,
|
|
133
|
+
id: control.id || undefined,
|
|
134
|
+
accessibleLabels: accessibleLabelsOf(control),
|
|
135
|
+
readOnly: control.readOnly,
|
|
136
|
+
// `.disabled` reflects only this element's own attribute — not the "actually disabled" state
|
|
137
|
+
// a control gets from an ancestor `<fieldset disabled>`. `:disabled` is the platform's own
|
|
138
|
+
// answer for both, and is what decides whether the value even reaches form submission.
|
|
139
|
+
disabled: control.matches(':disabled'),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Whether the element is actually rendered, or `undefined` when the platform cannot say.
|
|
144
|
+
*
|
|
145
|
+
* `checkVisibility` is the only answer that accounts for `display:none` on an ancestor, `hidden`,
|
|
146
|
+
* `content-visibility` and an empty box at once. Where it is missing, this reports nothing rather
|
|
147
|
+
* than falling back to a layout measurement — a headless DOM has no layout, so that fallback would
|
|
148
|
+
* confidently declare every element invisible.
|
|
149
|
+
*
|
|
150
|
+
* The options are passed explicitly rather than relying on the method's defaults: per spec every
|
|
151
|
+
* option defaults to `false`, so a bare `checkVisibility()` misses `visibility:hidden` and
|
|
152
|
+
* `opacity:0` ancestors — two of the most common real ways to hide interactive UI (fade
|
|
153
|
+
* transitions, visually-hidden-but-focusable patterns) — while still correctly reporting
|
|
154
|
+
* `display:none`. Confirmed in real Chromium: `checkOpacity`/`checkVisibilityCSS` (this build's
|
|
155
|
+
* spelling) turn a false `visible: true` on a `visibility:hidden`/`opacity:0` ancestor into the
|
|
156
|
+
* correct `false`; jsdom does not implement the method at all, so no unit test can show this.
|
|
157
|
+
*/
|
|
158
|
+
function visibilityOf(element) {
|
|
159
|
+
const check = element.checkVisibility;
|
|
160
|
+
return typeof check === 'function'
|
|
161
|
+
? check.call(element, { checkOpacity: true, checkVisibilityCSS: true })
|
|
162
|
+
: undefined;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* `disabled` for the controls that have one; `undefined` for everything else, which is not the
|
|
166
|
+
* same as `false`.
|
|
167
|
+
*
|
|
168
|
+
* `:disabled` rather than the `.disabled` IDL property: the property only reflects the element's
|
|
169
|
+
* own attribute, so a control disabled solely by an ancestor `<fieldset disabled>` reads as
|
|
170
|
+
* enabled even though a real user cannot type into or click it, and its value is excluded from
|
|
171
|
+
* form submission entirely. `:disabled` is the platform's own answer and covers both cases.
|
|
172
|
+
*/
|
|
173
|
+
function disabledOf(control) {
|
|
174
|
+
return control instanceof HTMLInputElement
|
|
175
|
+
|| control instanceof HTMLTextAreaElement
|
|
176
|
+
|| control instanceof HTMLButtonElement
|
|
177
|
+
|| control instanceof HTMLSelectElement
|
|
178
|
+
? control.matches(':disabled')
|
|
179
|
+
: undefined;
|
|
180
|
+
}
|
|
181
|
+
function describe(element, page) {
|
|
182
|
+
const role = element.getAttribute(AGENT_ROLE_ATTRIBUTE);
|
|
183
|
+
/* c8 ignore next -- `Node.textContent` is typed nullable for the abstract node; on an Element it is always a string, so the `?? ''` satisfies the type and is not a reachable path. */
|
|
184
|
+
const text = element.textContent ?? '';
|
|
185
|
+
// Live text is a fine fallback label for an ordinary element — it is the page's own ontology,
|
|
186
|
+
// rendered openly. For an editable region it is not: a contenteditable's content is the *user's
|
|
187
|
+
// data*, so falling back to it published the contents of a rich-text field to every
|
|
188
|
+
// `find_elements` caller, with no `withState` asked for and no read guard anywhere in the way
|
|
189
|
+
// (`<div contenteditable name="password">hunter2</div>` reported `label: "hunter2"`). Such a
|
|
190
|
+
// region is named by what labels it instead, exactly as `fieldDescriptorOf` describes it.
|
|
191
|
+
const fallback = isEditableRegion(element) ? (accessibleLabelsOf(element)[0] ?? '') : text;
|
|
192
|
+
const label = element.getAttribute(AGENT_LABEL_ATTRIBUTE) ?? fallback;
|
|
193
|
+
/* c8 ignore next -- only ever called on a `[data-agent-element]` match, so the attribute is present by construction. */
|
|
194
|
+
const handle = element.getAttribute(AGENT_ELEMENT_ATTRIBUTE) ?? '';
|
|
195
|
+
return {
|
|
196
|
+
handle,
|
|
197
|
+
role: isAgentElementRole(role) ? role : undefined,
|
|
198
|
+
// The executor normalizes and bounds this; raw here on purpose so the guard is applied in
|
|
199
|
+
// exactly one place rather than half-applied in two.
|
|
200
|
+
label,
|
|
201
|
+
labelTruncated: false,
|
|
202
|
+
page,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Builds a driver over `root`.
|
|
207
|
+
*
|
|
208
|
+
* @param options - The subtree, the navigable pages, and the current page id.
|
|
209
|
+
* @returns A driver ready to hand to `executePageCapability`.
|
|
210
|
+
*/
|
|
211
|
+
export function createDomPageDriver(options) {
|
|
212
|
+
const { root, pages } = options;
|
|
213
|
+
/**
|
|
214
|
+
* The page a specific element belongs to — its own nearest `[data-agent-page]` ancestor, not a
|
|
215
|
+
* single document-wide guess.
|
|
216
|
+
*
|
|
217
|
+
* A host that keeps more than one `[data-agent-page]` section mounted at once (tabs, wizard
|
|
218
|
+
* steps toggled with CSS rather than unmounted — the kind of SPA this driver's own options doc
|
|
219
|
+
* anticipates) previously had every element attributed to whichever section happened to be
|
|
220
|
+
* first in the DOM, regardless of which one it actually lived in. `closest` answers per element
|
|
221
|
+
* instead of once for the whole call.
|
|
222
|
+
*
|
|
223
|
+
* Still overridden by a pinned `currentPage`, unchanged: that value is the host's own claim
|
|
224
|
+
* about what is showing, taking precedence over whatever the DOM happens to say.
|
|
225
|
+
*/
|
|
226
|
+
const pageOf = (element) => options.currentPage ?? element.closest(`[${AGENT_PAGE_ATTRIBUTE}]`)?.getAttribute(AGENT_PAGE_ATTRIBUTE) ?? undefined;
|
|
227
|
+
/**
|
|
228
|
+
* Active markers keyed by handle, so re-highlighting the same element restarts rather than
|
|
229
|
+
* stacks — and carrying the styling to put back, which must be captured once, on the first
|
|
230
|
+
* highlight. Re-reading the element on a restart would snapshot the marker itself, and the
|
|
231
|
+
* element would keep it forever: a capability classified `read` precisely because it is
|
|
232
|
+
* transient would permanently change how someone's page looks.
|
|
233
|
+
*/
|
|
234
|
+
const highlights = new Map();
|
|
235
|
+
/**
|
|
236
|
+
* Every element published under `handle`. A page that (accidentally, or via a templating bug)
|
|
237
|
+
* publishes the same handle twice makes `find`'s single-match resolution ambiguous — surfaced
|
|
238
|
+
* here so both callers below can refuse rather than silently pick one.
|
|
239
|
+
*/
|
|
240
|
+
const findAll = (handle) => Array.from(root.querySelectorAll(resolveHandleSelector(handle)));
|
|
241
|
+
/**
|
|
242
|
+
* More than one element answers to the same handle — a page-authoring bug (duplicate
|
|
243
|
+
* `data-agent-element`), not something a caller passing that handle can resolve on its own.
|
|
244
|
+
*
|
|
245
|
+
* `findElements` still lists every one of them, so the collision is discoverable; resolving a
|
|
246
|
+
* single handle to act on refuses instead of silently taking the first DOM match and leaving the
|
|
247
|
+
* others permanently unreachable with no signal that they were ever unreachable at all.
|
|
248
|
+
*/
|
|
249
|
+
const ambiguousHandleError = (handle, count) => new Error(`"${handle}" is published on ${count} elements on this page, so which one it addresses is `
|
|
250
|
+
+ 'ambiguous — this is a page-authoring bug (duplicate data-agent-element), not something a '
|
|
251
|
+
+ 'caller can resolve; publish a unique handle per element');
|
|
252
|
+
const find = (handle) => {
|
|
253
|
+
const matches = findAll(handle);
|
|
254
|
+
if (matches.length === 0)
|
|
255
|
+
throw new Error(`no element published as "${handle}" on this page`);
|
|
256
|
+
if (matches.length > 1)
|
|
257
|
+
throw ambiguousHandleError(handle, matches.length);
|
|
258
|
+
return matches[0];
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* The control a handle addresses — for a wrapper like `<li><label><input>`, the input.
|
|
262
|
+
*
|
|
263
|
+
* `<details>` is called out because its interactive part is the `<summary>`: clicking the
|
|
264
|
+
* `<details>` element itself dispatches an event the platform ignores, so a disclosure widget
|
|
265
|
+
* would never open and the caller would be told the click landed.
|
|
266
|
+
*/
|
|
267
|
+
const controlOf = (element) => {
|
|
268
|
+
// An editable region IS the control. Descending would find a link inside a rich-text document
|
|
269
|
+
// and address that instead of the text the caller meant to write.
|
|
270
|
+
if (isEditableRegion(element))
|
|
271
|
+
return element;
|
|
272
|
+
// `:scope >` because only a *direct child* `<summary>` is the disclosure control. An
|
|
273
|
+
// unscoped query returns the first summary anywhere in the subtree, so a `<details>`
|
|
274
|
+
// containing another one would toggle the inner disclosure while reporting that the outer
|
|
275
|
+
// handle was clicked — the false success this whole suite exists to catch.
|
|
276
|
+
if (element instanceof HTMLDetailsElement) {
|
|
277
|
+
return element.querySelector(':scope > summary') ?? element;
|
|
278
|
+
}
|
|
279
|
+
if (element.matches('input, textarea, select, button, a'))
|
|
280
|
+
return element;
|
|
281
|
+
// A descendant that is itself independently published (its own `[data-agent-element]`) must
|
|
282
|
+
// never be silently adopted as this handle's control — it is a distinct, separately
|
|
283
|
+
// addressable target with its own label, not unhandled wrapper markup like the plain
|
|
284
|
+
// `<li><label><input>` this descent exists for. Filtering to descendants whose nearest
|
|
285
|
+
// published ancestor is still `element` itself excludes exactly that case while leaving
|
|
286
|
+
// ordinary wrapper descent untouched.
|
|
287
|
+
const owned = (node) => node.closest(`[${AGENT_ELEMENT_ATTRIBUTE}]`) === element;
|
|
288
|
+
const candidate = Array.from(element.querySelectorAll('input, textarea, select, button, a, summary'))
|
|
289
|
+
.find(owned);
|
|
290
|
+
return candidate ?? element;
|
|
291
|
+
};
|
|
292
|
+
return {
|
|
293
|
+
async findElements(filter) {
|
|
294
|
+
const found = Array.from(root.querySelectorAll(`[${AGENT_ELEMENT_ATTRIBUTE}]`))
|
|
295
|
+
.map((element) => describe(element, pageOf(element)))
|
|
296
|
+
.filter((element) => element.handle.length > 0);
|
|
297
|
+
const query = filter.query?.toLowerCase();
|
|
298
|
+
return found.filter((element) => {
|
|
299
|
+
if (filter.role !== undefined && element.role !== filter.role)
|
|
300
|
+
return false;
|
|
301
|
+
if (query === undefined)
|
|
302
|
+
return true;
|
|
303
|
+
return element.handle.toLowerCase().includes(query)
|
|
304
|
+
|| element.label.toLowerCase().includes(query);
|
|
305
|
+
});
|
|
306
|
+
},
|
|
307
|
+
async listPages() {
|
|
308
|
+
// `Object.entries`, same own-enumerable-keys-only guarantee `Object.keys` gives `navigate`'s
|
|
309
|
+
// own `hasOwnProperty` check above — no risk of a `pages.label`/`pages.navigate`-shaped
|
|
310
|
+
// prototype entry leaking in as a fake page id.
|
|
311
|
+
return Object.entries(pages).map(([id, page]) => ({ id, label: page.label }));
|
|
312
|
+
},
|
|
313
|
+
async describeField(handle) {
|
|
314
|
+
return fieldDescriptorOf(controlOf(find(handle)));
|
|
315
|
+
},
|
|
316
|
+
async describeState(handle) {
|
|
317
|
+
// Unlike every other method here, a handle that no longer resolves is an answer rather than
|
|
318
|
+
// an error: an element that a click removed is exactly what the caller is asking about. A
|
|
319
|
+
// handle resolving to *more than one* element is a different situation entirely — not an
|
|
320
|
+
// absence but an ambiguity — so that case still refuses, same as `find`.
|
|
321
|
+
const matches = findAll(handle);
|
|
322
|
+
if (matches.length === 0)
|
|
323
|
+
return null;
|
|
324
|
+
if (matches.length > 1)
|
|
325
|
+
throw ambiguousHandleError(handle, matches.length);
|
|
326
|
+
const element = matches[0];
|
|
327
|
+
const control = controlOf(element);
|
|
328
|
+
const field = fieldDescriptorOf(control);
|
|
329
|
+
const checkable = control instanceof HTMLInputElement
|
|
330
|
+
&& (control.type === 'checkbox' || control.type === 'radio');
|
|
331
|
+
// A dropdown reports its option texts alongside its value. Without them a caller can only
|
|
332
|
+
// guess what to pass to `page.select_option`, and a wrong guess is a refusal it could have
|
|
333
|
+
// avoided by looking. It carries a descriptor too, so the read guard still runs — a
|
|
334
|
+
// `<select name="secret_token">` is no more readable than an input of the same name.
|
|
335
|
+
const dropdown = control instanceof HTMLSelectElement ? control : undefined;
|
|
336
|
+
const readable = field ?? (dropdown === undefined ? null : {
|
|
337
|
+
type: 'select',
|
|
338
|
+
name: dropdown.name || undefined,
|
|
339
|
+
id: dropdown.id || undefined,
|
|
340
|
+
// Same reason `fieldDescriptorOf` carries these: a `<select aria-label="Card number"
|
|
341
|
+
// name="f9">` states its sensitivity nowhere else, and this descriptor is built by hand
|
|
342
|
+
// rather than going through that function, so it needs the labels wired up separately.
|
|
343
|
+
accessibleLabels: accessibleLabelsOf(dropdown),
|
|
344
|
+
// Same `:disabled` reasoning as `disabledOf` — a `<select>` inside a disabled `<fieldset>`
|
|
345
|
+
// is not itself flagged by the `.disabled` IDL property.
|
|
346
|
+
disabled: dropdown.matches(':disabled'),
|
|
347
|
+
});
|
|
348
|
+
const value = field !== null
|
|
349
|
+
? control.value
|
|
350
|
+
: dropdown?.value;
|
|
351
|
+
/* c8 ignore next -- as in `describe`: an Element's textContent is always a string. */
|
|
352
|
+
const text = element.textContent ?? '';
|
|
353
|
+
return {
|
|
354
|
+
text,
|
|
355
|
+
// A contenteditable region has no `.value`; its content IS its value, so `text` is the
|
|
356
|
+
// one channel carrying it and the executor has to gate it exactly like a value. Reported
|
|
357
|
+
// as a mechanical fact about the element, not as a judgment about the content — the
|
|
358
|
+
// decision of whether it may be shown stays in `projectElementState`, with every other
|
|
359
|
+
// secrecy rule.
|
|
360
|
+
...(isEditableRegion(control) ? { textIsValue: true } : {}),
|
|
361
|
+
// Reported raw and unconditionally; `projectElementState` applies the read guard. A
|
|
362
|
+
// driver deciding here which values are secret is a driver holding policy.
|
|
363
|
+
...(value !== undefined ? { value } : {}),
|
|
364
|
+
...(checkable ? { checked: control.checked } : {}),
|
|
365
|
+
...(disabledOf(control) !== undefined ? { disabled: disabledOf(control) } : {}),
|
|
366
|
+
...(visibilityOf(element) !== undefined ? { visible: visibilityOf(element) } : {}),
|
|
367
|
+
...(readable !== null ? { field: readable } : {}),
|
|
368
|
+
...(dropdown !== undefined
|
|
369
|
+
? {
|
|
370
|
+
options: Array.from(dropdown.options)
|
|
371
|
+
.map((entry) => entry.text.trim())
|
|
372
|
+
.filter((entry) => entry.length > 0),
|
|
373
|
+
}
|
|
374
|
+
: {}),
|
|
375
|
+
};
|
|
376
|
+
},
|
|
377
|
+
/**
|
|
378
|
+
* Two animation frames — long enough for a framework to have re-rendered and the browser to
|
|
379
|
+
* have painted — or {@link SETTLE_TIMEOUT_MS}, whichever lands first. Without the timeout this
|
|
380
|
+
* would hang for the whole life of a backgrounded tab; without the frames every write would
|
|
381
|
+
* report its own target unchanged, because the read would beat the render.
|
|
382
|
+
*/
|
|
383
|
+
async settle() {
|
|
384
|
+
await new Promise((resolve) => {
|
|
385
|
+
let timer;
|
|
386
|
+
// No re-entry guard: whichever side loses the race calls this again, and both of its
|
|
387
|
+
// effects are already idempotent — clearing an elapsed timer is a no-op, and a second
|
|
388
|
+
// `resolve` on a settled promise is ignored. A flag here would only be a branch nothing
|
|
389
|
+
// can distinguish.
|
|
390
|
+
const finish = () => {
|
|
391
|
+
if (timer !== undefined)
|
|
392
|
+
clearTimeout(timer);
|
|
393
|
+
resolve();
|
|
394
|
+
};
|
|
395
|
+
const raf = globalThis.requestAnimationFrame;
|
|
396
|
+
// With no animation frames to wait for, one macrotask is the whole mechanism, and waiting
|
|
397
|
+
// the full ceiling on every write would be pure latency.
|
|
398
|
+
timer = setTimeout(finish, typeof raf === 'function' ? SETTLE_TIMEOUT_MS : 0);
|
|
399
|
+
if (typeof raf === 'function')
|
|
400
|
+
raf(() => raf(finish));
|
|
401
|
+
});
|
|
402
|
+
},
|
|
403
|
+
async highlight(handle, durationMs) {
|
|
404
|
+
const element = find(handle);
|
|
405
|
+
// Same refusal as `click()`, for the same reason: an SVG (or other non-HTMLElement) target
|
|
406
|
+
// has no `.style` to draw an outline on, so silently returning here drew nothing while
|
|
407
|
+
// `page-executor.ts` unconditionally reports the highlight as successful — a guaranteed
|
|
408
|
+
// false positive with no way for the caller to tell. Refusing makes that visible instead.
|
|
409
|
+
if (!(element instanceof HTMLElement))
|
|
410
|
+
throw new Error(`"${handle}" is not highlightable`);
|
|
411
|
+
const active = highlights.get(handle);
|
|
412
|
+
if (active !== undefined)
|
|
413
|
+
clearTimeout(active.timer);
|
|
414
|
+
// Reuse the first capture on a restart; see the map's own doc for what re-reading would cost.
|
|
415
|
+
const restore = active?.restore ?? {
|
|
416
|
+
outline: element.style.outline,
|
|
417
|
+
outlineOffset: element.style.outlineOffset,
|
|
418
|
+
borderRadius: element.style.borderRadius,
|
|
419
|
+
};
|
|
420
|
+
Object.assign(element.style, HIGHLIGHT_STYLE);
|
|
421
|
+
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
422
|
+
const timer = setTimeout(() => {
|
|
423
|
+
// Restore what was there rather than clearing: the element may have had its own outline.
|
|
424
|
+
Object.assign(element.style, restore);
|
|
425
|
+
highlights.delete(handle);
|
|
426
|
+
}, durationMs);
|
|
427
|
+
highlights.set(handle, { timer, restore });
|
|
428
|
+
},
|
|
429
|
+
async scrollTo(handle) {
|
|
430
|
+
find(handle).scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
431
|
+
},
|
|
432
|
+
async click(handle) {
|
|
433
|
+
const control = controlOf(find(handle));
|
|
434
|
+
if (!(control instanceof HTMLElement))
|
|
435
|
+
throw new Error(`"${handle}" is not clickable`);
|
|
436
|
+
control.click();
|
|
437
|
+
},
|
|
438
|
+
async fill(handle, text) {
|
|
439
|
+
const control = controlOf(find(handle));
|
|
440
|
+
if (isEditableRegion(control)) {
|
|
441
|
+
// A contenteditable region has no `value`; its content *is* its children, and a real
|
|
442
|
+
// editor owns that subtree. See `fillEditableRegion` for why this drives it through
|
|
443
|
+
// selection + `execCommand` rather than replacing `textContent` directly.
|
|
444
|
+
fillEditableRegion(control, text);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (!(control instanceof HTMLInputElement) && !(control instanceof HTMLTextAreaElement)) {
|
|
448
|
+
throw new Error(`"${handle}" is not a fillable field`);
|
|
449
|
+
}
|
|
450
|
+
// Assign through the prototype setter, then dispatch: React tracks the previous value on
|
|
451
|
+
// the node and ignores an input event whose value it believes it already has, so a plain
|
|
452
|
+
// `control.value = text` updates the DOM but leaves React state stale.
|
|
453
|
+
const prototype = control instanceof HTMLInputElement
|
|
454
|
+
? HTMLInputElement.prototype
|
|
455
|
+
: HTMLTextAreaElement.prototype;
|
|
456
|
+
const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;
|
|
457
|
+
if (setter)
|
|
458
|
+
setter.call(control, text);
|
|
459
|
+
else
|
|
460
|
+
control.value = text;
|
|
461
|
+
control.dispatchEvent(new Event('input', { bubbles: true }));
|
|
462
|
+
control.dispatchEvent(new Event('change', { bubbles: true }));
|
|
463
|
+
},
|
|
464
|
+
async selectOption(handle, option, selected = true) {
|
|
465
|
+
const control = controlOf(find(handle));
|
|
466
|
+
if (!(control instanceof HTMLSelectElement)) {
|
|
467
|
+
throw new Error(`"${handle}" is not a dropdown`);
|
|
468
|
+
}
|
|
469
|
+
// A disabled control is not one a user could touch, so writing to it is a way around the
|
|
470
|
+
// page's own rule and the resulting selection cannot be submitted. The per-`<option>` filter
|
|
471
|
+
// below reads as if it enforced that, but only ever looked at each option — a
|
|
472
|
+
// `<select disabled>` (and a `<select>` inside a `<fieldset disabled>`, which `:disabled`
|
|
473
|
+
// also covers and `.disabled` does not) was written to happily, and the executor then
|
|
474
|
+
// reported `targetChanged: true` because the value really had changed.
|
|
475
|
+
//
|
|
476
|
+
// Checked before the option lookup on purpose: the refusal is about the control, so it must
|
|
477
|
+
// not depend on the caller having named a real option, and the "Available: ..." message must
|
|
478
|
+
// not enumerate a disabled control's options.
|
|
479
|
+
if (control.matches(':disabled')) {
|
|
480
|
+
throw new Error(`"${handle}" is disabled, so its selection cannot be changed — `
|
|
481
|
+
+ 'a user cannot change it either, and its value would not be submitted');
|
|
482
|
+
}
|
|
483
|
+
// Visible text first, then value: the text is what a caller saw in `find_elements`, and
|
|
484
|
+
// matching value first would make an option whose value collides with another's label
|
|
485
|
+
// resolve to the wrong entry.
|
|
486
|
+
// A disabled option is not selectable by the user either, for the same reason.
|
|
487
|
+
const options = Array.from(control.options).filter((entry) => !entry.disabled);
|
|
488
|
+
const match = options.find((entry) => entry.text.trim() === option)
|
|
489
|
+
?? options.find((entry) => entry.value === option);
|
|
490
|
+
if (match === undefined) {
|
|
491
|
+
// Every other piece of page-authored text this system hands to a model goes through
|
|
492
|
+
// normalizeAgentLabel first — bounded, control- and bidi-stripped. `available` is exactly
|
|
493
|
+
// that: the page's own <option> texts, read verbatim with no length bound of their own.
|
|
494
|
+
// The caller's `option` is sanitized too, for the same reason page.navigate's caller-
|
|
495
|
+
// supplied `page` now is — an unbounded value reaching a thrown message a model reads is
|
|
496
|
+
// the same shape of problem regardless of which side authored it. `handle` needs no
|
|
497
|
+
// treatment: it is already validated ASCII, length-capped by isValidElementHandle before
|
|
498
|
+
// this driver is ever called.
|
|
499
|
+
const safeOption = normalizeAgentLabel(option).text;
|
|
500
|
+
const available = options.map((entry) => normalizeAgentLabel(entry.text.trim()).text)
|
|
501
|
+
.filter((text) => text.length > 0);
|
|
502
|
+
throw new Error(`"${safeOption}" is not an option of "${handle}". Available: ${available.length > 0 ? available.join(', ') : '(none)'}`);
|
|
503
|
+
}
|
|
504
|
+
if (control.multiple) {
|
|
505
|
+
// A multi-select accumulates by default. Assigning `value` would silently clear every
|
|
506
|
+
// prior choice, so a caller building up a selection would end with only its last call's
|
|
507
|
+
// option — and setting `.selected` directly is also how a caller narrows one back off
|
|
508
|
+
// without disturbing the rest.
|
|
509
|
+
match.selected = selected;
|
|
510
|
+
}
|
|
511
|
+
else if (!selected) {
|
|
512
|
+
// A single-select's one option IS its value; there is no "off" state to put it in. Silently
|
|
513
|
+
// ignoring this would tell the caller a deselect happened when nothing changed, and clearing
|
|
514
|
+
// the control would invent a third state (no selection) that a native <select> cannot hold.
|
|
515
|
+
throw new Error(`"${handle}" is a single-select; its choice cannot be removed, only replaced — `
|
|
516
|
+
+ `select a different option instead of deselecting "${normalizeAgentLabel(option).text}"`);
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
// Same prototype-setter dance as `fill`, for the same reason: React tracks the previous
|
|
520
|
+
// value on the node and ignores a change event whose value it believes it already has.
|
|
521
|
+
const setter = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, 'value')?.set;
|
|
522
|
+
if (setter)
|
|
523
|
+
setter.call(control, match.value);
|
|
524
|
+
else
|
|
525
|
+
control.value = match.value;
|
|
526
|
+
}
|
|
527
|
+
control.dispatchEvent(new Event('input', { bubbles: true }));
|
|
528
|
+
control.dispatchEvent(new Event('change', { bubbles: true }));
|
|
529
|
+
},
|
|
530
|
+
async navigate(page) {
|
|
531
|
+
// `hasOwnProperty`, not `pages[page]`: a plain object literal inherits everything
|
|
532
|
+
// `Object.prototype` contributes, so a bare lookup answered `navigate("constructor")` with
|
|
533
|
+
// the `Object` constructor — a function the host never published, which this then called,
|
|
534
|
+
// reporting a navigation that never happened. `navigate("__proto__")` was worse in a
|
|
535
|
+
// different way: it resolved a non-callable object and failed with "go is not a function"
|
|
536
|
+
// instead of the refusal this check exists to produce. `listPages` already reports only own
|
|
537
|
+
// keys (`Object.keys`), so this is what makes the two agree.
|
|
538
|
+
const published = Object.prototype.hasOwnProperty.call(pages, page);
|
|
539
|
+
// The executor already checked the allowlist and sanitizes this same message before this
|
|
540
|
+
// driver-level check would ever be reached through it; this is the belt on the braces. But
|
|
541
|
+
// `PageDriver` is a public interface — a caller that reaches this method directly, bypassing
|
|
542
|
+
// the executor entirely, hands `page` in unsanitized, so the same bound-and-strip treatment
|
|
543
|
+
// applies here too rather than assuming the only caller is the one that already checked.
|
|
544
|
+
if (!published)
|
|
545
|
+
throw new Error(`"${normalizeAgentLabel(page).text}" is not a published page`);
|
|
546
|
+
pages[page].navigate();
|
|
547
|
+
},
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Writes into a contenteditable region the way a real user typing would, rather than by replacing
|
|
552
|
+
* its DOM directly.
|
|
553
|
+
*
|
|
554
|
+
* A framework rich-text editor (Lexical, ProseMirror, Quill, CodeMirror) owns its subtree: it
|
|
555
|
+
* keeps its own model and reconciles the DOM to match it, so a direct `textContent =` write is
|
|
556
|
+
* either invisible to that model or gets reverted on the editor's next render — and it always
|
|
557
|
+
* destroys whatever node structure the editor had built (a mention's `<span>`, a paragraph's `<p>`)
|
|
558
|
+
* in favour of one bare text node. Selecting the existing contents and issuing a real editing
|
|
559
|
+
* command instead is what Playwright's own `fill` does against a contenteditable, for the same
|
|
560
|
+
* reason: `execCommand('insertText'|'delete')` is the platform's own command, so the browser fires
|
|
561
|
+
* `beforeinput`/`input` itself with the correct `inputType` — the exact signal these editors
|
|
562
|
+
* listen for — rather than this driver guessing at a contract it does not own by hand-rolling one.
|
|
563
|
+
*
|
|
564
|
+
* jsdom implements neither a real editing host nor `execCommand`, so every unit test here exercises
|
|
565
|
+
* the fallback: the previous `textContent` + synthetic `input` behaviour, kept for exactly that
|
|
566
|
+
* case and for a real browser that reports the command did not run.
|
|
567
|
+
*
|
|
568
|
+
* Verified 2026-07-26 in Chromium against real Lexical 0.36.2 — the editor
|
|
569
|
+
* `packages/ui/src/features/rich-text-input/` is built on — driving both paths at one editor each
|
|
570
|
+
* and reading back Lexical's own `EditorState`, not the DOM. The `textContent` write left the
|
|
571
|
+
* model at its seed value and was reverted in the DOM too; the `execCommand` path landed in the
|
|
572
|
+
* model. That is the whole reason this function exists, and no unit test in this repo can show it.
|
|
573
|
+
*/
|
|
574
|
+
function fillEditableRegion(control, text) {
|
|
575
|
+
control.focus();
|
|
576
|
+
const selection = globalThis.getSelection?.();
|
|
577
|
+
if (selection) {
|
|
578
|
+
const range = control.ownerDocument.createRange();
|
|
579
|
+
range.selectNodeContents(control);
|
|
580
|
+
selection.removeAllRanges();
|
|
581
|
+
selection.addRange(range);
|
|
582
|
+
}
|
|
583
|
+
const execCommand = control.ownerDocument.execCommand?.bind(control.ownerDocument);
|
|
584
|
+
if (typeof execCommand === 'function') {
|
|
585
|
+
// An empty replacement has no text to insert; `delete` removes the just-selected contents,
|
|
586
|
+
// which `insertText` with an empty string is not guaranteed to do in every engine.
|
|
587
|
+
const ran = text.length > 0 ? execCommand('insertText', false, text) : execCommand('delete');
|
|
588
|
+
if (ran)
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
control.textContent = text;
|
|
592
|
+
// Editors listen for `input`, not `change`; `change` is a form-control event and firing it here
|
|
593
|
+
// would be inventing an event the platform never sends for this element.
|
|
594
|
+
control.dispatchEvent(new Event('input', { bubbles: true }));
|
|
595
|
+
}
|
|
596
|
+
/** Reads the current page's `data-agent-page`, for hosts that tag the body. */
|
|
597
|
+
export function currentAgentPage(root) {
|
|
598
|
+
const tagged = root.querySelector(`[${AGENT_PAGE_ATTRIBUTE}]`);
|
|
599
|
+
return tagged?.getAttribute(AGENT_PAGE_ATTRIBUTE) ?? undefined;
|
|
600
|
+
}
|
|
601
|
+
//# sourceMappingURL=dom-page-driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-page-driver.js","sourceRoot":"","sources":["../../../src/core/dom/dom-page-driver.ts"],"names":[],"mappings":"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"}
|