@crouton-kit/capture 1.3.12 → 1.3.13
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/capture +22 -8
- package/package.json +1 -1
- package/src/interact.ts +25 -2
- package/src/session/commands.ts +16 -3
package/bin/capture
CHANGED
|
@@ -9822,7 +9822,7 @@ function validateHarAppendBatch(value, source) {
|
|
|
9822
9822
|
return { entries, incompleteLifecycles };
|
|
9823
9823
|
}
|
|
9824
9824
|
function captureVersion() {
|
|
9825
|
-
const version = "1.3.
|
|
9825
|
+
const version = "1.3.13";
|
|
9826
9826
|
return typeof version === "string" && version.length > 0 ? version : "development";
|
|
9827
9827
|
}
|
|
9828
9828
|
function token() {
|
|
@@ -11581,7 +11581,7 @@ function wallTime(value) {
|
|
|
11581
11581
|
}
|
|
11582
11582
|
}
|
|
11583
11583
|
function captureVersion2() {
|
|
11584
|
-
const version = "1.3.
|
|
11584
|
+
const version = "1.3.13";
|
|
11585
11585
|
return typeof version === "string" && version.length > 0 ? version : "development";
|
|
11586
11586
|
}
|
|
11587
11587
|
function deepFreeze(value) {
|
|
@@ -13842,9 +13842,9 @@ function readSession(id) {
|
|
|
13842
13842
|
function generateId() {
|
|
13843
13843
|
return `cap-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
13844
13844
|
}
|
|
13845
|
-
async function waitForPageLoad(client, timeoutMs) {
|
|
13845
|
+
async function waitForPageLoad(client, timeoutMs, attachTimeoutMs = SESSION_TAB_ATTACH_TIMEOUT_MS) {
|
|
13846
13846
|
await client.waitReady();
|
|
13847
|
-
await client.send("Page.enable");
|
|
13847
|
+
await client.send("Page.enable", {}, attachTimeoutMs);
|
|
13848
13848
|
return await new Promise((resolve11) => {
|
|
13849
13849
|
const timer = setTimeout(() => resolve11(true), timeoutMs);
|
|
13850
13850
|
const done = (timedOut) => {
|
|
@@ -14581,7 +14581,7 @@ async function sessionMain(parsed, _args) {
|
|
|
14581
14581
|
return;
|
|
14582
14582
|
}
|
|
14583
14583
|
}
|
|
14584
|
-
var fs9, path12, VIEW_FILTERS, SECTION_LABELS, COMMAND_BLOCK, START_USAGE, STOP_USAGE, LIST_USAGE, VIEW_USAGE, HAR_USAGE, LOG_USAGE, productionStartWorld, startWorld;
|
|
14584
|
+
var fs9, path12, VIEW_FILTERS, SECTION_LABELS, SESSION_TAB_ATTACH_TIMEOUT_MS, COMMAND_BLOCK, START_USAGE, STOP_USAGE, LIST_USAGE, VIEW_USAGE, HAR_USAGE, LOG_USAGE, productionStartWorld, startWorld;
|
|
14585
14585
|
var init_commands = __esm({
|
|
14586
14586
|
"src/session/commands.ts"() {
|
|
14587
14587
|
fs9 = __toESM(require("fs"));
|
|
@@ -14612,6 +14612,7 @@ var init_commands = __esm({
|
|
|
14612
14612
|
recs: "motion",
|
|
14613
14613
|
other: "other"
|
|
14614
14614
|
};
|
|
14615
|
+
SESSION_TAB_ATTACH_TIMEOUT_MS = 5e3;
|
|
14615
14616
|
COMMAND_BLOCK = `<command name="session">
|
|
14616
14617
|
the artifact container \u2014 a session opens a tab, records HAR, and bundles every artifact; while active, every command auto-targets its tab
|
|
14617
14618
|
use when starting scoped work against a page: start first, then every other capture command needs no --target/--port threading
|
|
@@ -14751,7 +14752,14 @@ effects:
|
|
|
14751
14752
|
} catch (err) {
|
|
14752
14753
|
throw worldFailure(`capture session start could not attach to ${url}: ${err instanceof Error ? err.message : err}. Reuse an existing tab with --target.`, err);
|
|
14753
14754
|
}
|
|
14754
|
-
|
|
14755
|
+
try {
|
|
14756
|
+
return await waitForPageLoad(client, 1e4);
|
|
14757
|
+
} catch (err) {
|
|
14758
|
+
throw worldFailure(
|
|
14759
|
+
`capture session start could not attach to ${url}: Page.enable failed during its ${SESSION_TAB_ATTACH_TIMEOUT_MS}ms attachment window (${err instanceof Error ? err.message : err}). The CDP endpoint created the tab but cannot drive it; choose another endpoint from \`capture tab list\` and retry with \`--port\`.`,
|
|
14760
|
+
err
|
|
14761
|
+
);
|
|
14762
|
+
}
|
|
14755
14763
|
} finally {
|
|
14756
14764
|
client.close();
|
|
14757
14765
|
}
|
|
@@ -15098,7 +15106,13 @@ async function resolveLiveTarget(client, input) {
|
|
|
15098
15106
|
return resolveLiveCss(client, input, parsed.value);
|
|
15099
15107
|
}
|
|
15100
15108
|
}
|
|
15101
|
-
function
|
|
15109
|
+
function preferActionable(matches) {
|
|
15110
|
+
if (matches.length <= 1) return matches;
|
|
15111
|
+
const interactive = matches.filter((m) => m.role?.value !== void 0 && INTERACTIVE_ROLES.has(m.role.value));
|
|
15112
|
+
return interactive.length === 1 ? interactive : matches;
|
|
15113
|
+
}
|
|
15114
|
+
function settleAxMatches(kind, input, rawMatches) {
|
|
15115
|
+
const matches = preferActionable(rawMatches);
|
|
15102
15116
|
if (matches.length === 1) {
|
|
15103
15117
|
const m = matches[0];
|
|
15104
15118
|
return {
|
|
@@ -241042,7 +241056,7 @@ CDP_PORT / CDP_TARGET pin the browser + tab for orchestrators \u2014 precedence:
|
|
|
241042
241056
|
explicit flag > active session > env.`;
|
|
241043
241057
|
}
|
|
241044
241058
|
function printVersion() {
|
|
241045
|
-
const declared = "1.3.
|
|
241059
|
+
const declared = "1.3.13";
|
|
241046
241060
|
if (declared) {
|
|
241047
241061
|
console.log(declared);
|
|
241048
241062
|
return;
|
package/package.json
CHANGED
package/src/interact.ts
CHANGED
|
@@ -18,11 +18,19 @@
|
|
|
18
18
|
* nothing — command leaves own all output; returned facts carry the resolved
|
|
19
19
|
* identity (`backendNodeId`, role, name) and the dispatched coordinates.
|
|
20
20
|
*
|
|
21
|
+
* `ax:`/`axid:` tie-break: a same-name match set that mixes an
|
|
22
|
+
* {@link INTERACTIVE_ROLES} role (e.g. `button`) with non-interactive ones
|
|
23
|
+
* (e.g. `StaticText` — the common shape of a label rendering inside its own
|
|
24
|
+
* button) narrows to the interactive match when that leaves exactly one
|
|
25
|
+
* candidate, because that is the only element the caller can actually drive.
|
|
26
|
+
* A tie among multiple interactive roles (or zero) stays genuinely
|
|
27
|
+
* ambiguous — see {@link preferActionable}.
|
|
28
|
+
*
|
|
21
29
|
* Uses getFullAXTree instead of queryAXTree because queryAXTree hangs on
|
|
22
30
|
* complex pages (7000+ AX nodes) when given the DOM root nodeId.
|
|
23
31
|
*/
|
|
24
32
|
|
|
25
|
-
import { readFullAXTree, type FullAXNode } from './cdp/a11y.js';
|
|
33
|
+
import { INTERACTIVE_ROLES, readFullAXTree, type FullAXNode } from './cdp/a11y.js';
|
|
26
34
|
import { isScrollDestination } from './cdp/leaf-grammar.js';
|
|
27
35
|
import { captureError } from './errors.js';
|
|
28
36
|
import { parseSelectorInput, type SelectorInputKind } from './output/selector.js';
|
|
@@ -163,11 +171,26 @@ export async function resolveLiveTarget(
|
|
|
163
171
|
}
|
|
164
172
|
}
|
|
165
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Narrows a same-name match set to its lone {@link INTERACTIVE_ROLES} member
|
|
176
|
+
* when the set mixes interactive and non-interactive roles — e.g. a `button`
|
|
177
|
+
* plus the `StaticText` label rendered inside it, both carrying the same
|
|
178
|
+
* accessible name. Leaves `matches` untouched when zero or more than one
|
|
179
|
+
* interactive role is present, so a genuine tie (two buttons, or no
|
|
180
|
+
* interactive role at all) still surfaces every candidate.
|
|
181
|
+
*/
|
|
182
|
+
function preferActionable(matches: FullAXNode[]): FullAXNode[] {
|
|
183
|
+
if (matches.length <= 1) return matches;
|
|
184
|
+
const interactive = matches.filter((m) => m.role?.value !== undefined && INTERACTIVE_ROLES.has(m.role.value));
|
|
185
|
+
return interactive.length === 1 ? interactive : matches;
|
|
186
|
+
}
|
|
187
|
+
|
|
166
188
|
function settleAxMatches(
|
|
167
189
|
kind: LiveTargetKind,
|
|
168
190
|
input: string,
|
|
169
|
-
|
|
191
|
+
rawMatches: FullAXNode[],
|
|
170
192
|
): ResolvedTarget | ResolutionFailure {
|
|
193
|
+
const matches = preferActionable(rawMatches);
|
|
171
194
|
if (matches.length === 1) {
|
|
172
195
|
const m = matches[0];
|
|
173
196
|
return {
|
package/src/session/commands.ts
CHANGED
|
@@ -286,12 +286,18 @@ function generateId(): string {
|
|
|
286
286
|
return `cap-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
/** A fresh target must acknowledge Page.enable promptly. Target.createTarget
|
|
290
|
+
* already succeeded; waiting a full CDP request timeout here only postpones a
|
|
291
|
+
* failed session start before it can publish or roll back its target. */
|
|
292
|
+
export const SESSION_TAB_ATTACH_TIMEOUT_MS = 5_000;
|
|
293
|
+
|
|
289
294
|
export async function waitForPageLoad(
|
|
290
|
-
client: { waitReady(): Promise<void>; send(method: string, params?: Record<string, unknown
|
|
295
|
+
client: { waitReady(): Promise<void>; send(method: string, params?: Record<string, unknown>, timeout?: number): Promise<unknown>; on(event: string, handler: (params: unknown) => void): void; },
|
|
291
296
|
timeoutMs: number,
|
|
297
|
+
attachTimeoutMs = SESSION_TAB_ATTACH_TIMEOUT_MS,
|
|
292
298
|
): Promise<boolean> {
|
|
293
299
|
await client.waitReady();
|
|
294
|
-
await client.send('Page.enable');
|
|
300
|
+
await client.send('Page.enable', {}, attachTimeoutMs);
|
|
295
301
|
|
|
296
302
|
return await new Promise<boolean>((resolve) => {
|
|
297
303
|
const timer = setTimeout(() => resolve(true), timeoutMs);
|
|
@@ -502,7 +508,14 @@ const productionStartWorld: SessionStartWorld = {
|
|
|
502
508
|
} catch (err) {
|
|
503
509
|
throw worldFailure(`capture session start could not attach to ${url}: ${err instanceof Error ? err.message : err}. Reuse an existing tab with --target.`, err);
|
|
504
510
|
}
|
|
505
|
-
|
|
511
|
+
try {
|
|
512
|
+
return await waitForPageLoad(client, 10_000);
|
|
513
|
+
} catch (err) {
|
|
514
|
+
throw worldFailure(
|
|
515
|
+
`capture session start could not attach to ${url}: Page.enable failed during its ${SESSION_TAB_ATTACH_TIMEOUT_MS}ms attachment window (${err instanceof Error ? err.message : err}). The CDP endpoint created the tab but cannot drive it; choose another endpoint from \`capture tab list\` and retry with \`--port\`.`,
|
|
516
|
+
err,
|
|
517
|
+
);
|
|
518
|
+
}
|
|
506
519
|
} finally {
|
|
507
520
|
client.close();
|
|
508
521
|
}
|