dsh-builtin-browser 0.4.1 → 0.4.5
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 +1699 -0
- package/README.en.md +114 -33
- package/README.md +108 -32
- package/client.js +478 -29
- package/cordis.patch.yml +21 -0
- package/desktop-bridge/install.mjs +66 -6
- package/desktop-bridge/plugin-browser-bridge.js +426 -167
- package/docs/architecture.md +14 -4
- package/docs/tool-reference.md +13 -13
- package/docs/user-guide.md +58 -3
- package/docs/why-browser.md +1 -1
- package/lib/browser/types.d.ts +34 -3
- package/lib/browser-electron/bridge-connection.d.ts +49 -26
- package/lib/browser-electron/bridge-connection.js +185 -22
- package/lib/browser-electron/desktop-bridge-host.d.ts +69 -36
- package/lib/browser-electron/desktop-bridge-host.js +151 -86
- package/lib/browser-electron/entry.d.ts +1 -1
- package/lib/browser-electron/entry.js +133 -40
- package/lib/browser-electron/ephemeral-profile.d.ts +37 -0
- package/lib/browser-electron/ephemeral-profile.js +146 -0
- package/lib/browser-electron/history-store.d.ts +36 -2
- package/lib/browser-electron/history-store.js +76 -7
- package/lib/browser-electron/host-main.js +86 -28
- package/lib/browser-electron/provider.d.ts +106 -3
- package/lib/browser-electron/provider.js +407 -60
- package/lib/browser-electron/remote-host.d.ts +52 -3
- package/lib/browser-electron/remote-host.js +259 -25
- package/lib/browser-electron/rpc-ops.d.ts +18 -0
- package/lib/browser-electron/rpc-ops.js +36 -0
- package/lib/browser-electron/settings-route.d.ts +28 -0
- package/lib/browser-electron/settings-route.js +68 -0
- package/lib/browser-electron/settings-store.d.ts +40 -0
- package/lib/browser-electron/settings-store.js +160 -23
- package/lib/browser-electron/system-browser.d.ts +102 -12
- package/lib/browser-electron/system-browser.js +411 -51
- package/lib/index.js +0 -1
- package/lib/tool-browser/element-fields.d.ts +145 -0
- package/lib/tool-browser/element-fields.js +152 -0
- package/lib/tool-browser/index.js +149 -74
- package/package.json +10 -4
- package/scripts/verify-host-compat.mjs +195 -0
- package/src/browser/types.ts +34 -3
- package/src/browser-electron/bridge-connection.ts +179 -22
- package/src/browser-electron/desktop-bridge-host.ts +146 -84
- package/src/browser-electron/entry.ts +132 -39
- package/src/browser-electron/ephemeral-profile.ts +152 -0
- package/src/browser-electron/history-store.ts +81 -9
- package/src/browser-electron/host-main.ts +115 -36
- package/src/browser-electron/provider.ts +492 -69
- package/src/browser-electron/remote-host.ts +276 -38
- package/src/browser-electron/rpc-ops.ts +41 -0
- package/src/browser-electron/settings-route.ts +87 -0
- package/src/browser-electron/settings-store.ts +173 -24
- package/src/browser-electron/system-browser.ts +408 -39
- package/src/index.ts +3 -2
- package/src/tool-browser/element-fields.ts +167 -0
- package/src/tool-browser/index.ts +155 -76
package/lib/index.js
CHANGED
|
@@ -22,7 +22,6 @@ export const name = 'dsh-builtin-browser';
|
|
|
22
22
|
* never reaches the browser (the plugin works, its client half is invisible).
|
|
23
23
|
*/
|
|
24
24
|
export function apply() {
|
|
25
|
-
// Intentionally empty: see the module comment above.
|
|
26
25
|
}
|
|
27
26
|
export { BrowserError } from './browser/types.js';
|
|
28
27
|
export { BrowserRuntime } from './browser/runtime.js';
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The element and node field sets, declared once.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS FILE EXISTS
|
|
5
|
+
*
|
|
6
|
+
* The same field set used to be written out in four places: the page script produced an
|
|
7
|
+
* object, `types.ts` declared its shape, a tool's `execute` rebuilt it as an explicit list,
|
|
8
|
+
* and the tool's `output.schema` declared the properties. Nothing connected them, and the
|
|
9
|
+
* result is this project's most repeatable bug:
|
|
10
|
+
*
|
|
11
|
+
* - `browser_a11y`'s `selector` — computed by the provider, declared by the schema,
|
|
12
|
+
* printed by the renderer, and dropped by the projection. 134 tests stayed green.
|
|
13
|
+
* - `browser_screenshot`'s `width`/`height` — measured by the provider, declared by the
|
|
14
|
+
* schema, and dropped by the projection. 172 tests stayed green. It was found a round
|
|
15
|
+
* later, by reading the three places that agree and noticing the fourth did not.
|
|
16
|
+
*
|
|
17
|
+
* A hand-written allow-list is invisible to the type checker (it is an object literal) and to
|
|
18
|
+
* the tests (nothing ran a tool). The fix is not a careful list; it is one list. Both the
|
|
19
|
+
* schema fragment and the projection are derived from the tables below, so a field cannot be
|
|
20
|
+
* present in one and absent from the other.
|
|
21
|
+
*
|
|
22
|
+
* The schema is the dangerous half: `additionalProperties: false` means a field the schema
|
|
23
|
+
* does NOT declare causes the ENTIRE result to be rejected at run time, while a field the
|
|
24
|
+
* projection forgets is silently dropped. One table cannot get either wrong on its own.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* The `elements` item schema for browser_snapshot and browser_open.
|
|
28
|
+
*
|
|
29
|
+
* Written as a literal rather than generated, because `defineTool` validates the output schema
|
|
30
|
+
* against its own `ValueSchemaSpec` and a generated `Record<string, …>` is too wide to satisfy
|
|
31
|
+
* it. The field table above remains the single source of truth: `tests/element-fields.test.mjs`
|
|
32
|
+
* asserts that these properties and `projectElement`'s output are exactly the table's keys, so
|
|
33
|
+
* the two cannot drift even though one is typed by hand.
|
|
34
|
+
*/
|
|
35
|
+
export declare const ELEMENT_ITEM_SCHEMA: {
|
|
36
|
+
readonly type: "object";
|
|
37
|
+
readonly additionalProperties: false;
|
|
38
|
+
readonly properties: {
|
|
39
|
+
readonly ref: {
|
|
40
|
+
readonly type: "number";
|
|
41
|
+
readonly required: true;
|
|
42
|
+
};
|
|
43
|
+
readonly kind: {
|
|
44
|
+
readonly type: "string";
|
|
45
|
+
readonly required: true;
|
|
46
|
+
};
|
|
47
|
+
readonly label: {
|
|
48
|
+
readonly type: "string";
|
|
49
|
+
readonly required: true;
|
|
50
|
+
};
|
|
51
|
+
readonly selector: {
|
|
52
|
+
readonly type: "string";
|
|
53
|
+
};
|
|
54
|
+
readonly x: {
|
|
55
|
+
readonly type: "number";
|
|
56
|
+
readonly required: true;
|
|
57
|
+
};
|
|
58
|
+
readonly y: {
|
|
59
|
+
readonly type: "number";
|
|
60
|
+
readonly required: true;
|
|
61
|
+
};
|
|
62
|
+
readonly frame: {
|
|
63
|
+
readonly type: "boolean";
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* The `nodes` item schema for browser_a11y.
|
|
69
|
+
*
|
|
70
|
+
* Same reasoning as {@link ELEMENT_ITEM_SCHEMA}: literal for the type checker, table-backed by
|
|
71
|
+
* test.
|
|
72
|
+
*/
|
|
73
|
+
export declare const NODE_ITEM_SCHEMA: {
|
|
74
|
+
readonly type: "object";
|
|
75
|
+
readonly additionalProperties: false;
|
|
76
|
+
readonly properties: {
|
|
77
|
+
readonly ref: {
|
|
78
|
+
readonly type: "number";
|
|
79
|
+
readonly required: true;
|
|
80
|
+
};
|
|
81
|
+
readonly role: {
|
|
82
|
+
readonly type: "string";
|
|
83
|
+
readonly required: true;
|
|
84
|
+
};
|
|
85
|
+
readonly name: {
|
|
86
|
+
readonly type: "string";
|
|
87
|
+
readonly required: true;
|
|
88
|
+
};
|
|
89
|
+
readonly value: {
|
|
90
|
+
readonly type: "string";
|
|
91
|
+
};
|
|
92
|
+
readonly states: {
|
|
93
|
+
readonly type: "array";
|
|
94
|
+
readonly required: true;
|
|
95
|
+
readonly items: {
|
|
96
|
+
readonly type: "string";
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
readonly depth: {
|
|
100
|
+
readonly type: "number";
|
|
101
|
+
readonly required: true;
|
|
102
|
+
};
|
|
103
|
+
readonly tag: {
|
|
104
|
+
readonly type: "string";
|
|
105
|
+
readonly required: true;
|
|
106
|
+
};
|
|
107
|
+
readonly selector: {
|
|
108
|
+
readonly type: "string";
|
|
109
|
+
readonly required: true;
|
|
110
|
+
};
|
|
111
|
+
readonly x: {
|
|
112
|
+
readonly type: "number";
|
|
113
|
+
readonly required: true;
|
|
114
|
+
};
|
|
115
|
+
readonly y: {
|
|
116
|
+
readonly type: "number";
|
|
117
|
+
readonly required: true;
|
|
118
|
+
};
|
|
119
|
+
readonly frame: {
|
|
120
|
+
readonly type: "boolean";
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Project one snapshot element into the shape the schema declares.
|
|
126
|
+
*
|
|
127
|
+
* Generic in the source type so the caller keeps the precise shape `defineTool` inferred from
|
|
128
|
+
* the schema: the projection decides which fields survive at run time, and the field table is
|
|
129
|
+
* what guarantees that set matches the schema. Widening the return type here would only move
|
|
130
|
+
* the mismatch to the call site.
|
|
131
|
+
* @param element - the element as the provider produced it.
|
|
132
|
+
* @returns exactly the declared fields.
|
|
133
|
+
*/
|
|
134
|
+
export declare function projectElement<T extends object>(element: T): T;
|
|
135
|
+
/**
|
|
136
|
+
* Project one accessibility node into the shape the schema declares.
|
|
137
|
+
* @param node - the node as the provider produced it.
|
|
138
|
+
* @returns exactly the declared fields.
|
|
139
|
+
*/
|
|
140
|
+
export declare function projectNode<T extends object>(node: T): T;
|
|
141
|
+
/** The field names each table declares, for tests that check the two stay in step. */
|
|
142
|
+
export declare const DECLARED_FIELDS: {
|
|
143
|
+
readonly element: string[];
|
|
144
|
+
readonly node: string[];
|
|
145
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The element and node field sets, declared once.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS FILE EXISTS
|
|
5
|
+
*
|
|
6
|
+
* The same field set used to be written out in four places: the page script produced an
|
|
7
|
+
* object, `types.ts` declared its shape, a tool's `execute` rebuilt it as an explicit list,
|
|
8
|
+
* and the tool's `output.schema` declared the properties. Nothing connected them, and the
|
|
9
|
+
* result is this project's most repeatable bug:
|
|
10
|
+
*
|
|
11
|
+
* - `browser_a11y`'s `selector` — computed by the provider, declared by the schema,
|
|
12
|
+
* printed by the renderer, and dropped by the projection. 134 tests stayed green.
|
|
13
|
+
* - `browser_screenshot`'s `width`/`height` — measured by the provider, declared by the
|
|
14
|
+
* schema, and dropped by the projection. 172 tests stayed green. It was found a round
|
|
15
|
+
* later, by reading the three places that agree and noticing the fourth did not.
|
|
16
|
+
*
|
|
17
|
+
* A hand-written allow-list is invisible to the type checker (it is an object literal) and to
|
|
18
|
+
* the tests (nothing ran a tool). The fix is not a careful list; it is one list. Both the
|
|
19
|
+
* schema fragment and the projection are derived from the tables below, so a field cannot be
|
|
20
|
+
* present in one and absent from the other.
|
|
21
|
+
*
|
|
22
|
+
* The schema is the dangerous half: `additionalProperties: false` means a field the schema
|
|
23
|
+
* does NOT declare causes the ENTIRE result to be rejected at run time, while a field the
|
|
24
|
+
* projection forgets is silently dropped. One table cannot get either wrong on its own.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* The fields of one entry in `browser_snapshot` / `browser_open`'s `elements`.
|
|
28
|
+
*
|
|
29
|
+
* `selector` is the reason this file exists; see the header. It was added to the schema and
|
|
30
|
+
* the projection separately, and only one of them landed.
|
|
31
|
+
*/
|
|
32
|
+
const ELEMENT_FIELDS = {
|
|
33
|
+
ref: { schema: { type: 'number' }, required: true },
|
|
34
|
+
kind: { schema: { type: 'string' }, required: true },
|
|
35
|
+
label: { schema: { type: 'string' }, required: true },
|
|
36
|
+
selector: { schema: { type: 'string' } },
|
|
37
|
+
x: { schema: { type: 'number' }, required: true },
|
|
38
|
+
y: { schema: { type: 'number' }, required: true },
|
|
39
|
+
frame: { schema: { type: 'boolean' } },
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The fields of one entry in `browser_a11y`'s `nodes`.
|
|
43
|
+
*
|
|
44
|
+
* Different shape from an element on purpose: a role and an accessible name carry meaning a
|
|
45
|
+
* tag and a label string do not, and `depth` is what renders the tree.
|
|
46
|
+
*/
|
|
47
|
+
const NODE_FIELDS = {
|
|
48
|
+
ref: { schema: { type: 'number' }, required: true },
|
|
49
|
+
role: { schema: { type: 'string' }, required: true },
|
|
50
|
+
name: { schema: { type: 'string' }, required: true },
|
|
51
|
+
value: { schema: { type: 'string' } },
|
|
52
|
+
states: { schema: { type: 'array', items: { type: 'string' } }, required: true },
|
|
53
|
+
depth: { schema: { type: 'number' }, required: true },
|
|
54
|
+
tag: { schema: { type: 'string' }, required: true },
|
|
55
|
+
// Always present: the provider derives it for every node and yields an empty string when the
|
|
56
|
+
// element has neither an id nor a name. Declaring it optional described a shape the provider
|
|
57
|
+
// never produced, and the type said required — the same disagreement, one level down.
|
|
58
|
+
selector: { schema: { type: 'string' }, required: true },
|
|
59
|
+
x: { schema: { type: 'number' }, required: true },
|
|
60
|
+
y: { schema: { type: 'number' }, required: true },
|
|
61
|
+
frame: { schema: { type: 'boolean' } },
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Copy the declared fields out of a source value.
|
|
65
|
+
*
|
|
66
|
+
* Reads the table rather than naming fields, so anything the provider produces that the table
|
|
67
|
+
* declares is carried through, and anything the table does not declare is left out — which is
|
|
68
|
+
* what keeps the result and the schema in agreement in both directions.
|
|
69
|
+
* @param fields - the field table to project through.
|
|
70
|
+
* @param source - the value the provider produced.
|
|
71
|
+
* @returns a new object holding exactly the declared fields that are present.
|
|
72
|
+
*/
|
|
73
|
+
function project(fields, source) {
|
|
74
|
+
const out = {};
|
|
75
|
+
for (const name of Object.keys(fields)) {
|
|
76
|
+
const value = source[name];
|
|
77
|
+
if (value !== undefined && value !== null)
|
|
78
|
+
out[name] = value;
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The `elements` item schema for browser_snapshot and browser_open.
|
|
84
|
+
*
|
|
85
|
+
* Written as a literal rather than generated, because `defineTool` validates the output schema
|
|
86
|
+
* against its own `ValueSchemaSpec` and a generated `Record<string, …>` is too wide to satisfy
|
|
87
|
+
* it. The field table above remains the single source of truth: `tests/element-fields.test.mjs`
|
|
88
|
+
* asserts that these properties and `projectElement`'s output are exactly the table's keys, so
|
|
89
|
+
* the two cannot drift even though one is typed by hand.
|
|
90
|
+
*/
|
|
91
|
+
export const ELEMENT_ITEM_SCHEMA = {
|
|
92
|
+
type: 'object',
|
|
93
|
+
additionalProperties: false,
|
|
94
|
+
properties: {
|
|
95
|
+
ref: { type: 'number', required: true },
|
|
96
|
+
kind: { type: 'string', required: true },
|
|
97
|
+
label: { type: 'string', required: true },
|
|
98
|
+
selector: { type: 'string' },
|
|
99
|
+
x: { type: 'number', required: true },
|
|
100
|
+
y: { type: 'number', required: true },
|
|
101
|
+
frame: { type: 'boolean' },
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* The `nodes` item schema for browser_a11y.
|
|
106
|
+
*
|
|
107
|
+
* Same reasoning as {@link ELEMENT_ITEM_SCHEMA}: literal for the type checker, table-backed by
|
|
108
|
+
* test.
|
|
109
|
+
*/
|
|
110
|
+
export const NODE_ITEM_SCHEMA = {
|
|
111
|
+
type: 'object',
|
|
112
|
+
additionalProperties: false,
|
|
113
|
+
properties: {
|
|
114
|
+
ref: { type: 'number', required: true },
|
|
115
|
+
role: { type: 'string', required: true },
|
|
116
|
+
name: { type: 'string', required: true },
|
|
117
|
+
value: { type: 'string' },
|
|
118
|
+
states: { type: 'array', required: true, items: { type: 'string' } },
|
|
119
|
+
depth: { type: 'number', required: true },
|
|
120
|
+
tag: { type: 'string', required: true },
|
|
121
|
+
selector: { type: 'string', required: true },
|
|
122
|
+
x: { type: 'number', required: true },
|
|
123
|
+
y: { type: 'number', required: true },
|
|
124
|
+
frame: { type: 'boolean' },
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Project one snapshot element into the shape the schema declares.
|
|
129
|
+
*
|
|
130
|
+
* Generic in the source type so the caller keeps the precise shape `defineTool` inferred from
|
|
131
|
+
* the schema: the projection decides which fields survive at run time, and the field table is
|
|
132
|
+
* what guarantees that set matches the schema. Widening the return type here would only move
|
|
133
|
+
* the mismatch to the call site.
|
|
134
|
+
* @param element - the element as the provider produced it.
|
|
135
|
+
* @returns exactly the declared fields.
|
|
136
|
+
*/
|
|
137
|
+
export function projectElement(element) {
|
|
138
|
+
return project(ELEMENT_FIELDS, element);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Project one accessibility node into the shape the schema declares.
|
|
142
|
+
* @param node - the node as the provider produced it.
|
|
143
|
+
* @returns exactly the declared fields.
|
|
144
|
+
*/
|
|
145
|
+
export function projectNode(node) {
|
|
146
|
+
return project(NODE_FIELDS, node);
|
|
147
|
+
}
|
|
148
|
+
/** The field names each table declares, for tests that check the two stay in step. */
|
|
149
|
+
export const DECLARED_FIELDS = {
|
|
150
|
+
element: Object.keys(ELEMENT_FIELDS),
|
|
151
|
+
node: Object.keys(NODE_FIELDS),
|
|
152
|
+
};
|