@anweat/dsh-browser 0.1.15 → 0.2.0-rc.1
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 +272 -75
- package/assets/skills/dsh-browser/SKILL.md +120 -0
- package/assets/skills/dsh-browser/references/develop.md +48 -0
- package/assets/skills/dsh-browser/references/observe.md +91 -0
- package/assets/skills/dsh-browser/references/opencli.md +25 -0
- package/assets/skills/dsh-browser/references/recipes.md +149 -0
- package/assets/skills/dsh-browser/references/userscripts.md +44 -0
- package/cordis.patch.yml +4 -0
- package/lib/actions/act.d.ts +3 -0
- package/lib/actions/act.js +147 -0
- package/lib/actions/act.js.map +1 -0
- package/lib/actions/automation.d.ts +3 -0
- package/lib/actions/automation.js +356 -0
- package/lib/actions/automation.js.map +1 -0
- package/lib/actions/crawl.d.ts +3 -0
- package/lib/actions/crawl.js +28 -0
- package/lib/actions/crawl.js.map +1 -0
- package/lib/actions/errors.d.ts +53 -0
- package/lib/actions/errors.js +178 -0
- package/lib/actions/errors.js.map +1 -0
- package/lib/actions/index-view.d.ts +42 -0
- package/lib/actions/index-view.js +268 -0
- package/lib/actions/index-view.js.map +1 -0
- package/lib/actions/inspect.d.ts +3 -0
- package/lib/actions/inspect.js +35 -0
- package/lib/actions/inspect.js.map +1 -0
- package/lib/actions/observe.d.ts +3 -0
- package/lib/actions/observe.js +121 -0
- package/lib/actions/observe.js.map +1 -0
- package/lib/actions/opencli.d.ts +3 -0
- package/lib/actions/opencli.js +51 -0
- package/lib/actions/opencli.js.map +1 -0
- package/lib/actions/registry.d.ts +35 -0
- package/lib/actions/registry.js +89 -0
- package/lib/actions/registry.js.map +1 -0
- package/lib/actions/run.d.ts +34 -0
- package/lib/actions/run.js +226 -0
- package/lib/actions/run.js.map +1 -0
- package/lib/actions/runtime.d.ts +3 -0
- package/lib/actions/runtime.js +33 -0
- package/lib/actions/runtime.js.map +1 -0
- package/lib/actions/schema.d.ts +31 -0
- package/lib/actions/schema.js +309 -0
- package/lib/actions/schema.js.map +1 -0
- package/lib/actions/script.d.ts +3 -0
- package/lib/actions/script.js +97 -0
- package/lib/actions/script.js.map +1 -0
- package/lib/actions/shared.d.ts +31 -0
- package/lib/actions/shared.js +45 -0
- package/lib/actions/shared.js.map +1 -0
- package/lib/actions/surface.d.ts +22 -0
- package/lib/actions/surface.js +18 -0
- package/lib/actions/surface.js.map +1 -0
- package/lib/actions/target.d.ts +3 -0
- package/lib/actions/target.js +66 -0
- package/lib/actions/target.js.map +1 -0
- package/lib/actions/types.d.ts +181 -0
- package/lib/actions/types.js +48 -0
- package/lib/actions/types.js.map +1 -0
- package/lib/activation-rules.d.ts +29 -0
- package/lib/activation-rules.js +33 -0
- package/lib/activation-rules.js.map +1 -0
- package/lib/approval-policy.d.ts +40 -2
- package/lib/approval-policy.js +184 -97
- package/lib/approval-policy.js.map +1 -1
- package/lib/automation-assets-rpc.d.ts +8 -2
- package/lib/automation-assets-rpc.js +67 -6
- package/lib/automation-assets-rpc.js.map +1 -1
- package/lib/automation-assets.d.ts +152 -4
- package/lib/automation-assets.js +255 -15
- package/lib/automation-assets.js.map +1 -1
- package/lib/automation-convert.d.ts +23 -0
- package/lib/automation-convert.js +110 -0
- package/lib/automation-convert.js.map +1 -0
- package/lib/automation-development.d.ts +26 -0
- package/lib/automation-development.js +47 -0
- package/lib/automation-development.js.map +1 -1
- package/lib/automation-execution.d.ts +57 -1
- package/lib/automation-execution.js +167 -11
- package/lib/automation-execution.js.map +1 -1
- package/lib/automation-journal.d.ts +92 -0
- package/lib/automation-journal.js +449 -0
- package/lib/automation-journal.js.map +1 -0
- package/lib/automation-v2.d.ts +138 -0
- package/lib/automation-v2.js +631 -0
- package/lib/automation-v2.js.map +1 -0
- package/lib/automation.d.ts +82 -5
- package/lib/automation.js +239 -82
- package/lib/automation.js.map +1 -1
- package/lib/browser-service.d.ts +304 -32
- package/lib/browser-service.js +631 -182
- package/lib/browser-service.js.map +1 -1
- package/lib/client/SettingsCard.js +95 -57
- package/lib/client/SettingsCard.js.map +1 -1
- package/lib/client/asset-editor.d.ts +55 -0
- package/lib/client/asset-editor.js +101 -0
- package/lib/client/asset-editor.js.map +1 -0
- package/lib/client/automation-assets-client.d.ts +99 -2
- package/lib/client/automation-assets-client.js +243 -10
- package/lib/client/automation-assets-client.js.map +1 -1
- package/lib/client/form.d.ts +48 -4
- package/lib/client/form.js +142 -29
- package/lib/client/form.js.map +1 -1
- package/lib/client/index.d.ts +25 -2
- package/lib/client/index.js +5 -1
- package/lib/client/index.js.map +1 -1
- package/lib/client/locales.d.ts +85 -6
- package/lib/client/locales.js +64 -12
- package/lib/client/locales.js.map +1 -1
- package/lib/client/prompts-form.d.ts +67 -0
- package/lib/client/prompts-form.js +140 -0
- package/lib/client/prompts-form.js.map +1 -0
- package/lib/client/prompts-status-client.d.ts +34 -0
- package/lib/client/prompts-status-client.js +53 -0
- package/lib/client/prompts-status-client.js.map +1 -0
- package/lib/client.js +1914 -329
- package/lib/client.js.map +1 -1
- package/lib/config.d.ts +50 -1
- package/lib/config.js +31 -1
- package/lib/config.js.map +1 -1
- package/lib/dialogs.d.ts +37 -0
- package/lib/dialogs.js +73 -0
- package/lib/dialogs.js.map +1 -0
- package/lib/freedom.d.ts +23 -7
- package/lib/freedom.js +48 -59
- package/lib/freedom.js.map +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.js +36 -10
- package/lib/index.js.map +1 -1
- package/lib/journal.d.ts +109 -0
- package/lib/journal.js +238 -0
- package/lib/journal.js.map +1 -0
- package/lib/locator.d.ts +82 -0
- package/lib/locator.js +190 -0
- package/lib/locator.js.map +1 -0
- package/lib/observe-script.d.ts +27 -0
- package/lib/observe-script.js +565 -0
- package/lib/observe-script.js.map +1 -0
- package/lib/observe.d.ts +179 -0
- package/lib/observe.js +368 -0
- package/lib/observe.js.map +1 -0
- package/lib/policy-keys.d.ts +30 -0
- package/lib/policy-keys.js +34 -0
- package/lib/policy-keys.js.map +1 -0
- package/lib/prompt-limits.d.ts +30 -0
- package/lib/prompt-limits.js +31 -0
- package/lib/prompt-limits.js.map +1 -0
- package/lib/prompts.d.ts +131 -0
- package/lib/prompts.js +295 -0
- package/lib/prompts.js.map +1 -0
- package/lib/skill.d.ts +100 -0
- package/lib/skill.js +181 -0
- package/lib/skill.js.map +1 -0
- package/lib/tool-defs.d.ts +72 -0
- package/lib/tool-defs.js +67 -0
- package/lib/tool-defs.js.map +1 -0
- package/lib/tools.d.ts +19 -4
- package/lib/tools.js +124 -922
- package/lib/tools.js.map +1 -1
- package/package.json +35 -30
- package/scripts/dump-prompts.mjs +29 -0
- package/scripts/measure-tool-surface.mjs +190 -0
package/lib/locator.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One locator contract for atomic actions and v2 recipe steps.
|
|
3
|
+
*
|
|
4
|
+
* A locator is strict by default: it must match exactly one element. When it
|
|
5
|
+
* matches several, the step is not performed and the caller gets
|
|
6
|
+
* {@link LocatorAmbiguousError} with a short summary of the first candidates, so
|
|
7
|
+
* the model can tell which element it meant instead of acting on whichever one
|
|
8
|
+
* happened to come first. Choosing one anyway takes an explicit `index` with an
|
|
9
|
+
* `indexReason`; `explicitFirst` is the marker a v1 migration leaves behind for
|
|
10
|
+
* "this used to be `.first()`".
|
|
11
|
+
* @module dsh-browser/locator
|
|
12
|
+
*/
|
|
13
|
+
export interface BrowserFrameSpec {
|
|
14
|
+
selector?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
url?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface BrowserLocatorSpec {
|
|
19
|
+
/** CSS selector. `css` is accepted as an alias (the v2 recipe spelling). */
|
|
20
|
+
selector?: string;
|
|
21
|
+
css?: string;
|
|
22
|
+
role?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
text?: string;
|
|
25
|
+
label?: string;
|
|
26
|
+
testId?: string;
|
|
27
|
+
exact?: boolean;
|
|
28
|
+
/** Atomic-action iframe: exactly one of selector, name, url. */
|
|
29
|
+
frame?: BrowserFrameSpec;
|
|
30
|
+
/** Nested iframes, outermost first, each a CSS selector for an iframe element. */
|
|
31
|
+
framePath?: string[];
|
|
32
|
+
/** Pick the n-th match (0-based). Requires `indexReason`. */
|
|
33
|
+
index?: number;
|
|
34
|
+
indexReason?: string;
|
|
35
|
+
/** Take the first match without an ambiguity error. Set by the v1 converter; meant to be resolved later. */
|
|
36
|
+
explicitFirst?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export type BrowserTarget = string | BrowserLocatorSpec;
|
|
39
|
+
/** What one of several matches looks like, enough to choose between them. */
|
|
40
|
+
export interface LocatorCandidate {
|
|
41
|
+
index: number;
|
|
42
|
+
role: string;
|
|
43
|
+
name: string;
|
|
44
|
+
text: string;
|
|
45
|
+
visible: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface LocatorAmbiguity {
|
|
48
|
+
total: number;
|
|
49
|
+
items: LocatorCandidate[];
|
|
50
|
+
}
|
|
51
|
+
/** Raised instead of acting when a strict locator matches more than one element. */
|
|
52
|
+
export declare class LocatorAmbiguousError extends Error {
|
|
53
|
+
readonly ambiguity: LocatorAmbiguity;
|
|
54
|
+
constructor(message: string, ambiguity: LocatorAmbiguity);
|
|
55
|
+
}
|
|
56
|
+
/** Where a page stands now: what a stale caller needs to re-bind to it. */
|
|
57
|
+
export interface PageGeneration {
|
|
58
|
+
targetId: string;
|
|
59
|
+
generation: number;
|
|
60
|
+
}
|
|
61
|
+
/** Raised, before anything is done, when `expectGeneration` is not the page's current generation. */
|
|
62
|
+
export declare class TargetStaleError extends Error {
|
|
63
|
+
readonly current: PageGeneration;
|
|
64
|
+
constructor(message: string, current: PageGeneration);
|
|
65
|
+
}
|
|
66
|
+
export declare const MAX_CANDIDATES = 5;
|
|
67
|
+
/** Check a locator's shape. Throws a message the model can act on; used before resolving and when a recipe is saved. */
|
|
68
|
+
export declare function validateLocatorSpec(spec: unknown, label?: string): BrowserLocatorSpec;
|
|
69
|
+
/**
|
|
70
|
+
* Build the Playwright locator for a target. No `.first()` unless the spec asks
|
|
71
|
+
* for it, so Playwright's own strict-mode check stays in force.
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolveLocator(page: any, target: BrowserTarget): any;
|
|
74
|
+
/** The first candidates of a locator, best effort: a failure here must not hide the ambiguity itself. */
|
|
75
|
+
export declare function describeCandidates(locator: any): Promise<LocatorAmbiguity>;
|
|
76
|
+
export declare function formatCandidates(ambiguity: LocatorAmbiguity): string;
|
|
77
|
+
/**
|
|
78
|
+
* Run an action on a strict locator. If the locator turns out to match several
|
|
79
|
+
* elements, nothing was acted on (Playwright checks before it acts) and the
|
|
80
|
+
* error is upgraded with the candidate summary.
|
|
81
|
+
*/
|
|
82
|
+
export declare function withStrictLocator<T>(locator: any, run: (locator: any) => Promise<T>): Promise<T>;
|
package/lib/locator.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One locator contract for atomic actions and v2 recipe steps.
|
|
3
|
+
*
|
|
4
|
+
* A locator is strict by default: it must match exactly one element. When it
|
|
5
|
+
* matches several, the step is not performed and the caller gets
|
|
6
|
+
* {@link LocatorAmbiguousError} with a short summary of the first candidates, so
|
|
7
|
+
* the model can tell which element it meant instead of acting on whichever one
|
|
8
|
+
* happened to come first. Choosing one anyway takes an explicit `index` with an
|
|
9
|
+
* `indexReason`; `explicitFirst` is the marker a v1 migration leaves behind for
|
|
10
|
+
* "this used to be `.first()`".
|
|
11
|
+
* @module dsh-browser/locator
|
|
12
|
+
*/
|
|
13
|
+
/** Raised instead of acting when a strict locator matches more than one element. */
|
|
14
|
+
export class LocatorAmbiguousError extends Error {
|
|
15
|
+
ambiguity;
|
|
16
|
+
constructor(message, ambiguity) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.ambiguity = ambiguity;
|
|
19
|
+
this.name = 'LocatorAmbiguousError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Raised, before anything is done, when `expectGeneration` is not the page's current generation. */
|
|
23
|
+
export class TargetStaleError extends Error {
|
|
24
|
+
current;
|
|
25
|
+
constructor(message, current) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.current = current;
|
|
28
|
+
this.name = 'TargetStaleError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const MODES = ['selector', 'css', 'role', 'text', 'label', 'testId'];
|
|
32
|
+
const KNOWN_KEYS = new Set([...MODES, 'name', 'exact', 'frame', 'framePath', 'index', 'indexReason', 'explicitFirst']);
|
|
33
|
+
const MAX_FRAME_PATH = 5;
|
|
34
|
+
export const MAX_CANDIDATES = 5;
|
|
35
|
+
function bounded(value, label, max) {
|
|
36
|
+
if (typeof value !== 'string' || value.length < 1 || value.length > max)
|
|
37
|
+
throw new Error(`${label} must contain 1 to ${max} characters`);
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
/** Check a locator's shape. Throws a message the model can act on; used before resolving and when a recipe is saved. */
|
|
41
|
+
export function validateLocatorSpec(spec, label = 'browser target') {
|
|
42
|
+
if (!spec || typeof spec !== 'object' || Array.isArray(spec))
|
|
43
|
+
throw new Error(`${label} must be a selector string or locator object`);
|
|
44
|
+
const value = spec;
|
|
45
|
+
for (const key of Object.keys(value))
|
|
46
|
+
if (!KNOWN_KEYS.has(key))
|
|
47
|
+
throw new Error(`${label} has an unsupported field "${key}"; use ${[...KNOWN_KEYS].join(', ')}`);
|
|
48
|
+
const modes = MODES.filter(mode => value[mode] !== undefined);
|
|
49
|
+
if (modes.length !== 1)
|
|
50
|
+
throw new Error(`${label} requires exactly one of selector (css), role, text, label, or testId`);
|
|
51
|
+
if (value.name !== undefined && value.role === undefined)
|
|
52
|
+
throw new Error(`${label} name is only valid with role`);
|
|
53
|
+
if (value.exact !== undefined && typeof value.exact !== 'boolean')
|
|
54
|
+
throw new Error(`${label} exact must be a boolean`);
|
|
55
|
+
if (value.index !== undefined) {
|
|
56
|
+
if (!Number.isInteger(value.index) || value.index < 0 || value.index > 1000)
|
|
57
|
+
throw new Error(`${label} index must be an integer from 0 to 1000`);
|
|
58
|
+
if (typeof value.indexReason !== 'string' || !value.indexReason.trim())
|
|
59
|
+
throw new Error(`${label} index requires indexReason: say why this match and not the others`);
|
|
60
|
+
}
|
|
61
|
+
if (value.indexReason !== undefined && value.index === undefined)
|
|
62
|
+
throw new Error(`${label} indexReason is only valid with index`);
|
|
63
|
+
if (value.index !== undefined && value.explicitFirst)
|
|
64
|
+
throw new Error(`${label} cannot combine index and explicitFirst`);
|
|
65
|
+
if (value.explicitFirst !== undefined && typeof value.explicitFirst !== 'boolean')
|
|
66
|
+
throw new Error(`${label} explicitFirst must be a boolean`);
|
|
67
|
+
if (value.frame !== undefined && value.framePath !== undefined)
|
|
68
|
+
throw new Error(`${label} cannot combine frame and framePath`);
|
|
69
|
+
if (value.framePath !== undefined) {
|
|
70
|
+
if (!Array.isArray(value.framePath) || value.framePath.length < 1 || value.framePath.length > MAX_FRAME_PATH)
|
|
71
|
+
throw new Error(`${label} framePath must list 1 to ${MAX_FRAME_PATH} iframe selectors`);
|
|
72
|
+
value.framePath.forEach(entry => bounded(entry, `${label} framePath entry`, 500));
|
|
73
|
+
}
|
|
74
|
+
if (value.frame !== undefined) {
|
|
75
|
+
const frameModes = [value.frame.selector, value.frame.name, value.frame.url].filter(entry => entry !== undefined);
|
|
76
|
+
if (frameModes.length !== 1)
|
|
77
|
+
throw new Error('browser frame requires exactly one of selector, name, or url');
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Build the Playwright locator for a target. No `.first()` unless the spec asks
|
|
83
|
+
* for it, so Playwright's own strict-mode check stays in force.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveLocator(page, target) {
|
|
86
|
+
if (!target || (typeof target !== 'string' && typeof target !== 'object'))
|
|
87
|
+
throw new Error('browser target must be a selector string or locator object');
|
|
88
|
+
const spec = validateLocatorSpec(typeof target === 'string' ? { selector: target } : target);
|
|
89
|
+
let root = page;
|
|
90
|
+
if (spec.frame) {
|
|
91
|
+
if (spec.frame.selector)
|
|
92
|
+
root = page.frameLocator(bounded(spec.frame.selector, 'frame selector', 500));
|
|
93
|
+
else {
|
|
94
|
+
const frame = page.frame(spec.frame.name
|
|
95
|
+
? { name: bounded(spec.frame.name, 'frame name', 500) }
|
|
96
|
+
: { url: bounded(spec.frame.url, 'frame url', 2_000) });
|
|
97
|
+
if (!frame)
|
|
98
|
+
throw new Error('browser target frame was not found');
|
|
99
|
+
root = frame;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const entry of spec.framePath ?? [])
|
|
103
|
+
root = root.frameLocator(entry);
|
|
104
|
+
const css = spec.selector ?? spec.css;
|
|
105
|
+
let locator;
|
|
106
|
+
if (css !== undefined)
|
|
107
|
+
locator = root.locator(bounded(css, 'selector', 500));
|
|
108
|
+
else if (spec.role !== undefined) {
|
|
109
|
+
locator = root.getByRole(bounded(spec.role, 'role', 100), {
|
|
110
|
+
...spec.name !== undefined ? { name: bounded(spec.name, 'role name', 2_000) } : {},
|
|
111
|
+
exact: spec.exact ?? false,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (spec.text !== undefined)
|
|
115
|
+
locator = root.getByText(bounded(spec.text, 'text locator', 2_000), { exact: spec.exact ?? false });
|
|
116
|
+
else if (spec.label !== undefined)
|
|
117
|
+
locator = root.getByLabel(bounded(spec.label, 'label locator', 2_000), { exact: spec.exact ?? false });
|
|
118
|
+
else
|
|
119
|
+
locator = root.getByTestId(bounded(spec.testId, 'testId', 500));
|
|
120
|
+
if (spec.index !== undefined)
|
|
121
|
+
return locator.nth(spec.index);
|
|
122
|
+
if (spec.explicitFirst)
|
|
123
|
+
return locator.first();
|
|
124
|
+
return locator;
|
|
125
|
+
}
|
|
126
|
+
/** Summarize the matches of an ambiguous locator, in the page. A raw string: it must not carry transpiler helpers. */
|
|
127
|
+
const SUMMARIZE = `(els) => {
|
|
128
|
+
const implicit = { a: 'link', button: 'button', select: 'combobox', textarea: 'textbox', img: 'img', ul: 'list', ol: 'list', li: 'listitem', nav: 'navigation', main: 'main', table: 'table', form: 'form', h1: 'heading', h2: 'heading', h3: 'heading', h4: 'heading', h5: 'heading', h6: 'heading', summary: 'button', dialog: 'dialog' }
|
|
129
|
+
const inputRole = { checkbox: 'checkbox', radio: 'radio', button: 'button', submit: 'button', reset: 'button', image: 'button', range: 'slider', search: 'searchbox', number: 'spinbutton' }
|
|
130
|
+
const named = ['button', 'link', 'heading', 'menuitem', 'tab', 'option', 'checkbox', 'radio', 'listitem']
|
|
131
|
+
return els.slice(0, ${MAX_CANDIDATES}).map((el, index) => {
|
|
132
|
+
const tag = el.tagName.toLowerCase()
|
|
133
|
+
let role = el.getAttribute('role') || ''
|
|
134
|
+
if (!role) role = tag === 'input' ? (inputRole[(el.getAttribute('type') || 'text').toLowerCase()] || 'textbox') : (implicit[tag] || '')
|
|
135
|
+
const text = String(el.innerText || el.textContent || el.value || '').trim().replace(/\\s+/g, ' ')
|
|
136
|
+
let name = el.getAttribute('aria-label') || ''
|
|
137
|
+
if (!name && el.labels && el.labels.length) name = el.labels[0].innerText || ''
|
|
138
|
+
if (!name) name = el.getAttribute('title') || el.getAttribute('placeholder') || el.getAttribute('alt') || ''
|
|
139
|
+
if (!name && named.includes(role)) name = text
|
|
140
|
+
const rect = el.getBoundingClientRect()
|
|
141
|
+
const style = getComputedStyle(el)
|
|
142
|
+
const visible = rect.width > 0 && rect.height > 0 && style.visibility !== 'hidden' && style.display !== 'none'
|
|
143
|
+
return { index, role: role || tag, name: String(name).trim().slice(0, 80), text: text.slice(0, 80), visible }
|
|
144
|
+
})
|
|
145
|
+
}`;
|
|
146
|
+
/** The first candidates of a locator, best effort: a failure here must not hide the ambiguity itself. */
|
|
147
|
+
export async function describeCandidates(locator) {
|
|
148
|
+
let total = 0;
|
|
149
|
+
let items = [];
|
|
150
|
+
try {
|
|
151
|
+
total = Number(await locator.count()) || 0;
|
|
152
|
+
}
|
|
153
|
+
catch { /* keep 0 */ }
|
|
154
|
+
try {
|
|
155
|
+
// Playwright treats a plain string as an expression, not a function; a Function built from the string serializes as one.
|
|
156
|
+
const rows = await locator.evaluateAll(new Function('els', `return (${SUMMARIZE})(els)`));
|
|
157
|
+
if (Array.isArray(rows))
|
|
158
|
+
items = rows.slice(0, MAX_CANDIDATES).map((row, index) => ({
|
|
159
|
+
index: typeof row?.index === 'number' ? row.index : index,
|
|
160
|
+
role: String(row?.role ?? ''), name: String(row?.name ?? ''), text: String(row?.text ?? ''), visible: row?.visible !== false,
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
catch { /* the summary is optional */ }
|
|
164
|
+
return { total: Math.max(total, items.length), items };
|
|
165
|
+
}
|
|
166
|
+
export function formatCandidates(ambiguity) {
|
|
167
|
+
const rows = ambiguity.items.map(item => `#${item.index} ${item.role}${item.name ? ' ' + JSON.stringify(item.name) : ''}${item.text && item.text !== item.name ? ' text ' + JSON.stringify(item.text) : ''} ${item.visible ? 'visible' : 'hidden'}`);
|
|
168
|
+
return rows.join('; ') + (ambiguity.total > ambiguity.items.length ? `; …${ambiguity.total - ambiguity.items.length} more` : '');
|
|
169
|
+
}
|
|
170
|
+
function isStrictViolation(error) {
|
|
171
|
+
return !(error instanceof LocatorAmbiguousError) && /strict mode violation/i.test(error instanceof Error ? error.message : String(error));
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Run an action on a strict locator. If the locator turns out to match several
|
|
175
|
+
* elements, nothing was acted on (Playwright checks before it acts) and the
|
|
176
|
+
* error is upgraded with the candidate summary.
|
|
177
|
+
*/
|
|
178
|
+
export async function withStrictLocator(locator, run) {
|
|
179
|
+
try {
|
|
180
|
+
return await run(locator);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
if (!isStrictViolation(error))
|
|
184
|
+
throw error;
|
|
185
|
+
const ambiguity = await describeCandidates(locator);
|
|
186
|
+
const first = (error instanceof Error ? error.message : String(error)).split('\n')[0];
|
|
187
|
+
throw new LocatorAmbiguousError(`Locator matched ${ambiguity.total || 'more than one'} elements, so nothing was done. Candidates: ${formatCandidates(ambiguity) || '(unavailable)'}. Make the locator unique (role+name, exact, label, testId, frame), or pass index with indexReason. [${first}]`, ambiguity);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=locator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator.js","sourceRoot":"","sources":["../src/locator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The in-page half of observe.read: one function, shipped to the page as source text.
|
|
3
|
+
*
|
|
4
|
+
* It is a raw string on purpose: it runs inside the page, so it must not carry transpiler helpers, and
|
|
5
|
+
* it must not use template literals (the string is written with String.raw). It reads the DOM and
|
|
6
|
+
* returns plain data; it never changes the page. The only thing it leaves behind is a WeakMap stored
|
|
7
|
+
* under a registered symbol on each scanned window, which maps each listed element to its number, so
|
|
8
|
+
* that the Node side can later prove that a candidate locator points at that very element.
|
|
9
|
+
*
|
|
10
|
+
* Coverage: the document, open shadow roots (recursively), and same-origin iframes (recursively, up to
|
|
11
|
+
* the frame-path limit). A cross-origin or sandboxed iframe is listed as a frame with its limitation,
|
|
12
|
+
* never entered. Closed shadow roots cannot be seen from page script and are not covered.
|
|
13
|
+
* @module dsh-browser/observe-script
|
|
14
|
+
*/
|
|
15
|
+
/** Symbol (registered with Symbol.for) under which each scanned window keeps its element-number map. */
|
|
16
|
+
export declare const OBSERVE_SYMBOL = "dsh.observe";
|
|
17
|
+
/** Maximum iframe nesting the scan enters; matches the locator framePath limit. */
|
|
18
|
+
export declare const OBSERVE_MAX_FRAME_DEPTH = 5;
|
|
19
|
+
/** Maximum number of iframes entered in one observation. */
|
|
20
|
+
export declare const OBSERVE_MAX_FRAMES = 20;
|
|
21
|
+
/** Reads, in the element's own window, the numbers of the elements a locator matched. */
|
|
22
|
+
export declare const IDENTITY_SOURCE: string;
|
|
23
|
+
/**
|
|
24
|
+
* `(root, opts) => result`. `root` is a Document or the Element an observation is scoped to.
|
|
25
|
+
* opts: { controls, links, tables, maxItems, includeValues, region?, maxFrameDepth, maxFrames }.
|
|
26
|
+
*/
|
|
27
|
+
export declare const OBSERVE_SOURCE: string;
|