@unotest/grounder-client 0.43.1 → 0.44.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/CHANGELOG.md +18 -0
- package/dist/dom-walker/index.d.ts +9 -1
- package/dist/dom-walker/index.js +23 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.44.0] - 2026-09-30
|
|
4
|
+
|
|
5
|
+
## [0.43.2] - 2026-09-30
|
|
6
|
+
|
|
7
|
+
### Patch Changes
|
|
8
|
+
|
|
9
|
+
- 70c37be: Screenshots and saved page HTML leave out what a page keeps secret.
|
|
10
|
+
|
|
11
|
+
- Password and one-time-code fields are painted over on every screenshot: the agent's screenshots, per-step captures, and the screenshot taken when a run fails.
|
|
12
|
+
- The `page.html` saved when a run fails no longer contains the content of `<script>` elements or the values of hidden form fields; each is replaced by a label with its length. The same applies to the element HTML written in debug mode.
|
|
13
|
+
- Text in elements that are not rendered, and in `noembed` and `noframes`, no longer reaches element names.
|
|
14
|
+
|
|
15
|
+
What changes for you: if you read `page.html` for script data or hidden field values, set `failureBundle.pageHtml: "full"` in your config to keep them. Password fields stay hidden either way.
|
|
16
|
+
|
|
17
|
+
- 0e15c53: An element's text, wherever the page is read for it — the locator a ref becomes in a recorded scenario, `check_locator`'s matches and the failure's semantic snapshot — no longer includes the content of `<noscript>`, `<script>`, `<style>` or `<template>`: with JavaScript on, a page could repeat a secret inside `<noscript>` and it went into the customer's scenario (or into the refusal the agent read) in a locator that, since Playwright's own text matching skips `<noscript>`, did not even find the element.
|
|
18
|
+
|
|
19
|
+
The same goes for a failed `assertText` (and `assert_text`): it still compares against the element's full text, as before, but the "got" it quotes — in the MCP reply, the run's output, `runtime.json`, `steps.jsonl` and `failure.json` — leaves those blocks out, and when they alone made the comparison fail it says so. **What changes for you:** a failure message may now read `got "API key"` where it used to show the text of a `<noscript>` as well, followed by a note that the comparison includes hidden text; very long texts are quoted up to 10,000 characters, with a note when cut.
|
|
20
|
+
|
|
3
21
|
## [0.43.1] - 2026-09-29
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -9,6 +9,10 @@ interface DomHelperOptions {
|
|
|
9
9
|
* subtrees, and the per-element ancestor climb would be O(depth²) on
|
|
10
10
|
* its hot path. Default "ancestors". */
|
|
11
11
|
ariaHidden?: "ancestors" | "self";
|
|
12
|
+
/** Elements whose content is not an element's text (`TEXT_HIDDEN_TAGS`,
|
|
13
|
+
* text-rules.ts). Passed in rather than written here: this factory is
|
|
14
|
+
* serialized into the page and cannot import the declaration. */
|
|
15
|
+
hiddenTextTags?: readonly string[];
|
|
12
16
|
}
|
|
13
17
|
/** Subset of accessibility state shared by the walker's `RawState` and
|
|
14
18
|
* outline capture in @unotest/web (`disabled` and `level` handling
|
|
@@ -30,6 +34,8 @@ declare function createDomHelpers(opts?: DomHelperOptions): {
|
|
|
30
34
|
cap: (s: string) => string;
|
|
31
35
|
cssEscape: (s: string) => string;
|
|
32
36
|
textContentVisible: (el: Element) => string;
|
|
37
|
+
isHiddenText: (el: Element) => boolean;
|
|
38
|
+
textWithoutHidden: (node: Node) => string;
|
|
33
39
|
interactiveRoles: Set<string>;
|
|
34
40
|
formFieldRole: (el: Element, tag: string) => string | null;
|
|
35
41
|
implicitRole: (el: Element) => string | null;
|
|
@@ -71,6 +77,8 @@ declare function composeInjectedScript(algorithm: (h: InjectedDomHelpers, ...res
|
|
|
71
77
|
|
|
72
78
|
declare function captureRawTree(h: InjectedDomHelpers): RawCapture;
|
|
73
79
|
|
|
80
|
+
declare const TEXT_HIDDEN_TAGS: readonly string[];
|
|
81
|
+
|
|
74
82
|
/** DOM attribute carrying an element's `eN` ref (interactive elements). */
|
|
75
83
|
declare const REF_ATTRIBUTE = "data-unotest-ref";
|
|
76
84
|
/** <html> attribute persisting the ref counter across captures. */
|
|
@@ -112,4 +120,4 @@ declare function highlightByNodeIdScript(nodeIds: number[], color?: string): str
|
|
|
112
120
|
/** Evaluate-ready script removing the highlight overlay, if any. */
|
|
113
121
|
declare function clearHighlightScript(): string;
|
|
114
122
|
|
|
115
|
-
export { type DomHelperOptions, type InjectedDomHelpers, type InjectedScriptOptions, NODE_REGISTRY_GLOBAL, REF_ATTRIBUTE, REF_COUNTER_ATTRIBUTE, RawCapture, captureRawTree, clearHighlightScript, composeInjectedScript, createDomHelpers, domWalkerScript, highlightByNodeIdScript, materializeRefScript, nodeByIdScript };
|
|
123
|
+
export { type DomHelperOptions, type InjectedDomHelpers, type InjectedScriptOptions, NODE_REGISTRY_GLOBAL, REF_ATTRIBUTE, REF_COUNTER_ATTRIBUTE, RawCapture, TEXT_HIDDEN_TAGS, captureRawTree, clearHighlightScript, composeInjectedScript, createDomHelpers, domWalkerScript, highlightByNodeIdScript, materializeRefScript, nodeByIdScript };
|
package/dist/dom-walker/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/dom-walker/text-rules.ts
|
|
2
|
+
var TEXT_HIDDEN_TAGS = ["noscript", "script", "style", "template", "noembed", "noframes"];
|
|
3
|
+
|
|
1
4
|
// src/dom-walker/injected-helpers.ts
|
|
2
5
|
function createDomHelpers(opts) {
|
|
3
6
|
const MAX_NAME = opts && opts.maxName || 80;
|
|
@@ -51,10 +54,23 @@ function createDomHelpers(opts) {
|
|
|
51
54
|
function cssEscape(s) {
|
|
52
55
|
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
53
56
|
}
|
|
57
|
+
const HIDDEN_TEXT_TAGS = new Set(opts && opts.hiddenTextTags || []);
|
|
58
|
+
function isHiddenText(el) {
|
|
59
|
+
return HIDDEN_TEXT_TAGS.has(el.tagName.toLowerCase());
|
|
60
|
+
}
|
|
61
|
+
function textWithoutHidden(node) {
|
|
62
|
+
if (node.nodeType === 1 && isHiddenText(node)) return "";
|
|
63
|
+
let out = "";
|
|
64
|
+
for (const child of Array.from(node.childNodes)) {
|
|
65
|
+
if (child.nodeType === 3) out += child.nodeValue ?? "";
|
|
66
|
+
else if (child.nodeType === 1) out += textWithoutHidden(child);
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
54
70
|
function textContentVisible(el) {
|
|
55
71
|
const ht = el;
|
|
56
|
-
if (typeof ht.innerText === "string") return ht.innerText;
|
|
57
|
-
return el
|
|
72
|
+
if (typeof ht.innerText === "string" && ht.checkVisibility?.() === true) return ht.innerText;
|
|
73
|
+
return textWithoutHidden(el);
|
|
58
74
|
}
|
|
59
75
|
const INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
|
|
60
76
|
"button",
|
|
@@ -457,6 +473,8 @@ function createDomHelpers(opts) {
|
|
|
457
473
|
cap,
|
|
458
474
|
cssEscape,
|
|
459
475
|
textContentVisible,
|
|
476
|
+
isHiddenText,
|
|
477
|
+
textWithoutHidden,
|
|
460
478
|
interactiveRoles: INTERACTIVE_ROLES,
|
|
461
479
|
formFieldRole,
|
|
462
480
|
implicitRole,
|
|
@@ -483,7 +501,7 @@ function createDomHelpers(opts) {
|
|
|
483
501
|
};
|
|
484
502
|
}
|
|
485
503
|
function composeInjectedScript(algorithm, opts = {}) {
|
|
486
|
-
const helperOpts = JSON.stringify(opts.helperOpts ?? {});
|
|
504
|
+
const helperOpts = JSON.stringify({ hiddenTextTags: TEXT_HIDDEN_TAGS, ...opts.helperOpts ?? {} });
|
|
487
505
|
const args = ["__h", ...opts.argLiterals ?? []].join(", ");
|
|
488
506
|
return `(() => { var __name = (fn) => fn; var __h = (${createDomHelpers.toString()})(${helperOpts}); return (${algorithm.toString()})(${args}); })()`;
|
|
489
507
|
}
|
|
@@ -605,7 +623,7 @@ function captureRawTree(h) {
|
|
|
605
623
|
if (n.nodeType !== 1) return;
|
|
606
624
|
const el = n;
|
|
607
625
|
const tag = el.tagName.toLowerCase();
|
|
608
|
-
if (
|
|
626
|
+
if (h.isHiddenText(el)) return;
|
|
609
627
|
if (tag === "svg") return;
|
|
610
628
|
if (!h.isVisible(el)) return;
|
|
611
629
|
bucket.push(buildElement(el));
|
|
@@ -712,6 +730,7 @@ export {
|
|
|
712
730
|
NODE_REGISTRY_GLOBAL,
|
|
713
731
|
REF_ATTRIBUTE,
|
|
714
732
|
REF_COUNTER_ATTRIBUTE,
|
|
733
|
+
TEXT_HIDDEN_TAGS,
|
|
715
734
|
captureRawTree,
|
|
716
735
|
clearHighlightScript,
|
|
717
736
|
composeInjectedScript,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unotest/grounder-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "Client contract for the unotest semantic UI grounder: typed intent/resolution types, the injectable DOM walker (RawCapture), the content hash, and the HTTP client + wire protocol of the grounding service. The grounding engine itself is a private service; this package is everything a consumer needs to capture a page and talk to it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|