@qa-ai-stlc/explorer 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ import type { AccessibilityNode } from '@qa-ai-stlc/schemas';
2
+ import { type NormalizeLimits } from './normalize.js';
3
+ export interface NormalizedAccessibilityTree {
4
+ readonly tree: AccessibilityNode;
5
+ readonly truncated: boolean;
6
+ }
7
+ /**
8
+ * Turns the free-form JSON `AuthPage.ariaSnapshotJSON()` returns into a capped
9
+ * `AccessibilityNode` tree. The input is untrusted, page-derived data (development plan section
10
+ * 2.6): every field is read defensively, string fields are length-capped, and the total node
11
+ * count is capped so one page can never grow the artifact — or an agent's context — unbounded.
12
+ */
13
+ export declare function normalizeAccessibilityTree(raw: unknown, limits?: NormalizeLimits): NormalizedAccessibilityTree;
14
+ //# sourceMappingURL=accessibility-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility-tree.d.ts","sourceRoot":"","sources":["../src/accessibility-tree.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAO9F,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,OAAO,EACZ,MAAM,GAAE,eAA0C,GACjD,2BAA2B,CA+D7B"}
@@ -0,0 +1,73 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { DEFAULT_NORMALIZE_LIMITS, truncateText } from './normalize.js';
4
+ const FALLBACK_ROLE = 'generic';
5
+ const TEXT_ROLE = 'text';
6
+ const ROOT_ROLE = 'root';
7
+ const BOOLEAN_FLAGS = ['checked', 'disabled', 'expanded', 'pressed', 'selected'];
8
+ /**
9
+ * Turns the free-form JSON `AuthPage.ariaSnapshotJSON()` returns into a capped
10
+ * `AccessibilityNode` tree. The input is untrusted, page-derived data (development plan section
11
+ * 2.6): every field is read defensively, string fields are length-capped, and the total node
12
+ * count is capped so one page can never grow the artifact — or an agent's context — unbounded.
13
+ */
14
+ export function normalizeAccessibilityTree(raw, limits = DEFAULT_NORMALIZE_LIMITS) {
15
+ let nodeCount = 0;
16
+ let truncated = false;
17
+ function normalizeText(text) {
18
+ const result = truncateText(text, limits);
19
+ if (result.truncated) {
20
+ truncated = true;
21
+ }
22
+ return result.text;
23
+ }
24
+ function visit(node) {
25
+ nodeCount += 1;
26
+ if (nodeCount > limits.maxTreeNodes) {
27
+ truncated = true;
28
+ return undefined;
29
+ }
30
+ // A static text fragment is serialized as a bare string, not an object with role: "text"
31
+ // (Playwright's own `ariaSnapshotJSON` format).
32
+ if (typeof node === 'string') {
33
+ return { role: TEXT_ROLE, text: normalizeText(node) };
34
+ }
35
+ const record = typeof node === 'object' && node !== null && !Array.isArray(node)
36
+ ? node
37
+ : {};
38
+ const result = { role: typeof record.role === 'string' ? record.role : FALLBACK_ROLE };
39
+ if (typeof record.name === 'string') {
40
+ result.name = normalizeText(record.name);
41
+ }
42
+ if (typeof record.text === 'string') {
43
+ result.text = normalizeText(record.text);
44
+ }
45
+ for (const flag of BOOLEAN_FLAGS) {
46
+ if (typeof record[flag] === 'boolean') {
47
+ result[flag] = record[flag];
48
+ }
49
+ }
50
+ if (Array.isArray(record.children)) {
51
+ const children = record.children
52
+ .map(visit)
53
+ .filter((child) => child !== undefined);
54
+ if (children.length > 0) {
55
+ result.children = children;
56
+ }
57
+ }
58
+ return result;
59
+ }
60
+ // `ariaSnapshotJSON()` returns a *list* of root-level nodes, not one root object; a
61
+ // single-element list is unwrapped, and a multi-element one gets a synthetic root so the
62
+ // schema's single `accessibilityTree` field always has exactly one tree to hold.
63
+ // `Array.isArray` narrows to `any[]`, so the elements are re-typed `unknown` explicitly.
64
+ const rootNodes = Array.isArray(raw) ? raw : undefined;
65
+ const rootInput = rootNodes === undefined
66
+ ? raw
67
+ : rootNodes.length === 1
68
+ ? rootNodes[0]
69
+ : { role: ROOT_ROLE, children: rootNodes };
70
+ const tree = visit(rootInput) ?? { role: FALLBACK_ROLE };
71
+ return { tree, truncated };
72
+ }
73
+ //# sourceMappingURL=accessibility-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility-tree.js","sourceRoot":"","sources":["../src/accessibility-tree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { AuthPage } from '@qa-ai-stlc/core';
2
+ import type { PageModel } from '@qa-ai-stlc/schemas';
3
+ import { type NormalizeLimits } from './normalize.js';
4
+ /**
5
+ * Builds a `PageModel` for the page `page` is currently showing: its accessibility tree,
6
+ * interactive elements, forms, tables and dialogs, all length- and count-capped (development plan
7
+ * section 2.6). Does not navigate; the caller decides which URL is current.
8
+ */
9
+ export declare function analyzePage(page: AuthPage, url: string, limits?: NormalizeLimits): Promise<PageModel>;
10
+ //# sourceMappingURL=analyze-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-page.d.ts","sourceRoot":"","sources":["../src/analyze-page.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGhF;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,eAA0C,GACjD,OAAO,CAAC,SAAS,CAAC,CAiBpB"}
@@ -0,0 +1,25 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { normalizeAccessibilityTree } from './accessibility-tree.js';
4
+ import { DEFAULT_NORMALIZE_LIMITS } from './normalize.js';
5
+ import { extractPageElements } from './page-elements.js';
6
+ /**
7
+ * Builds a `PageModel` for the page `page` is currently showing: its accessibility tree,
8
+ * interactive elements, forms, tables and dialogs, all length- and count-capped (development plan
9
+ * section 2.6). Does not navigate; the caller decides which URL is current.
10
+ */
11
+ export async function analyzePage(page, url, limits = DEFAULT_NORMALIZE_LIMITS) {
12
+ const rawAccessibilityTree = await page.ariaSnapshotJSON();
13
+ const { tree, truncated: accessibilityTruncated } = normalizeAccessibilityTree(rawAccessibilityTree, limits);
14
+ const elements = await extractPageElements(page, limits);
15
+ return {
16
+ url,
17
+ accessibilityTree: tree,
18
+ interactiveElements: elements.interactiveElements,
19
+ forms: elements.forms,
20
+ tables: elements.tables,
21
+ dialogs: elements.dialogs,
22
+ truncated: accessibilityTruncated || elements.truncated,
23
+ };
24
+ }
25
+ //# sourceMappingURL=analyze-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-page.js","sourceRoot":"","sources":["../src/analyze-page.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAItC,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAwB,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAc,EACd,GAAW,EACX,SAA0B,wBAAwB;IAElD,MAAM,oBAAoB,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC3D,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,sBAAsB,EAAE,GAAG,0BAA0B,CAC5E,oBAAoB,EACpB,MAAM,CACP,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAEzD,OAAO;QACL,GAAG;QACH,iBAAiB,EAAE,IAAI;QACvB,mBAAmB,EAAE,QAAQ,CAAC,mBAAmB;QACjD,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,SAAS,EAAE,sBAAsB,IAAI,QAAQ,CAAC,SAAS;KACxD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { type BrowserLauncher, type Clock } from '@qa-ai-stlc/core';
2
+ import { type PageModelSet } from '@qa-ai-stlc/schemas';
3
+ import { type ExplorerIdentity } from './identity.js';
4
+ import { type NormalizeLimits } from './normalize.js';
5
+ export interface AnalyzePagesOptions {
6
+ readonly urls: readonly string[];
7
+ readonly browserLauncher: BrowserLauncher;
8
+ /** Signs in before analyzing, reusing `authenticate()` (P1-05). Omit to analyze anonymously. */
9
+ readonly identity?: ExplorerIdentity;
10
+ readonly limits?: NormalizeLimits;
11
+ readonly clock?: Clock;
12
+ }
13
+ export interface AnalyzePagesResult {
14
+ readonly pageModelSet: PageModelSet;
15
+ /** Every non-GET request safe mode intercepted and cancelled; always 0 unless something is broken. */
16
+ readonly blockedRequestCount: number;
17
+ }
18
+ /**
19
+ * Analyzes every URL in `urls` — typically a crawl's `RouteMap.routes` — in one browser session,
20
+ * in safe mode (development plan section 6.3 step 3): navigating for analysis never sends a
21
+ * non-GET request either. Produces one `PageModel` per URL, in the given order.
22
+ */
23
+ export declare function analyzePages(options: AnalyzePagesOptions): Promise<AnalyzePagesResult>;
24
+ //# sourceMappingURL=analyze-pages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-pages.d.ts","sourceRoot":"","sources":["../src/analyze-pages.ts"],"names":[],"mappings":"AAGA,OAAO,EAAe,KAAK,eAAe,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACjF,OAAO,EAAkC,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExF,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGhF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,sGAAsG;IACtG,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAgC5F"}
@@ -0,0 +1,42 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { systemClock } from '@qa-ai-stlc/core';
4
+ import { SCHEMA_VERSION } from '@qa-ai-stlc/schemas';
5
+ import { analyzePage } from './analyze-page.js';
6
+ import { resolveStorageState } from './identity.js';
7
+ import { DEFAULT_NORMALIZE_LIMITS } from './normalize.js';
8
+ import { createSafeModeRouteHandler } from './safe-mode.js';
9
+ /**
10
+ * Analyzes every URL in `urls` — typically a crawl's `RouteMap.routes` — in one browser session,
11
+ * in safe mode (development plan section 6.3 step 3): navigating for analysis never sends a
12
+ * non-GET request either. Produces one `PageModel` per URL, in the given order.
13
+ */
14
+ export async function analyzePages(options) {
15
+ const clock = options.clock ?? systemClock;
16
+ const limits = options.limits ?? DEFAULT_NORMALIZE_LIMITS;
17
+ const storageState = await resolveStorageState(options.browserLauncher, options.identity);
18
+ const browser = await options.browserLauncher.launch();
19
+ try {
20
+ const context = await browser.newContext(storageState === undefined ? {} : { storageState });
21
+ const page = await context.newPage();
22
+ let blockedRequestCount = 0;
23
+ await page.route('**/*', createSafeModeRouteHandler(() => {
24
+ blockedRequestCount += 1;
25
+ }));
26
+ const pages = [];
27
+ for (const url of options.urls) {
28
+ await page.goto(url);
29
+ pages.push(await analyzePage(page, url, limits));
30
+ }
31
+ const pageModelSet = {
32
+ schemaVersion: SCHEMA_VERSION,
33
+ generatedAt: clock.now().toISOString(),
34
+ pages,
35
+ };
36
+ return { pageModelSet, blockedRequestCount };
37
+ }
38
+ finally {
39
+ await browser.close();
40
+ }
41
+ }
42
+ //# sourceMappingURL=analyze-pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-pages.js","sourceRoot":"","sources":["../src/analyze-pages.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,OAAO,EAAE,WAAW,EAAoC,MAAM,kBAAkB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAqC,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAyB,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAwB,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAiB5D;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAA4B;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,WAAW,CAAC;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,wBAAwB,CAAC;IAC1D,MAAM,YAAY,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE1F,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;IACvD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC;QAC7F,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,mBAAmB,GAAG,CAAC,CAAC;QAC5B,MAAM,IAAI,CAAC,KAAK,CACd,MAAM,EACN,0BAA0B,CAAC,GAAG,EAAE;YAC9B,mBAAmB,IAAI,CAAC,CAAC;QAC3B,CAAC,CAAC,CACH,CAAC;QAEF,MAAM,KAAK,GAAgB,EAAE,CAAC;QAC9B,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,MAAM,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,YAAY,GAAiB;YACjC,aAAa,EAAE,cAAc;YAC7B,WAAW,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;YACtC,KAAK;SACN,CAAC;QACF,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC;IAC/C,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
package/dist/crawl.d.ts CHANGED
@@ -1,11 +1,7 @@
1
1
  import { type BrowserLauncher, type Clock } from '@qa-ai-stlc/core';
2
- import { type IdentityConfig, type RouteMap } from '@qa-ai-stlc/schemas';
3
- export interface CrawlIdentity {
4
- readonly config: IdentityConfig;
5
- readonly env: Readonly<Record<string, string | undefined>>;
6
- /** Required, and only used, when `identity.config.auth` is `"cdp-attach"`. */
7
- readonly cdpEndpointUrl?: string;
8
- }
2
+ import { type RouteMap } from '@qa-ai-stlc/schemas';
3
+ import { type ExplorerIdentity } from './identity.js';
4
+ export type CrawlIdentity = ExplorerIdentity;
9
5
  export interface CrawlOptions {
10
6
  readonly startUrl: string;
11
7
  /** Hostnames the crawl may navigate to (development plan section 6.4); a link outside it is dropped. */
@@ -1 +1 @@
1
- {"version":3,"file":"crawl.d.ts","sourceRoot":"","sources":["../src/crawl.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,KAAK,EAEX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACd,MAAM,qBAAqB,CAAC;AAQ7B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC3D,8EAA8E;IAC9E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,wGAAwG;IACxG,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,6FAA6F;IAC7F,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;IAClC,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,sGAAsG;IACtG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,sGAAsG;IACtG,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAQD;;;;;GAKG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAoCvE"}
1
+ {"version":3,"file":"crawl.d.ts","sourceRoot":"","sources":["../src/crawl.ts"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,eAAe,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAwC,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG1F,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAM3E,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC;AAE7C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,wGAAwG;IACxG,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,6FAA6F;IAC7F,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;IAClC,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,sGAAsG;IACtG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,sGAAsG;IACtG,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAQD;;;;;GAKG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAoCvE"}
package/dist/crawl.js CHANGED
@@ -1,9 +1,10 @@
1
1
  // Copyright The QA-AI-STLC Authors
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { authenticate, systemClock, } from '@qa-ai-stlc/core';
4
- import { SCHEMA_VERSION, } from '@qa-ai-stlc/schemas';
3
+ import { systemClock } from '@qa-ai-stlc/core';
4
+ import { SCHEMA_VERSION } from '@qa-ai-stlc/schemas';
5
5
  import { isAllowedUrl, normalizeUrl } from './allowlist.js';
6
6
  import { extractLinks } from './extract-links.js';
7
+ import { resolveStorageState } from './identity.js';
7
8
  import { buildRequestLogHar } from './request-log.js';
8
9
  import { createSafeModeRouteHandler } from './safe-mode.js';
9
10
  const DEFAULT_MAX_PAGES = 50;
@@ -16,7 +17,7 @@ const DEFAULT_MAX_PAGES = 50;
16
17
  export async function crawl(options) {
17
18
  const clock = options.clock ?? systemClock;
18
19
  const maxPages = options.maxPages ?? DEFAULT_MAX_PAGES;
19
- const storageState = await resolveStorageState(options);
20
+ const storageState = await resolveStorageState(options.browserLauncher, options.identity);
20
21
  const browser = await options.browserLauncher.launch();
21
22
  try {
22
23
  const context = await browser.newContext(storageState === undefined ? {} : { storageState });
@@ -44,16 +45,6 @@ export async function crawl(options) {
44
45
  await browser.close();
45
46
  }
46
47
  }
47
- async function resolveStorageState(options) {
48
- if (options.identity === undefined) {
49
- return undefined;
50
- }
51
- return authenticate(options.browserLauncher, options.identity.config, options.identity.env, {
52
- ...(options.identity.cdpEndpointUrl !== undefined
53
- ? { cdpEndpointUrl: options.identity.cdpEndpointUrl }
54
- : {}),
55
- });
56
- }
57
48
  async function visitAllowedRoutes(page, options, maxPages, logEntries) {
58
49
  const visited = new Set();
59
50
  const routes = [];
package/dist/crawl.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"crawl.js","sourceRoot":"","sources":["../src/crawl.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"crawl.js","sourceRoot":"","sources":["../src/crawl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type BrowserLauncher, type StorageState } from '@qa-ai-stlc/core';
2
+ import type { IdentityConfig } from '@qa-ai-stlc/schemas';
3
+ export interface ExplorerIdentity {
4
+ readonly config: IdentityConfig;
5
+ readonly env: Readonly<Record<string, string | undefined>>;
6
+ /** Required, and only used, when `identity.config.auth` is `"cdp-attach"`. */
7
+ readonly cdpEndpointUrl?: string;
8
+ }
9
+ /** Signs in through P1-05's `authenticate()` when an identity is given; `undefined` for an anonymous session. */
10
+ export declare function resolveStorageState(browserLauncher: BrowserLauncher, identity: ExplorerIdentity | undefined): Promise<StorageState | undefined>;
11
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC3D,8EAA8E;IAC9E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,iHAAiH;AACjH,wBAAsB,mBAAmB,CACvC,eAAe,EAAE,eAAe,EAChC,QAAQ,EAAE,gBAAgB,GAAG,SAAS,GACrC,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAOnC"}
@@ -0,0 +1,13 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { authenticate } from '@qa-ai-stlc/core';
4
+ /** Signs in through P1-05's `authenticate()` when an identity is given; `undefined` for an anonymous session. */
5
+ export async function resolveStorageState(browserLauncher, identity) {
6
+ if (identity === undefined) {
7
+ return undefined;
8
+ }
9
+ return authenticate(browserLauncher, identity.config, identity.env, {
10
+ ...(identity.cdpEndpointUrl !== undefined ? { cdpEndpointUrl: identity.cdpEndpointUrl } : {}),
11
+ });
12
+ }
13
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,OAAO,EAAE,YAAY,EAA2C,MAAM,kBAAkB,CAAC;AAUzF,iHAAiH;AACjH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,eAAgC,EAChC,QAAsC;IAEtC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,YAAY,CAAC,eAAe,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,EAAE;QAClE,GAAG,CAAC,QAAQ,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9F,CAAC,CAAC;AACL,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,13 @@
1
+ export * from './accessibility-tree.js';
1
2
  export * from './allowlist.js';
3
+ export * from './analyze-page.js';
4
+ export * from './analyze-pages.js';
2
5
  export * from './crawl.js';
3
6
  export * from './extract-links.js';
7
+ export * from './identity.js';
8
+ export * from './normalize.js';
9
+ export * from './page-elements.js';
4
10
  export * from './request-log.js';
11
+ export * from './safe-mode.js';
12
+ export * from './synthesize-locators.js';
5
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,15 @@
1
1
  // Copyright The QA-AI-STLC Authors
2
2
  // SPDX-License-Identifier: Apache-2.0
3
+ export * from './accessibility-tree.js';
3
4
  export * from './allowlist.js';
5
+ export * from './analyze-page.js';
6
+ export * from './analyze-pages.js';
4
7
  export * from './crawl.js';
5
8
  export * from './extract-links.js';
9
+ export * from './identity.js';
10
+ export * from './normalize.js';
11
+ export * from './page-elements.js';
6
12
  export * from './request-log.js';
13
+ export * from './safe-mode.js';
14
+ export * from './synthesize-locators.js';
7
15
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC"}
@@ -0,0 +1,25 @@
1
+ export interface NormalizeLimits {
2
+ readonly maxTextLength: number;
3
+ readonly maxArrayLength: number;
4
+ readonly maxTreeNodes: number;
5
+ }
6
+ export declare const DEFAULT_NORMALIZE_LIMITS: NormalizeLimits;
7
+ /**
8
+ * Caps a single piece of page-derived text (development plan section 2.6): untrusted content
9
+ * from the application under test is never passed on unbounded. Returns the text unchanged, and
10
+ * `truncated: false`, when it is already within the limit.
11
+ */
12
+ export declare function truncateText(text: string, limits?: NormalizeLimits): {
13
+ readonly text: string;
14
+ readonly truncated: boolean;
15
+ };
16
+ /**
17
+ * Caps how many items of a page-derived list (interactive elements, table rows, ...) are kept.
18
+ * Every element on a page still gets *counted* elsewhere; this only bounds how many are carried
19
+ * into the artifact and, eventually, into an agent's context.
20
+ */
21
+ export declare function capArray<T>(items: readonly T[], limits?: NormalizeLimits): {
22
+ readonly items: readonly T[];
23
+ readonly truncated: boolean;
24
+ };
25
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,wBAAwB,EAAE,eAItC,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,eAA0C,GACjD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAKxD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,MAAM,GAAE,eAA0C,GACjD;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAK/D"}
@@ -0,0 +1,31 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export const DEFAULT_NORMALIZE_LIMITS = {
4
+ maxTextLength: 200,
5
+ maxArrayLength: 200,
6
+ maxTreeNodes: 500,
7
+ };
8
+ const TRUNCATION_SUFFIX = '…';
9
+ /**
10
+ * Caps a single piece of page-derived text (development plan section 2.6): untrusted content
11
+ * from the application under test is never passed on unbounded. Returns the text unchanged, and
12
+ * `truncated: false`, when it is already within the limit.
13
+ */
14
+ export function truncateText(text, limits = DEFAULT_NORMALIZE_LIMITS) {
15
+ if (text.length <= limits.maxTextLength) {
16
+ return { text, truncated: false };
17
+ }
18
+ return { text: text.slice(0, limits.maxTextLength) + TRUNCATION_SUFFIX, truncated: true };
19
+ }
20
+ /**
21
+ * Caps how many items of a page-derived list (interactive elements, table rows, ...) are kept.
22
+ * Every element on a page still gets *counted* elsewhere; this only bounds how many are carried
23
+ * into the artifact and, eventually, into an agent's context.
24
+ */
25
+ export function capArray(items, limits = DEFAULT_NORMALIZE_LIMITS) {
26
+ if (items.length <= limits.maxArrayLength) {
27
+ return { items, truncated: false };
28
+ }
29
+ return { items: items.slice(0, limits.maxArrayLength), truncated: true };
30
+ }
31
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAQtC,MAAM,CAAC,MAAM,wBAAwB,GAAoB;IACvD,aAAa,EAAE,GAAG;IAClB,cAAc,EAAE,GAAG;IACnB,YAAY,EAAE,GAAG;CAClB,CAAC;AAEF,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,SAA0B,wBAAwB;IAElD,IAAI,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACxC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACpC,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,aAAa,CAAC,GAAG,iBAAiB,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAC5F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,KAAmB,EACnB,SAA0B,wBAAwB;IAElD,IAAI,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC1C,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { AuthPage } from '@qa-ai-stlc/core';
2
+ import type { Dialog, Form, InteractiveElement, Table } from '@qa-ai-stlc/schemas';
3
+ import { type NormalizeLimits } from './normalize.js';
4
+ export interface PageElements {
5
+ readonly interactiveElements: InteractiveElement[];
6
+ readonly forms: Form[];
7
+ readonly tables: Table[];
8
+ readonly dialogs: Dialog[];
9
+ readonly truncated: boolean;
10
+ }
11
+ /** Extracts and normalizes every interactive element, form, table and dialog on the current page. */
12
+ export declare function extractPageElements(page: AuthPage, limits: NormalizeLimits): Promise<PageElements>;
13
+ //# sourceMappingURL=page-elements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-elements.d.ts","sourceRoot":"","sources":["../src/page-elements.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EACV,MAAM,EACN,IAAI,EAEJ,kBAAkB,EAElB,KAAK,EACN,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA2C9E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAkPD,qGAAqG;AACrG,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAuCxG"}
@@ -0,0 +1,255 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { capArray, truncateText } from './normalize.js';
4
+ const INTERACTIVE_ELEMENT_KINDS = [
5
+ 'button',
6
+ 'link',
7
+ 'input',
8
+ 'select',
9
+ 'textarea',
10
+ ];
11
+ function isInteractiveElementKind(value) {
12
+ return INTERACTIVE_ELEMENT_KINDS.includes(value);
13
+ }
14
+ /**
15
+ * Reads every interactive element, form, table and dialog on the current page. The callback runs
16
+ * inside the browser (Playwright serializes it across the CDP boundary), so it can reference DOM
17
+ * globals unavailable to the rest of this package.
18
+ */
19
+ async function readRawPageElements(page) {
20
+ /* v8 ignore next 90 -- runs in the browser's own V8 instance, invisible to Node coverage */
21
+ const result = await page.evaluate(() => {
22
+ function accessibleName(element) {
23
+ const ariaLabel = element.getAttribute('aria-label');
24
+ if (ariaLabel !== null && ariaLabel.trim().length > 0) {
25
+ return ariaLabel;
26
+ }
27
+ const text = element.textContent.trim();
28
+ return text.length > 0 ? text : undefined;
29
+ }
30
+ // A small, explicit table rather than full ARIA role computation: locator synthesis only
31
+ // needs a role plausible enough for `getByRole`, and the five kinds below are the only ones
32
+ // this extractor recognizes in the first place.
33
+ function computeRole(element, kind) {
34
+ const explicit = element.getAttribute('role');
35
+ if (explicit !== null && explicit.trim().length > 0) {
36
+ return explicit;
37
+ }
38
+ if (kind === 'textarea') {
39
+ return 'textbox';
40
+ }
41
+ if (kind === 'select') {
42
+ return 'combobox';
43
+ }
44
+ if (kind === 'input') {
45
+ const type = (element.getAttribute('type') ?? 'text').toLowerCase();
46
+ if (type === 'checkbox' || type === 'radio') {
47
+ return type;
48
+ }
49
+ if (type === 'button' || type === 'submit' || type === 'reset') {
50
+ return 'button';
51
+ }
52
+ return 'textbox';
53
+ }
54
+ return kind === 'button' || kind === 'link' ? kind : undefined;
55
+ }
56
+ // `for`/`id` association first, then an ancestor `<label>` that wraps the field.
57
+ function labelText(element) {
58
+ const id = element.getAttribute('id');
59
+ if (id !== null && id.length > 0) {
60
+ const associated = document.querySelector(`label[for="${CSS.escape(id)}"]`);
61
+ const text = associated?.textContent.trim();
62
+ if (text !== undefined && text.length > 0) {
63
+ return text;
64
+ }
65
+ }
66
+ const wrapping = element.closest('label')?.textContent.trim();
67
+ return wrapping !== undefined && wrapping.length > 0 ? wrapping : undefined;
68
+ }
69
+ // Position among same-tag siblings, so a CSS fallback candidate can be built without a live
70
+ // page: `tagName:nth-of-type(nthOfType)` scoped to the element's own parent.
71
+ function nthOfType(element) {
72
+ const parent = element.parentElement;
73
+ if (parent === null) {
74
+ return 1;
75
+ }
76
+ let index = 0;
77
+ for (const sibling of parent.children) {
78
+ if (sibling.tagName === element.tagName) {
79
+ index += 1;
80
+ if (sibling === element) {
81
+ return index;
82
+ }
83
+ }
84
+ }
85
+ return 1;
86
+ }
87
+ const kindByTagName = {
88
+ a: 'link',
89
+ button: 'button',
90
+ input: 'input',
91
+ select: 'select',
92
+ textarea: 'textarea',
93
+ };
94
+ const interactiveElements = Array.from(document.querySelectorAll('button, a[href], input, select, textarea')).map((element) => {
95
+ const tagName = element.tagName.toLowerCase();
96
+ const kind = kindByTagName[tagName] ?? tagName;
97
+ return {
98
+ kind,
99
+ accessibleName: accessibleName(element),
100
+ testId: element.getAttribute('data-testid') ?? undefined,
101
+ role: computeRole(element, kind),
102
+ label: labelText(element),
103
+ placeholder: element.getAttribute('placeholder') ?? undefined,
104
+ htmlId: element.getAttribute('id') ?? undefined,
105
+ tagName,
106
+ nthOfType: nthOfType(element),
107
+ };
108
+ });
109
+ const forms = Array.from(document.querySelectorAll('form')).map((form) => ({
110
+ action: form.getAttribute('action') ?? undefined,
111
+ method: (form.getAttribute('method') ?? 'get').toLowerCase(),
112
+ fields: Array.from(form.querySelectorAll('input, select, textarea')).map((field) => ({
113
+ name: field.getAttribute('name') ?? undefined,
114
+ type: field.getAttribute('type') ?? field.tagName.toLowerCase(),
115
+ required: field.hasAttribute('required'),
116
+ })),
117
+ }));
118
+ const tables = Array.from(document.querySelectorAll('table')).map((table) => ({
119
+ columnHeaders: Array.from(table.querySelectorAll('thead th')).map((header) => header.textContent.trim()),
120
+ rowCount: table.querySelectorAll('tbody tr').length,
121
+ }));
122
+ const dialogs = Array.from(document.querySelectorAll('dialog, [role="dialog"]')).map((dialog) => ({
123
+ accessibleName: accessibleName(dialog),
124
+ open: dialog.hasAttribute('open'),
125
+ }));
126
+ return { interactiveElements, forms, tables, dialogs };
127
+ });
128
+ return isRawPageElements(result) ? result : undefined;
129
+ }
130
+ function isRawPageElements(value) {
131
+ if (typeof value !== 'object' || value === null) {
132
+ return false;
133
+ }
134
+ const record = value;
135
+ return (Array.isArray(record.interactiveElements) &&
136
+ Array.isArray(record.forms) &&
137
+ Array.isArray(record.tables) &&
138
+ Array.isArray(record.dialogs));
139
+ }
140
+ function normalizeInteractiveElement(raw, limits, onTruncated) {
141
+ if (!isInteractiveElementKind(raw.kind)) {
142
+ return undefined;
143
+ }
144
+ const element = { kind: raw.kind, tagName: raw.tagName, nthOfType: raw.nthOfType };
145
+ if (raw.accessibleName !== undefined) {
146
+ const { text, truncated } = truncateText(raw.accessibleName, limits);
147
+ element.accessibleName = text;
148
+ if (truncated) {
149
+ onTruncated();
150
+ }
151
+ }
152
+ if (raw.testId !== undefined) {
153
+ element.testId = raw.testId;
154
+ }
155
+ if (raw.role !== undefined) {
156
+ element.role = raw.role;
157
+ }
158
+ if (raw.label !== undefined) {
159
+ const { text, truncated } = truncateText(raw.label, limits);
160
+ element.label = text;
161
+ if (truncated) {
162
+ onTruncated();
163
+ }
164
+ }
165
+ if (raw.placeholder !== undefined) {
166
+ const { text, truncated } = truncateText(raw.placeholder, limits);
167
+ element.placeholder = text;
168
+ if (truncated) {
169
+ onTruncated();
170
+ }
171
+ }
172
+ if (raw.htmlId !== undefined) {
173
+ const { text, truncated } = truncateText(raw.htmlId, limits);
174
+ element.htmlId = text;
175
+ if (truncated) {
176
+ onTruncated();
177
+ }
178
+ }
179
+ return element;
180
+ }
181
+ function normalizeForm(raw) {
182
+ const form = {
183
+ method: raw.method,
184
+ fields: raw.fields.map((field) => {
185
+ const result = { type: field.type, required: field.required };
186
+ if (field.name !== undefined) {
187
+ result.name = field.name;
188
+ }
189
+ return result;
190
+ }),
191
+ };
192
+ if (raw.action !== undefined) {
193
+ form.action = raw.action;
194
+ }
195
+ return form;
196
+ }
197
+ function normalizeTable(raw, limits, onTruncated) {
198
+ const headers = raw.columnHeaders.map((header) => {
199
+ const { text, truncated } = truncateText(header, limits);
200
+ if (truncated) {
201
+ onTruncated();
202
+ }
203
+ return text;
204
+ });
205
+ return { columnHeaders: headers, rowCount: raw.rowCount };
206
+ }
207
+ function normalizeDialog(raw, limits, onTruncated) {
208
+ const dialog = { open: raw.open };
209
+ if (raw.accessibleName !== undefined) {
210
+ const { text, truncated } = truncateText(raw.accessibleName, limits);
211
+ dialog.accessibleName = text;
212
+ if (truncated) {
213
+ onTruncated();
214
+ }
215
+ }
216
+ return dialog;
217
+ }
218
+ /** Extracts and normalizes every interactive element, form, table and dialog on the current page. */
219
+ export async function extractPageElements(page, limits) {
220
+ const raw = await readRawPageElements(page);
221
+ if (raw === undefined) {
222
+ return { interactiveElements: [], forms: [], tables: [], dialogs: [], truncated: true };
223
+ }
224
+ let truncated = false;
225
+ const markTruncated = () => {
226
+ truncated = true;
227
+ };
228
+ const interactiveElements = capArray(raw.interactiveElements, limits);
229
+ if (interactiveElements.truncated) {
230
+ truncated = true;
231
+ }
232
+ const forms = capArray(raw.forms, limits);
233
+ if (forms.truncated) {
234
+ truncated = true;
235
+ }
236
+ const tables = capArray(raw.tables, limits);
237
+ if (tables.truncated) {
238
+ truncated = true;
239
+ }
240
+ const dialogs = capArray(raw.dialogs, limits);
241
+ if (dialogs.truncated) {
242
+ truncated = true;
243
+ }
244
+ const normalizedInteractiveElements = interactiveElements.items
245
+ .map((element) => normalizeInteractiveElement(element, limits, markTruncated))
246
+ .filter((element) => element !== undefined);
247
+ return {
248
+ interactiveElements: normalizedInteractiveElements,
249
+ forms: forms.items.map(normalizeForm),
250
+ tables: tables.items.map((table) => normalizeTable(table, limits, markTruncated)),
251
+ dialogs: dialogs.items.map((dialog) => normalizeDialog(dialog, limits, markTruncated)),
252
+ truncated,
253
+ };
254
+ }
255
+ //# sourceMappingURL=page-elements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-elements.js","sourceRoot":"","sources":["../src/page-elements.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { InteractiveElement, LocatorCandidate } from '@qa-ai-stlc/schemas';
2
+ export declare const LOCATOR_POLICIES: readonly ["playwright-default", "testid-first", "strict-no-css"];
3
+ export type LocatorPolicy = (typeof LOCATOR_POLICIES)[number];
4
+ /** Generates locator candidates for one interactive element, ordered by the given policy. */
5
+ export declare function synthesizeLocatorCandidates(element: InteractiveElement, policy: LocatorPolicy): LocatorCandidate[];
6
+ //# sourceMappingURL=synthesize-locators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synthesize-locators.d.ts","sourceRoot":"","sources":["../src/synthesize-locators.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEhF,eAAO,MAAM,gBAAgB,kEAAmE,CAAC;AACjG,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAyE9D,6FAA6F;AAC7F,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kBAAkB,EAC3B,MAAM,EAAE,aAAa,GACpB,gBAAgB,EAAE,CAapB"}
@@ -0,0 +1,74 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export const LOCATOR_POLICIES = ['playwright-default', 'testid-first', 'strict-no-css'];
4
+ // A CSS `id` is untrusted, page-derived text: it can contain anything, including characters that
5
+ // are not valid unescaped in a CSS identifier. Rather than escaping, an id outside this safe
6
+ // pattern falls back to the nth-of-type candidate below.
7
+ const CSS_SAFE_ID = /^[A-Za-z_][A-Za-z0-9_-]*$/;
8
+ function buildRoleCandidate(element) {
9
+ const name = element.accessibleName ?? element.label;
10
+ if (element.role === undefined || name === undefined) {
11
+ return undefined;
12
+ }
13
+ return { strategy: 'role', value: JSON.stringify({ role: element.role, name }), fragile: false };
14
+ }
15
+ function buildTestIdCandidate(element) {
16
+ return element.testId === undefined
17
+ ? undefined
18
+ : { strategy: 'testId', value: element.testId, fragile: false };
19
+ }
20
+ function buildLabelCandidate(element) {
21
+ return element.label === undefined
22
+ ? undefined
23
+ : { strategy: 'label', value: element.label, fragile: false };
24
+ }
25
+ function buildPlaceholderCandidate(element) {
26
+ return element.placeholder === undefined
27
+ ? undefined
28
+ : { strategy: 'placeholder', value: element.placeholder, fragile: false };
29
+ }
30
+ function buildTextCandidate(element) {
31
+ return element.accessibleName === undefined
32
+ ? undefined
33
+ : { strategy: 'text', value: element.accessibleName, fragile: false };
34
+ }
35
+ // The last-resort candidate: always present, always flagged fragile, since a CSS selector breaks
36
+ // under refactors that Playwright's role/label/testid locators survive (development plan 6.3.4).
37
+ function buildCssCandidate(element) {
38
+ const value = element.htmlId !== undefined && CSS_SAFE_ID.test(element.htmlId)
39
+ ? `#${element.htmlId}`
40
+ : `${element.tagName}:nth-of-type(${String(element.nthOfType)})`;
41
+ return { strategy: 'css', value, fragile: true };
42
+ }
43
+ const NON_CSS_BUILDERS = {
44
+ role: buildRoleCandidate,
45
+ testId: buildTestIdCandidate,
46
+ label: buildLabelCandidate,
47
+ placeholder: buildPlaceholderCandidate,
48
+ text: buildTextCandidate,
49
+ };
50
+ // Order follows Playwright's own guidance (development plan 6.3.4): role with an accessible name
51
+ // first, then test ID, then label/placeholder, then text, with CSS always last. `testid-first`
52
+ // swaps the first two for teams that mandate stable test IDs; `strict-no-css` drops the fragile
53
+ // fallback entirely, so an element with none of the other signals gets zero candidates.
54
+ const POLICY_ORDER = {
55
+ 'playwright-default': ['role', 'testId', 'label', 'placeholder', 'text', 'css'],
56
+ 'testid-first': ['testId', 'role', 'label', 'placeholder', 'text', 'css'],
57
+ 'strict-no-css': ['role', 'testId', 'label', 'placeholder', 'text'],
58
+ };
59
+ /** Generates locator candidates for one interactive element, ordered by the given policy. */
60
+ export function synthesizeLocatorCandidates(element, policy) {
61
+ const candidates = [];
62
+ for (const strategy of POLICY_ORDER[policy]) {
63
+ if (strategy === 'css') {
64
+ candidates.push(buildCssCandidate(element));
65
+ continue;
66
+ }
67
+ const candidate = NON_CSS_BUILDERS[strategy](element);
68
+ if (candidate !== undefined) {
69
+ candidates.push(candidate);
70
+ }
71
+ }
72
+ return candidates;
73
+ }
74
+ //# sourceMappingURL=synthesize-locators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synthesize-locators.js","sourceRoot":"","sources":["../src/synthesize-locators.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qa-ai-stlc/explorer",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "The crawler: route discovery in safe mode, redacted request logging, and the route map every later explorer phase builds on.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -28,8 +28,8 @@
28
28
  "clean": "node ../../scripts/clean-dir.mjs dist"
29
29
  },
30
30
  "dependencies": {
31
- "@qa-ai-stlc/core": "0.1.0",
32
- "@qa-ai-stlc/schemas": "0.1.0"
31
+ "@qa-ai-stlc/core": "0.2.1",
32
+ "@qa-ai-stlc/schemas": "0.3.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@qa-ai-stlc/test-utils": "0.0.0"