@nexface/tools 0.1.15-alpha.1 → 0.1.15-alpha.11
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/browser/actions.js +34 -10
- package/dist/browser/controller.js +91 -76
- package/dist/browser/diagnostics.js +8 -3
- package/dist/browser/dom.js +23 -30
- package/dist/browser/feedback.js +6 -5
- package/dist/browser/focus.d.ts +0 -1
- package/dist/browser/focus.js +1 -13
- package/dist/browser/locator.js +3 -9
- package/dist/browser/prompt.d.ts +1 -1
- package/dist/browser/prompt.generated.d.ts +1 -1
- package/dist/browser/prompt.generated.js +9 -4
- package/dist/browser/prompt.js +0 -1
- package/dist/browser/snapshot.js +199 -117
- package/dist/browser/toolset.js +42 -36
- package/dist/browser/types.d.ts +1 -1
- package/dist/browser/types.js +0 -1
- package/dist/browser/worker-runner.js +0 -1
- package/dist/definition.d.ts +1 -3
- package/dist/definition.js +2 -2
- package/dist/index.browser.js +0 -1
- package/dist/index.js +0 -1
- package/dist/internal-react.d.ts +1 -1
- package/dist/internal-react.js +3 -4
- package/dist/internal-types.js +0 -1
- package/dist/navigation.js +0 -1
- package/dist/operation-lock.d.ts +1 -1
- package/dist/operation-lock.js +36 -19
- package/dist/operation-runtime.js +41 -13
- package/dist/operation-surface.js +118 -41
- package/dist/operation-types.js +0 -1
- package/dist/operation-view.d.ts +1 -1
- package/dist/operation-view.js +232 -82
- package/dist/react.js +9 -14
- package/dist/registration.js +4 -3
- package/dist/runtime.d.ts +4 -2
- package/dist/runtime.js +114 -52
- package/dist/shared/definition.js +2 -3
- package/dist/shared/errors.d.ts +1 -1
- package/dist/shared/errors.js +0 -1
- package/dist/shared/page-protocol.js +0 -1
- package/dist/shared/page-tools.js +2 -3
- package/dist/shared/runtime-controller.d.ts +1 -0
- package/dist/shared/runtime-controller.js +0 -1
- package/dist/shared/runtime-execution.js +0 -1
- package/dist/shared/schema.js +45 -19
- package/dist/styles.css +882 -109
- package/dist/tool-search.d.ts +11 -0
- package/dist/tool-search.js +117 -0
- package/dist/types.js +0 -1
- package/dist/webmcp.js +39 -13
- package/package.json +8 -4
- package/dist/browser/actions.js.map +0 -1
- package/dist/browser/controller.js.map +0 -1
- package/dist/browser/diagnostics.js.map +0 -1
- package/dist/browser/dom.js.map +0 -1
- package/dist/browser/feedback.js.map +0 -1
- package/dist/browser/focus.js.map +0 -1
- package/dist/browser/locator.js.map +0 -1
- package/dist/browser/prompt.generated.js.map +0 -1
- package/dist/browser/prompt.js.map +0 -1
- package/dist/browser/snapshot.js.map +0 -1
- package/dist/browser/toolset.js.map +0 -1
- package/dist/browser/types.js.map +0 -1
- package/dist/browser/worker-runner.js.map +0 -1
- package/dist/definition.js.map +0 -1
- package/dist/index.browser.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal-react.js.map +0 -1
- package/dist/internal-types.js.map +0 -1
- package/dist/navigation.js.map +0 -1
- package/dist/operation-lock.js.map +0 -1
- package/dist/operation-runtime.js.map +0 -1
- package/dist/operation-surface.js.map +0 -1
- package/dist/operation-types.js.map +0 -1
- package/dist/operation-view.js.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/registration.js.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/shared/definition.js.map +0 -1
- package/dist/shared/errors.js.map +0 -1
- package/dist/shared/page-protocol.js.map +0 -1
- package/dist/shared/page-tools.js.map +0 -1
- package/dist/shared/runtime-controller.js.map +0 -1
- package/dist/shared/runtime-execution.js.map +0 -1
- package/dist/shared/schema.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/webmcp.js.map +0 -1
package/dist/browser/actions.js
CHANGED
|
@@ -6,12 +6,22 @@ export function collectElementActions(actions, ref, element, role, label, state,
|
|
|
6
6
|
return;
|
|
7
7
|
const id = `a${actions.size + 1}`;
|
|
8
8
|
actions.set(id, {
|
|
9
|
-
candidate: {
|
|
9
|
+
candidate: {
|
|
10
|
+
id,
|
|
11
|
+
operation,
|
|
12
|
+
ref,
|
|
13
|
+
role,
|
|
14
|
+
label: (optionElement ? `${label} / ${option}` : label).slice(0, 500),
|
|
15
|
+
state,
|
|
16
|
+
...(option === undefined ? {} : { option: option.slice(0, 500) }),
|
|
17
|
+
},
|
|
10
18
|
command: { target, method, args },
|
|
11
19
|
...(optionElement ? { optionElement } : {}),
|
|
12
20
|
});
|
|
13
21
|
};
|
|
14
|
-
if (state.disabled ||
|
|
22
|
+
if (state.disabled ||
|
|
23
|
+
element.closest('[inert],[aria-disabled="true"]') ||
|
|
24
|
+
element.matches(":disabled"))
|
|
15
25
|
return;
|
|
16
26
|
if (scrollable) {
|
|
17
27
|
if (element.scrollTop > 0)
|
|
@@ -30,18 +40,25 @@ export function collectElementActions(actions, ref, element, role, label, state,
|
|
|
30
40
|
counts.set(option.value, (counts.get(option.value) ?? 0) + 1);
|
|
31
41
|
for (const option of element.options) {
|
|
32
42
|
if (element.multiple) {
|
|
33
|
-
if (counts.get(option.value) === 1 &&
|
|
43
|
+
if (counts.get(option.value) === 1 &&
|
|
44
|
+
!option.disabled &&
|
|
45
|
+
!option.closest("optgroup[disabled]")) {
|
|
34
46
|
add(option.selected ? "uncheck" : "check", "setOptionSelected", [option.value, !option.selected, option.label], option.label, option);
|
|
35
47
|
}
|
|
36
48
|
continue;
|
|
37
49
|
}
|
|
38
|
-
if (counts.get(option.value) === 1 &&
|
|
50
|
+
if (counts.get(option.value) === 1 &&
|
|
51
|
+
!(element.required && option.value === "") &&
|
|
52
|
+
!option.disabled &&
|
|
53
|
+
!option.selected &&
|
|
54
|
+
!option.closest("optgroup[disabled]"))
|
|
39
55
|
add("select", "selectOption", [option.value], option.label);
|
|
40
56
|
}
|
|
41
57
|
return;
|
|
42
58
|
}
|
|
43
|
-
if (role === "option" &&
|
|
44
|
-
|
|
59
|
+
if (role === "option" &&
|
|
60
|
+
element.closest('[role="listbox"]')?.getAttribute("aria-multiselectable") === "true" &&
|
|
61
|
+
["true", "false"].includes(element.getAttribute("aria-selected") ?? "")) {
|
|
45
62
|
const field = computeName(element.closest('[role="listbox"]'));
|
|
46
63
|
label = field ? `${field} / ${label}` : label;
|
|
47
64
|
add(state.selected ? "uncheck" : "check", state.selected ? "uncheck" : "check", []);
|
|
@@ -54,15 +71,23 @@ export function collectElementActions(actions, ref, element, role, label, state,
|
|
|
54
71
|
add("uncheck", "uncheck", []);
|
|
55
72
|
return;
|
|
56
73
|
}
|
|
57
|
-
const editable = element instanceof HTMLInputElement ||
|
|
58
|
-
|
|
74
|
+
const editable = element instanceof HTMLInputElement ||
|
|
75
|
+
element instanceof HTMLTextAreaElement ||
|
|
76
|
+
(element instanceof HTMLElement && element.isContentEditable);
|
|
77
|
+
if (editable &&
|
|
78
|
+
!("readOnly" in element && element.readOnly) &&
|
|
79
|
+
element.getAttribute("aria-readonly") !== "true" &&
|
|
80
|
+
["textbox", "searchbox", "combobox", "spinbutton"].includes(role)) {
|
|
59
81
|
add("fill", "fill", []);
|
|
60
82
|
}
|
|
61
83
|
add("click", "click", []);
|
|
62
84
|
}
|
|
63
85
|
export function collectPageActions(actions) {
|
|
64
86
|
const add = (id, operation, method, args) => {
|
|
65
|
-
actions.set(id, {
|
|
87
|
+
actions.set(id, {
|
|
88
|
+
candidate: { id, operation, label: operation, role: "page", state: {} },
|
|
89
|
+
command: { target: { type: "page" }, method, args },
|
|
90
|
+
});
|
|
66
91
|
};
|
|
67
92
|
if (window.scrollY > 0)
|
|
68
93
|
add("pageUp", "scrollUp", "scrollBy", [{ y: -560 }]);
|
|
@@ -70,4 +95,3 @@ export function collectPageActions(actions) {
|
|
|
70
95
|
add("pageDown", "scrollDown", "scrollBy", [{ y: 560 }]);
|
|
71
96
|
add("wait", "wait", "waitForTimeout", [100]);
|
|
72
97
|
}
|
|
73
|
-
//# sourceMappingURL=actions.js.map
|
|
@@ -20,11 +20,7 @@ const LOCATOR_METHODS = new Set([
|
|
|
20
20
|
"text",
|
|
21
21
|
"waitFor",
|
|
22
22
|
]);
|
|
23
|
-
const PAGE_METHODS = new Set([
|
|
24
|
-
"scrollBy",
|
|
25
|
-
"waitForTimeout",
|
|
26
|
-
"waitForURL",
|
|
27
|
-
]);
|
|
23
|
+
const PAGE_METHODS = new Set(["scrollBy", "waitForTimeout", "waitForURL"]);
|
|
28
24
|
const WAIT_FOR_URL_TIMEOUT_MS = 3_000;
|
|
29
25
|
const URL_POLL_INTERVAL_MS = 50;
|
|
30
26
|
const ACTION_METHODS = new Set([
|
|
@@ -65,7 +61,7 @@ function dispatchClickSequence(element) {
|
|
|
65
61
|
message: "Click target does not have an uncovered hit point.",
|
|
66
62
|
});
|
|
67
63
|
}
|
|
68
|
-
const { element: hitTarget, clientX, clientY
|
|
64
|
+
const { element: hitTarget, clientX, clientY } = hit;
|
|
69
65
|
const init = {
|
|
70
66
|
bubbles: true,
|
|
71
67
|
cancelable: true,
|
|
@@ -195,8 +191,7 @@ function validateArgs(command, maxChars) {
|
|
|
195
191
|
}
|
|
196
192
|
if (command.method === "selectOption") {
|
|
197
193
|
if (command.args.length !== 1 ||
|
|
198
|
-
(typeof command.args[0] !== "string" &&
|
|
199
|
-
!Array.isArray(command.args[0]))) {
|
|
194
|
+
(typeof command.args[0] !== "string" && !Array.isArray(command.args[0]))) {
|
|
200
195
|
throw invalidCommand("selectOption() requires a string or string array.");
|
|
201
196
|
}
|
|
202
197
|
if (Array.isArray(command.args[0]) &&
|
|
@@ -204,8 +199,11 @@ function validateArgs(command, maxChars) {
|
|
|
204
199
|
throw invalidCommand("selectOption() array values must be strings.");
|
|
205
200
|
}
|
|
206
201
|
}
|
|
207
|
-
if (command.method === "setOptionSelected" &&
|
|
208
|
-
|
|
202
|
+
if (command.method === "setOptionSelected" &&
|
|
203
|
+
(command.args.length !== 3 ||
|
|
204
|
+
typeof command.args[0] !== "string" ||
|
|
205
|
+
typeof command.args[1] !== "boolean" ||
|
|
206
|
+
typeof command.args[2] !== "string")) {
|
|
209
207
|
throw invalidCommand("Invalid observed option selection.");
|
|
210
208
|
}
|
|
211
209
|
if (command.method === "waitFor") {
|
|
@@ -221,9 +219,7 @@ function validateArgs(command, maxChars) {
|
|
|
221
219
|
throw invalidCommand("waitFor() state is invalid.");
|
|
222
220
|
}
|
|
223
221
|
if (value?.timeout !== undefined &&
|
|
224
|
-
(typeof value.timeout !== "number" ||
|
|
225
|
-
!Number.isFinite(value.timeout) ||
|
|
226
|
-
value.timeout < 0)) {
|
|
222
|
+
(typeof value.timeout !== "number" || !Number.isFinite(value.timeout) || value.timeout < 0)) {
|
|
227
223
|
throw invalidCommand("waitFor() timeout is invalid.");
|
|
228
224
|
}
|
|
229
225
|
}
|
|
@@ -264,10 +260,8 @@ function validateArgs(command, maxChars) {
|
|
|
264
260
|
Array.isArray(value) ||
|
|
265
261
|
Object.keys(value).length === 0 ||
|
|
266
262
|
Object.keys(value).some((key) => !["x", "y"].includes(key)) ||
|
|
267
|
-
(value.x !== undefined &&
|
|
268
|
-
|
|
269
|
-
(value.y !== undefined &&
|
|
270
|
-
(typeof value.y !== "number" || !Number.isFinite(value.y)))) {
|
|
263
|
+
(value.x !== undefined && (typeof value.x !== "number" || !Number.isFinite(value.x))) ||
|
|
264
|
+
(value.y !== undefined && (typeof value.y !== "number" || !Number.isFinite(value.y)))) {
|
|
271
265
|
throw invalidCommand("scrollBy() requires finite numeric x/y values.");
|
|
272
266
|
}
|
|
273
267
|
}
|
|
@@ -281,8 +275,7 @@ function validateArgs(command, maxChars) {
|
|
|
281
275
|
Object.keys(value).some((key) => !["left", "top"].includes(key)) ||
|
|
282
276
|
(value.left !== undefined &&
|
|
283
277
|
(typeof value.left !== "number" || !Number.isFinite(value.left))) ||
|
|
284
|
-
(value.top !== undefined &&
|
|
285
|
-
(typeof value.top !== "number" || !Number.isFinite(value.top)))) {
|
|
278
|
+
(value.top !== undefined && (typeof value.top !== "number" || !Number.isFinite(value.top)))) {
|
|
286
279
|
throw invalidCommand("scrollTo() requires finite numeric left/top values.");
|
|
287
280
|
}
|
|
288
281
|
}
|
|
@@ -340,9 +333,7 @@ export class PageController {
|
|
|
340
333
|
throw invalidCommand("Command envelope is invalid.");
|
|
341
334
|
}
|
|
342
335
|
const record = this.executions.get(command.executionId);
|
|
343
|
-
if (!record ||
|
|
344
|
-
record.status !== "running" ||
|
|
345
|
-
record.abortController.signal.aborted) {
|
|
336
|
+
if (!record || record.status !== "running" || record.abortController.signal.aborted) {
|
|
346
337
|
throw new PageCommandError({
|
|
347
338
|
code: "EXECUTION_CANCELLED",
|
|
348
339
|
message: "The execution is no longer active.",
|
|
@@ -393,15 +384,16 @@ export class PageController {
|
|
|
393
384
|
record = this.requireActive(command);
|
|
394
385
|
const value = command.target.type === "page"
|
|
395
386
|
? await this.executePage(command, record)
|
|
396
|
-
: await this.executeTarget(command, record, beforeAction &&
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
387
|
+
: await this.executeTarget(command, record, beforeAction &&
|
|
388
|
+
(async () => {
|
|
389
|
+
try {
|
|
390
|
+
await beforeAction();
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
presentationFailed = true;
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
396
|
+
}));
|
|
405
397
|
if (ACTION_METHODS.has(command.method) &&
|
|
406
398
|
!record.completedActions.some((action) => action.callIndex === command.commandSeq)) {
|
|
407
399
|
this.recordCompletedAction(record, command);
|
|
@@ -521,9 +513,7 @@ export class PageController {
|
|
|
521
513
|
return record.registry.refs.get(command.target.ref);
|
|
522
514
|
}
|
|
523
515
|
locator(command) {
|
|
524
|
-
return command.target.type === "locator"
|
|
525
|
-
? command.target.locator
|
|
526
|
-
: undefined;
|
|
516
|
+
return command.target.type === "locator" ? command.target.locator : undefined;
|
|
527
517
|
}
|
|
528
518
|
async executeTarget(command, record, beforeAction) {
|
|
529
519
|
const signal = record.abortController.signal;
|
|
@@ -557,11 +547,7 @@ export class PageController {
|
|
|
557
547
|
return null;
|
|
558
548
|
}
|
|
559
549
|
if (command.method === "text") {
|
|
560
|
-
const element = ref
|
|
561
|
-
? requireValidRef(ref)
|
|
562
|
-
: locator
|
|
563
|
-
? requireUnique(locator)
|
|
564
|
-
: undefined;
|
|
550
|
+
const element = ref ? requireValidRef(ref) : locator ? requireUnique(locator) : undefined;
|
|
565
551
|
if (!element)
|
|
566
552
|
throw invalidCommand("Target is missing.");
|
|
567
553
|
if (element instanceof HTMLInputElement ||
|
|
@@ -572,11 +558,7 @@ export class PageController {
|
|
|
572
558
|
return normalizeText(element.textContent);
|
|
573
559
|
}
|
|
574
560
|
if (command.method === "scrollIntoView") {
|
|
575
|
-
const element = ref
|
|
576
|
-
? requireValidRef(ref)
|
|
577
|
-
: locator
|
|
578
|
-
? requireUnique(locator)
|
|
579
|
-
: undefined;
|
|
561
|
+
const element = ref ? requireValidRef(ref) : locator ? requireUnique(locator) : undefined;
|
|
580
562
|
if (!element)
|
|
581
563
|
throw invalidCommand("Target is missing.");
|
|
582
564
|
const beforeIdentity = createIdentity(element);
|
|
@@ -597,11 +579,7 @@ export class PageController {
|
|
|
597
579
|
return null;
|
|
598
580
|
}
|
|
599
581
|
if (command.method === "scrollBy" || command.method === "scrollTo") {
|
|
600
|
-
const element = ref
|
|
601
|
-
? ref.element
|
|
602
|
-
: locator
|
|
603
|
-
? requireUnique(locator)
|
|
604
|
-
: undefined;
|
|
582
|
+
const element = ref ? ref.element : locator ? requireUnique(locator) : undefined;
|
|
605
583
|
if (!(element instanceof HTMLElement)) {
|
|
606
584
|
throw new PageCommandError({
|
|
607
585
|
code: "UNSUPPORTED_OPERATION",
|
|
@@ -648,12 +626,8 @@ export class PageController {
|
|
|
648
626
|
}
|
|
649
627
|
else {
|
|
650
628
|
const value = command.args[0];
|
|
651
|
-
if ((value.left !== undefined &&
|
|
652
|
-
value.
|
|
653
|
-
!scrollability.x) ||
|
|
654
|
-
(value.top !== undefined &&
|
|
655
|
-
value.top !== element.scrollTop &&
|
|
656
|
-
!scrollability.y)) {
|
|
629
|
+
if ((value.left !== undefined && value.left !== element.scrollLeft && !scrollability.x) ||
|
|
630
|
+
(value.top !== undefined && value.top !== element.scrollTop && !scrollability.y)) {
|
|
657
631
|
throw new PageCommandError({
|
|
658
632
|
code: "UNSUPPORTED_OPERATION",
|
|
659
633
|
message: "Requested axis does not have a scroll range.",
|
|
@@ -682,7 +656,9 @@ export class PageController {
|
|
|
682
656
|
this.requireStillActive(record);
|
|
683
657
|
const element = ref
|
|
684
658
|
? await prepareRefForAction(ref, signal)
|
|
685
|
-
: locator
|
|
659
|
+
: locator
|
|
660
|
+
? await prepareLocatorForAction(locator, signal)
|
|
661
|
+
: undefined;
|
|
686
662
|
if (!element)
|
|
687
663
|
throw invalidCommand("Target is missing.");
|
|
688
664
|
this.requireStillActive(record);
|
|
@@ -692,9 +668,13 @@ export class PageController {
|
|
|
692
668
|
await feedback;
|
|
693
669
|
this.requireStillActive(record);
|
|
694
670
|
await beforeAction?.();
|
|
695
|
-
if (!element.isConnected ||
|
|
696
|
-
|
|
697
|
-
|
|
671
|
+
if (!element.isConnected ||
|
|
672
|
+
createIdentity(element) !== identity ||
|
|
673
|
+
(locator && requireUnique(locator) !== element)) {
|
|
674
|
+
throw new PageCommandError({
|
|
675
|
+
code: "REF_ACTION_FAILED",
|
|
676
|
+
message: "Target changed before the action.",
|
|
677
|
+
});
|
|
698
678
|
}
|
|
699
679
|
ensureActionable(element);
|
|
700
680
|
this.requireStillActive(record);
|
|
@@ -714,8 +694,7 @@ export class PageController {
|
|
|
714
694
|
});
|
|
715
695
|
}
|
|
716
696
|
requireStillActive(record) {
|
|
717
|
-
if (record.status !== "running" ||
|
|
718
|
-
record.abortController.signal.aborted) {
|
|
697
|
+
if (record.status !== "running" || record.abortController.signal.aborted) {
|
|
719
698
|
throw new PageCommandError({
|
|
720
699
|
code: "EXECUTION_CANCELLED",
|
|
721
700
|
message: "Execution was cancelled before the DOM action.",
|
|
@@ -729,8 +708,7 @@ export class PageController {
|
|
|
729
708
|
}
|
|
730
709
|
if (command.method === "fill") {
|
|
731
710
|
const value = String(command.args[0] ?? "");
|
|
732
|
-
if ((element instanceof HTMLInputElement ||
|
|
733
|
-
element instanceof HTMLTextAreaElement) &&
|
|
711
|
+
if ((element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) &&
|
|
734
712
|
!element.readOnly) {
|
|
735
713
|
focusForAgent(element);
|
|
736
714
|
setNativeValue(element, value);
|
|
@@ -749,21 +727,56 @@ export class PageController {
|
|
|
749
727
|
}
|
|
750
728
|
if (command.method === "press") {
|
|
751
729
|
const key = String(command.args[0] ?? "");
|
|
752
|
-
|
|
753
|
-
|
|
730
|
+
const keyCodes = {
|
|
731
|
+
Backspace: 8,
|
|
732
|
+
Tab: 9,
|
|
733
|
+
Enter: 13,
|
|
734
|
+
Shift: 16,
|
|
735
|
+
Control: 17,
|
|
736
|
+
Alt: 18,
|
|
737
|
+
Escape: 27,
|
|
738
|
+
" ": 32,
|
|
739
|
+
PageUp: 33,
|
|
740
|
+
PageDown: 34,
|
|
741
|
+
End: 35,
|
|
742
|
+
Home: 36,
|
|
743
|
+
ArrowLeft: 37,
|
|
744
|
+
ArrowUp: 38,
|
|
745
|
+
ArrowRight: 39,
|
|
746
|
+
ArrowDown: 40,
|
|
747
|
+
Delete: 46,
|
|
748
|
+
};
|
|
749
|
+
const keyCode = keyCodes[key] ?? (/^[a-z0-9]$/i.test(key) ? key.toUpperCase().charCodeAt(0) : 0);
|
|
750
|
+
const code = /^[a-z]$/i.test(key)
|
|
751
|
+
? `Key${key.toUpperCase()}`
|
|
752
|
+
: /^[0-9]$/.test(key)
|
|
753
|
+
? `Digit${key}`
|
|
754
|
+
: key === " "
|
|
755
|
+
? "Space"
|
|
756
|
+
: key;
|
|
757
|
+
const init = {
|
|
758
|
+
key,
|
|
759
|
+
code,
|
|
760
|
+
keyCode,
|
|
761
|
+
which: keyCode,
|
|
762
|
+
bubbles: true,
|
|
763
|
+
cancelable: true,
|
|
764
|
+
composed: true,
|
|
765
|
+
};
|
|
766
|
+
element.dispatchEvent(new KeyboardEvent("keydown", init));
|
|
767
|
+
element.dispatchEvent(new KeyboardEvent("keyup", init));
|
|
754
768
|
return null;
|
|
755
769
|
}
|
|
756
770
|
if (command.method === "check" || command.method === "uncheck") {
|
|
757
771
|
const checked = command.method === "check";
|
|
758
|
-
if (element.getAttribute("role") === "option"
|
|
759
|
-
|
|
760
|
-
|
|
772
|
+
if (element.getAttribute("role") === "option" &&
|
|
773
|
+
element.closest('[role="listbox"]')?.getAttribute("aria-multiselectable") === "true" &&
|
|
774
|
+
["true", "false"].includes(element.getAttribute("aria-selected") ?? "")) {
|
|
761
775
|
if ((element.getAttribute("aria-selected") === "true") !== checked)
|
|
762
776
|
dispatchClickSequence(element);
|
|
763
777
|
return null;
|
|
764
778
|
}
|
|
765
|
-
if (element instanceof HTMLInputElement &&
|
|
766
|
-
["checkbox", "radio"].includes(element.type)) {
|
|
779
|
+
if (element instanceof HTMLInputElement && ["checkbox", "radio"].includes(element.type)) {
|
|
767
780
|
if (element.type === "radio" && !checked) {
|
|
768
781
|
throw new PageCommandError({
|
|
769
782
|
code: "UNSUPPORTED_OPERATION",
|
|
@@ -789,9 +802,14 @@ export class PageController {
|
|
|
789
802
|
if (command.method === "setOptionSelected") {
|
|
790
803
|
const [value, selected, label] = command.args;
|
|
791
804
|
const options = element instanceof HTMLSelectElement && element.multiple
|
|
792
|
-
? [...element.options].filter((option) => option.value === value)
|
|
805
|
+
? [...element.options].filter((option) => option.value === value)
|
|
806
|
+
: [];
|
|
793
807
|
const option = options[0];
|
|
794
|
-
if (options.length !== 1 ||
|
|
808
|
+
if (options.length !== 1 ||
|
|
809
|
+
!option ||
|
|
810
|
+
option.label !== label ||
|
|
811
|
+
option.disabled ||
|
|
812
|
+
option.closest("optgroup[disabled]")) {
|
|
795
813
|
throw invalidCommand("The observed multiple-select option is no longer available.");
|
|
796
814
|
}
|
|
797
815
|
if (option.selected !== selected) {
|
|
@@ -808,9 +826,7 @@ export class PageController {
|
|
|
808
826
|
});
|
|
809
827
|
}
|
|
810
828
|
const requested = command.args[0];
|
|
811
|
-
const values = Array.isArray(requested)
|
|
812
|
-
? requested.map(String)
|
|
813
|
-
: [String(requested ?? "")];
|
|
829
|
+
const values = Array.isArray(requested) ? requested.map(String) : [String(requested ?? "")];
|
|
814
830
|
const availableValues = new Set(Array.from(element.options, (option) => option.value));
|
|
815
831
|
const missing = values.filter((value) => !availableValues.has(value));
|
|
816
832
|
if (missing.length > 0) {
|
|
@@ -841,4 +857,3 @@ export class PageController {
|
|
|
841
857
|
throw invalidCommand(`Unsupported locator method: ${command.method}`);
|
|
842
858
|
}
|
|
843
859
|
}
|
|
844
|
-
//# sourceMappingURL=controller.js.map
|
|
@@ -11,10 +11,15 @@ export async function measurePagePhase(phase, execute) {
|
|
|
11
11
|
}
|
|
12
12
|
finally {
|
|
13
13
|
try {
|
|
14
|
-
performance.measure("nexface.browser.phase", {
|
|
14
|
+
performance.measure("nexface.browser.phase", {
|
|
15
|
+
start,
|
|
16
|
+
end: performance.now(),
|
|
17
|
+
detail: { phase, status },
|
|
18
|
+
});
|
|
15
19
|
performance.clearMeasures("nexface.browser.phase");
|
|
16
20
|
}
|
|
17
|
-
catch {
|
|
21
|
+
catch {
|
|
22
|
+
/* Diagnostics must never interrupt execution. */
|
|
23
|
+
}
|
|
18
24
|
}
|
|
19
25
|
}
|
|
20
|
-
//# sourceMappingURL=diagnostics.js.map
|
package/dist/browser/dom.js
CHANGED
|
@@ -108,9 +108,7 @@ export function* walkComposedElements(root = document) {
|
|
|
108
108
|
function hasGenericInteractionEvidence(element) {
|
|
109
109
|
if (!(element instanceof HTMLElement))
|
|
110
110
|
return false;
|
|
111
|
-
return
|
|
112
|
-
element.hasAttribute("onclick") ||
|
|
113
|
-
element.isContentEditable);
|
|
111
|
+
return element.tabIndex >= 0 || element.hasAttribute("onclick") || element.isContentEditable;
|
|
114
112
|
}
|
|
115
113
|
export function computeRole(element) {
|
|
116
114
|
const explicit = normalizeText(element.getAttribute("role"))
|
|
@@ -146,9 +144,7 @@ export function computeRole(element) {
|
|
|
146
144
|
}
|
|
147
145
|
function referencedText(element, attribute) {
|
|
148
146
|
const root = element.getRootNode();
|
|
149
|
-
const ids = normalizeText(element.getAttribute(attribute))
|
|
150
|
-
.split(" ")
|
|
151
|
-
.filter(Boolean);
|
|
147
|
+
const ids = normalizeText(element.getAttribute(attribute)).split(" ").filter(Boolean);
|
|
152
148
|
return normalizeText(ids
|
|
153
149
|
.map((id) => {
|
|
154
150
|
if (root instanceof Document || root instanceof ShadowRoot) {
|
|
@@ -187,24 +183,14 @@ export function computeNameInfo(element) {
|
|
|
187
183
|
if (caption)
|
|
188
184
|
return { value: caption, source: "explicit" };
|
|
189
185
|
}
|
|
190
|
-
if (element instanceof HTMLInputElement &&
|
|
191
|
-
["button", "submit", "reset"].includes(element.type)) {
|
|
186
|
+
if (element instanceof HTMLInputElement && ["button", "submit", "reset"].includes(element.type)) {
|
|
192
187
|
const value = normalizeText(element.value);
|
|
193
188
|
if (value)
|
|
194
189
|
return { value, source: "fallback" };
|
|
195
190
|
}
|
|
196
191
|
const role = computeRole(element);
|
|
197
192
|
if (isActionableRole(role) ||
|
|
198
|
-
[
|
|
199
|
-
"cell",
|
|
200
|
-
"columnheader",
|
|
201
|
-
"heading",
|
|
202
|
-
"listitem",
|
|
203
|
-
"option",
|
|
204
|
-
"row",
|
|
205
|
-
"tab",
|
|
206
|
-
"treeitem",
|
|
207
|
-
].includes(role)) {
|
|
193
|
+
["cell", "columnheader", "heading", "listitem", "option", "row", "tab", "treeitem"].includes(role)) {
|
|
208
194
|
const content = normalizeText(element.textContent);
|
|
209
195
|
if (content)
|
|
210
196
|
return { value: content, source: "content" };
|
|
@@ -212,8 +198,7 @@ export function computeNameInfo(element) {
|
|
|
212
198
|
const title = normalizeText(element.getAttribute("title"));
|
|
213
199
|
if (title)
|
|
214
200
|
return { value: title, source: "fallback" };
|
|
215
|
-
if (element instanceof HTMLInputElement ||
|
|
216
|
-
element instanceof HTMLTextAreaElement) {
|
|
201
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
|
|
217
202
|
return {
|
|
218
203
|
value: normalizeText(element.placeholder),
|
|
219
204
|
source: "fallback",
|
|
@@ -228,10 +213,8 @@ const SCROLLABLE_OVERFLOW = new Set(["auto", "scroll", "overlay", "hidden"]);
|
|
|
228
213
|
export function getElementScrollability(element, style = getComputedStyle(element)) {
|
|
229
214
|
if (!(element instanceof HTMLElement))
|
|
230
215
|
return undefined;
|
|
231
|
-
const x = SCROLLABLE_OVERFLOW.has(style.overflowX) &&
|
|
232
|
-
|
|
233
|
-
const y = SCROLLABLE_OVERFLOW.has(style.overflowY) &&
|
|
234
|
-
element.scrollHeight > element.clientHeight + 2;
|
|
216
|
+
const x = SCROLLABLE_OVERFLOW.has(style.overflowX) && element.scrollWidth > element.clientWidth + 2;
|
|
217
|
+
const y = SCROLLABLE_OVERFLOW.has(style.overflowY) && element.scrollHeight > element.clientHeight + 2;
|
|
235
218
|
if (!x && !y)
|
|
236
219
|
return undefined;
|
|
237
220
|
return {
|
|
@@ -329,7 +312,10 @@ export function computeState(element) {
|
|
|
329
312
|
if (element instanceof HTMLSelectElement) {
|
|
330
313
|
if (element.multiple) {
|
|
331
314
|
state.multiple = true;
|
|
332
|
-
state.selectedOptions = [...element.selectedOptions].map((option) => ({
|
|
315
|
+
state.selectedOptions = [...element.selectedOptions].map((option) => ({
|
|
316
|
+
value: option.value,
|
|
317
|
+
label: normalizeText(option.label),
|
|
318
|
+
}));
|
|
333
319
|
}
|
|
334
320
|
if (element.value)
|
|
335
321
|
state.value = element.value;
|
|
@@ -367,11 +353,20 @@ export function findSemanticContext(element) {
|
|
|
367
353
|
}
|
|
368
354
|
return {};
|
|
369
355
|
}
|
|
356
|
+
function composedTextContent(node) {
|
|
357
|
+
if (node instanceof Text)
|
|
358
|
+
return node.data;
|
|
359
|
+
if (!(node instanceof Element))
|
|
360
|
+
return "";
|
|
361
|
+
return composedChildren(node).map(composedTextContent).join("");
|
|
362
|
+
}
|
|
370
363
|
export function createIdentity(element) {
|
|
371
364
|
const context = findSemanticContext(element);
|
|
365
|
+
const role = computeRole(element);
|
|
366
|
+
const name = computeName(element) || (!role ? composedTextContent(element) : "");
|
|
372
367
|
return JSON.stringify([
|
|
373
|
-
|
|
374
|
-
normalizeText(
|
|
368
|
+
role,
|
|
369
|
+
normalizeText(name),
|
|
375
370
|
context.role ?? "",
|
|
376
371
|
normalizeText(context.text),
|
|
377
372
|
]);
|
|
@@ -382,8 +377,7 @@ export function isElementRendered(element, style = getComputedStyle(element)) {
|
|
|
382
377
|
}
|
|
383
378
|
if (isAgentIgnored(element))
|
|
384
379
|
return false;
|
|
385
|
-
if (element.hasAttribute("hidden") ||
|
|
386
|
-
element.getAttribute("aria-hidden") === "true") {
|
|
380
|
+
if (element.hasAttribute("hidden") || element.getAttribute("aria-hidden") === "true") {
|
|
387
381
|
return false;
|
|
388
382
|
}
|
|
389
383
|
return style.display !== "none" && style.visibility !== "hidden";
|
|
@@ -442,4 +436,3 @@ export function matchesText(value, matcher, exact = false) {
|
|
|
442
436
|
const expected = normalizeText(matcher);
|
|
443
437
|
return exact ? normalized === expected : normalized.includes(expected);
|
|
444
438
|
}
|
|
445
|
-
//# sourceMappingURL=dom.js.map
|
package/dist/browser/feedback.js
CHANGED
|
@@ -5,10 +5,11 @@ export class ActionFeedback {
|
|
|
5
5
|
this.assertActive?.();
|
|
6
6
|
const read = ["text", "exists", "waitFor", "waitForTimeout", "waitForURL"].includes(action);
|
|
7
7
|
const scroll = ["scrollBy", "scrollTo", "scrollIntoView"].includes(action);
|
|
8
|
-
return this.listener?.(scroll
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
return this.listener?.(scroll
|
|
9
|
+
? {}
|
|
10
|
+
: {
|
|
11
|
+
effect: read ? "read" : "action",
|
|
12
|
+
...(!read && target ? { target } : {}),
|
|
13
|
+
});
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
|
-
//# sourceMappingURL=feedback.js.map
|
package/dist/browser/focus.d.ts
CHANGED
package/dist/browser/focus.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
let focusingForAgent = false;
|
|
2
1
|
let activatingForAgent = false;
|
|
3
2
|
export function isAgentActivation() {
|
|
4
3
|
return activatingForAgent;
|
|
@@ -13,17 +12,6 @@ export function withAgentActivation(activate) {
|
|
|
13
12
|
activatingForAgent = previous;
|
|
14
13
|
}
|
|
15
14
|
}
|
|
16
|
-
export function isAgentFocus() {
|
|
17
|
-
return focusingForAgent;
|
|
18
|
-
}
|
|
19
15
|
export function focusForAgent(element) {
|
|
20
|
-
|
|
21
|
-
focusingForAgent = true;
|
|
22
|
-
try {
|
|
23
|
-
element.focus({ preventScroll: true });
|
|
24
|
-
}
|
|
25
|
-
finally {
|
|
26
|
-
focusingForAgent = previous;
|
|
27
|
-
}
|
|
16
|
+
element.focus({ preventScroll: true });
|
|
28
17
|
}
|
|
29
|
-
//# sourceMappingURL=focus.js.map
|
package/dist/browser/locator.js
CHANGED
|
@@ -123,7 +123,7 @@ function animationFrame(signal) {
|
|
|
123
123
|
reject(abortError());
|
|
124
124
|
return;
|
|
125
125
|
}
|
|
126
|
-
requestAnimationFrame(() => signal.aborted ? reject(abortError()) : resolve());
|
|
126
|
+
requestAnimationFrame(() => (signal.aborted ? reject(abortError()) : resolve()));
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
async function waitForStable(element, signal) {
|
|
@@ -159,15 +159,10 @@ function ensureNotCovered(element) {
|
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
161
|
export function findElementHitTarget(element) {
|
|
162
|
-
const candidates = [
|
|
163
|
-
element,
|
|
164
|
-
...Array.from(walkComposedElements(element)).slice(0, 127),
|
|
165
|
-
].filter((candidate) => !isAgentIgnored(candidate));
|
|
162
|
+
const candidates = [element, ...Array.from(walkComposedElements(element)).slice(0, 127)].filter((candidate) => !isAgentIgnored(candidate));
|
|
166
163
|
for (const candidate of candidates) {
|
|
167
164
|
const clientRects = Array.from(candidate.getClientRects()).filter((rect) => rect.width > 0 && rect.height > 0);
|
|
168
|
-
const rects = clientRects.length > 0
|
|
169
|
-
? clientRects
|
|
170
|
-
: [candidate.getBoundingClientRect()];
|
|
165
|
+
const rects = clientRects.length > 0 ? clientRects : [candidate.getBoundingClientRect()];
|
|
171
166
|
for (const rect of rects) {
|
|
172
167
|
const left = Math.max(0, rect.left);
|
|
173
168
|
const right = Math.min(window.innerWidth, rect.right);
|
|
@@ -274,4 +269,3 @@ export function requireValidRef(target) {
|
|
|
274
269
|
validateIdentity(target);
|
|
275
270
|
return target.element;
|
|
276
271
|
}
|
|
277
|
-
//# sourceMappingURL=locator.js.map
|