@noego/wood 0.4.8 → 0.4.10
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/bin/axe.js +313 -98
- package/bin/wood.js +7 -1
- package/dist/automation/actions.cjs +17 -0
- package/dist/automation/actions.cjs.map +1 -0
- package/dist/automation/actions.d.cts +57 -0
- package/dist/automation/actions.d.ts +57 -0
- package/dist/automation/actions.js +1 -0
- package/dist/automation/actions.js.map +1 -0
- package/dist/automation/actions_executor.cjs +101 -0
- package/dist/automation/actions_executor.cjs.map +1 -0
- package/dist/automation/actions_executor.d.cts +16 -0
- package/dist/automation/actions_executor.d.ts +16 -0
- package/dist/automation/actions_executor.js +76 -0
- package/dist/automation/actions_executor.js.map +1 -0
- package/dist/automation/connection.cjs +86 -0
- package/dist/automation/connection.cjs.map +1 -0
- package/dist/automation/connection.d.cts +18 -0
- package/dist/automation/connection.d.ts +18 -0
- package/dist/automation/connection.js +51 -0
- package/dist/automation/connection.js.map +1 -0
- package/dist/automation/errors.cjs +50 -0
- package/dist/automation/errors.cjs.map +1 -0
- package/dist/automation/errors.d.cts +31 -0
- package/dist/automation/errors.d.ts +31 -0
- package/dist/automation/errors.js +24 -0
- package/dist/automation/errors.js.map +1 -0
- package/dist/automation/index.cjs +49 -0
- package/dist/automation/index.cjs.map +1 -0
- package/dist/automation/index.d.cts +16 -0
- package/dist/automation/index.d.ts +16 -0
- package/dist/automation/index.js +15 -0
- package/dist/automation/index.js.map +1 -0
- package/dist/automation/introspection_reader.cjs +75 -0
- package/dist/automation/introspection_reader.cjs.map +1 -0
- package/dist/automation/introspection_reader.d.cts +20 -0
- package/dist/automation/introspection_reader.d.ts +20 -0
- package/dist/automation/introspection_reader.js +48 -0
- package/dist/automation/introspection_reader.js.map +1 -0
- package/dist/automation/locator.cjs +102 -0
- package/dist/automation/locator.cjs.map +1 -0
- package/dist/automation/locator.d.cts +51 -0
- package/dist/automation/locator.d.ts +51 -0
- package/dist/automation/locator.js +75 -0
- package/dist/automation/locator.js.map +1 -0
- package/dist/automation/observation.cjs +17 -0
- package/dist/automation/observation.cjs.map +1 -0
- package/dist/automation/observation.d.cts +44 -0
- package/dist/automation/observation.d.ts +44 -0
- package/dist/automation/observation.js +1 -0
- package/dist/automation/observation.js.map +1 -0
- package/dist/automation/receipts.cjs +17 -0
- package/dist/automation/receipts.cjs.map +1 -0
- package/dist/automation/receipts.d.cts +27 -0
- package/dist/automation/receipts.d.ts +27 -0
- package/dist/automation/receipts.js +1 -0
- package/dist/automation/receipts.js.map +1 -0
- package/dist/automation/scenario.cjs +696 -0
- package/dist/automation/scenario.cjs.map +1 -0
- package/dist/automation/scenario.d.cts +89 -0
- package/dist/automation/scenario.d.ts +89 -0
- package/dist/automation/scenario.js +660 -0
- package/dist/automation/scenario.js.map +1 -0
- package/dist/automation/scrolling.cjs +17 -0
- package/dist/automation/scrolling.cjs.map +1 -0
- package/dist/automation/scrolling.d.cts +51 -0
- package/dist/automation/scrolling.d.ts +51 -0
- package/dist/automation/scrolling.js +1 -0
- package/dist/automation/scrolling.js.map +1 -0
- package/dist/automation/scrolling_executor.cjs +109 -0
- package/dist/automation/scrolling_executor.cjs.map +1 -0
- package/dist/automation/scrolling_executor.d.cts +13 -0
- package/dist/automation/scrolling_executor.d.ts +13 -0
- package/dist/automation/scrolling_executor.js +84 -0
- package/dist/automation/scrolling_executor.js.map +1 -0
- package/dist/automation/target_identity.cjs +17 -0
- package/dist/automation/target_identity.cjs.map +1 -0
- package/dist/automation/target_identity.d.cts +28 -0
- package/dist/automation/target_identity.d.ts +28 -0
- package/dist/automation/target_identity.js +1 -0
- package/dist/automation/target_identity.js.map +1 -0
- package/dist/automation/target_registry.cjs +148 -0
- package/dist/automation/target_registry.cjs.map +1 -0
- package/dist/automation/target_registry.d.cts +32 -0
- package/dist/automation/target_registry.d.ts +32 -0
- package/dist/automation/target_registry.js +122 -0
- package/dist/automation/target_registry.js.map +1 -0
- package/dist/automation/waits.cjs +17 -0
- package/dist/automation/waits.cjs.map +1 -0
- package/dist/automation/waits.d.cts +44 -0
- package/dist/automation/waits.d.ts +44 -0
- package/dist/automation/waits.js +1 -0
- package/dist/automation/waits.js.map +1 -0
- package/dist/automation/waits_executor.cjs +104 -0
- package/dist/automation/waits_executor.cjs.map +1 -0
- package/dist/automation/waits_executor.d.cts +14 -0
- package/dist/automation/waits_executor.d.ts +14 -0
- package/dist/automation/waits_executor.js +79 -0
- package/dist/automation/waits_executor.js.map +1 -0
- package/dist/client/index.d.cts +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/codegen/renderer_app_generator.cjs +16 -1
- package/dist/codegen/renderer_app_generator.cjs.map +1 -1
- package/dist/codegen/renderer_app_generator.js +16 -1
- package/dist/codegen/renderer_app_generator.js.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/navigation/automation_introspection.cjs +110 -0
- package/dist/navigation/automation_introspection.cjs.map +1 -0
- package/dist/navigation/automation_introspection.d.cts +60 -0
- package/dist/navigation/automation_introspection.d.ts +60 -0
- package/dist/navigation/automation_introspection.js +85 -0
- package/dist/navigation/automation_introspection.js.map +1 -0
- package/dist/navigation/index.cjs +7 -2
- package/dist/navigation/index.cjs.map +1 -1
- package/dist/navigation/index.d.cts +2 -0
- package/dist/navigation/index.d.ts +2 -0
- package/dist/navigation/index.js +4 -1
- package/dist/navigation/index.js.map +1 -1
- package/dist/testing/automation_assertions.cjs +417 -0
- package/dist/testing/automation_assertions.cjs.map +1 -0
- package/dist/testing/automation_assertions.d.cts +44 -0
- package/dist/testing/automation_assertions.d.ts +44 -0
- package/dist/testing/automation_assertions.js +385 -0
- package/dist/testing/automation_assertions.js.map +1 -0
- package/dist/testing/browser.cjs +26 -34
- package/dist/testing/browser.cjs.map +1 -1
- package/dist/testing/browser.d.cts +2 -1
- package/dist/testing/browser.d.ts +2 -1
- package/dist/testing/browser.js +30 -31
- package/dist/testing/browser.js.map +1 -1
- package/dist/testing/create_controller_harness.d.cts +1 -0
- package/dist/testing/create_controller_harness.d.ts +1 -0
- package/dist/testing/geometry.cjs +109 -0
- package/dist/testing/geometry.cjs.map +1 -0
- package/dist/testing/geometry.d.cts +45 -0
- package/dist/testing/geometry.d.ts +45 -0
- package/dist/testing/geometry.js +79 -0
- package/dist/testing/geometry.js.map +1 -0
- package/dist/testing/index.cjs +31 -2
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +4 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +31 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/tracing/trace_hub.cjs +3 -0
- package/dist/tracing/trace_hub.cjs.map +1 -1
- package/dist/tracing/trace_hub.js +3 -0
- package/dist/tracing/trace_hub.js.map +1 -1
- package/package.json +7 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var target_registry_exports = {};
|
|
20
|
+
__export(target_registry_exports, {
|
|
21
|
+
WOOD_INTERNAL_BRIDGE_KEY: () => WOOD_INTERNAL_BRIDGE_KEY,
|
|
22
|
+
discoverTargets: () => discoverTargets,
|
|
23
|
+
selectTarget: () => selectTarget
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(target_registry_exports);
|
|
26
|
+
var import_errors = require("./errors.cjs");
|
|
27
|
+
var import_introspection_reader = require("./introspection_reader.cjs");
|
|
28
|
+
const WOOD_INTERNAL_BRIDGE_KEY = "__NOEGO_WOOD_BRIDGE__";
|
|
29
|
+
async function readBridgeWindow(page) {
|
|
30
|
+
try {
|
|
31
|
+
const current = await page.evaluate(async (bridgeKey) => {
|
|
32
|
+
const bridge = globalThis[bridgeKey];
|
|
33
|
+
if (!bridge || typeof bridge !== "object") {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const windowApi = bridge.__window;
|
|
37
|
+
if (!windowApi || typeof windowApi.current !== "function") {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return await windowApi.current();
|
|
41
|
+
}, WOOD_INTERNAL_BRIDGE_KEY);
|
|
42
|
+
if (!current) {
|
|
43
|
+
return { windowId: null, defaultRoute: null, present: false };
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
windowId: current.ok ? current.windowId ?? null : null,
|
|
47
|
+
defaultRoute: current.ok ? current.defaultRoute ?? null : null,
|
|
48
|
+
present: true
|
|
49
|
+
};
|
|
50
|
+
} catch {
|
|
51
|
+
return { windowId: null, defaultRoute: null, present: false };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function readCdpIdentity(page) {
|
|
55
|
+
try {
|
|
56
|
+
const session = await page.context().newCDPSession(page);
|
|
57
|
+
try {
|
|
58
|
+
const info = await session.send("Target.getTargetInfo");
|
|
59
|
+
return {
|
|
60
|
+
targetId: info.targetInfo.targetId,
|
|
61
|
+
browserContextId: info.targetInfo.browserContextId
|
|
62
|
+
};
|
|
63
|
+
} finally {
|
|
64
|
+
await session.detach().catch(() => void 0);
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
return { targetId: null, browserContextId: void 0 };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function discoverTargets(browser) {
|
|
71
|
+
const entries = [];
|
|
72
|
+
let fallbackIndex = 0;
|
|
73
|
+
for (const context of browser.contexts()) {
|
|
74
|
+
for (const page of context.pages()) {
|
|
75
|
+
const [bridge, identity, snapshot] = await Promise.all([
|
|
76
|
+
readBridgeWindow(page),
|
|
77
|
+
readCdpIdentity(page),
|
|
78
|
+
(0, import_introspection_reader.readAutomationSnapshot)(page)
|
|
79
|
+
]);
|
|
80
|
+
const title = await page.title().catch(() => "");
|
|
81
|
+
entries.push({
|
|
82
|
+
page,
|
|
83
|
+
target: {
|
|
84
|
+
targetId: identity.targetId ?? `unstable:page-${fallbackIndex}`,
|
|
85
|
+
windowId: snapshot?.windowId ?? bridge.windowId,
|
|
86
|
+
browserContextId: identity.browserContextId,
|
|
87
|
+
pageGeneration: 1,
|
|
88
|
+
url: page.url(),
|
|
89
|
+
title,
|
|
90
|
+
currentRoute: (0, import_introspection_reader.formatRoute)(snapshot?.currentRoute ?? null),
|
|
91
|
+
configuredDefaultRoute: bridge.defaultRoute ?? void 0,
|
|
92
|
+
woodBridgePresent: bridge.present,
|
|
93
|
+
eligible: bridge.present
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
fallbackIndex += 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return entries;
|
|
100
|
+
}
|
|
101
|
+
function selectTarget(entries, requestedWindowId) {
|
|
102
|
+
if (entries.length === 0) {
|
|
103
|
+
return (0, import_errors.automationError)("NO_ELIGIBLE_TARGETS", "No renderer targets found in the running Wood app.", {
|
|
104
|
+
requiredAction: 'Verify the app is running under "wood dev" and has at least one window.'
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (requestedWindowId) {
|
|
108
|
+
const matches = entries.filter((entry) => entry.target.windowId === requestedWindowId);
|
|
109
|
+
if (matches.length === 0) {
|
|
110
|
+
return (0, import_errors.automationError)("TARGET_NOT_FOUND", `No Wood window target found for "${requestedWindowId}".`, {
|
|
111
|
+
details: { candidates: entries.map((entry) => entry.target.windowId).filter(Boolean) },
|
|
112
|
+
requiredAction: 'Run "axe targets" and pass an existing windowId via --window.'
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (matches.length > 1) {
|
|
116
|
+
return (0, import_errors.automationError)("TARGET_AMBIGUOUS", `Multiple targets match windowId "${requestedWindowId}".`, {
|
|
117
|
+
details: { matchCount: matches.length },
|
|
118
|
+
requiredAction: "Close duplicate windows or report this as a window-identity bug."
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return (0, import_errors.automationOk)(matches[0]);
|
|
122
|
+
}
|
|
123
|
+
const eligible = entries.filter((entry) => entry.target.eligible);
|
|
124
|
+
if (eligible.length === 0) {
|
|
125
|
+
return (0, import_errors.automationError)("NO_ELIGIBLE_TARGETS", "No Wood-bridged renderer targets found.", {
|
|
126
|
+
details: { discovered: entries.length },
|
|
127
|
+
requiredAction: "Only pages exposing the Wood bridge are automatable; check the app booted correctly."
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (eligible.length > 1) {
|
|
131
|
+
return (0, import_errors.automationError)(
|
|
132
|
+
"TARGET_AMBIGUOUS",
|
|
133
|
+
`${eligible.length} eligible Wood windows found; an explicit target is required.`,
|
|
134
|
+
{
|
|
135
|
+
details: { candidates: eligible.map((entry) => entry.target.windowId) },
|
|
136
|
+
requiredAction: 'Pass --window <windowId> (see "axe targets").'
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return (0, import_errors.automationOk)(eligible[0]);
|
|
141
|
+
}
|
|
142
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
143
|
+
0 && (module.exports = {
|
|
144
|
+
WOOD_INTERNAL_BRIDGE_KEY,
|
|
145
|
+
discoverTargets,
|
|
146
|
+
selectTarget
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=target_registry.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/automation/target_registry.ts"],"sourcesContent":["/**\n * Target discovery and deterministic selection (AXE-WP-01, AXE-002).\n *\n * Selection rules:\n * - pages without a Wood bridge are ineligible by default;\n * - an explicit windowId must match exactly one target;\n * - without a windowId, exactly one eligible target must exist —\n * multiple eligible windows are an explicit ambiguity error, never\n * a silent \"first page wins\".\n */\n\nimport type { Browser, Page } from 'playwright';\nimport { automationError, automationOk, type WoodAutomationResult } from './errors.cjs';\nimport { formatRoute, readAutomationSnapshot } from './introspection_reader.cjs';\nimport type { WoodLiveTarget } from './target_identity.cjs';\n\nexport const WOOD_INTERNAL_BRIDGE_KEY = '__NOEGO_WOOD_BRIDGE__';\n\nexport interface WoodTargetEntry {\n target: WoodLiveTarget;\n page: Page;\n}\n\ninterface BridgeWindowInfo {\n windowId: string | null;\n defaultRoute: string | null;\n present: boolean;\n}\n\nasync function readBridgeWindow(page: Page): Promise<BridgeWindowInfo> {\n try {\n const current = await page.evaluate(async (bridgeKey) => {\n const bridge = (globalThis as Record<string, unknown>)[bridgeKey];\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n const windowApi = (bridge as { __window?: { current?: () => Promise<unknown> } }).__window;\n if (!windowApi || typeof windowApi.current !== 'function') {\n return null;\n }\n return (await windowApi.current()) as { ok?: boolean; windowId?: string; defaultRoute?: string };\n }, WOOD_INTERNAL_BRIDGE_KEY);\n\n if (!current) {\n return { windowId: null, defaultRoute: null, present: false };\n }\n return {\n windowId: current.ok ? current.windowId ?? null : null,\n defaultRoute: current.ok ? current.defaultRoute ?? null : null,\n present: true,\n };\n } catch {\n return { windowId: null, defaultRoute: null, present: false };\n }\n}\n\nasync function readCdpIdentity(\n page: Page,\n): Promise<{ targetId: string | null; browserContextId: string | undefined }> {\n try {\n const session = await page.context().newCDPSession(page);\n try {\n const info = await session.send('Target.getTargetInfo');\n return {\n targetId: info.targetInfo.targetId,\n browserContextId: info.targetInfo.browserContextId,\n };\n } finally {\n await session.detach().catch(() => undefined);\n }\n } catch {\n return { targetId: null, browserContextId: undefined };\n }\n}\n\n/**\n * Enumerate all renderer pages with Wood identity attached.\n * `pageGeneration` starts at 1 per discovery; persistent generation tracking\n * arrives with scenario mode (AXE-WP-04).\n */\nexport async function discoverTargets(browser: Browser): Promise<WoodTargetEntry[]> {\n const entries: WoodTargetEntry[] = [];\n let fallbackIndex = 0;\n\n for (const context of browser.contexts()) {\n for (const page of context.pages()) {\n const [bridge, identity, snapshot] = await Promise.all([\n readBridgeWindow(page),\n readCdpIdentity(page),\n readAutomationSnapshot(page),\n ]);\n const title = await page.title().catch(() => '');\n\n entries.push({\n page,\n target: {\n targetId: identity.targetId ?? `unstable:page-${fallbackIndex}`,\n windowId: snapshot?.windowId ?? bridge.windowId,\n browserContextId: identity.browserContextId,\n pageGeneration: 1,\n url: page.url(),\n title,\n currentRoute: formatRoute(snapshot?.currentRoute ?? null),\n configuredDefaultRoute: bridge.defaultRoute ?? undefined,\n woodBridgePresent: bridge.present,\n eligible: bridge.present,\n },\n });\n fallbackIndex += 1;\n }\n }\n\n return entries;\n}\n\n/**\n * Deterministic target selection (pure; unit-tested without a browser).\n */\nexport function selectTarget(\n entries: WoodTargetEntry[],\n requestedWindowId?: string | null,\n): WoodAutomationResult<WoodTargetEntry> {\n if (entries.length === 0) {\n return automationError('NO_ELIGIBLE_TARGETS', 'No renderer targets found in the running Wood app.', {\n requiredAction: 'Verify the app is running under \"wood dev\" and has at least one window.',\n });\n }\n\n if (requestedWindowId) {\n const matches = entries.filter((entry) => entry.target.windowId === requestedWindowId);\n if (matches.length === 0) {\n return automationError('TARGET_NOT_FOUND', `No Wood window target found for \"${requestedWindowId}\".`, {\n details: { candidates: entries.map((entry) => entry.target.windowId).filter(Boolean) },\n requiredAction: 'Run \"axe targets\" and pass an existing windowId via --window.',\n });\n }\n if (matches.length > 1) {\n return automationError('TARGET_AMBIGUOUS', `Multiple targets match windowId \"${requestedWindowId}\".`, {\n details: { matchCount: matches.length },\n requiredAction: 'Close duplicate windows or report this as a window-identity bug.',\n });\n }\n return automationOk(matches[0]);\n }\n\n const eligible = entries.filter((entry) => entry.target.eligible);\n if (eligible.length === 0) {\n return automationError('NO_ELIGIBLE_TARGETS', 'No Wood-bridged renderer targets found.', {\n details: { discovered: entries.length },\n requiredAction: 'Only pages exposing the Wood bridge are automatable; check the app booted correctly.',\n });\n }\n if (eligible.length > 1) {\n return automationError(\n 'TARGET_AMBIGUOUS',\n `${eligible.length} eligible Wood windows found; an explicit target is required.`,\n {\n details: { candidates: eligible.map((entry) => entry.target.windowId) },\n requiredAction: 'Pass --window <windowId> (see \"axe targets\").',\n },\n );\n }\n\n return automationOk(eligible[0]);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Page, Browser } from 'playwright';
|
|
2
|
+
import { WoodAutomationResult } from './errors.cjs';
|
|
3
|
+
import { WoodLiveTarget } from './target_identity.cjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Target discovery and deterministic selection (AXE-WP-01, AXE-002).
|
|
7
|
+
*
|
|
8
|
+
* Selection rules:
|
|
9
|
+
* - pages without a Wood bridge are ineligible by default;
|
|
10
|
+
* - an explicit windowId must match exactly one target;
|
|
11
|
+
* - without a windowId, exactly one eligible target must exist —
|
|
12
|
+
* multiple eligible windows are an explicit ambiguity error, never
|
|
13
|
+
* a silent "first page wins".
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
declare const WOOD_INTERNAL_BRIDGE_KEY = "__NOEGO_WOOD_BRIDGE__";
|
|
17
|
+
interface WoodTargetEntry {
|
|
18
|
+
target: WoodLiveTarget;
|
|
19
|
+
page: Page;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Enumerate all renderer pages with Wood identity attached.
|
|
23
|
+
* `pageGeneration` starts at 1 per discovery; persistent generation tracking
|
|
24
|
+
* arrives with scenario mode (AXE-WP-04).
|
|
25
|
+
*/
|
|
26
|
+
declare function discoverTargets(browser: Browser): Promise<WoodTargetEntry[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Deterministic target selection (pure; unit-tested without a browser).
|
|
29
|
+
*/
|
|
30
|
+
declare function selectTarget(entries: WoodTargetEntry[], requestedWindowId?: string | null): WoodAutomationResult<WoodTargetEntry>;
|
|
31
|
+
|
|
32
|
+
export { WOOD_INTERNAL_BRIDGE_KEY, type WoodTargetEntry, discoverTargets, selectTarget };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Page, Browser } from 'playwright';
|
|
2
|
+
import { WoodAutomationResult } from './errors.js';
|
|
3
|
+
import { WoodLiveTarget } from './target_identity.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Target discovery and deterministic selection (AXE-WP-01, AXE-002).
|
|
7
|
+
*
|
|
8
|
+
* Selection rules:
|
|
9
|
+
* - pages without a Wood bridge are ineligible by default;
|
|
10
|
+
* - an explicit windowId must match exactly one target;
|
|
11
|
+
* - without a windowId, exactly one eligible target must exist —
|
|
12
|
+
* multiple eligible windows are an explicit ambiguity error, never
|
|
13
|
+
* a silent "first page wins".
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
declare const WOOD_INTERNAL_BRIDGE_KEY = "__NOEGO_WOOD_BRIDGE__";
|
|
17
|
+
interface WoodTargetEntry {
|
|
18
|
+
target: WoodLiveTarget;
|
|
19
|
+
page: Page;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Enumerate all renderer pages with Wood identity attached.
|
|
23
|
+
* `pageGeneration` starts at 1 per discovery; persistent generation tracking
|
|
24
|
+
* arrives with scenario mode (AXE-WP-04).
|
|
25
|
+
*/
|
|
26
|
+
declare function discoverTargets(browser: Browser): Promise<WoodTargetEntry[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Deterministic target selection (pure; unit-tested without a browser).
|
|
29
|
+
*/
|
|
30
|
+
declare function selectTarget(entries: WoodTargetEntry[], requestedWindowId?: string | null): WoodAutomationResult<WoodTargetEntry>;
|
|
31
|
+
|
|
32
|
+
export { WOOD_INTERNAL_BRIDGE_KEY, type WoodTargetEntry, discoverTargets, selectTarget };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { automationError, automationOk } from "./errors.js";
|
|
2
|
+
import { formatRoute, readAutomationSnapshot } from "./introspection_reader.js";
|
|
3
|
+
const WOOD_INTERNAL_BRIDGE_KEY = "__NOEGO_WOOD_BRIDGE__";
|
|
4
|
+
async function readBridgeWindow(page) {
|
|
5
|
+
try {
|
|
6
|
+
const current = await page.evaluate(async (bridgeKey) => {
|
|
7
|
+
const bridge = globalThis[bridgeKey];
|
|
8
|
+
if (!bridge || typeof bridge !== "object") {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const windowApi = bridge.__window;
|
|
12
|
+
if (!windowApi || typeof windowApi.current !== "function") {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return await windowApi.current();
|
|
16
|
+
}, WOOD_INTERNAL_BRIDGE_KEY);
|
|
17
|
+
if (!current) {
|
|
18
|
+
return { windowId: null, defaultRoute: null, present: false };
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
windowId: current.ok ? current.windowId ?? null : null,
|
|
22
|
+
defaultRoute: current.ok ? current.defaultRoute ?? null : null,
|
|
23
|
+
present: true
|
|
24
|
+
};
|
|
25
|
+
} catch {
|
|
26
|
+
return { windowId: null, defaultRoute: null, present: false };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function readCdpIdentity(page) {
|
|
30
|
+
try {
|
|
31
|
+
const session = await page.context().newCDPSession(page);
|
|
32
|
+
try {
|
|
33
|
+
const info = await session.send("Target.getTargetInfo");
|
|
34
|
+
return {
|
|
35
|
+
targetId: info.targetInfo.targetId,
|
|
36
|
+
browserContextId: info.targetInfo.browserContextId
|
|
37
|
+
};
|
|
38
|
+
} finally {
|
|
39
|
+
await session.detach().catch(() => void 0);
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
return { targetId: null, browserContextId: void 0 };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function discoverTargets(browser) {
|
|
46
|
+
const entries = [];
|
|
47
|
+
let fallbackIndex = 0;
|
|
48
|
+
for (const context of browser.contexts()) {
|
|
49
|
+
for (const page of context.pages()) {
|
|
50
|
+
const [bridge, identity, snapshot] = await Promise.all([
|
|
51
|
+
readBridgeWindow(page),
|
|
52
|
+
readCdpIdentity(page),
|
|
53
|
+
readAutomationSnapshot(page)
|
|
54
|
+
]);
|
|
55
|
+
const title = await page.title().catch(() => "");
|
|
56
|
+
entries.push({
|
|
57
|
+
page,
|
|
58
|
+
target: {
|
|
59
|
+
targetId: identity.targetId ?? `unstable:page-${fallbackIndex}`,
|
|
60
|
+
windowId: snapshot?.windowId ?? bridge.windowId,
|
|
61
|
+
browserContextId: identity.browserContextId,
|
|
62
|
+
pageGeneration: 1,
|
|
63
|
+
url: page.url(),
|
|
64
|
+
title,
|
|
65
|
+
currentRoute: formatRoute(snapshot?.currentRoute ?? null),
|
|
66
|
+
configuredDefaultRoute: bridge.defaultRoute ?? void 0,
|
|
67
|
+
woodBridgePresent: bridge.present,
|
|
68
|
+
eligible: bridge.present
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
fallbackIndex += 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return entries;
|
|
75
|
+
}
|
|
76
|
+
function selectTarget(entries, requestedWindowId) {
|
|
77
|
+
if (entries.length === 0) {
|
|
78
|
+
return automationError("NO_ELIGIBLE_TARGETS", "No renderer targets found in the running Wood app.", {
|
|
79
|
+
requiredAction: 'Verify the app is running under "wood dev" and has at least one window.'
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (requestedWindowId) {
|
|
83
|
+
const matches = entries.filter((entry) => entry.target.windowId === requestedWindowId);
|
|
84
|
+
if (matches.length === 0) {
|
|
85
|
+
return automationError("TARGET_NOT_FOUND", `No Wood window target found for "${requestedWindowId}".`, {
|
|
86
|
+
details: { candidates: entries.map((entry) => entry.target.windowId).filter(Boolean) },
|
|
87
|
+
requiredAction: 'Run "axe targets" and pass an existing windowId via --window.'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (matches.length > 1) {
|
|
91
|
+
return automationError("TARGET_AMBIGUOUS", `Multiple targets match windowId "${requestedWindowId}".`, {
|
|
92
|
+
details: { matchCount: matches.length },
|
|
93
|
+
requiredAction: "Close duplicate windows or report this as a window-identity bug."
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return automationOk(matches[0]);
|
|
97
|
+
}
|
|
98
|
+
const eligible = entries.filter((entry) => entry.target.eligible);
|
|
99
|
+
if (eligible.length === 0) {
|
|
100
|
+
return automationError("NO_ELIGIBLE_TARGETS", "No Wood-bridged renderer targets found.", {
|
|
101
|
+
details: { discovered: entries.length },
|
|
102
|
+
requiredAction: "Only pages exposing the Wood bridge are automatable; check the app booted correctly."
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (eligible.length > 1) {
|
|
106
|
+
return automationError(
|
|
107
|
+
"TARGET_AMBIGUOUS",
|
|
108
|
+
`${eligible.length} eligible Wood windows found; an explicit target is required.`,
|
|
109
|
+
{
|
|
110
|
+
details: { candidates: eligible.map((entry) => entry.target.windowId) },
|
|
111
|
+
requiredAction: 'Pass --window <windowId> (see "axe targets").'
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return automationOk(eligible[0]);
|
|
116
|
+
}
|
|
117
|
+
export {
|
|
118
|
+
WOOD_INTERNAL_BRIDGE_KEY,
|
|
119
|
+
discoverTargets,
|
|
120
|
+
selectTarget
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=target_registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/automation/target_registry.ts"],"sourcesContent":["/**\n * Target discovery and deterministic selection (AXE-WP-01, AXE-002).\n *\n * Selection rules:\n * - pages without a Wood bridge are ineligible by default;\n * - an explicit windowId must match exactly one target;\n * - without a windowId, exactly one eligible target must exist —\n * multiple eligible windows are an explicit ambiguity error, never\n * a silent \"first page wins\".\n */\n\nimport type { Browser, Page } from 'playwright';\nimport { automationError, automationOk, type WoodAutomationResult } from './errors.js';\nimport { formatRoute, readAutomationSnapshot } from './introspection_reader.js';\nimport type { WoodLiveTarget } from './target_identity.js';\n\nexport const WOOD_INTERNAL_BRIDGE_KEY = '__NOEGO_WOOD_BRIDGE__';\n\nexport interface WoodTargetEntry {\n target: WoodLiveTarget;\n page: Page;\n}\n\ninterface BridgeWindowInfo {\n windowId: string | null;\n defaultRoute: string | null;\n present: boolean;\n}\n\nasync function readBridgeWindow(page: Page): Promise<BridgeWindowInfo> {\n try {\n const current = await page.evaluate(async (bridgeKey) => {\n const bridge = (globalThis as Record<string, unknown>)[bridgeKey];\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n const windowApi = (bridge as { __window?: { current?: () => Promise<unknown> } }).__window;\n if (!windowApi || typeof windowApi.current !== 'function') {\n return null;\n }\n return (await windowApi.current()) as { ok?: boolean; windowId?: string; defaultRoute?: string };\n }, WOOD_INTERNAL_BRIDGE_KEY);\n\n if (!current) {\n return { windowId: null, defaultRoute: null, present: false };\n }\n return {\n windowId: current.ok ? current.windowId ?? null : null,\n defaultRoute: current.ok ? current.defaultRoute ?? null : null,\n present: true,\n };\n } catch {\n return { windowId: null, defaultRoute: null, present: false };\n }\n}\n\nasync function readCdpIdentity(\n page: Page,\n): Promise<{ targetId: string | null; browserContextId: string | undefined }> {\n try {\n const session = await page.context().newCDPSession(page);\n try {\n const info = await session.send('Target.getTargetInfo');\n return {\n targetId: info.targetInfo.targetId,\n browserContextId: info.targetInfo.browserContextId,\n };\n } finally {\n await session.detach().catch(() => undefined);\n }\n } catch {\n return { targetId: null, browserContextId: undefined };\n }\n}\n\n/**\n * Enumerate all renderer pages with Wood identity attached.\n * `pageGeneration` starts at 1 per discovery; persistent generation tracking\n * arrives with scenario mode (AXE-WP-04).\n */\nexport async function discoverTargets(browser: Browser): Promise<WoodTargetEntry[]> {\n const entries: WoodTargetEntry[] = [];\n let fallbackIndex = 0;\n\n for (const context of browser.contexts()) {\n for (const page of context.pages()) {\n const [bridge, identity, snapshot] = await Promise.all([\n readBridgeWindow(page),\n readCdpIdentity(page),\n readAutomationSnapshot(page),\n ]);\n const title = await page.title().catch(() => '');\n\n entries.push({\n page,\n target: {\n targetId: identity.targetId ?? `unstable:page-${fallbackIndex}`,\n windowId: snapshot?.windowId ?? bridge.windowId,\n browserContextId: identity.browserContextId,\n pageGeneration: 1,\n url: page.url(),\n title,\n currentRoute: formatRoute(snapshot?.currentRoute ?? null),\n configuredDefaultRoute: bridge.defaultRoute ?? undefined,\n woodBridgePresent: bridge.present,\n eligible: bridge.present,\n },\n });\n fallbackIndex += 1;\n }\n }\n\n return entries;\n}\n\n/**\n * Deterministic target selection (pure; unit-tested without a browser).\n */\nexport function selectTarget(\n entries: WoodTargetEntry[],\n requestedWindowId?: string | null,\n): WoodAutomationResult<WoodTargetEntry> {\n if (entries.length === 0) {\n return automationError('NO_ELIGIBLE_TARGETS', 'No renderer targets found in the running Wood app.', {\n requiredAction: 'Verify the app is running under \"wood dev\" and has at least one window.',\n });\n }\n\n if (requestedWindowId) {\n const matches = entries.filter((entry) => entry.target.windowId === requestedWindowId);\n if (matches.length === 0) {\n return automationError('TARGET_NOT_FOUND', `No Wood window target found for \"${requestedWindowId}\".`, {\n details: { candidates: entries.map((entry) => entry.target.windowId).filter(Boolean) },\n requiredAction: 'Run \"axe targets\" and pass an existing windowId via --window.',\n });\n }\n if (matches.length > 1) {\n return automationError('TARGET_AMBIGUOUS', `Multiple targets match windowId \"${requestedWindowId}\".`, {\n details: { matchCount: matches.length },\n requiredAction: 'Close duplicate windows or report this as a window-identity bug.',\n });\n }\n return automationOk(matches[0]);\n }\n\n const eligible = entries.filter((entry) => entry.target.eligible);\n if (eligible.length === 0) {\n return automationError('NO_ELIGIBLE_TARGETS', 'No Wood-bridged renderer targets found.', {\n details: { discovered: entries.length },\n requiredAction: 'Only pages exposing the Wood bridge are automatable; check the app booted correctly.',\n });\n }\n if (eligible.length > 1) {\n return automationError(\n 'TARGET_AMBIGUOUS',\n `${eligible.length} eligible Wood windows found; an explicit target is required.`,\n {\n details: { candidates: eligible.map((entry) => entry.target.windowId) },\n requiredAction: 'Pass --window <windowId> (see \"axe targets\").',\n },\n );\n }\n\n return automationOk(eligible[0]);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var waits_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(waits_exports);
|
|
17
|
+
//# sourceMappingURL=waits.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/automation/waits.ts"],"sourcesContent":["/**\n * Wait and stability contract (frozen — Axe.md \"Wait and stability model\").\n * Type surface only in WP-00/01; execution lands in AXE-WP-03.\n */\n\nimport type { WoodLocator } from './locator.cjs';\nimport type { WoodScrollContainer } from './scrolling.cjs';\n\nexport type WoodWaitCondition =\n | { kind: 'route'; page: string; params?: Record<string, string>; query?: Record<string, string> }\n | { kind: 'url'; value: string; match?: 'exact' | 'glob' | 'regex' }\n | { kind: 'target'; windowId: string; state: 'present' | 'active' | 'closed' }\n | { kind: 'visible' | 'hidden' | 'attached' | 'detached' | 'enabled'; target: WoodLocator }\n | { kind: 'trace'; type: string; source?: string; count?: number }\n | { kind: 'scroll_edge'; container: WoodScrollContainer; edge: 'top' | 'bottom' | 'left' | 'right' }\n | { kind: 'visual_stable'; target?: WoodLocator; frames?: number }\n | { kind: 'app_idle' };\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { WoodLocator } from './locator.cjs';
|
|
2
|
+
import { WoodScrollContainer } from './scrolling.cjs';
|
|
3
|
+
import 'playwright';
|
|
4
|
+
import './errors.cjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wait and stability contract (frozen — Axe.md "Wait and stability model").
|
|
8
|
+
* Type surface only in WP-00/01; execution lands in AXE-WP-03.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type WoodWaitCondition = {
|
|
12
|
+
kind: 'route';
|
|
13
|
+
page: string;
|
|
14
|
+
params?: Record<string, string>;
|
|
15
|
+
query?: Record<string, string>;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'url';
|
|
18
|
+
value: string;
|
|
19
|
+
match?: 'exact' | 'glob' | 'regex';
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'target';
|
|
22
|
+
windowId: string;
|
|
23
|
+
state: 'present' | 'active' | 'closed';
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'visible' | 'hidden' | 'attached' | 'detached' | 'enabled';
|
|
26
|
+
target: WoodLocator;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'trace';
|
|
29
|
+
type: string;
|
|
30
|
+
source?: string;
|
|
31
|
+
count?: number;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'scroll_edge';
|
|
34
|
+
container: WoodScrollContainer;
|
|
35
|
+
edge: 'top' | 'bottom' | 'left' | 'right';
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'visual_stable';
|
|
38
|
+
target?: WoodLocator;
|
|
39
|
+
frames?: number;
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'app_idle';
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type { WoodWaitCondition };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { WoodLocator } from './locator.js';
|
|
2
|
+
import { WoodScrollContainer } from './scrolling.js';
|
|
3
|
+
import 'playwright';
|
|
4
|
+
import './errors.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wait and stability contract (frozen — Axe.md "Wait and stability model").
|
|
8
|
+
* Type surface only in WP-00/01; execution lands in AXE-WP-03.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type WoodWaitCondition = {
|
|
12
|
+
kind: 'route';
|
|
13
|
+
page: string;
|
|
14
|
+
params?: Record<string, string>;
|
|
15
|
+
query?: Record<string, string>;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'url';
|
|
18
|
+
value: string;
|
|
19
|
+
match?: 'exact' | 'glob' | 'regex';
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'target';
|
|
22
|
+
windowId: string;
|
|
23
|
+
state: 'present' | 'active' | 'closed';
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'visible' | 'hidden' | 'attached' | 'detached' | 'enabled';
|
|
26
|
+
target: WoodLocator;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'trace';
|
|
29
|
+
type: string;
|
|
30
|
+
source?: string;
|
|
31
|
+
count?: number;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'scroll_edge';
|
|
34
|
+
container: WoodScrollContainer;
|
|
35
|
+
edge: 'top' | 'bottom' | 'left' | 'right';
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'visual_stable';
|
|
38
|
+
target?: WoodLocator;
|
|
39
|
+
frames?: number;
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'app_idle';
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type { WoodWaitCondition };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=waits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var waits_executor_exports = {};
|
|
20
|
+
__export(waits_executor_exports, {
|
|
21
|
+
awaitCondition: () => awaitCondition,
|
|
22
|
+
matchesUrl: () => matchesUrl
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(waits_executor_exports);
|
|
25
|
+
var import_errors = require("./errors.cjs");
|
|
26
|
+
var import_introspection_reader = require("./introspection_reader.cjs");
|
|
27
|
+
var import_locator = require("./locator.cjs");
|
|
28
|
+
const DEFAULT_TIMEOUT_MS = 5e3;
|
|
29
|
+
function matchesUrl(actual, expected, match = "exact") {
|
|
30
|
+
if (match === "exact") return actual === expected;
|
|
31
|
+
if (match === "regex") {
|
|
32
|
+
try {
|
|
33
|
+
return new RegExp(expected).test(actual);
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const escaped = expected.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
39
|
+
return new RegExp(`^${escaped}$`).test(actual);
|
|
40
|
+
}
|
|
41
|
+
async function awaitCondition(page, condition, options) {
|
|
42
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
43
|
+
const started = Date.now();
|
|
44
|
+
let lastObserved = null;
|
|
45
|
+
if (condition.kind === "trace" || condition.kind === "target") {
|
|
46
|
+
return (0, import_errors.automationError)("UNSUPPORTED", `${condition.kind} waits require an unavailable ${condition.kind} observation capability.`, { details: { capability: condition.kind, lastObserved } });
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
if (condition.kind === "visible" || condition.kind === "hidden" || condition.kind === "attached" || condition.kind === "detached") {
|
|
50
|
+
await (0, import_locator.toPlaywrightLocator)(page, condition.target).waitFor({ state: condition.kind, timeout: timeoutMs });
|
|
51
|
+
} else if (condition.kind === "enabled") {
|
|
52
|
+
const locator = (0, import_locator.toPlaywrightLocator)(page, condition.target);
|
|
53
|
+
await locator.waitFor({ state: "visible", timeout: timeoutMs });
|
|
54
|
+
await page.waitForFunction((element) => !element.disabled && element.getAttribute("aria-disabled") !== "true", await locator.elementHandle(), { timeout: timeoutMs });
|
|
55
|
+
} else if (condition.kind === "app_idle") {
|
|
56
|
+
lastObserved = await (0, import_introspection_reader.readAutomationSnapshot)(page);
|
|
57
|
+
if (!await (0, import_introspection_reader.waitForRendererIdle)(page, { timeoutMs })) throw new Error("Renderer did not become idle.");
|
|
58
|
+
} else if (condition.kind === "url") {
|
|
59
|
+
while (!matchesUrl(lastObserved = page.url(), condition.value, condition.match)) {
|
|
60
|
+
if (Date.now() - started >= timeoutMs) throw new Error("URL did not match.");
|
|
61
|
+
await page.waitForTimeout(25);
|
|
62
|
+
}
|
|
63
|
+
} else if (condition.kind === "route") {
|
|
64
|
+
while (true) {
|
|
65
|
+
const snapshot = await (0, import_introspection_reader.readAutomationSnapshot)(page);
|
|
66
|
+
lastObserved = snapshot?.currentRoute ?? null;
|
|
67
|
+
const route = snapshot?.currentRoute;
|
|
68
|
+
const includes = (wanted, actual) => !wanted || Object.entries(wanted).every(([key, value]) => actual[key] === value);
|
|
69
|
+
if (route?.page === condition.page && includes(condition.params, route.params) && includes(condition.query, route.query)) break;
|
|
70
|
+
if (Date.now() - started >= timeoutMs) throw new Error("Route did not match.");
|
|
71
|
+
await page.waitForTimeout(25);
|
|
72
|
+
}
|
|
73
|
+
} else if (condition.kind === "scroll_edge") {
|
|
74
|
+
const locator = condition.container.kind === "locator" ? (0, import_locator.toPlaywrightLocator)(page, condition.container.target) : null;
|
|
75
|
+
await page.waitForFunction(({ element, edge }) => {
|
|
76
|
+
const node = element ?? document.scrollingElement ?? document.documentElement;
|
|
77
|
+
return edge === "top" ? node.scrollTop <= 0 : edge === "bottom" ? node.scrollTop + node.clientHeight >= node.scrollHeight - 1 : edge === "left" ? node.scrollLeft <= 0 : node.scrollLeft + node.clientWidth >= node.scrollWidth - 1;
|
|
78
|
+
}, { element: locator ? await locator.elementHandle() : null, edge: condition.edge }, { timeout: timeoutMs });
|
|
79
|
+
} else {
|
|
80
|
+
const locator = condition.target ? (0, import_locator.toPlaywrightLocator)(page, condition.target) : null;
|
|
81
|
+
lastObserved = await page.evaluate(async ({ element, frames }) => {
|
|
82
|
+
const target = element;
|
|
83
|
+
let previous = target?.getBoundingClientRect() ?? { x: 0, y: 0, width: innerWidth, height: innerHeight };
|
|
84
|
+
let stable = 0;
|
|
85
|
+
while (stable < frames) {
|
|
86
|
+
await new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
87
|
+
const next = target?.getBoundingClientRect() ?? { x: 0, y: 0, width: innerWidth, height: innerHeight };
|
|
88
|
+
stable = next.x === previous.x && next.y === previous.y && next.width === previous.width && next.height === previous.height ? stable + 1 : 0;
|
|
89
|
+
previous = next;
|
|
90
|
+
}
|
|
91
|
+
return { x: previous.x, y: previous.y, width: previous.width, height: previous.height, stableFrames: stable };
|
|
92
|
+
}, { element: locator ? await locator.elementHandle() : null, frames: condition.kind === "visual_stable" ? condition.frames ?? 3 : 3 });
|
|
93
|
+
}
|
|
94
|
+
return (0, import_errors.automationOk)({ waitedMs: Date.now() - started });
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return (0, import_errors.automationError)("ACTION_FAILED", `Timed out waiting for ${condition.kind} after ${timeoutMs}ms.`, { details: { timeoutMs, lastObserved, cause: error instanceof Error ? error.message : String(error) } });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
100
|
+
0 && (module.exports = {
|
|
101
|
+
awaitCondition,
|
|
102
|
+
matchesUrl
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=waits_executor.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/automation/waits_executor.ts"],"sourcesContent":["import type { Page } from 'playwright';\nimport { automationError, automationOk, type WoodAutomationResult } from './errors.cjs';\nimport { readAutomationSnapshot, waitForRendererIdle } from './introspection_reader.cjs';\nimport { toPlaywrightLocator } from './locator.cjs';\nimport type { WoodWaitCondition } from './waits.cjs';\n\nconst DEFAULT_TIMEOUT_MS = 5_000;\n\nexport function matchesUrl(actual: string, expected: string, match: 'exact' | 'glob' | 'regex' = 'exact'): boolean {\n if (match === 'exact') return actual === expected;\n if (match === 'regex') {\n try { return new RegExp(expected).test(actual); } catch { return false; }\n }\n const escaped = expected.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*').replace(/\\?/g, '.');\n return new RegExp(`^${escaped}$`).test(actual);\n}\n\nexport async function awaitCondition(page: Page, condition: WoodWaitCondition, options?: { timeoutMs?: number }): Promise<WoodAutomationResult<{ waitedMs: number }>> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const started = Date.now();\n let lastObserved: unknown = null;\n if (condition.kind === 'trace' || condition.kind === 'target') {\n return automationError('UNSUPPORTED', `${condition.kind} waits require an unavailable ${condition.kind} observation capability.`, { details: { capability: condition.kind, lastObserved } });\n }\n try {\n if (condition.kind === 'visible' || condition.kind === 'hidden' || condition.kind === 'attached' || condition.kind === 'detached') {\n await toPlaywrightLocator(page, condition.target).waitFor({ state: condition.kind, timeout: timeoutMs });\n } else if (condition.kind === 'enabled') {\n const locator = toPlaywrightLocator(page, condition.target);\n await locator.waitFor({ state: 'visible', timeout: timeoutMs });\n await page.waitForFunction((element) => !(element as HTMLInputElement).disabled && element.getAttribute('aria-disabled') !== 'true', await locator.elementHandle(), { timeout: timeoutMs });\n } else if (condition.kind === 'app_idle') {\n lastObserved = await readAutomationSnapshot(page);\n if (!(await waitForRendererIdle(page, { timeoutMs }))) throw new Error('Renderer did not become idle.');\n } else if (condition.kind === 'url') {\n while (!matchesUrl(lastObserved = page.url(), condition.value, condition.match)) {\n if (Date.now() - started >= timeoutMs) throw new Error('URL did not match.');\n await page.waitForTimeout(25);\n }\n } else if (condition.kind === 'route') {\n while (true) {\n const snapshot = await readAutomationSnapshot(page);\n lastObserved = snapshot?.currentRoute ?? null;\n const route = snapshot?.currentRoute;\n const includes = (wanted: Record<string, string> | undefined, actual: Record<string, string>) =>\n !wanted || Object.entries(wanted).every(([key, value]) => actual[key] === value);\n if (route?.page === condition.page && includes(condition.params, route.params) && includes(condition.query, route.query)) break;\n if (Date.now() - started >= timeoutMs) throw new Error('Route did not match.');\n await page.waitForTimeout(25);\n }\n } else if (condition.kind === 'scroll_edge') {\n const locator = condition.container.kind === 'locator' ? toPlaywrightLocator(page, condition.container.target) : null;\n await page.waitForFunction(({ element, edge }) => {\n const node = (element as HTMLElement | null) ?? document.scrollingElement ?? document.documentElement;\n return edge === 'top' ? node.scrollTop <= 0 : edge === 'bottom' ? node.scrollTop + node.clientHeight >= node.scrollHeight - 1 : edge === 'left' ? node.scrollLeft <= 0 : node.scrollLeft + node.clientWidth >= node.scrollWidth - 1;\n }, { element: locator ? await locator.elementHandle() : null, edge: condition.edge }, { timeout: timeoutMs });\n } else {\n const locator = condition.target ? toPlaywrightLocator(page, condition.target) : null;\n lastObserved = await page.evaluate(async ({ element, frames }) => {\n const target = element as Element | null;\n let previous = target?.getBoundingClientRect() ?? { x: 0, y: 0, width: innerWidth, height: innerHeight };\n let stable = 0;\n while (stable < frames) {\n await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n const next = target?.getBoundingClientRect() ?? { x: 0, y: 0, width: innerWidth, height: innerHeight };\n stable = next.x === previous.x && next.y === previous.y && next.width === previous.width && next.height === previous.height ? stable + 1 : 0;\n previous = next;\n }\n return { x: previous.x, y: previous.y, width: previous.width, height: previous.height, stableFrames: stable };\n }, { element: locator ? await locator.elementHandle() : null, frames: condition.kind === 'visual_stable' ? condition.frames ?? 3 : 3 });\n }\n return automationOk({ waitedMs: Date.now() - started });\n } catch (error) {\n return automationError('ACTION_FAILED', `Timed out waiting for ${condition.kind} after ${timeoutMs}ms.`, { details: { timeoutMs, lastObserved, cause: error instanceof Error ? error.message : String(error) } });\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Page } from 'playwright';
|
|
2
|
+
import { WoodAutomationResult } from './errors.cjs';
|
|
3
|
+
import { WoodWaitCondition } from './waits.cjs';
|
|
4
|
+
import './locator.cjs';
|
|
5
|
+
import './scrolling.cjs';
|
|
6
|
+
|
|
7
|
+
declare function matchesUrl(actual: string, expected: string, match?: 'exact' | 'glob' | 'regex'): boolean;
|
|
8
|
+
declare function awaitCondition(page: Page, condition: WoodWaitCondition, options?: {
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
}): Promise<WoodAutomationResult<{
|
|
11
|
+
waitedMs: number;
|
|
12
|
+
}>>;
|
|
13
|
+
|
|
14
|
+
export { awaitCondition, matchesUrl };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Page } from 'playwright';
|
|
2
|
+
import { WoodAutomationResult } from './errors.js';
|
|
3
|
+
import { WoodWaitCondition } from './waits.js';
|
|
4
|
+
import './locator.js';
|
|
5
|
+
import './scrolling.js';
|
|
6
|
+
|
|
7
|
+
declare function matchesUrl(actual: string, expected: string, match?: 'exact' | 'glob' | 'regex'): boolean;
|
|
8
|
+
declare function awaitCondition(page: Page, condition: WoodWaitCondition, options?: {
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
}): Promise<WoodAutomationResult<{
|
|
11
|
+
waitedMs: number;
|
|
12
|
+
}>>;
|
|
13
|
+
|
|
14
|
+
export { awaitCondition, matchesUrl };
|