@legionworks/facet 1.9.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-APACHE +202 -0
- package/LICENSE-MIT +23 -0
- package/README.md +158 -0
- package/dist/gallery/chunk-4g8rem85.css +656 -0
- package/dist/gallery/chunk-c6s9k9ty.js +5970 -0
- package/dist/gallery/frame/artifact.css +8424 -0
- package/dist/gallery/frame/chunks/abnfDiagram-N423BO3Z-jj74szbm.js +155 -0
- package/dist/gallery/frame/chunks/architecture-TIHT7OUA-cvdjn7dt.js +10 -0
- package/dist/gallery/frame/chunks/architectureDiagram-T3A2C74G-n4z77eg6.js +8675 -0
- package/dist/gallery/frame/chunks/blockDiagram-VBNYF7ZC-7xsqzrd1.js +3529 -0
- package/dist/gallery/frame/chunks/c4Diagram-5PPSVZJV-z2zjs7wx.js +2413 -0
- package/dist/gallery/frame/chunks/classDiagram-JCYQIIEL-ve8rngqp.js +47 -0
- package/dist/gallery/frame/chunks/classDiagram-v2-OCEON4UE-8cfaxb8z.js +47 -0
- package/dist/gallery/frame/chunks/cose-bilkent-JH36ORCC-sn92qexx.js +4822 -0
- package/dist/gallery/frame/chunks/cynefin-VYW2F7L2-tqjv913d.js +10 -0
- package/dist/gallery/frame/chunks/cynefinDiagram-MW4NZA55-c1c0dj12.js +500 -0
- package/dist/gallery/frame/chunks/dagre-VZM6K2ZE-7tyg4yt2.js +476 -0
- package/dist/gallery/frame/chunks/diagram-7IWD3JNH-y4eng9sc.js +526 -0
- package/dist/gallery/frame/chunks/diagram-B4RE2ZJO-rn3p7j5h.js +673 -0
- package/dist/gallery/frame/chunks/diagram-LBJQPF4R-w4a2cm9q.js +255 -0
- package/dist/gallery/frame/chunks/diagram-Q27KOJAE-aqc2v7wy.js +575 -0
- package/dist/gallery/frame/chunks/diagram-UB23O5K3-xncy8nyd.js +355 -0
- package/dist/gallery/frame/chunks/ebnfDiagram-BXEA7PRR-acwgxz16.js +175 -0
- package/dist/gallery/frame/chunks/erDiagram-JOGREHBK-mzp5nswt.js +1332 -0
- package/dist/gallery/frame/chunks/eventmodeling-45OFAUF4-sg07qr0c.js +10 -0
- package/dist/gallery/frame/chunks/flowDiagram-UKHOOZJN-2e5ygs44.js +29 -0
- package/dist/gallery/frame/chunks/ganttDiagram-PKOTCBZU-p2hhh7hr.js +2620 -0
- package/dist/gallery/frame/chunks/gitGraph-TEB2WS4Q-j6kw0naa.js +10 -0
- package/dist/gallery/frame/chunks/gitGraphDiagram-DS77QQ5N-4z9z8wjm.js +1347 -0
- package/dist/gallery/frame/chunks/info-DKCQHKI2-mpt80jd3.js +10 -0
- package/dist/gallery/frame/chunks/infoDiagram-6WML65LV-5rqm3mgg.js +65 -0
- package/dist/gallery/frame/chunks/ishikawaDiagram-WSZJBQD7-b51z96jy.js +975 -0
- package/dist/gallery/frame/chunks/journeyDiagram-NVQOT4AX-9jbfp0pk.js +1272 -0
- package/dist/gallery/frame/chunks/kanban-definition-27J2QSJJ-4x0vx4se.js +1116 -0
- package/dist/gallery/frame/chunks/katex-xfgk95tv.js +14034 -0
- package/dist/gallery/frame/chunks/markdown-1s5vwkf2.js +9 -0
- package/dist/gallery/frame/chunks/markdown-2h6bwsms.js +3239 -0
- package/dist/gallery/frame/chunks/markdown-2x113yh8.js +107 -0
- package/dist/gallery/frame/chunks/markdown-30b7rwmp.js +1943 -0
- package/dist/gallery/frame/chunks/markdown-356bfj4g.js +2031 -0
- package/dist/gallery/frame/chunks/markdown-44n9ea69.js +2406 -0
- package/dist/gallery/frame/chunks/markdown-56enrf2r.js +39 -0
- package/dist/gallery/frame/chunks/markdown-5avymm8t.js +725 -0
- package/dist/gallery/frame/chunks/markdown-6r4svars.js +33 -0
- package/dist/gallery/frame/chunks/markdown-6zz8efx2.js +24 -0
- package/dist/gallery/frame/chunks/markdown-7jp6prnb.js +19 -0
- package/dist/gallery/frame/chunks/markdown-809k7q41.js +55 -0
- package/dist/gallery/frame/chunks/markdown-82xmzhzd.js +1076 -0
- package/dist/gallery/frame/chunks/markdown-92n37gd4.js +7627 -0
- package/dist/gallery/frame/chunks/markdown-ag2sssf8.js +36 -0
- package/dist/gallery/frame/chunks/markdown-b4s7ntq2.js +19 -0
- package/dist/gallery/frame/chunks/markdown-c9k0fvp1.js +100 -0
- package/dist/gallery/frame/chunks/markdown-ceefymn8.js +125 -0
- package/dist/gallery/frame/chunks/markdown-d1wfbs70.js +243 -0
- package/dist/gallery/frame/chunks/markdown-dy5sgjhc.js +11022 -0
- package/dist/gallery/frame/chunks/markdown-dz3z5w30.js +30868 -0
- package/dist/gallery/frame/chunks/markdown-eg6ngz7y.js +30340 -0
- package/dist/gallery/frame/chunks/markdown-ff5wvb43.js +1149 -0
- package/dist/gallery/frame/chunks/markdown-ffg06nfz.js +86 -0
- package/dist/gallery/frame/chunks/markdown-fvfncadf.js +472 -0
- package/dist/gallery/frame/chunks/markdown-gvyszqpv.js +19 -0
- package/dist/gallery/frame/chunks/markdown-gx1p0ew0.js +88 -0
- package/dist/gallery/frame/chunks/markdown-gxye5frz.js +65 -0
- package/dist/gallery/frame/chunks/markdown-h7pzv34e.js +105 -0
- package/dist/gallery/frame/chunks/markdown-hab789t9.js +47 -0
- package/dist/gallery/frame/chunks/markdown-hjp6sj8t.js +125 -0
- package/dist/gallery/frame/chunks/markdown-jka64qgp.js +1993 -0
- package/dist/gallery/frame/chunks/markdown-k8xkn8mw.js +56 -0
- package/dist/gallery/frame/chunks/markdown-m7frq8x8.js +112 -0
- package/dist/gallery/frame/chunks/markdown-p740v4qm.js +36 -0
- package/dist/gallery/frame/chunks/markdown-pxfeh231.js +57 -0
- package/dist/gallery/frame/chunks/markdown-pzgd8z3s.js +87 -0
- package/dist/gallery/frame/chunks/markdown-qx3cngz2.js +1673 -0
- package/dist/gallery/frame/chunks/markdown-rawhfvvc.js +85 -0
- package/dist/gallery/frame/chunks/markdown-rd8w0ng9.js +49 -0
- package/dist/gallery/frame/chunks/markdown-rm2yxfjj.js +2773 -0
- package/dist/gallery/frame/chunks/markdown-s1jatrnk.js +993 -0
- package/dist/gallery/frame/chunks/markdown-sgfg5vvq.js +59 -0
- package/dist/gallery/frame/chunks/markdown-t4ethv9d.js +409 -0
- package/dist/gallery/frame/chunks/markdown-tnmabqvz.js +36 -0
- package/dist/gallery/frame/chunks/markdown-tvdpppw8.js +5292 -0
- package/dist/gallery/frame/chunks/markdown-v0x27twr.js +22 -0
- package/dist/gallery/frame/chunks/markdown-v14td68x.js +36 -0
- package/dist/gallery/frame/chunks/markdown-w6sgd54z.js +36 -0
- package/dist/gallery/frame/chunks/markdown-xw8dsk8b.js +2125 -0
- package/dist/gallery/frame/chunks/markdown-y18mve73.js +85 -0
- package/dist/gallery/frame/chunks/markdown-yengsms5.js +427 -0
- package/dist/gallery/frame/chunks/markdown-z1wc0v0d.js +1826 -0
- package/dist/gallery/frame/chunks/markdown-zazx9h4n.js +329 -0
- package/dist/gallery/frame/chunks/mermaid-de7p4v0x.js +30 -0
- package/dist/gallery/frame/chunks/mindmap-definition-FAOFIHXS-ra44cbem.js +1216 -0
- package/dist/gallery/frame/chunks/packet-7NZHBO7P-xppm5913.js +10 -0
- package/dist/gallery/frame/chunks/pegDiagram-VL7TDLO6-a2a4wg03.js +163 -0
- package/dist/gallery/frame/chunks/pie-RZYD4A2V-hnbxk152.js +10 -0
- package/dist/gallery/frame/chunks/pieDiagram-7S7Q4E2Y-f1vxjtmx.js +311 -0
- package/dist/gallery/frame/chunks/quadrantDiagram-CIZ2JOQS-ywafa6kh.js +1390 -0
- package/dist/gallery/frame/chunks/radar-I7S5WNFK-ewqd27se.js +10 -0
- package/dist/gallery/frame/chunks/railroad-3IZDKUUU-5hy4bdx3.js +10 -0
- package/dist/gallery/frame/chunks/railroad-abnf-AHOZXSZD-bh5sxz0d.js +10 -0
- package/dist/gallery/frame/chunks/railroad-ebnf-EBAXGLYW-6b9c3zeh.js +10 -0
- package/dist/gallery/frame/chunks/railroad-peg-LSFZ7HO6-avc8v39s.js +10 -0
- package/dist/gallery/frame/chunks/railroadDiagram-AXF67PYL-qc61wax4.js +131 -0
- package/dist/gallery/frame/chunks/requirementDiagram-LRYGKXZP-9bs4786c.js +1295 -0
- package/dist/gallery/frame/chunks/sankeyDiagram-W5VNT64P-awt1em8b.js +1339 -0
- package/dist/gallery/frame/chunks/sequenceDiagram-SI44F4Z6-qaf9mz3t.js +4324 -0
- package/dist/gallery/frame/chunks/sizeCapture-X5ZJPWSS-9fq685xd.js +64 -0
- package/dist/gallery/frame/chunks/stateDiagram-OKZ733FA-vx7a7hh0.js +456 -0
- package/dist/gallery/frame/chunks/stateDiagram-v2-UEYNNEHI-x4m6ws5t.js +46 -0
- package/dist/gallery/frame/chunks/swimlanes-SLNWSIFB-e4mxm1gh.js +8287 -0
- package/dist/gallery/frame/chunks/swimlanesDiagram-ULZ7WXOC-0h93e2qq.js +42 -0
- package/dist/gallery/frame/chunks/timeline-definition-Z64GVDOM-wp0bhbzg.js +1549 -0
- package/dist/gallery/frame/chunks/treeView-QDETBFTQ-hcx7n0vt.js +10 -0
- package/dist/gallery/frame/chunks/treemap-6X3UGDF4-av02skdw.js +10 -0
- package/dist/gallery/frame/chunks/vennDiagram-T6HMQDX7-pmvk1p7n.js +2566 -0
- package/dist/gallery/frame/chunks/wardley-OPB4EBWU-njsp6e40.js +10 -0
- package/dist/gallery/frame/chunks/wardleyDiagram-T6FBY63Y-eggmnv4z.js +979 -0
- package/dist/gallery/frame/chunks/xychartDiagram-ELKLHX3M-q40tnpe5.js +1954 -0
- package/dist/gallery/frame/frame.css +183 -0
- package/dist/gallery/frame/runtime/chart.js +42744 -0
- package/dist/gallery/frame/runtime/html.js +14 -0
- package/dist/gallery/frame/runtime/markdown.js +1403 -0
- package/dist/gallery/frame/runtime/mermaid.js +32 -0
- package/dist/gallery/frame/runtime/svg.js +14 -0
- package/dist/gallery/frame/runtime/tsx.js +52 -0
- package/dist/gallery/index.html +104 -0
- package/docs/reference/cli.md +163 -0
- package/docs/reference/export.md +106 -0
- package/docs/reference/html.md +215 -0
- package/docs/reference/http.md +85 -0
- package/docs/reference/mcp.md +78 -0
- package/docs/reference/security.md +119 -0
- package/docs/reference/storage.md +93 -0
- package/docs/reference/tsx.md +101 -0
- package/docs/reference/validation.md +212 -0
- package/package.json +89 -0
- package/scripts/build-gallery.ts +127 -0
- package/scripts/launch-netns.sh +67 -0
- package/skills/facet/SKILL.md +100 -0
- package/src/cli/client.ts +311 -0
- package/src/cli/commands/create.ts +49 -0
- package/src/cli/commands/doctor.ts +249 -0
- package/src/cli/commands/export.ts +179 -0
- package/src/cli/commands/instantiate.ts +35 -0
- package/src/cli/commands/list.ts +49 -0
- package/src/cli/commands/open.ts +75 -0
- package/src/cli/commands/pin.ts +39 -0
- package/src/cli/commands/promote.ts +49 -0
- package/src/cli/commands/publish.ts +122 -0
- package/src/cli/commands/read-back.ts +56 -0
- package/src/cli/commands/status.ts +114 -0
- package/src/cli/commands/watch.ts +146 -0
- package/src/cli/main.ts +522 -0
- package/src/cli/output.ts +106 -0
- package/src/cli/parser.ts +376 -0
- package/src/cli/presenter.ts +212 -0
- package/src/cli/service-metadata.ts +134 -0
- package/src/cli/spawn-service.ts +326 -0
- package/src/css.d.ts +4 -0
- package/src/gallery-web/app.ts +1581 -0
- package/src/gallery-web/export-menu.ts +121 -0
- package/src/gallery-web/export.ts +112 -0
- package/src/gallery-web/favicon.ts +54 -0
- package/src/gallery-web/frame/entries/chart.ts +6 -0
- package/src/gallery-web/frame/entries/html.ts +6 -0
- package/src/gallery-web/frame/entries/markdown.ts +6 -0
- package/src/gallery-web/frame/entries/mermaid.ts +6 -0
- package/src/gallery-web/frame/entries/svg.ts +6 -0
- package/src/gallery-web/frame/entries/tsx.ts +6 -0
- package/src/gallery-web/frame/frame-payload.ts +91 -0
- package/src/gallery-web/frame/renderer-validation.ts +13 -0
- package/src/gallery-web/frame/renderers/chart.ts +182 -0
- package/src/gallery-web/frame/renderers/dompurify-shim.ts +112 -0
- package/src/gallery-web/frame/renderers/html.ts +48 -0
- package/src/gallery-web/frame/renderers/markdown.ts +81 -0
- package/src/gallery-web/frame/renderers/mermaid.ts +120 -0
- package/src/gallery-web/frame/renderers/registry.ts +260 -0
- package/src/gallery-web/frame/renderers/svg.ts +393 -0
- package/src/gallery-web/frame/renderers/tsx.ts +61 -0
- package/src/gallery-web/frame/runtime.ts +501 -0
- package/src/gallery-web/frame/styles/artifact.css +8424 -0
- package/src/gallery-web/frame/styles/frame.css +183 -0
- package/src/gallery-web/frame/styles/html-source.css +23 -0
- package/src/gallery-web/frame/view-box.ts +33 -0
- package/src/gallery-web/frame-html.ts +72 -0
- package/src/gallery-web/index.html +104 -0
- package/src/gallery-web/session.ts +107 -0
- package/src/gallery-web/sse-client.ts +212 -0
- package/src/gallery-web/styles/app.css +420 -0
- package/src/gallery-web/styles/tokens.css +112 -0
- package/src/gallery-web/styles/verdict.css +186 -0
- package/src/gallery-web/swap.ts +64 -0
- package/src/gallery-web/theme.ts +40 -0
- package/src/gallery-web/view-state.ts +93 -0
- package/src/harness-adapters/claude-code/README.md +10 -0
- package/src/harness-adapters/claude-code/facet.sh +4 -0
- package/src/harness-adapters/codex/README.md +10 -0
- package/src/harness-adapters/codex/facet.sh +4 -0
- package/src/harness-adapters/mcp/README.md +13 -0
- package/src/harness-adapters/mcp/cli-bridge.ts +216 -0
- package/src/harness-adapters/mcp/main.ts +15 -0
- package/src/harness-adapters/mcp/server.ts +164 -0
- package/src/harness-adapters/mcp/tool-schemas.ts +53 -0
- package/src/harness-adapters/opencode/README.md +10 -0
- package/src/harness-adapters/opencode/facet.sh +4 -0
- package/src/runtime/compiled-entrypoints.ts +69 -0
- package/src/service/dispatcher.ts +717 -0
- package/src/service/export.ts +218 -0
- package/src/service/http-utils.ts +55 -0
- package/src/service/lexical/expectations.ts +210 -0
- package/src/service/lifecycle/idle-controller.ts +125 -0
- package/src/service/lifecycle/orphan-cleanup.ts +128 -0
- package/src/service/lifecycle/process-lock.ts +235 -0
- package/src/service/main.ts +136 -0
- package/src/service/process.ts +240 -0
- package/src/service/router-guards.ts +239 -0
- package/src/service/router.ts +627 -0
- package/src/service/security/auth.ts +168 -0
- package/src/service/security/host-origin.ts +138 -0
- package/src/service/security/http-guards.ts +13 -0
- package/src/service/security/leases.ts +182 -0
- package/src/service/security/token-store.ts +138 -0
- package/src/service/server.ts +356 -0
- package/src/service/store/database.ts +61 -0
- package/src/service/store/evidence-retention.ts +234 -0
- package/src/service/store/migrations.ts +167 -0
- package/src/service/store/repository-lifecycle.ts +121 -0
- package/src/service/store/repository.ts +732 -0
- package/src/service/store/schema.ts +218 -0
- package/src/service/stored-verdict.ts +97 -0
- package/src/service/stream.ts +301 -0
- package/src/service/verdict-enrichment.ts +45 -0
- package/src/shared/build/frame-bundle-plugins.ts +61 -0
- package/src/shared/build-mode.ts +28 -0
- package/src/shared/config/evidence-read.ts +98 -0
- package/src/shared/config/limits.ts +56 -0
- package/src/shared/config/paths.ts +96 -0
- package/src/shared/contracts/artifact-types.ts +2 -0
- package/src/shared/contracts/artifact.ts +98 -0
- package/src/shared/contracts/commands/_shared.ts +59 -0
- package/src/shared/contracts/commands/guards.ts +86 -0
- package/src/shared/contracts/commands/index.ts +204 -0
- package/src/shared/contracts/commands/names.ts +35 -0
- package/src/shared/contracts/commands/requests.ts +149 -0
- package/src/shared/contracts/commands/results.ts +200 -0
- package/src/shared/contracts/envelope.ts +140 -0
- package/src/shared/contracts/events.ts +41 -0
- package/src/shared/contracts/observed-counts.ts +19 -0
- package/src/shared/contracts/renderers.ts +6 -0
- package/src/shared/contracts/validation.ts +296 -0
- package/src/shared/errors/facet-error.ts +127 -0
- package/src/shared/errors/store-error.ts +68 -0
- package/src/shared/evidence-image.ts +37 -0
- package/src/shared/export.ts +31 -0
- package/src/shared/html/policy.ts +108 -0
- package/src/shared/html/style-vocabulary.ts +352 -0
- package/src/shared/logging/logger.ts +108 -0
- package/src/shared/logging/redact.ts +49 -0
- package/src/shared/security/frozen-csp.ts +13 -0
- package/src/shared/storage-version.ts +2 -0
- package/src/shared/tsx/execution.ts +29 -0
- package/src/shared/tsx/import-policy.ts +94 -0
- package/src/shared/util/dir-permissions.ts +94 -0
- package/src/shared/util/mermaid-nodes.ts +230 -0
- package/src/shared/util/process.ts +67 -0
- package/src/shared/util/time.ts +21 -0
- package/src/shared/version.ts +3 -0
- package/src/validation/sandbox/limits.ts +41 -0
- package/src/validation/sandbox/netns.ts +177 -0
- package/src/validation/tier0/chart.ts +177 -0
- package/src/validation/tier0/dom-shim.ts +54 -0
- package/src/validation/tier0/html.ts +408 -0
- package/src/validation/tier0/markdown.ts +285 -0
- package/src/validation/tier0/mermaid.ts +98 -0
- package/src/validation/tier0/runner.ts +504 -0
- package/src/validation/tier0/svg.ts +321 -0
- package/src/validation/tier0/tsx/allowlist-resolver.ts +77 -0
- package/src/validation/tier0/tsx/ast-policy-capabilities.ts +238 -0
- package/src/validation/tier0/tsx/ast-policy-shared.ts +71 -0
- package/src/validation/tier0/tsx/ast-policy.ts +181 -0
- package/src/validation/tier0/tsx/compiler.ts +156 -0
- package/src/validation/tier0/worker-dispatch.ts +211 -0
- package/src/validation/tier0/worker-entry.ts +96 -0
- package/src/validation/tier0/worker-input.ts +75 -0
- package/src/validation/tier1/browser-process.ts +147 -0
- package/src/validation/tier1/cdp-pipe.ts +281 -0
- package/src/validation/tier1/entries/chart.ts +5 -0
- package/src/validation/tier1/entries/html.ts +6 -0
- package/src/validation/tier1/entries/markdown.ts +5 -0
- package/src/validation/tier1/entries/mermaid.ts +5 -0
- package/src/validation/tier1/entries/svg.ts +5 -0
- package/src/validation/tier1/entries/tsx.ts +5 -0
- package/src/validation/tier1/frame-target.ts +162 -0
- package/src/validation/tier1/harness-entry.ts +261 -0
- package/src/validation/tier1/harness.ts +215 -0
- package/src/validation/tier1/isolated-probe.ts +94 -0
- package/src/validation/tier1/launcher.ts +177 -0
- package/src/validation/tier1/limits.ts +80 -0
- package/src/validation/tier1/nonce.ts +17 -0
- package/src/validation/tier1/protocol-probe.ts +519 -0
- package/src/validation/tier1/runner.ts +1122 -0
- package/src/validation/tier1/verdict.ts +246 -0
- package/src/validation/tier1/webp.ts +69 -0
- package/templates/README.md +88 -0
- package/templates/bar-compare.vl.json +63 -0
- package/templates/capacity-report.tsx +102 -0
- package/templates/decision-record.md +24 -0
- package/templates/deployment-state.mmd +40 -0
- package/templates/exemplar.md +91 -0
- package/templates/facet-story.tsx +799 -0
- package/templates/fleet-dashboard.html +141 -0
- package/templates/html-release-ledger.html +91 -0
- package/templates/html-status-report.html +114 -0
- package/templates/incident-console.tsx +147 -0
- package/templates/legion-boundaries.mmd +23 -0
- package/templates/legion-flow.mmd +15 -0
- package/templates/legion-sequence.mmd +14 -0
- package/templates/legion-state.mmd +7 -0
- package/templates/metric-card.svg +29 -0
- package/templates/observability-map.svg +101 -0
- package/templates/pipeline-audit.md +108 -0
- package/templates/release-metrics.vl.json +98 -0
- package/templates/service-topology.mmd +47 -0
- package/templates/status-report.md +32 -0
- package/templates/system-map.svg +53 -0
- package/templates/timeseries.vl.json +71 -0
- package/templates/tsx-interactive-counter.tsx +19 -0
- package/templates/tsx-status-report.tsx +31 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The protocol-authority probe.
|
|
3
|
+
*
|
|
4
|
+
* Every observation in this module is taken from the Chrome DevTools
|
|
5
|
+
* Protocol — NOT from page-world JavaScript. Page-world JS can be
|
|
6
|
+
* monkey-patched by a hostile artifact; the protocol data cannot.
|
|
7
|
+
* Two channels are surfaced:
|
|
8
|
+
*
|
|
9
|
+
* - `DOMSnapshot.captureSnapshot` — primary. Returns the document
|
|
10
|
+
* tree as a flat string-table-plus-nodes structure with
|
|
11
|
+
* `DocumentSnapshot.frameId` we cross-check against the resolved
|
|
12
|
+
* child frame id. Counts `svg`, `g.node`, `facet-error`, and
|
|
13
|
+
* captures viewBoxes for the layout-observability check.
|
|
14
|
+
*
|
|
15
|
+
* - `DOM.getDocument({ depth: -1, pierce: true })` — corroboration.
|
|
16
|
+
* Walks the same resolved child-frame document via a different protocol path so the
|
|
17
|
+
* counts from one channel can be cross-checked against the other.
|
|
18
|
+
* A divergence here is itself an anomaly; the verdict layer
|
|
19
|
+
* treats both as authority and the test layer asserts agreement.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { HtmlStructureCounts, ProtocolObservation } from "../../shared/contracts/validation";
|
|
23
|
+
import { HTML_STRUCTURAL_GROUPS } from "../../shared/html/policy";
|
|
24
|
+
|
|
25
|
+
import type { VerifierCdpSession } from "./browser-process";
|
|
26
|
+
import type { ResolvedChildFrame } from "./frame-target";
|
|
27
|
+
|
|
28
|
+
/** Raw shape of `DOMSnapshot.captureSnapshot` for one document. */
|
|
29
|
+
export interface SnapshotDocument {
|
|
30
|
+
readonly frameId: number;
|
|
31
|
+
readonly nodes: {
|
|
32
|
+
readonly nodeName: number[];
|
|
33
|
+
/** Parent node index for every entry in nodeName. */
|
|
34
|
+
readonly parentIndex: readonly number[];
|
|
35
|
+
/**
|
|
36
|
+
* Per-node attribute payload. The wire format is a FLAT array of
|
|
37
|
+
* alternating string-table indices — `[nameIdx, valueIdx, …]` —
|
|
38
|
+
* not `{name, value}` pairs (verified against the pinned shell:
|
|
39
|
+
* misparsing this shape silently zeroes every attribute-derived
|
|
40
|
+
* count, which the verdict layer then reads as a channel
|
|
41
|
+
* divergence → `tampered`).
|
|
42
|
+
*/
|
|
43
|
+
readonly attributes?: readonly (readonly number[])[];
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface SnapshotResponse {
|
|
48
|
+
readonly documents: readonly SnapshotDocument[];
|
|
49
|
+
readonly strings: readonly string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface ProtocolDomNode {
|
|
53
|
+
readonly backendNodeId?: number;
|
|
54
|
+
readonly contentDocument?: unknown;
|
|
55
|
+
readonly children?: unknown[];
|
|
56
|
+
readonly shadowRoots?: unknown[];
|
|
57
|
+
readonly shadowRootType?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function findFrameDocument(node: unknown, ownerBackendNodeId: number): unknown | null {
|
|
61
|
+
if (node === null || typeof node !== "object") return null;
|
|
62
|
+
const record = node as ProtocolDomNode;
|
|
63
|
+
if (record.backendNodeId === ownerBackendNodeId) return record.contentDocument ?? null;
|
|
64
|
+
const nested = [
|
|
65
|
+
record.contentDocument,
|
|
66
|
+
...(record.children ?? []),
|
|
67
|
+
...(record.shadowRoots ?? []),
|
|
68
|
+
];
|
|
69
|
+
for (const candidate of nested) {
|
|
70
|
+
const document = findFrameDocument(candidate, ownerBackendNodeId);
|
|
71
|
+
if (document !== null) return document;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readString(table: readonly string[], index: number): string {
|
|
77
|
+
return table[index] ?? "";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function countByName(snapshot: SnapshotResponse, documentIndex: number, name: string): number {
|
|
81
|
+
const document = snapshot.documents[documentIndex];
|
|
82
|
+
if (document === undefined) return 0;
|
|
83
|
+
let count = 0;
|
|
84
|
+
for (const nodeNameIndex of document.nodes.nodeName) {
|
|
85
|
+
if (readString(snapshot.strings, nodeNameIndex).toLowerCase() === name) count += 1;
|
|
86
|
+
}
|
|
87
|
+
return count;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Iterate one node's attributes as decoded `[name, value]` pairs. */
|
|
91
|
+
function* attributePairs(
|
|
92
|
+
snapshot: SnapshotResponse,
|
|
93
|
+
attr: readonly number[],
|
|
94
|
+
): Generator<readonly [string, string]> {
|
|
95
|
+
for (let i = 0; i + 1 < attr.length; i += 2) {
|
|
96
|
+
yield [
|
|
97
|
+
readString(snapshot.strings, attr[i] ?? 0),
|
|
98
|
+
readString(snapshot.strings, attr[i + 1] ?? 0),
|
|
99
|
+
] as const;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function attributeValue(
|
|
104
|
+
snapshot: SnapshotResponse,
|
|
105
|
+
document: SnapshotDocument,
|
|
106
|
+
nodeIndex: number,
|
|
107
|
+
wanted: string,
|
|
108
|
+
): string | undefined {
|
|
109
|
+
const attr = document.nodes.attributes?.[nodeIndex];
|
|
110
|
+
if (attr === undefined) return undefined;
|
|
111
|
+
for (const [name, value] of attributePairs(snapshot, attr)) {
|
|
112
|
+
if (name.toLowerCase() === wanted.toLowerCase()) return value;
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isMarkedRoot(
|
|
118
|
+
snapshot: SnapshotResponse,
|
|
119
|
+
document: SnapshotDocument,
|
|
120
|
+
nodeIndex: number,
|
|
121
|
+
): boolean {
|
|
122
|
+
return attributeValue(snapshot, document, nodeIndex, "data-facet-renderer-root") === "true";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function isRendererRoot(
|
|
126
|
+
snapshot: SnapshotResponse,
|
|
127
|
+
document: SnapshotDocument,
|
|
128
|
+
nodeIndex: number,
|
|
129
|
+
): boolean {
|
|
130
|
+
const name = readString(snapshot.strings, document.nodes.nodeName[nodeIndex] ?? 0).toLowerCase();
|
|
131
|
+
return name === "svg" && isMarkedRoot(snapshot, document, nodeIndex);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function hasAncestorIn(
|
|
135
|
+
document: SnapshotDocument,
|
|
136
|
+
nodeIndex: number,
|
|
137
|
+
ancestors: ReadonlySet<number>,
|
|
138
|
+
): boolean {
|
|
139
|
+
const seen = new Set<number>();
|
|
140
|
+
let parentIndex = document.nodes.parentIndex[nodeIndex] ?? -1;
|
|
141
|
+
while (parentIndex >= 0 && !seen.has(parentIndex)) {
|
|
142
|
+
if (ancestors.has(parentIndex)) return true;
|
|
143
|
+
seen.add(parentIndex);
|
|
144
|
+
parentIndex = document.nodes.parentIndex[parentIndex] ?? -1;
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function htmlRootIndexes(snapshot: SnapshotResponse, documentIndex: number): number[] {
|
|
150
|
+
const document = snapshot.documents[documentIndex];
|
|
151
|
+
if (document === undefined) return [];
|
|
152
|
+
const candidates = new Set<number>();
|
|
153
|
+
for (let nodeIndex = 0; nodeIndex < document.nodes.nodeName.length; nodeIndex += 1) {
|
|
154
|
+
if (isMarkedRoot(snapshot, document, nodeIndex)) candidates.add(nodeIndex);
|
|
155
|
+
}
|
|
156
|
+
return [...candidates].filter((nodeIndex) => {
|
|
157
|
+
const name = readString(
|
|
158
|
+
snapshot.strings,
|
|
159
|
+
document.nodes.nodeName[nodeIndex] ?? 0,
|
|
160
|
+
).toLowerCase();
|
|
161
|
+
return name !== "svg" && !hasAncestorIn(document, nodeIndex, candidates);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function isDescendantOf(
|
|
166
|
+
document: SnapshotDocument,
|
|
167
|
+
nodeIndex: number,
|
|
168
|
+
roots: ReadonlySet<number>,
|
|
169
|
+
): boolean {
|
|
170
|
+
let parent = document.nodes.parentIndex[nodeIndex] ?? -1;
|
|
171
|
+
while (parent >= 0) {
|
|
172
|
+
if (roots.has(parent)) return true;
|
|
173
|
+
parent = document.nodes.parentIndex[parent] ?? -1;
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function isExternalHttps(value: string | undefined): boolean {
|
|
179
|
+
if (value === undefined) return false;
|
|
180
|
+
try {
|
|
181
|
+
return new URL(value).protocol === "https:";
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function countSnapshotHtml(
|
|
188
|
+
snapshot: SnapshotResponse,
|
|
189
|
+
documentIndex: number,
|
|
190
|
+
): HtmlStructureCounts | undefined {
|
|
191
|
+
const document = snapshot.documents[documentIndex];
|
|
192
|
+
if (document === undefined) return undefined;
|
|
193
|
+
const roots = htmlRootIndexes(snapshot, documentIndex);
|
|
194
|
+
if (roots.length === 0) return undefined;
|
|
195
|
+
const rootSet = new Set(roots);
|
|
196
|
+
const counts = {
|
|
197
|
+
rendererRootCount: roots.length,
|
|
198
|
+
headingCount: 0,
|
|
199
|
+
tableCount: 0,
|
|
200
|
+
listCount: 0,
|
|
201
|
+
imageCount: 0,
|
|
202
|
+
canvasCount: 0,
|
|
203
|
+
externalImageCount: 0,
|
|
204
|
+
};
|
|
205
|
+
for (let nodeIndex = 0; nodeIndex < document.nodes.nodeName.length; nodeIndex += 1) {
|
|
206
|
+
if (!isDescendantOf(document, nodeIndex, rootSet)) continue;
|
|
207
|
+
const name = readString(
|
|
208
|
+
snapshot.strings,
|
|
209
|
+
document.nodes.nodeName[nodeIndex] ?? 0,
|
|
210
|
+
).toLowerCase();
|
|
211
|
+
if ((HTML_STRUCTURAL_GROUPS.headings as readonly string[]).includes(name))
|
|
212
|
+
counts.headingCount += 1;
|
|
213
|
+
if ((HTML_STRUCTURAL_GROUPS.tables as readonly string[]).includes(name)) counts.tableCount += 1;
|
|
214
|
+
if ((HTML_STRUCTURAL_GROUPS.lists as readonly string[]).includes(name)) counts.listCount += 1;
|
|
215
|
+
if ((HTML_STRUCTURAL_GROUPS.images as readonly string[]).includes(name)) {
|
|
216
|
+
counts.imageCount += 1;
|
|
217
|
+
if (isExternalHttps(attributeValue(snapshot, document, nodeIndex, "src"))) {
|
|
218
|
+
counts.externalImageCount += 1;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if ((HTML_STRUCTURAL_GROUPS.canvases as readonly string[]).includes(name))
|
|
222
|
+
counts.canvasCount += 1;
|
|
223
|
+
}
|
|
224
|
+
return counts;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function rendererRootIndexes(snapshot: SnapshotResponse, documentIndex: number): number[] {
|
|
228
|
+
const document = snapshot.documents[documentIndex];
|
|
229
|
+
if (document === undefined) return [];
|
|
230
|
+
const candidates = new Set<number>();
|
|
231
|
+
for (let nodeIdx = 0; nodeIdx < document.nodes.nodeName.length; nodeIdx += 1) {
|
|
232
|
+
if (isRendererRoot(snapshot, document, nodeIdx)) candidates.add(nodeIdx);
|
|
233
|
+
}
|
|
234
|
+
return [...candidates].filter((nodeIdx) => !hasAncestorIn(document, nodeIdx, candidates));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function graphRootIndexes(
|
|
238
|
+
snapshot: SnapshotResponse,
|
|
239
|
+
documentIndex: number,
|
|
240
|
+
rendererRoots: readonly number[],
|
|
241
|
+
): number[] {
|
|
242
|
+
const document = snapshot.documents[documentIndex];
|
|
243
|
+
if (document === undefined) return [];
|
|
244
|
+
return rendererRoots.filter(
|
|
245
|
+
(nodeIdx) =>
|
|
246
|
+
attributeValue(snapshot, document, nodeIdx, "data-facet-renderer-graph") === "true",
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function countGNode(
|
|
251
|
+
snapshot: SnapshotResponse,
|
|
252
|
+
documentIndex: number,
|
|
253
|
+
graphRoots: readonly number[],
|
|
254
|
+
): number {
|
|
255
|
+
const document = snapshot.documents[documentIndex];
|
|
256
|
+
if (document === undefined) return 0;
|
|
257
|
+
const graphRootSet = new Set(graphRoots);
|
|
258
|
+
let count = 0;
|
|
259
|
+
for (let nodeIdx = 0; nodeIdx < document.nodes.nodeName.length; nodeIdx += 1) {
|
|
260
|
+
const tag = readString(snapshot.strings, document.nodes.nodeName[nodeIdx] ?? 0).toLowerCase();
|
|
261
|
+
if (tag !== "g" || !hasAncestorIn(document, nodeIdx, graphRootSet)) continue;
|
|
262
|
+
if (attributeValue(snapshot, document, nodeIdx, "class")?.split(/\s+/).includes("node"))
|
|
263
|
+
count += 1;
|
|
264
|
+
}
|
|
265
|
+
return count;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function isNonDegenerateViewBox(viewBox: string): boolean {
|
|
269
|
+
const parts = viewBox
|
|
270
|
+
.trim()
|
|
271
|
+
.split(/[\s,]+/)
|
|
272
|
+
.map((part) => Number.parseFloat(part));
|
|
273
|
+
if (parts.length !== 4) return false;
|
|
274
|
+
const [, , width, height] = parts as [number, number, number, number];
|
|
275
|
+
return Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function collectViewBoxes(
|
|
279
|
+
snapshot: SnapshotResponse,
|
|
280
|
+
documentIndex: number,
|
|
281
|
+
rendererRoots: readonly number[],
|
|
282
|
+
): string[] {
|
|
283
|
+
const document = snapshot.documents[documentIndex];
|
|
284
|
+
if (document === undefined) return [];
|
|
285
|
+
const result: string[] = [];
|
|
286
|
+
for (const nodeIdx of rendererRoots) {
|
|
287
|
+
const viewBox = attributeValue(snapshot, document, nodeIdx, "viewbox");
|
|
288
|
+
if (viewBox !== undefined && viewBox.length > 0) result.push(viewBox);
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function collectDiscriminativeErrors(
|
|
294
|
+
snapshot: SnapshotResponse,
|
|
295
|
+
documentIndex: number,
|
|
296
|
+
): readonly { code: string; message: string }[] {
|
|
297
|
+
const document = snapshot.documents[documentIndex];
|
|
298
|
+
if (document === undefined) return [];
|
|
299
|
+
const result: { code: string; message: string }[] = [];
|
|
300
|
+
for (let i = 0; i < document.nodes.nodeName.length; i += 1) {
|
|
301
|
+
if (attributeValue(snapshot, document, i, "data-facet-error") !== undefined) {
|
|
302
|
+
result.push({ code: "facet_error", message: "facet-error element" });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Pick the document index whose `frameId` matches the resolved
|
|
310
|
+
* child frame id. The snapshot string table maps frameId strings
|
|
311
|
+
* to integer indices; the document's `frameId` field is the
|
|
312
|
+
* INTEGER index into `strings`.
|
|
313
|
+
*/
|
|
314
|
+
function findDocumentIndex(snapshot: SnapshotResponse, childFrameId: string): number {
|
|
315
|
+
for (let i = 0; i < snapshot.documents.length; i += 1) {
|
|
316
|
+
const document = snapshot.documents[i];
|
|
317
|
+
if (document === undefined) continue;
|
|
318
|
+
const indexed = readString(snapshot.strings, document.frameId);
|
|
319
|
+
if (indexed === childFrameId) return i;
|
|
320
|
+
}
|
|
321
|
+
return -1;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Run `DOMSnapshot.captureSnapshot` scoped to the resolved child
|
|
326
|
+
* frame and surface the canonical `ProtocolObservation`.
|
|
327
|
+
*/
|
|
328
|
+
export async function probeProtocolSnapshot(
|
|
329
|
+
session: VerifierCdpSession,
|
|
330
|
+
childFrame: ResolvedChildFrame,
|
|
331
|
+
): Promise<ProtocolObservation> {
|
|
332
|
+
const snapshot = (await session.send("DOMSnapshot.captureSnapshot", {
|
|
333
|
+
computedStyles: [],
|
|
334
|
+
})) as SnapshotResponse;
|
|
335
|
+
const documentIndex = findDocumentIndex(snapshot, childFrame.frameId);
|
|
336
|
+
if (documentIndex < 0) {
|
|
337
|
+
return {
|
|
338
|
+
rendererRootSvgCount: 0,
|
|
339
|
+
graphCount: 0,
|
|
340
|
+
mermaidNodeCount: 0,
|
|
341
|
+
visibleSvgCount: 0,
|
|
342
|
+
viewBoxes: [],
|
|
343
|
+
errorCount: 0,
|
|
344
|
+
opaqueRegionCount: 0,
|
|
345
|
+
externalImageCount: 0,
|
|
346
|
+
discriminativeErrors: [],
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
const rendererRoots = rendererRootIndexes(snapshot, documentIndex);
|
|
350
|
+
const graphRoots = graphRootIndexes(snapshot, documentIndex, rendererRoots);
|
|
351
|
+
const viewBoxes = collectViewBoxes(snapshot, documentIndex, rendererRoots);
|
|
352
|
+
const discriminativeErrors = collectDiscriminativeErrors(snapshot, documentIndex);
|
|
353
|
+
const errorCount = discriminativeErrors.length;
|
|
354
|
+
const htmlCounts = countSnapshotHtml(snapshot, documentIndex);
|
|
355
|
+
return {
|
|
356
|
+
rendererRootSvgCount: rendererRoots.length,
|
|
357
|
+
graphCount: graphRoots.length,
|
|
358
|
+
mermaidNodeCount: countGNode(snapshot, documentIndex, graphRoots),
|
|
359
|
+
visibleSvgCount: viewBoxes.filter(isNonDegenerateViewBox).length,
|
|
360
|
+
opaqueRegionCount: countByName(snapshot, documentIndex, "canvas"),
|
|
361
|
+
externalImageCount: htmlCounts?.externalImageCount ?? 0,
|
|
362
|
+
...(htmlCounts === undefined ? {} : { html: htmlCounts }),
|
|
363
|
+
viewBoxes,
|
|
364
|
+
errorCount,
|
|
365
|
+
discriminativeErrors: discriminativeErrors.map((entry) => ({
|
|
366
|
+
code: entry.code,
|
|
367
|
+
message: entry.message,
|
|
368
|
+
})),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Run `DOM.getDocument({ depth: -1, pierce: true })` as a
|
|
374
|
+
* corroborating channel. Returns the same canonical shape so the
|
|
375
|
+
* verdict layer can either-or both channels and any divergence
|
|
376
|
+
* surfaces as `tampered`.
|
|
377
|
+
*/
|
|
378
|
+
export async function probeProtocolGetDocument(
|
|
379
|
+
session: VerifierCdpSession,
|
|
380
|
+
childFrame: ResolvedChildFrame,
|
|
381
|
+
): Promise<ProtocolObservation> {
|
|
382
|
+
const result = (await session.send("DOM.getDocument", {
|
|
383
|
+
depth: -1,
|
|
384
|
+
pierce: true,
|
|
385
|
+
})) as {
|
|
386
|
+
root: {
|
|
387
|
+
nodeName: string;
|
|
388
|
+
backendNodeId?: number;
|
|
389
|
+
contentDocument?: { nodeName: string; children?: unknown[] };
|
|
390
|
+
children?: unknown[];
|
|
391
|
+
shadowRoots?: unknown[];
|
|
392
|
+
shadowRootType?: string;
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
const owner = (await session.send("DOM.getFrameOwner", {
|
|
396
|
+
frameId: childFrame.frameId,
|
|
397
|
+
})) as { backendNodeId: number };
|
|
398
|
+
let rendererRootSvgCount = 0;
|
|
399
|
+
let graphCount = 0;
|
|
400
|
+
let errorCount = 0;
|
|
401
|
+
let gNodeCount = 0;
|
|
402
|
+
let opaqueRegionCount = 0;
|
|
403
|
+
let visibleSvgCount = 0;
|
|
404
|
+
let html: HtmlStructureCounts | undefined;
|
|
405
|
+
const viewBoxes: string[] = [];
|
|
406
|
+
const visit = (
|
|
407
|
+
node: unknown,
|
|
408
|
+
withinRendererRoot = false,
|
|
409
|
+
withinGraphRoot = false,
|
|
410
|
+
withinHtmlRoot = false,
|
|
411
|
+
withinMarkedRoot = false,
|
|
412
|
+
): void => {
|
|
413
|
+
if (node === null || typeof node !== "object") return;
|
|
414
|
+
const record = node as {
|
|
415
|
+
nodeName?: string;
|
|
416
|
+
// DOM.Node attributes arrive as a FLAT string array
|
|
417
|
+
// [name1, value1, name2, value2, …] — not {name, value} objects.
|
|
418
|
+
attributes?: string[];
|
|
419
|
+
contentDocument?: unknown;
|
|
420
|
+
children?: unknown[];
|
|
421
|
+
shadowRoots?: unknown[];
|
|
422
|
+
shadowRootType?: string;
|
|
423
|
+
};
|
|
424
|
+
const findAttr = (wanted: string): string | undefined => {
|
|
425
|
+
const attrs = record.attributes;
|
|
426
|
+
if (!Array.isArray(attrs)) return undefined;
|
|
427
|
+
for (let i = 0; i + 1 < attrs.length; i += 2) {
|
|
428
|
+
if (attrs[i]?.toLowerCase() === wanted.toLowerCase()) return attrs[i + 1];
|
|
429
|
+
}
|
|
430
|
+
return undefined;
|
|
431
|
+
};
|
|
432
|
+
const name = (record.nodeName ?? "").toLowerCase();
|
|
433
|
+
const markedRoot = findAttr("data-facet-renderer-root") === "true";
|
|
434
|
+
const rendererRoot = name === "svg" && markedRoot && !withinRendererRoot;
|
|
435
|
+
const htmlRoot = name !== "svg" && markedRoot && !withinMarkedRoot;
|
|
436
|
+
const graphRoot = rendererRoot && findAttr("data-facet-renderer-graph") === "true";
|
|
437
|
+
if (htmlRoot) {
|
|
438
|
+
html ??= {
|
|
439
|
+
rendererRootCount: 0,
|
|
440
|
+
headingCount: 0,
|
|
441
|
+
tableCount: 0,
|
|
442
|
+
listCount: 0,
|
|
443
|
+
imageCount: 0,
|
|
444
|
+
canvasCount: 0,
|
|
445
|
+
externalImageCount: 0,
|
|
446
|
+
};
|
|
447
|
+
html.rendererRootCount += 1;
|
|
448
|
+
}
|
|
449
|
+
if (rendererRoot) {
|
|
450
|
+
rendererRootSvgCount += 1;
|
|
451
|
+
const vb = findAttr("viewBox");
|
|
452
|
+
if (vb !== undefined) {
|
|
453
|
+
viewBoxes.push(vb);
|
|
454
|
+
if (isNonDegenerateViewBox(vb)) visibleSvgCount += 1;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (graphRoot) graphCount += 1;
|
|
458
|
+
if (name === "g" && withinGraphRoot && findAttr("class")?.split(/\s+/).includes("node")) {
|
|
459
|
+
gNodeCount += 1;
|
|
460
|
+
}
|
|
461
|
+
// Renderer markers scope SVG counts, but the canvas census deliberately
|
|
462
|
+
// covers the entire child-frame document so smuggled canvases stay visible.
|
|
463
|
+
// getContext() would create a context and make the observation self-fulfilling.
|
|
464
|
+
if (name === "canvas") opaqueRegionCount += 1;
|
|
465
|
+
if (withinHtmlRoot && html !== undefined) {
|
|
466
|
+
if ((HTML_STRUCTURAL_GROUPS.headings as readonly string[]).includes(name))
|
|
467
|
+
html.headingCount += 1;
|
|
468
|
+
if ((HTML_STRUCTURAL_GROUPS.tables as readonly string[]).includes(name)) html.tableCount += 1;
|
|
469
|
+
if ((HTML_STRUCTURAL_GROUPS.lists as readonly string[]).includes(name)) html.listCount += 1;
|
|
470
|
+
if ((HTML_STRUCTURAL_GROUPS.images as readonly string[]).includes(name)) {
|
|
471
|
+
html.imageCount += 1;
|
|
472
|
+
if (isExternalHttps(findAttr("src"))) html.externalImageCount += 1;
|
|
473
|
+
}
|
|
474
|
+
if ((HTML_STRUCTURAL_GROUPS.canvases as readonly string[]).includes(name))
|
|
475
|
+
html.canvasCount += 1;
|
|
476
|
+
}
|
|
477
|
+
if (findAttr("data-facet-error") !== undefined) errorCount += 1;
|
|
478
|
+
const nextWithinRendererRoot = withinRendererRoot || rendererRoot;
|
|
479
|
+
const nextWithinGraphRoot = withinGraphRoot || graphRoot;
|
|
480
|
+
const nextWithinHtmlRoot = withinHtmlRoot || htmlRoot;
|
|
481
|
+
const nextWithinMarkedRoot = withinMarkedRoot || markedRoot;
|
|
482
|
+
if (record.children !== undefined)
|
|
483
|
+
for (const child of record.children)
|
|
484
|
+
visit(
|
|
485
|
+
child,
|
|
486
|
+
nextWithinRendererRoot,
|
|
487
|
+
nextWithinGraphRoot,
|
|
488
|
+
nextWithinHtmlRoot,
|
|
489
|
+
nextWithinMarkedRoot,
|
|
490
|
+
);
|
|
491
|
+
if (record.shadowRoots !== undefined)
|
|
492
|
+
for (const shadow of record.shadowRoots) {
|
|
493
|
+
const shadowRecord = shadow as ProtocolDomNode;
|
|
494
|
+
if (shadowRecord.shadowRootType === "user-agent") continue;
|
|
495
|
+
visit(
|
|
496
|
+
shadow,
|
|
497
|
+
nextWithinRendererRoot,
|
|
498
|
+
nextWithinGraphRoot,
|
|
499
|
+
nextWithinHtmlRoot,
|
|
500
|
+
nextWithinMarkedRoot,
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
const frameDocument = findFrameDocument(result.root, owner.backendNodeId);
|
|
505
|
+
if (frameDocument !== null) visit(frameDocument);
|
|
506
|
+
return {
|
|
507
|
+
rendererRootSvgCount,
|
|
508
|
+
graphCount,
|
|
509
|
+
mermaidNodeCount: gNodeCount,
|
|
510
|
+
visibleSvgCount,
|
|
511
|
+
opaqueRegionCount,
|
|
512
|
+
externalImageCount: html?.externalImageCount ?? 0,
|
|
513
|
+
...(html === undefined ? {} : { html }),
|
|
514
|
+
viewBoxes,
|
|
515
|
+
errorCount,
|
|
516
|
+
discriminativeErrors:
|
|
517
|
+
errorCount > 0 ? [{ code: "facet_error", message: "DOM.getDocument" }] : [],
|
|
518
|
+
};
|
|
519
|
+
}
|