agent-web-interface 4.0.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.
- package/LICENSE +21 -0
- package/README.md +226 -0
- package/dist/src/browser/ensure-browser.d.ts +39 -0
- package/dist/src/browser/ensure-browser.d.ts.map +1 -0
- package/dist/src/browser/ensure-browser.js +65 -0
- package/dist/src/browser/ensure-browser.js.map +1 -0
- package/dist/src/browser/index.d.ts +8 -0
- package/dist/src/browser/index.d.ts.map +1 -0
- package/dist/src/browser/index.js +8 -0
- package/dist/src/browser/index.js.map +1 -0
- package/dist/src/browser/page-network-tracker.d.ts +96 -0
- package/dist/src/browser/page-network-tracker.d.ts.map +1 -0
- package/dist/src/browser/page-network-tracker.js +235 -0
- package/dist/src/browser/page-network-tracker.js.map +1 -0
- package/dist/src/browser/page-registry.d.ts +137 -0
- package/dist/src/browser/page-registry.d.ts.map +1 -0
- package/dist/src/browser/page-registry.js +194 -0
- package/dist/src/browser/page-registry.js.map +1 -0
- package/dist/src/browser/page-stabilization.d.ts +35 -0
- package/dist/src/browser/page-stabilization.d.ts.map +1 -0
- package/dist/src/browser/page-stabilization.js +42 -0
- package/dist/src/browser/page-stabilization.js.map +1 -0
- package/dist/src/browser/session-manager.d.ts +336 -0
- package/dist/src/browser/session-manager.d.ts.map +1 -0
- package/dist/src/browser/session-manager.js +964 -0
- package/dist/src/browser/session-manager.js.map +1 -0
- package/dist/src/cdp/cdp-client.interface.d.ts +193 -0
- package/dist/src/cdp/cdp-client.interface.d.ts.map +1 -0
- package/dist/src/cdp/cdp-client.interface.js +9 -0
- package/dist/src/cdp/cdp-client.interface.js.map +1 -0
- package/dist/src/cdp/index.d.ts +9 -0
- package/dist/src/cdp/index.d.ts.map +1 -0
- package/dist/src/cdp/index.js +8 -0
- package/dist/src/cdp/index.js.map +1 -0
- package/dist/src/cdp/puppeteer-cdp-client.d.ts +97 -0
- package/dist/src/cdp/puppeteer-cdp-client.d.ts.map +1 -0
- package/dist/src/cdp/puppeteer-cdp-client.js +273 -0
- package/dist/src/cdp/puppeteer-cdp-client.js.map +1 -0
- package/dist/src/cli/args.d.ts +35 -0
- package/dist/src/cli/args.d.ts.map +1 -0
- package/dist/src/cli/args.js +76 -0
- package/dist/src/cli/args.js.map +1 -0
- package/dist/src/delta/dom-stabilizer.d.ts +46 -0
- package/dist/src/delta/dom-stabilizer.d.ts.map +1 -0
- package/dist/src/delta/dom-stabilizer.js +121 -0
- package/dist/src/delta/dom-stabilizer.js.map +1 -0
- package/dist/src/delta/index.d.ts +8 -0
- package/dist/src/delta/index.d.ts.map +1 -0
- package/dist/src/delta/index.js +7 -0
- package/dist/src/delta/index.js.map +1 -0
- package/dist/src/factpack/action-selector.d.ts +36 -0
- package/dist/src/factpack/action-selector.d.ts.map +1 -0
- package/dist/src/factpack/action-selector.js +367 -0
- package/dist/src/factpack/action-selector.js.map +1 -0
- package/dist/src/factpack/dialog-detector.d.ts +19 -0
- package/dist/src/factpack/dialog-detector.d.ts.map +1 -0
- package/dist/src/factpack/dialog-detector.js +354 -0
- package/dist/src/factpack/dialog-detector.js.map +1 -0
- package/dist/src/factpack/form-detector.d.ts +28 -0
- package/dist/src/factpack/form-detector.d.ts.map +1 -0
- package/dist/src/factpack/form-detector.js +555 -0
- package/dist/src/factpack/form-detector.js.map +1 -0
- package/dist/src/factpack/index.d.ts +32 -0
- package/dist/src/factpack/index.d.ts.map +1 -0
- package/dist/src/factpack/index.js +73 -0
- package/dist/src/factpack/index.js.map +1 -0
- package/dist/src/factpack/page-classifier.d.ts +22 -0
- package/dist/src/factpack/page-classifier.d.ts.map +1 -0
- package/dist/src/factpack/page-classifier.js +526 -0
- package/dist/src/factpack/page-classifier.js.map +1 -0
- package/dist/src/factpack/types.d.ts +307 -0
- package/dist/src/factpack/types.d.ts.map +1 -0
- package/dist/src/factpack/types.js +12 -0
- package/dist/src/factpack/types.js.map +1 -0
- package/dist/src/form/dependency-tracker.d.ts +108 -0
- package/dist/src/form/dependency-tracker.d.ts.map +1 -0
- package/dist/src/form/dependency-tracker.js +298 -0
- package/dist/src/form/dependency-tracker.js.map +1 -0
- package/dist/src/form/field-extractor.d.ts +32 -0
- package/dist/src/form/field-extractor.d.ts.map +1 -0
- package/dist/src/form/field-extractor.js +544 -0
- package/dist/src/form/field-extractor.js.map +1 -0
- package/dist/src/form/form-detector.d.ts +103 -0
- package/dist/src/form/form-detector.d.ts.map +1 -0
- package/dist/src/form/form-detector.js +704 -0
- package/dist/src/form/form-detector.js.map +1 -0
- package/dist/src/form/form-state.d.ts +23 -0
- package/dist/src/form/form-state.d.ts.map +1 -0
- package/dist/src/form/form-state.js +39 -0
- package/dist/src/form/form-state.js.map +1 -0
- package/dist/src/form/index.d.ts +23 -0
- package/dist/src/form/index.d.ts.map +1 -0
- package/dist/src/form/index.js +27 -0
- package/dist/src/form/index.js.map +1 -0
- package/dist/src/form/runtime-value-reader.d.ts +72 -0
- package/dist/src/form/runtime-value-reader.d.ts.map +1 -0
- package/dist/src/form/runtime-value-reader.js +232 -0
- package/dist/src/form/runtime-value-reader.js.map +1 -0
- package/dist/src/form/types.d.ts +384 -0
- package/dist/src/form/types.d.ts.map +1 -0
- package/dist/src/form/types.js +17 -0
- package/dist/src/form/types.js.map +1 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +212 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/constants.d.ts +27 -0
- package/dist/src/lib/constants.d.ts.map +1 -0
- package/dist/src/lib/constants.js +63 -0
- package/dist/src/lib/constants.js.map +1 -0
- package/dist/src/lib/index.d.ts +12 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +17 -0
- package/dist/src/lib/index.js.map +1 -0
- package/dist/src/lib/regions.d.ts +29 -0
- package/dist/src/lib/regions.d.ts.map +1 -0
- package/dist/src/lib/regions.js +93 -0
- package/dist/src/lib/regions.js.map +1 -0
- package/dist/src/lib/scoring.d.ts +47 -0
- package/dist/src/lib/scoring.d.ts.map +1 -0
- package/dist/src/lib/scoring.js +79 -0
- package/dist/src/lib/scoring.js.map +1 -0
- package/dist/src/lib/selectors.d.ts +42 -0
- package/dist/src/lib/selectors.d.ts.map +1 -0
- package/dist/src/lib/selectors.js +138 -0
- package/dist/src/lib/selectors.js.map +1 -0
- package/dist/src/lib/text-utils.d.ts +155 -0
- package/dist/src/lib/text-utils.d.ts.map +1 -0
- package/dist/src/lib/text-utils.js +391 -0
- package/dist/src/lib/text-utils.js.map +1 -0
- package/dist/src/observation/eid-linker.d.ts +104 -0
- package/dist/src/observation/eid-linker.d.ts.map +1 -0
- package/dist/src/observation/eid-linker.js +403 -0
- package/dist/src/observation/eid-linker.js.map +1 -0
- package/dist/src/observation/index.d.ts +12 -0
- package/dist/src/observation/index.d.ts.map +1 -0
- package/dist/src/observation/index.js +15 -0
- package/dist/src/observation/index.js.map +1 -0
- package/dist/src/observation/observation-accumulator.d.ts +58 -0
- package/dist/src/observation/observation-accumulator.d.ts.map +1 -0
- package/dist/src/observation/observation-accumulator.js +213 -0
- package/dist/src/observation/observation-accumulator.js.map +1 -0
- package/dist/src/observation/observation.types.d.ts +139 -0
- package/dist/src/observation/observation.types.d.ts.map +1 -0
- package/dist/src/observation/observation.types.js +59 -0
- package/dist/src/observation/observation.types.js.map +1 -0
- package/dist/src/observation/observer-script.d.ts +19 -0
- package/dist/src/observation/observer-script.d.ts.map +1 -0
- package/dist/src/observation/observer-script.js +569 -0
- package/dist/src/observation/observer-script.js.map +1 -0
- package/dist/src/query/index.d.ts +9 -0
- package/dist/src/query/index.d.ts.map +1 -0
- package/dist/src/query/index.js +10 -0
- package/dist/src/query/index.js.map +1 -0
- package/dist/src/query/query-engine.d.ts +111 -0
- package/dist/src/query/query-engine.d.ts.map +1 -0
- package/dist/src/query/query-engine.js +509 -0
- package/dist/src/query/query-engine.js.map +1 -0
- package/dist/src/query/types/index.d.ts +5 -0
- package/dist/src/query/types/index.d.ts.map +1 -0
- package/dist/src/query/types/index.js +5 -0
- package/dist/src/query/types/index.js.map +1 -0
- package/dist/src/query/types/query.types.d.ts +141 -0
- package/dist/src/query/types/query.types.d.ts.map +1 -0
- package/dist/src/query/types/query.types.js +19 -0
- package/dist/src/query/types/query.types.js.map +1 -0
- package/dist/src/renderer/budget-manager.d.ts +46 -0
- package/dist/src/renderer/budget-manager.d.ts.map +1 -0
- package/dist/src/renderer/budget-manager.js +133 -0
- package/dist/src/renderer/budget-manager.js.map +1 -0
- package/dist/src/renderer/constants.d.ts +38 -0
- package/dist/src/renderer/constants.d.ts.map +1 -0
- package/dist/src/renderer/constants.js +29 -0
- package/dist/src/renderer/constants.js.map +1 -0
- package/dist/src/renderer/index.d.ts +12 -0
- package/dist/src/renderer/index.d.ts.map +1 -0
- package/dist/src/renderer/index.js +16 -0
- package/dist/src/renderer/index.js.map +1 -0
- package/dist/src/renderer/section-renderers.d.ts +42 -0
- package/dist/src/renderer/section-renderers.d.ts.map +1 -0
- package/dist/src/renderer/section-renderers.js +252 -0
- package/dist/src/renderer/section-renderers.js.map +1 -0
- package/dist/src/renderer/token-counter.d.ts +45 -0
- package/dist/src/renderer/token-counter.d.ts.map +1 -0
- package/dist/src/renderer/token-counter.js +65 -0
- package/dist/src/renderer/token-counter.js.map +1 -0
- package/dist/src/renderer/types.d.ts +71 -0
- package/dist/src/renderer/types.d.ts.map +1 -0
- package/dist/src/renderer/types.js +7 -0
- package/dist/src/renderer/types.js.map +1 -0
- package/dist/src/renderer/xml-renderer.d.ts +42 -0
- package/dist/src/renderer/xml-renderer.d.ts.map +1 -0
- package/dist/src/renderer/xml-renderer.js +103 -0
- package/dist/src/renderer/xml-renderer.js.map +1 -0
- package/dist/src/server/index.d.ts +8 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +8 -0
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/server/mcp-server.d.ts +59 -0
- package/dist/src/server/mcp-server.d.ts.map +1 -0
- package/dist/src/server/mcp-server.js +140 -0
- package/dist/src/server/mcp-server.js.map +1 -0
- package/dist/src/server/server-config.d.ts +41 -0
- package/dist/src/server/server-config.d.ts.map +1 -0
- package/dist/src/server/server-config.js +80 -0
- package/dist/src/server/server-config.js.map +1 -0
- package/dist/src/server/session-store.d.ts +148 -0
- package/dist/src/server/session-store.d.ts.map +1 -0
- package/dist/src/server/session-store.js +224 -0
- package/dist/src/server/session-store.js.map +1 -0
- package/dist/src/shared/errors/browser-session.error.d.ts +102 -0
- package/dist/src/shared/errors/browser-session.error.d.ts.map +1 -0
- package/dist/src/shared/errors/browser-session.error.js +153 -0
- package/dist/src/shared/errors/browser-session.error.js.map +1 -0
- package/dist/src/shared/errors/index.d.ts +5 -0
- package/dist/src/shared/errors/index.d.ts.map +1 -0
- package/dist/src/shared/errors/index.js +5 -0
- package/dist/src/shared/errors/index.js.map +1 -0
- package/dist/src/shared/services/dom-transformer.service.d.ts +71 -0
- package/dist/src/shared/services/dom-transformer.service.d.ts.map +1 -0
- package/dist/src/shared/services/dom-transformer.service.js +190 -0
- package/dist/src/shared/services/dom-transformer.service.js.map +1 -0
- package/dist/src/shared/services/index.d.ts +7 -0
- package/dist/src/shared/services/index.d.ts.map +1 -0
- package/dist/src/shared/services/index.js +7 -0
- package/dist/src/shared/services/index.js.map +1 -0
- package/dist/src/shared/services/logging.service.d.ts +154 -0
- package/dist/src/shared/services/logging.service.d.ts.map +1 -0
- package/dist/src/shared/services/logging.service.js +267 -0
- package/dist/src/shared/services/logging.service.js.map +1 -0
- package/dist/src/shared/services/selector-builder.service.d.ts +53 -0
- package/dist/src/shared/services/selector-builder.service.d.ts.map +1 -0
- package/dist/src/shared/services/selector-builder.service.js +240 -0
- package/dist/src/shared/services/selector-builder.service.js.map +1 -0
- package/dist/src/shared/types/base.types.d.ts +45 -0
- package/dist/src/shared/types/base.types.d.ts.map +1 -0
- package/dist/src/shared/types/base.types.js +8 -0
- package/dist/src/shared/types/base.types.js.map +1 -0
- package/dist/src/shared/types/index.d.ts +5 -0
- package/dist/src/shared/types/index.d.ts.map +1 -0
- package/dist/src/shared/types/index.js +5 -0
- package/dist/src/shared/types/index.js.map +1 -0
- package/dist/src/snapshot/element-resolver.d.ts +102 -0
- package/dist/src/snapshot/element-resolver.d.ts.map +1 -0
- package/dist/src/snapshot/element-resolver.js +379 -0
- package/dist/src/snapshot/element-resolver.js.map +1 -0
- package/dist/src/snapshot/extractors/attribute-extractor.d.ts +40 -0
- package/dist/src/snapshot/extractors/attribute-extractor.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/attribute-extractor.js +237 -0
- package/dist/src/snapshot/extractors/attribute-extractor.js.map +1 -0
- package/dist/src/snapshot/extractors/ax-extractor.d.ts +36 -0
- package/dist/src/snapshot/extractors/ax-extractor.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/ax-extractor.js +144 -0
- package/dist/src/snapshot/extractors/ax-extractor.js.map +1 -0
- package/dist/src/snapshot/extractors/dom-extractor.d.ts +21 -0
- package/dist/src/snapshot/extractors/dom-extractor.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/dom-extractor.js +137 -0
- package/dist/src/snapshot/extractors/dom-extractor.js.map +1 -0
- package/dist/src/snapshot/extractors/grouping-resolver.d.ts +39 -0
- package/dist/src/snapshot/extractors/grouping-resolver.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/grouping-resolver.js +260 -0
- package/dist/src/snapshot/extractors/grouping-resolver.js.map +1 -0
- package/dist/src/snapshot/extractors/index.d.ts +19 -0
- package/dist/src/snapshot/extractors/index.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/index.js +27 -0
- package/dist/src/snapshot/extractors/index.js.map +1 -0
- package/dist/src/snapshot/extractors/label-resolver.d.ts +44 -0
- package/dist/src/snapshot/extractors/label-resolver.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/label-resolver.js +173 -0
- package/dist/src/snapshot/extractors/label-resolver.js.map +1 -0
- package/dist/src/snapshot/extractors/layout-extractor.d.ts +52 -0
- package/dist/src/snapshot/extractors/layout-extractor.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/layout-extractor.js +382 -0
- package/dist/src/snapshot/extractors/layout-extractor.js.map +1 -0
- package/dist/src/snapshot/extractors/locator-builder.d.ts +27 -0
- package/dist/src/snapshot/extractors/locator-builder.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/locator-builder.js +223 -0
- package/dist/src/snapshot/extractors/locator-builder.js.map +1 -0
- package/dist/src/snapshot/extractors/region-resolver.d.ts +31 -0
- package/dist/src/snapshot/extractors/region-resolver.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/region-resolver.js +168 -0
- package/dist/src/snapshot/extractors/region-resolver.js.map +1 -0
- package/dist/src/snapshot/extractors/state-extractor.d.ts +30 -0
- package/dist/src/snapshot/extractors/state-extractor.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/state-extractor.js +181 -0
- package/dist/src/snapshot/extractors/state-extractor.js.map +1 -0
- package/dist/src/snapshot/extractors/types.d.ts +213 -0
- package/dist/src/snapshot/extractors/types.d.ts.map +1 -0
- package/dist/src/snapshot/extractors/types.js +145 -0
- package/dist/src/snapshot/extractors/types.js.map +1 -0
- package/dist/src/snapshot/index.d.ts +14 -0
- package/dist/src/snapshot/index.d.ts.map +1 -0
- package/dist/src/snapshot/index.js +18 -0
- package/dist/src/snapshot/index.js.map +1 -0
- package/dist/src/snapshot/snapshot-compiler.d.ts +73 -0
- package/dist/src/snapshot/snapshot-compiler.d.ts.map +1 -0
- package/dist/src/snapshot/snapshot-compiler.js +763 -0
- package/dist/src/snapshot/snapshot-compiler.js.map +1 -0
- package/dist/src/snapshot/snapshot-health.d.ts +97 -0
- package/dist/src/snapshot/snapshot-health.d.ts.map +1 -0
- package/dist/src/snapshot/snapshot-health.js +214 -0
- package/dist/src/snapshot/snapshot-health.js.map +1 -0
- package/dist/src/snapshot/snapshot-store.d.ts +137 -0
- package/dist/src/snapshot/snapshot-store.d.ts.map +1 -0
- package/dist/src/snapshot/snapshot-store.js +202 -0
- package/dist/src/snapshot/snapshot-store.js.map +1 -0
- package/dist/src/snapshot/snapshot.types.d.ts +250 -0
- package/dist/src/snapshot/snapshot.types.d.ts.map +1 -0
- package/dist/src/snapshot/snapshot.types.js +54 -0
- package/dist/src/snapshot/snapshot.types.js.map +1 -0
- package/dist/src/state/actionables-filter.d.ts +47 -0
- package/dist/src/state/actionables-filter.d.ts.map +1 -0
- package/dist/src/state/actionables-filter.js +173 -0
- package/dist/src/state/actionables-filter.js.map +1 -0
- package/dist/src/state/atoms-extractor.d.ts +23 -0
- package/dist/src/state/atoms-extractor.d.ts.map +1 -0
- package/dist/src/state/atoms-extractor.js +160 -0
- package/dist/src/state/atoms-extractor.js.map +1 -0
- package/dist/src/state/constants.d.ts +125 -0
- package/dist/src/state/constants.d.ts.map +1 -0
- package/dist/src/state/constants.js +131 -0
- package/dist/src/state/constants.js.map +1 -0
- package/dist/src/state/diff-engine.d.ts +23 -0
- package/dist/src/state/diff-engine.d.ts.map +1 -0
- package/dist/src/state/diff-engine.js +475 -0
- package/dist/src/state/diff-engine.js.map +1 -0
- package/dist/src/state/element-identity.d.ts +75 -0
- package/dist/src/state/element-identity.d.ts.map +1 -0
- package/dist/src/state/element-identity.js +129 -0
- package/dist/src/state/element-identity.js.map +1 -0
- package/dist/src/state/element-ref.types.d.ts +135 -0
- package/dist/src/state/element-ref.types.d.ts.map +1 -0
- package/dist/src/state/element-ref.types.js +13 -0
- package/dist/src/state/element-ref.types.js.map +1 -0
- package/dist/src/state/element-registry.d.ts +118 -0
- package/dist/src/state/element-registry.d.ts.map +1 -0
- package/dist/src/state/element-registry.js +222 -0
- package/dist/src/state/element-registry.js.map +1 -0
- package/dist/src/state/health.types.d.ts +93 -0
- package/dist/src/state/health.types.d.ts.map +1 -0
- package/dist/src/state/health.types.js +56 -0
- package/dist/src/state/health.types.js.map +1 -0
- package/dist/src/state/layer-detector.d.ts +23 -0
- package/dist/src/state/layer-detector.d.ts.map +1 -0
- package/dist/src/state/layer-detector.js +368 -0
- package/dist/src/state/layer-detector.js.map +1 -0
- package/dist/src/state/locator-generator.d.ts +21 -0
- package/dist/src/state/locator-generator.d.ts.map +1 -0
- package/dist/src/state/locator-generator.js +137 -0
- package/dist/src/state/locator-generator.js.map +1 -0
- package/dist/src/state/state-manager.d.ts +104 -0
- package/dist/src/state/state-manager.d.ts.map +1 -0
- package/dist/src/state/state-manager.js +618 -0
- package/dist/src/state/state-manager.js.map +1 -0
- package/dist/src/state/state-renderer.d.ts +63 -0
- package/dist/src/state/state-renderer.d.ts.map +1 -0
- package/dist/src/state/state-renderer.js +340 -0
- package/dist/src/state/state-renderer.js.map +1 -0
- package/dist/src/state/types.d.ts +353 -0
- package/dist/src/state/types.d.ts.map +1 -0
- package/dist/src/state/types.js +8 -0
- package/dist/src/state/types.js.map +1 -0
- package/dist/src/tools/browser-tools.d.ts +140 -0
- package/dist/src/tools/browser-tools.d.ts.map +1 -0
- package/dist/src/tools/browser-tools.js +657 -0
- package/dist/src/tools/browser-tools.js.map +1 -0
- package/dist/src/tools/errors.d.ts +63 -0
- package/dist/src/tools/errors.d.ts.map +1 -0
- package/dist/src/tools/errors.js +86 -0
- package/dist/src/tools/errors.js.map +1 -0
- package/dist/src/tools/execute-action.d.ts +135 -0
- package/dist/src/tools/execute-action.d.ts.map +1 -0
- package/dist/src/tools/execute-action.js +579 -0
- package/dist/src/tools/execute-action.js.map +1 -0
- package/dist/src/tools/form-tools.d.ts +109 -0
- package/dist/src/tools/form-tools.d.ts.map +1 -0
- package/dist/src/tools/form-tools.js +434 -0
- package/dist/src/tools/form-tools.js.map +1 -0
- package/dist/src/tools/index.d.ts +11 -0
- package/dist/src/tools/index.d.ts.map +1 -0
- package/dist/src/tools/index.js +49 -0
- package/dist/src/tools/index.js.map +1 -0
- package/dist/src/tools/response-builder.d.ts +98 -0
- package/dist/src/tools/response-builder.d.ts.map +1 -0
- package/dist/src/tools/response-builder.js +219 -0
- package/dist/src/tools/response-builder.js.map +1 -0
- package/dist/src/tools/tool-schemas.d.ts +2482 -0
- package/dist/src/tools/tool-schemas.d.ts.map +1 -0
- package/dist/src/tools/tool-schemas.js +606 -0
- package/dist/src/tools/tool-schemas.js.map +1 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/vitest.config.js +16 -0
- package/dist/vitest.config.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot Compiler
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates all extractors to produce a complete BaseSnapshot.
|
|
5
|
+
*
|
|
6
|
+
* @module snapshot/snapshot-compiler
|
|
7
|
+
*
|
|
8
|
+
* CDP Domains Required:
|
|
9
|
+
* - DOM: Document structure
|
|
10
|
+
* - Accessibility: Semantic information
|
|
11
|
+
* - CSS: Computed styles (optional, for layout)
|
|
12
|
+
*/
|
|
13
|
+
import { createExtractorContext, extractDom, extractAx, extractLayout, extractState, resolveLabel, resolveRegion, buildLocators, resolveGrouping, classifyAxRole, extractAttributes, } from './extractors/index.js';
|
|
14
|
+
import { getTextContent } from '../lib/text-utils.js';
|
|
15
|
+
const ROOT_CONTEXT = 'root';
|
|
16
|
+
const LIGHT_DOM_CONTEXT = 'light';
|
|
17
|
+
/**
|
|
18
|
+
* Build a context key based on iframe and shadow ancestry.
|
|
19
|
+
*/
|
|
20
|
+
function buildContextKey(node) {
|
|
21
|
+
const frameKey = node.framePath?.length ? node.framePath.join('/') : ROOT_CONTEXT;
|
|
22
|
+
const shadowKey = node.shadowPath?.length ? node.shadowPath.join('/') : LIGHT_DOM_CONTEXT;
|
|
23
|
+
return `${frameKey}|${shadowKey}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build context-scoped ID maps to avoid cross-frame/shadow collisions.
|
|
27
|
+
*/
|
|
28
|
+
function buildIdMapsByContext(domResult) {
|
|
29
|
+
const idMaps = new Map();
|
|
30
|
+
for (const node of domResult.nodes.values()) {
|
|
31
|
+
const id = node.attributes?.id;
|
|
32
|
+
if (!id)
|
|
33
|
+
continue;
|
|
34
|
+
const contextKey = buildContextKey(node);
|
|
35
|
+
let map = idMaps.get(contextKey);
|
|
36
|
+
if (!map) {
|
|
37
|
+
map = new Map();
|
|
38
|
+
idMaps.set(contextKey, map);
|
|
39
|
+
}
|
|
40
|
+
map.set(id, node);
|
|
41
|
+
}
|
|
42
|
+
return idMaps;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the ID map scoped to a node's frame/shadow context.
|
|
46
|
+
*/
|
|
47
|
+
function getIdMapForNode(node, idMapsByContext) {
|
|
48
|
+
if (!node)
|
|
49
|
+
return undefined;
|
|
50
|
+
return idMapsByContext.get(buildContextKey(node));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build adjacency maps for shadow roots and iframe content documents.
|
|
54
|
+
* Single O(n) pass through all nodes.
|
|
55
|
+
*
|
|
56
|
+
* @param domResult - DOM extraction result
|
|
57
|
+
* @returns Adjacency maps for shadow roots and content documents
|
|
58
|
+
*/
|
|
59
|
+
function buildAdjacencyMaps(domResult) {
|
|
60
|
+
const shadowRootsByHost = new Map();
|
|
61
|
+
const contentDocsByFrame = new Map();
|
|
62
|
+
for (const [nodeId, node] of domResult.nodes) {
|
|
63
|
+
if (node.parentId === undefined)
|
|
64
|
+
continue;
|
|
65
|
+
if (node.nodeName === '#document-fragment') {
|
|
66
|
+
// This is a shadow root - add to shadow host's children
|
|
67
|
+
const existing = shadowRootsByHost.get(node.parentId) ?? [];
|
|
68
|
+
existing.push(nodeId);
|
|
69
|
+
shadowRootsByHost.set(node.parentId, existing);
|
|
70
|
+
}
|
|
71
|
+
else if (node.nodeName === '#document') {
|
|
72
|
+
// This is a content document - add to iframe's children
|
|
73
|
+
const existing = contentDocsByFrame.get(node.parentId) ?? [];
|
|
74
|
+
existing.push(nodeId);
|
|
75
|
+
contentDocsByFrame.set(node.parentId, existing);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { shadowRootsByHost, contentDocsByFrame };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Build DOM pre-order index by traversing the DOM tree.
|
|
82
|
+
* Also traverses into shadow roots and iframe content documents.
|
|
83
|
+
*
|
|
84
|
+
* @param domResult - DOM extraction result with nodes and rootId
|
|
85
|
+
* @param adjacencyMaps - Precomputed maps for shadow roots and content documents
|
|
86
|
+
* @returns Map of backendNodeId -> DOM order index
|
|
87
|
+
*/
|
|
88
|
+
function buildDomOrderIndex(domResult, adjacencyMaps) {
|
|
89
|
+
const orderIndex = new Map();
|
|
90
|
+
const shadowHostSet = new Set(domResult.shadowRoots);
|
|
91
|
+
let index = 0;
|
|
92
|
+
function traverse(nodeId) {
|
|
93
|
+
const node = domResult.nodes.get(nodeId);
|
|
94
|
+
if (!node)
|
|
95
|
+
return;
|
|
96
|
+
orderIndex.set(nodeId, index++);
|
|
97
|
+
// 1. Process light DOM children first (pre-order DFS)
|
|
98
|
+
if (node.childNodeIds) {
|
|
99
|
+
for (const childId of node.childNodeIds) {
|
|
100
|
+
traverse(childId);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// 2. If this node hosts a shadow root, traverse shadow content (O(1) lookup)
|
|
104
|
+
if (shadowHostSet.has(nodeId)) {
|
|
105
|
+
const shadowRoots = adjacencyMaps.shadowRootsByHost.get(nodeId) ?? [];
|
|
106
|
+
for (const shadowRootId of shadowRoots) {
|
|
107
|
+
traverse(shadowRootId);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// 3. If this node is an iframe, traverse content document (O(1) lookup)
|
|
111
|
+
if (node.frameId || node.nodeName.toUpperCase() === 'IFRAME') {
|
|
112
|
+
const contentDocs = adjacencyMaps.contentDocsByFrame.get(nodeId) ?? [];
|
|
113
|
+
for (const contentDocId of contentDocs) {
|
|
114
|
+
traverse(contentDocId);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
traverse(domResult.rootId);
|
|
119
|
+
return orderIndex;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Build heading index mapping each backendNodeId to its heading context.
|
|
123
|
+
* Uses DOM order to determine the most recent preceding heading.
|
|
124
|
+
* Also traverses into shadow roots and iframe content documents.
|
|
125
|
+
*
|
|
126
|
+
* Heading context is isolated at iframe boundaries:
|
|
127
|
+
* - Heading from parent document does NOT propagate into iframe
|
|
128
|
+
* - Heading from iframe does NOT propagate back to parent document
|
|
129
|
+
* - Shadow DOM shares heading context with its host document
|
|
130
|
+
*
|
|
131
|
+
* @param domResult - DOM extraction result
|
|
132
|
+
* @param axResult - AX extraction result for heading names
|
|
133
|
+
* @param idMap - Map of DOM ID to RawDomNode for aria-labelledby resolution
|
|
134
|
+
* @param adjacencyMaps - Precomputed maps for shadow roots and content documents
|
|
135
|
+
* @returns Map of backendNodeId -> heading context string
|
|
136
|
+
*/
|
|
137
|
+
function buildHeadingIndex(domResult, axResult, idMapsByContext, adjacencyMaps) {
|
|
138
|
+
const headingIndex = new Map();
|
|
139
|
+
const shadowHostSet = new Set(domResult.shadowRoots);
|
|
140
|
+
// Helper to check if a node is a heading and resolve its name
|
|
141
|
+
function isHeading(backendNodeId) {
|
|
142
|
+
const domNode = domResult.nodes.get(backendNodeId);
|
|
143
|
+
const axNode = axResult?.nodes.get(backendNodeId);
|
|
144
|
+
// Check AX role first
|
|
145
|
+
const scopedIdMap = domNode ? getIdMapForNode(domNode, idMapsByContext) : undefined;
|
|
146
|
+
if (axNode?.role === 'heading') {
|
|
147
|
+
// Priority: AX name -> resolveLabel -> DOM text content
|
|
148
|
+
let name = axNode.name;
|
|
149
|
+
if (!name && domNode) {
|
|
150
|
+
const labelResult = resolveLabel(domNode, axNode, scopedIdMap);
|
|
151
|
+
if (labelResult.source !== 'none') {
|
|
152
|
+
name = labelResult.label;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
name ??= getTextContent(backendNodeId, domResult.nodes);
|
|
156
|
+
return { isHeading: true, name };
|
|
157
|
+
}
|
|
158
|
+
// Check DOM tag (H1-H6)
|
|
159
|
+
if (domNode?.nodeName?.match(/^H[1-6]$/i)) {
|
|
160
|
+
// Priority: AX name -> resolveLabel -> DOM text content
|
|
161
|
+
let name = axNode?.name;
|
|
162
|
+
if (!name) {
|
|
163
|
+
const labelResult = resolveLabel(domNode, axNode, scopedIdMap);
|
|
164
|
+
if (labelResult.source !== 'none') {
|
|
165
|
+
name = labelResult.label;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
name ??= getTextContent(backendNodeId, domResult.nodes);
|
|
169
|
+
return { isHeading: true, name };
|
|
170
|
+
}
|
|
171
|
+
return { isHeading: false };
|
|
172
|
+
}
|
|
173
|
+
// Traverse DOM in pre-order, passing and returning heading context
|
|
174
|
+
function traverse(nodeId, currentHeading) {
|
|
175
|
+
const node = domResult.nodes.get(nodeId);
|
|
176
|
+
if (!node)
|
|
177
|
+
return currentHeading;
|
|
178
|
+
// Check if this node is a heading
|
|
179
|
+
const headingInfo = isHeading(nodeId);
|
|
180
|
+
if (headingInfo.isHeading && headingInfo.name) {
|
|
181
|
+
currentHeading = headingInfo.name;
|
|
182
|
+
}
|
|
183
|
+
// Record the current heading context for this node
|
|
184
|
+
if (currentHeading) {
|
|
185
|
+
headingIndex.set(nodeId, currentHeading);
|
|
186
|
+
}
|
|
187
|
+
// 1. Process light DOM children first (pre-order DFS)
|
|
188
|
+
// Heading context propagates and updates through light DOM
|
|
189
|
+
if (node.childNodeIds) {
|
|
190
|
+
for (const childId of node.childNodeIds) {
|
|
191
|
+
currentHeading = traverse(childId, currentHeading) ?? currentHeading;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// 2. If this node hosts a shadow root, traverse shadow content (O(1) lookup)
|
|
195
|
+
// Shadow DOM shares heading context with host document (same logical document)
|
|
196
|
+
if (shadowHostSet.has(nodeId)) {
|
|
197
|
+
const shadowRoots = adjacencyMaps.shadowRootsByHost.get(nodeId) ?? [];
|
|
198
|
+
for (const shadowRootId of shadowRoots) {
|
|
199
|
+
currentHeading = traverse(shadowRootId, currentHeading) ?? currentHeading;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// 3. If this node is an iframe, traverse content document (O(1) lookup)
|
|
203
|
+
// IMPORTANT: Heading context resets at iframe boundary (separate document)
|
|
204
|
+
// - Pass undefined to reset context inside iframe
|
|
205
|
+
// - Discard returned heading (iframe headings don't affect parent)
|
|
206
|
+
if (node.frameId || node.nodeName.toUpperCase() === 'IFRAME') {
|
|
207
|
+
const contentDocs = adjacencyMaps.contentDocsByFrame.get(nodeId) ?? [];
|
|
208
|
+
for (const contentDocId of contentDocs) {
|
|
209
|
+
traverse(contentDocId, undefined);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return currentHeading;
|
|
213
|
+
}
|
|
214
|
+
traverse(domResult.rootId, undefined);
|
|
215
|
+
return headingIndex;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Recursively collect frame loaderIds from frame tree.
|
|
219
|
+
*/
|
|
220
|
+
function collectFrameLoaderIds(frameTree, frameLoaderIds, _isMainFrame = true) {
|
|
221
|
+
const frame = frameTree.frame;
|
|
222
|
+
frameLoaderIds.set(frame.id, {
|
|
223
|
+
frameId: frame.id,
|
|
224
|
+
loaderId: frame.loaderId,
|
|
225
|
+
isMainFrame: !frame.parentId,
|
|
226
|
+
});
|
|
227
|
+
if (frameTree.childFrames) {
|
|
228
|
+
for (const child of frameTree.childFrames) {
|
|
229
|
+
collectFrameLoaderIds(child, frameLoaderIds, false);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Default compile options
|
|
235
|
+
*/
|
|
236
|
+
const DEFAULT_OPTIONS = {
|
|
237
|
+
include_hidden: false,
|
|
238
|
+
max_nodes: 2000,
|
|
239
|
+
timeout: 30000,
|
|
240
|
+
redact_sensitive: true,
|
|
241
|
+
include_values: true, // Enable value extraction with password redaction
|
|
242
|
+
includeReadable: true,
|
|
243
|
+
includeLayout: true,
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* Map AX role to NodeKind.
|
|
247
|
+
*/
|
|
248
|
+
function mapRoleToKind(role) {
|
|
249
|
+
if (!role)
|
|
250
|
+
return undefined;
|
|
251
|
+
const normalized = role.toLowerCase();
|
|
252
|
+
const kindMap = {
|
|
253
|
+
// Interactive
|
|
254
|
+
button: 'button',
|
|
255
|
+
link: 'link',
|
|
256
|
+
textbox: 'input',
|
|
257
|
+
searchbox: 'input',
|
|
258
|
+
combobox: 'combobox',
|
|
259
|
+
listbox: 'select',
|
|
260
|
+
checkbox: 'checkbox',
|
|
261
|
+
radio: 'radio',
|
|
262
|
+
switch: 'switch',
|
|
263
|
+
slider: 'slider',
|
|
264
|
+
spinbutton: 'slider',
|
|
265
|
+
tab: 'tab',
|
|
266
|
+
menuitem: 'menuitem',
|
|
267
|
+
menuitemcheckbox: 'menuitem',
|
|
268
|
+
menuitemradio: 'menuitem',
|
|
269
|
+
option: 'menuitem',
|
|
270
|
+
// Readable
|
|
271
|
+
heading: 'heading',
|
|
272
|
+
paragraph: 'paragraph',
|
|
273
|
+
text: 'text',
|
|
274
|
+
statictext: 'text',
|
|
275
|
+
list: 'list',
|
|
276
|
+
listitem: 'listitem',
|
|
277
|
+
tree: 'list',
|
|
278
|
+
treeitem: 'listitem',
|
|
279
|
+
image: 'image',
|
|
280
|
+
img: 'image',
|
|
281
|
+
figure: 'image',
|
|
282
|
+
table: 'table',
|
|
283
|
+
grid: 'table',
|
|
284
|
+
treegrid: 'table',
|
|
285
|
+
// Structural
|
|
286
|
+
form: 'form',
|
|
287
|
+
dialog: 'dialog',
|
|
288
|
+
alertdialog: 'dialog',
|
|
289
|
+
navigation: 'navigation',
|
|
290
|
+
region: 'section',
|
|
291
|
+
article: 'section',
|
|
292
|
+
main: 'section',
|
|
293
|
+
banner: 'section',
|
|
294
|
+
complementary: 'section',
|
|
295
|
+
contentinfo: 'section',
|
|
296
|
+
};
|
|
297
|
+
return kindMap[normalized];
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* SnapshotCompiler class
|
|
301
|
+
*
|
|
302
|
+
* Orchestrates the extraction and compilation of page snapshots.
|
|
303
|
+
*/
|
|
304
|
+
export class SnapshotCompiler {
|
|
305
|
+
options;
|
|
306
|
+
/** Counter for generating unique snapshot IDs */
|
|
307
|
+
snapshotCounter = 0;
|
|
308
|
+
constructor(options) {
|
|
309
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Generate a unique snapshot ID.
|
|
313
|
+
*/
|
|
314
|
+
generateSnapshotId() {
|
|
315
|
+
this.snapshotCounter++;
|
|
316
|
+
return `snap-${Date.now()}-${this.snapshotCounter}`;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Compile a snapshot from the current page state.
|
|
320
|
+
*
|
|
321
|
+
* @param cdp - CDP client for the page
|
|
322
|
+
* @param page - Puppeteer Page instance
|
|
323
|
+
* @param _pageId - Page identifier (for logging/tracking)
|
|
324
|
+
* @returns Complete BaseSnapshot
|
|
325
|
+
*/
|
|
326
|
+
async compile(cdp, page, _pageId) {
|
|
327
|
+
const startTime = Date.now();
|
|
328
|
+
// Get viewport - prefer page.viewport() but fall back to CDP layout metrics
|
|
329
|
+
// for external browsers where no viewport is explicitly set
|
|
330
|
+
let viewport = page.viewport();
|
|
331
|
+
let viewportFallbackError;
|
|
332
|
+
if (!viewport) {
|
|
333
|
+
try {
|
|
334
|
+
const metrics = await cdp.send('Page.getLayoutMetrics', undefined);
|
|
335
|
+
// Use cssLayoutViewport which gives CSS pixels (not device pixels)
|
|
336
|
+
const cssViewport = metrics.cssLayoutViewport;
|
|
337
|
+
viewport = {
|
|
338
|
+
width: Math.round(cssViewport.clientWidth),
|
|
339
|
+
height: Math.round(cssViewport.clientHeight),
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
// Fall back to common desktop viewport if CDP call fails (e.g., target closed, permissions)
|
|
344
|
+
viewport = { width: 1280, height: 720 };
|
|
345
|
+
viewportFallbackError = err instanceof Error ? err.message : String(err);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const ctx = createExtractorContext(cdp, viewport, this.options);
|
|
349
|
+
let partial = false;
|
|
350
|
+
const warnings = [];
|
|
351
|
+
// Add viewport fallback warning if CDP detection failed
|
|
352
|
+
if (viewportFallbackError) {
|
|
353
|
+
warnings.push(`Viewport detection failed, using fallback 1280x720: ${viewportFallbackError}`);
|
|
354
|
+
}
|
|
355
|
+
// Phase 1: Extract DOM first to discover frame IDs, then AX with frame context
|
|
356
|
+
let domResult;
|
|
357
|
+
let axResult;
|
|
358
|
+
let domOrderAvailable = false;
|
|
359
|
+
// Step 1: Extract DOM tree (discovers iframes and their frame IDs)
|
|
360
|
+
try {
|
|
361
|
+
domResult = await extractDom(ctx);
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
365
|
+
warnings.push(`DOM extraction failed: ${message}`);
|
|
366
|
+
partial = true;
|
|
367
|
+
}
|
|
368
|
+
// Step 2: Extract AX tree with discovered frame IDs for multi-frame support
|
|
369
|
+
// This enables accessibility extraction from iframes (e.g., cookie consent popups)
|
|
370
|
+
try {
|
|
371
|
+
const iframeFrameIds = domResult?.frameIds ?? [];
|
|
372
|
+
axResult = await extractAx(ctx, iframeFrameIds);
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
376
|
+
warnings.push(`AX extraction failed: ${message}`);
|
|
377
|
+
partial = true;
|
|
378
|
+
}
|
|
379
|
+
const idMapsByContext = domResult
|
|
380
|
+
? buildIdMapsByContext(domResult)
|
|
381
|
+
: new Map();
|
|
382
|
+
// Query frame tree for loader IDs (needed for delta computation)
|
|
383
|
+
const frameLoaderIds = new Map();
|
|
384
|
+
let mainFrameId;
|
|
385
|
+
let hasUnknownFrames = false;
|
|
386
|
+
try {
|
|
387
|
+
const frameTreeResult = await cdp.send('Page.getFrameTree', undefined);
|
|
388
|
+
collectFrameLoaderIds(frameTreeResult.frameTree, frameLoaderIds);
|
|
389
|
+
// Find main frame ID
|
|
390
|
+
for (const [frameId, info] of frameLoaderIds) {
|
|
391
|
+
if (info.isMainFrame) {
|
|
392
|
+
mainFrameId = frameId;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
catch (err) {
|
|
398
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
399
|
+
warnings.push(`Frame tree query failed: ${message}`);
|
|
400
|
+
hasUnknownFrames = true;
|
|
401
|
+
}
|
|
402
|
+
// Build DOM order index for deterministic ordering
|
|
403
|
+
let domOrderIndex;
|
|
404
|
+
let headingIndex;
|
|
405
|
+
if (domResult) {
|
|
406
|
+
const adjacencyMaps = buildAdjacencyMaps(domResult);
|
|
407
|
+
domOrderIndex = buildDomOrderIndex(domResult, adjacencyMaps);
|
|
408
|
+
headingIndex = buildHeadingIndex(domResult, axResult, idMapsByContext, adjacencyMaps);
|
|
409
|
+
domOrderAvailable = true;
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
// Add warning about DOM order fallback
|
|
413
|
+
warnings.push('DOM order unavailable; using AX order');
|
|
414
|
+
}
|
|
415
|
+
// Phase 2: Correlate nodes and identify what to include
|
|
416
|
+
const nodesToProcess = [];
|
|
417
|
+
// Structural roles that must be included for FactPack features
|
|
418
|
+
// (form detection, dialog detection)
|
|
419
|
+
const essentialStructuralRoles = new Set(['form', 'dialog', 'alertdialog']);
|
|
420
|
+
if (axResult) {
|
|
421
|
+
// Build from AX tree (has semantic information)
|
|
422
|
+
for (const [backendNodeId, axNode] of axResult.nodes) {
|
|
423
|
+
const classification = classifyAxRole(axNode.role);
|
|
424
|
+
const isInteractive = classification === 'interactive';
|
|
425
|
+
const isReadable = classification === 'readable' && this.options.includeReadable;
|
|
426
|
+
const isEssentialStructural = classification === 'structural' &&
|
|
427
|
+
essentialStructuralRoles.has(axNode.role?.toLowerCase() ?? '');
|
|
428
|
+
if (isInteractive || isReadable || isEssentialStructural) {
|
|
429
|
+
const domNode = domResult?.nodes.get(backendNodeId);
|
|
430
|
+
nodesToProcess.push({
|
|
431
|
+
backendNodeId,
|
|
432
|
+
domNode,
|
|
433
|
+
axNode,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
else if (domResult) {
|
|
439
|
+
// Fallback: Use DOM-only for interactive tags and essential structural elements
|
|
440
|
+
for (const [backendNodeId, domNode] of domResult.nodes) {
|
|
441
|
+
const tagName = domNode.nodeName.toUpperCase();
|
|
442
|
+
if (['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA', 'FORM', 'DIALOG'].includes(tagName)) {
|
|
443
|
+
nodesToProcess.push({
|
|
444
|
+
backendNodeId,
|
|
445
|
+
domNode,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// Sort by DOM order if available (before max_nodes slicing)
|
|
451
|
+
if (domOrderAvailable && domOrderIndex) {
|
|
452
|
+
const orderMap = domOrderIndex; // Capture for closure to avoid reassignment issues
|
|
453
|
+
nodesToProcess.sort((a, b) => {
|
|
454
|
+
const orderA = orderMap.get(a.backendNodeId);
|
|
455
|
+
const orderB = orderMap.get(b.backendNodeId);
|
|
456
|
+
// If missing from DOM order index (detached/cross-origin), place after ordered nodes
|
|
457
|
+
if (orderA === undefined && orderB === undefined)
|
|
458
|
+
return 0;
|
|
459
|
+
if (orderA === undefined)
|
|
460
|
+
return 1;
|
|
461
|
+
if (orderB === undefined)
|
|
462
|
+
return -1;
|
|
463
|
+
return orderA - orderB;
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
// Limit nodes (now respects DOM order)
|
|
467
|
+
const limitedNodes = nodesToProcess.slice(0, this.options.max_nodes);
|
|
468
|
+
// Phase 3: Layout extraction (batched)
|
|
469
|
+
let layoutResult;
|
|
470
|
+
if (this.options.includeLayout && limitedNodes.length > 0) {
|
|
471
|
+
const nodeIds = limitedNodes.map((n) => n.backendNodeId);
|
|
472
|
+
layoutResult = await this.extractLayoutSafe(ctx, nodeIds, domResult?.nodes, warnings);
|
|
473
|
+
}
|
|
474
|
+
// Merge layout into node data
|
|
475
|
+
if (layoutResult) {
|
|
476
|
+
for (const nodeData of limitedNodes) {
|
|
477
|
+
nodeData.layout = layoutResult.layouts.get(nodeData.backendNodeId);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
// Phase 4: Transform to ReadableNode[]
|
|
481
|
+
const transformedNodes = [];
|
|
482
|
+
for (const nodeData of limitedNodes) {
|
|
483
|
+
const node = this.transformNode(nodeData, domResult?.nodes ?? new Map(), axResult?.nodes ?? new Map(), limitedNodes, idMapsByContext, headingIndex, frameLoaderIds, mainFrameId);
|
|
484
|
+
// Track if any node has unknown frame (loader_id lookup failed)
|
|
485
|
+
if (!node.loader_id) {
|
|
486
|
+
hasUnknownFrames = true;
|
|
487
|
+
}
|
|
488
|
+
// Filter by visibility (unless include_hidden)
|
|
489
|
+
if (this.options.include_hidden || node.state?.visible !== false) {
|
|
490
|
+
transformedNodes.push(node);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
// Phase 4.5: Filter noise nodes (empty containers, duplicate text)
|
|
494
|
+
const nodes = this.filterNoiseNodes(transformedNodes, domResult?.nodes ?? new Map(), axResult?.nodes ?? new Map());
|
|
495
|
+
// Phase 5: Build BaseSnapshot
|
|
496
|
+
const duration = Date.now() - startTime;
|
|
497
|
+
const interactiveCount = nodes.filter((n) => [
|
|
498
|
+
'button',
|
|
499
|
+
'link',
|
|
500
|
+
'input',
|
|
501
|
+
'textarea',
|
|
502
|
+
'select',
|
|
503
|
+
'combobox',
|
|
504
|
+
'checkbox',
|
|
505
|
+
'radio',
|
|
506
|
+
'switch',
|
|
507
|
+
'slider',
|
|
508
|
+
'tab',
|
|
509
|
+
'menuitem',
|
|
510
|
+
].includes(n.kind)).length;
|
|
511
|
+
const meta = {
|
|
512
|
+
node_count: nodes.length,
|
|
513
|
+
interactive_count: interactiveCount,
|
|
514
|
+
capture_duration_ms: duration,
|
|
515
|
+
};
|
|
516
|
+
if (partial) {
|
|
517
|
+
meta.partial = true;
|
|
518
|
+
}
|
|
519
|
+
if (hasUnknownFrames) {
|
|
520
|
+
warnings.push('Some nodes have unknown frame loaderIds; delta computation may be unreliable');
|
|
521
|
+
}
|
|
522
|
+
if (warnings.length > 0) {
|
|
523
|
+
meta.warnings = warnings;
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
snapshot_id: this.generateSnapshotId(),
|
|
527
|
+
url: page.url(),
|
|
528
|
+
title: await page.title(),
|
|
529
|
+
captured_at: new Date().toISOString(),
|
|
530
|
+
viewport,
|
|
531
|
+
nodes,
|
|
532
|
+
meta,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Extract layout with error handling.
|
|
537
|
+
*/
|
|
538
|
+
async extractLayoutSafe(ctx, nodeIds, domNodes, warnings) {
|
|
539
|
+
try {
|
|
540
|
+
return await extractLayout(ctx, nodeIds, domNodes);
|
|
541
|
+
}
|
|
542
|
+
catch (err) {
|
|
543
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
544
|
+
warnings.push(`Layout extraction failed: ${message}`);
|
|
545
|
+
return undefined;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Transform raw node data to ReadableNode.
|
|
550
|
+
*/
|
|
551
|
+
transformNode(nodeData, domTree, axTree, allNodes, idMapsByContext, headingIndex, frameLoaderIds, mainFrameId) {
|
|
552
|
+
const { domNode, axNode, layout, backendNodeId } = nodeData;
|
|
553
|
+
// Determine frame_id and loader_id for this node
|
|
554
|
+
// If domNode has frameId, use it; otherwise default to mainFrameId
|
|
555
|
+
const nodeFrameId = domNode?.frameId ?? mainFrameId ?? 'unknown';
|
|
556
|
+
const frameInfo = frameLoaderIds.get(nodeFrameId);
|
|
557
|
+
const frameId = frameInfo?.frameId ?? nodeFrameId;
|
|
558
|
+
const loaderId = frameInfo?.loaderId ?? '';
|
|
559
|
+
// Determine kind
|
|
560
|
+
let kind = 'generic';
|
|
561
|
+
if (axNode?.role) {
|
|
562
|
+
kind = mapRoleToKind(axNode.role) ?? 'generic';
|
|
563
|
+
}
|
|
564
|
+
else if (domNode) {
|
|
565
|
+
const tagKind = this.getKindFromTag(domNode.nodeName);
|
|
566
|
+
if (tagKind)
|
|
567
|
+
kind = tagKind;
|
|
568
|
+
}
|
|
569
|
+
// Resolve label
|
|
570
|
+
const scopedIdMap = getIdMapForNode(domNode, idMapsByContext);
|
|
571
|
+
const labelResult = resolveLabel(domNode, axNode, scopedIdMap);
|
|
572
|
+
const label = labelResult.label;
|
|
573
|
+
// Resolve region (pass axTree for ancestor AX role lookup)
|
|
574
|
+
const region = resolveRegion(domNode, axNode, domTree, axTree);
|
|
575
|
+
// Resolve grouping (for group_id and group_path only)
|
|
576
|
+
const grouping = resolveGrouping(backendNodeId, domTree, axTree, allNodes, {
|
|
577
|
+
includeHeadingContext: !headingIndex,
|
|
578
|
+
});
|
|
579
|
+
// Get heading context from pre-computed heading index (DOM order-based)
|
|
580
|
+
// Fall back to grouping's heading_context if headingIndex not available
|
|
581
|
+
const headingContext = headingIndex
|
|
582
|
+
? headingIndex.get(backendNodeId)
|
|
583
|
+
: grouping.heading_context;
|
|
584
|
+
// Build location
|
|
585
|
+
const where = {
|
|
586
|
+
region,
|
|
587
|
+
group_id: grouping.group_id,
|
|
588
|
+
group_path: grouping.group_path,
|
|
589
|
+
heading_context: headingContext,
|
|
590
|
+
};
|
|
591
|
+
// Build layout
|
|
592
|
+
const nodeLayout = layout
|
|
593
|
+
? {
|
|
594
|
+
bbox: layout.bbox,
|
|
595
|
+
display: layout.display,
|
|
596
|
+
screen_zone: layout.screenZone,
|
|
597
|
+
zIndex: layout.zIndex,
|
|
598
|
+
}
|
|
599
|
+
: {
|
|
600
|
+
bbox: { x: 0, y: 0, w: 0, h: 0 },
|
|
601
|
+
};
|
|
602
|
+
// Extract state
|
|
603
|
+
const state = extractState(domNode, axNode, layout);
|
|
604
|
+
// Build locators
|
|
605
|
+
const locators = buildLocators(domNode, axNode, label);
|
|
606
|
+
// Build attributes using extractor module
|
|
607
|
+
const attributes = extractAttributes(domNode, kind, {
|
|
608
|
+
includeValues: this.options.include_values,
|
|
609
|
+
redactSensitive: this.options.redact_sensitive,
|
|
610
|
+
sanitizeUrls: true,
|
|
611
|
+
}, axNode);
|
|
612
|
+
// Build the node - node_id is derived from backend_node_id for stability across snapshots
|
|
613
|
+
const node = {
|
|
614
|
+
node_id: String(backendNodeId),
|
|
615
|
+
backend_node_id: backendNodeId,
|
|
616
|
+
frame_id: frameId,
|
|
617
|
+
loader_id: loaderId,
|
|
618
|
+
kind,
|
|
619
|
+
label,
|
|
620
|
+
where,
|
|
621
|
+
layout: nodeLayout,
|
|
622
|
+
find: locators,
|
|
623
|
+
};
|
|
624
|
+
// Add optional fields
|
|
625
|
+
if (Object.keys(state).length > 0) {
|
|
626
|
+
node.state = state;
|
|
627
|
+
}
|
|
628
|
+
if (attributes && Object.keys(attributes).length > 0) {
|
|
629
|
+
node.attributes = attributes;
|
|
630
|
+
}
|
|
631
|
+
return node;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Get NodeKind from HTML tag name.
|
|
635
|
+
*/
|
|
636
|
+
getKindFromTag(tagName) {
|
|
637
|
+
const tag = tagName.toUpperCase();
|
|
638
|
+
const tagMap = {
|
|
639
|
+
BUTTON: 'button',
|
|
640
|
+
A: 'link',
|
|
641
|
+
INPUT: 'input',
|
|
642
|
+
TEXTAREA: 'textarea',
|
|
643
|
+
SELECT: 'select',
|
|
644
|
+
H1: 'heading',
|
|
645
|
+
H2: 'heading',
|
|
646
|
+
H3: 'heading',
|
|
647
|
+
H4: 'heading',
|
|
648
|
+
H5: 'heading',
|
|
649
|
+
H6: 'heading',
|
|
650
|
+
P: 'paragraph',
|
|
651
|
+
IMG: 'image',
|
|
652
|
+
TABLE: 'table',
|
|
653
|
+
UL: 'list',
|
|
654
|
+
OL: 'list',
|
|
655
|
+
LI: 'listitem',
|
|
656
|
+
FORM: 'form',
|
|
657
|
+
DIALOG: 'dialog',
|
|
658
|
+
NAV: 'navigation',
|
|
659
|
+
};
|
|
660
|
+
return tagMap[tag];
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Filter out noise nodes to reduce snapshot size.
|
|
664
|
+
*
|
|
665
|
+
* Filters:
|
|
666
|
+
* 1. Empty list/listitem containers with no semantic name AND no interactive descendants
|
|
667
|
+
* 2. StaticText/text nodes that mirror their parent's label exactly
|
|
668
|
+
*/
|
|
669
|
+
filterNoiseNodes(nodes, domTree, axTree) {
|
|
670
|
+
// Build set of interactive node backend IDs for descendant checking
|
|
671
|
+
const interactiveKinds = new Set([
|
|
672
|
+
'button',
|
|
673
|
+
'link',
|
|
674
|
+
'input',
|
|
675
|
+
'textarea',
|
|
676
|
+
'select',
|
|
677
|
+
'combobox',
|
|
678
|
+
'checkbox',
|
|
679
|
+
'radio',
|
|
680
|
+
'switch',
|
|
681
|
+
'slider',
|
|
682
|
+
'tab',
|
|
683
|
+
'menuitem',
|
|
684
|
+
]);
|
|
685
|
+
const interactiveBackendIds = new Set(nodes.filter((n) => interactiveKinds.has(n.kind)).map((n) => n.backend_node_id));
|
|
686
|
+
// Build parent-child relationship from DOM tree
|
|
687
|
+
const childToParent = new Map();
|
|
688
|
+
for (const [nodeId, domNode] of domTree) {
|
|
689
|
+
if (domNode.parentId !== undefined) {
|
|
690
|
+
childToParent.set(nodeId, domNode.parentId);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// Check if a node has any interactive descendants in the DOM tree
|
|
694
|
+
const hasInteractiveDescendant = (nodeId) => {
|
|
695
|
+
const domNode = domTree.get(nodeId);
|
|
696
|
+
if (!domNode)
|
|
697
|
+
return false;
|
|
698
|
+
// Check direct children
|
|
699
|
+
if (domNode.childNodeIds) {
|
|
700
|
+
for (const childId of domNode.childNodeIds) {
|
|
701
|
+
if (interactiveBackendIds.has(childId)) {
|
|
702
|
+
return true;
|
|
703
|
+
}
|
|
704
|
+
if (hasInteractiveDescendant(childId)) {
|
|
705
|
+
return true;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return false;
|
|
710
|
+
};
|
|
711
|
+
// Get label of parent node in the node list
|
|
712
|
+
const getParentLabel = (nodeId) => {
|
|
713
|
+
const parentId = childToParent.get(nodeId);
|
|
714
|
+
if (parentId === undefined)
|
|
715
|
+
return undefined;
|
|
716
|
+
// Look up parent in our node list
|
|
717
|
+
const parentNode = nodes.find((n) => n.backend_node_id === parentId);
|
|
718
|
+
if (parentNode) {
|
|
719
|
+
return parentNode.label;
|
|
720
|
+
}
|
|
721
|
+
// Parent might be further up - check AX tree for parent's name
|
|
722
|
+
const parentAx = axTree.get(parentId);
|
|
723
|
+
return parentAx?.name;
|
|
724
|
+
};
|
|
725
|
+
// Container kinds that can be noisy when empty
|
|
726
|
+
const containerKinds = new Set(['list', 'listitem']);
|
|
727
|
+
// Text kinds that can duplicate parent labels
|
|
728
|
+
const textKinds = new Set(['text']);
|
|
729
|
+
return nodes.filter((node) => {
|
|
730
|
+
// Rule 1: Filter empty container nodes without interactive descendants
|
|
731
|
+
if (containerKinds.has(node.kind)) {
|
|
732
|
+
const hasSemanticName = node.label && node.label.trim().length > 0;
|
|
733
|
+
if (!hasSemanticName) {
|
|
734
|
+
const hasInteractive = hasInteractiveDescendant(node.backend_node_id);
|
|
735
|
+
if (!hasInteractive) {
|
|
736
|
+
return false; // Filter out empty container without interactive content
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
// Rule 2: Filter text nodes that mirror parent's label
|
|
741
|
+
if (textKinds.has(node.kind)) {
|
|
742
|
+
const parentLabel = getParentLabel(node.backend_node_id);
|
|
743
|
+
if (parentLabel && node.label) {
|
|
744
|
+
// Normalize and compare
|
|
745
|
+
const normalizedParent = parentLabel.trim().toLowerCase();
|
|
746
|
+
const normalizedNode = node.label.trim().toLowerCase();
|
|
747
|
+
if (normalizedNode === normalizedParent) {
|
|
748
|
+
return false; // Filter out duplicate text
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return true; // Keep the node
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Export a compile function for simpler usage.
|
|
758
|
+
*/
|
|
759
|
+
export async function compileSnapshot(cdp, page, pageId, options) {
|
|
760
|
+
const compiler = new SnapshotCompiler(options);
|
|
761
|
+
return compiler.compile(cdp, page, pageId);
|
|
762
|
+
}
|
|
763
|
+
//# sourceMappingURL=snapshot-compiler.js.map
|