@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,75 @@
|
|
|
1
|
+
import { ARTIFACT_TYPES, type ArtifactType } from "../../shared/contracts/artifact-types";
|
|
2
|
+
import type { Renderer } from "../../shared/contracts/artifact";
|
|
3
|
+
import type { LexicalCounters, TsxExecutionMode } from "../../shared/contracts/validation";
|
|
4
|
+
|
|
5
|
+
export interface WorkerInput {
|
|
6
|
+
readonly schemaVersion: "facet.tier0.v2";
|
|
7
|
+
readonly requestId: string;
|
|
8
|
+
readonly revisionSha: string;
|
|
9
|
+
readonly artifactType: ArtifactType;
|
|
10
|
+
readonly renderer: Renderer;
|
|
11
|
+
readonly source: Uint8Array;
|
|
12
|
+
readonly lexical: LexicalCounters;
|
|
13
|
+
readonly execution?: TsxExecutionMode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface WorkerInputJson {
|
|
17
|
+
readonly schemaVersion?: unknown;
|
|
18
|
+
readonly requestId?: unknown;
|
|
19
|
+
readonly revisionSha?: unknown;
|
|
20
|
+
readonly artifactType?: unknown;
|
|
21
|
+
readonly renderer?: unknown;
|
|
22
|
+
readonly sourceBase64?: unknown;
|
|
23
|
+
readonly lexical?: unknown;
|
|
24
|
+
readonly execution?: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function parseWorkerInput(text: string): WorkerInput {
|
|
28
|
+
const raw = JSON.parse(text) as WorkerInputJson;
|
|
29
|
+
if (raw.schemaVersion !== "facet.tier0.v2") {
|
|
30
|
+
throw new Error(`unknown schemaVersion: ${String(raw.schemaVersion)}`);
|
|
31
|
+
}
|
|
32
|
+
if (typeof raw.requestId !== "string" || raw.requestId.length === 0)
|
|
33
|
+
throw new Error("invalid requestId");
|
|
34
|
+
if (typeof raw.revisionSha !== "string" || !/^[a-f0-9]{64}$/.test(raw.revisionSha)) {
|
|
35
|
+
throw new Error("invalid revisionSha");
|
|
36
|
+
}
|
|
37
|
+
if (
|
|
38
|
+
typeof raw.artifactType !== "string" ||
|
|
39
|
+
!ARTIFACT_TYPES.includes(raw.artifactType as ArtifactType)
|
|
40
|
+
) {
|
|
41
|
+
throw new Error(`invalid artifactType: ${String(raw.artifactType)}`);
|
|
42
|
+
}
|
|
43
|
+
if (raw.renderer !== "svg" && raw.renderer !== "canvas") {
|
|
44
|
+
throw new Error(`invalid renderer: ${String(raw.renderer)}`);
|
|
45
|
+
}
|
|
46
|
+
if (typeof raw.sourceBase64 !== "string") throw new Error("missing sourceBase64");
|
|
47
|
+
const lexical = raw.lexical as LexicalCounters | undefined;
|
|
48
|
+
if (
|
|
49
|
+
lexical === undefined ||
|
|
50
|
+
typeof lexical.rendererRootSvgCount !== "number" ||
|
|
51
|
+
(typeof lexical.mermaidNodeCount !== "number" && lexical.mermaidNodeCount !== null) ||
|
|
52
|
+
typeof lexical.visibleSvgCount !== "number"
|
|
53
|
+
) {
|
|
54
|
+
throw new Error("invalid lexical counters");
|
|
55
|
+
}
|
|
56
|
+
const source = Uint8Array.from(Buffer.from(raw.sourceBase64, "base64"));
|
|
57
|
+
const execution =
|
|
58
|
+
raw.execution === "static" || raw.execution === "interactive" ? raw.execution : undefined;
|
|
59
|
+
if (raw.artifactType === "tsx" && execution !== "static" && execution !== "interactive") {
|
|
60
|
+
throw new Error("TSX input requires execution mode");
|
|
61
|
+
}
|
|
62
|
+
if (raw.artifactType !== "tsx" && raw.execution !== undefined) {
|
|
63
|
+
throw new Error("execution is only valid for TSX input");
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
schemaVersion: "facet.tier0.v2",
|
|
67
|
+
requestId: raw.requestId,
|
|
68
|
+
revisionSha: raw.revisionSha,
|
|
69
|
+
artifactType: raw.artifactType as ArtifactType,
|
|
70
|
+
renderer: raw.renderer,
|
|
71
|
+
source,
|
|
72
|
+
lexical,
|
|
73
|
+
...(execution === undefined ? {} : { execution }),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-process lifecycle for the Tier 1 verifier.
|
|
3
|
+
*
|
|
4
|
+
* The interface intentionally exposes only what the verifier needs
|
|
5
|
+
* (open a target, drive CDP, close) so a future raw-CDP impl can
|
|
6
|
+
* replace the puppeteer-core adapter without changing any caller.
|
|
7
|
+
*
|
|
8
|
+
* The ephemeral user-data-dir (mode 0700, removed in `finally`)
|
|
9
|
+
* makes the on-disk footprint zero between runs. The orphan
|
|
10
|
+
* cleanup helper registers the (pid, startTime) so a parent that
|
|
11
|
+
* crashes before the finally block can still reap the tmpdir.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
15
|
+
import { chmodSync, mkdtempSync, rmSync } from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
|
|
19
|
+
import { FacetError } from "../../shared/errors/facet-error";
|
|
20
|
+
|
|
21
|
+
import { TIER1_USER_DATA_DIR_MODE } from "./limits";
|
|
22
|
+
import { resolveLauncher, type ResolvedLauncher } from "./launcher";
|
|
23
|
+
|
|
24
|
+
const TIER1_TRACE = process.env.FACET_TIER1_TRACE === "1";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Minimal CDP session surface the verifier uses. Puppeteer-core's
|
|
28
|
+
* `CDPSession` satisfies this shape; a raw-CDP impl can too.
|
|
29
|
+
*/
|
|
30
|
+
export interface VerifierCdpSession {
|
|
31
|
+
send<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>;
|
|
32
|
+
on(event: string, listener: (params: unknown) => void): void;
|
|
33
|
+
off(event: string, listener: (params: unknown) => void): void;
|
|
34
|
+
detach(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface VerifierTarget {
|
|
38
|
+
/** The browser-level CDP session (page-level for puppeteer-core). */
|
|
39
|
+
readonly session: VerifierCdpSession;
|
|
40
|
+
/**
|
|
41
|
+
* Frame-tree snapshot — every CDP `Page` call that depends on the
|
|
42
|
+
* frame tree reads from here so the verifier's "resolve child
|
|
43
|
+
* frame" step has a stable input.
|
|
44
|
+
*/
|
|
45
|
+
getFrameTree(): Promise<unknown>;
|
|
46
|
+
/**
|
|
47
|
+
* Tear down the browser. The implementation MUST close the CDP
|
|
48
|
+
* session, signal the browser, reap the tmpdir, and remove any
|
|
49
|
+
* orphan-cleanup registration.
|
|
50
|
+
*/
|
|
51
|
+
close(): Promise<void>;
|
|
52
|
+
/** Browser pid — the orphan cleanup key. */
|
|
53
|
+
readonly pid: number;
|
|
54
|
+
readonly startTime: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build a fresh ephemeral user-data directory under `os.tmpdir()`. The
|
|
59
|
+
* returned path is mode 0700 (matches `TIER1_USER_DATA_DIR_MODE`); the
|
|
60
|
+
* caller MUST `rm -rf` it (via the verifier's `finally` block).
|
|
61
|
+
*/
|
|
62
|
+
export function createEphemeralProfileDir(): string {
|
|
63
|
+
const base = tmpdir();
|
|
64
|
+
const dir = mkdtempSync(join(base, "facet-tier1-"));
|
|
65
|
+
try {
|
|
66
|
+
chmodSync(dir, TIER1_USER_DATA_DIR_MODE);
|
|
67
|
+
} catch {
|
|
68
|
+
// best-effort; chmod may fail under restrictive umask
|
|
69
|
+
}
|
|
70
|
+
return dir;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Remove the ephemeral profile directory; best-effort. */
|
|
74
|
+
export function removeEphemeralProfileDir(path: string): void {
|
|
75
|
+
const startedAt = performance.now();
|
|
76
|
+
if (TIER1_TRACE) process.stderr.write(`[tier1] browser-profile-remove:start path=${path}\n`);
|
|
77
|
+
try {
|
|
78
|
+
rmSync(path, { recursive: true, force: true });
|
|
79
|
+
} catch {
|
|
80
|
+
// best-effort
|
|
81
|
+
} finally {
|
|
82
|
+
if (TIER1_TRACE) {
|
|
83
|
+
process.stderr.write(
|
|
84
|
+
`[tier1] browser-profile-remove:complete durationMs=${Math.round(performance.now() - startedAt)} path=${path}\n`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function closeAndRemoveEphemeralProfile(
|
|
91
|
+
close: () => Promise<void>,
|
|
92
|
+
profileDir: string,
|
|
93
|
+
timeoutMs = 2_000,
|
|
94
|
+
): Promise<void> {
|
|
95
|
+
try {
|
|
96
|
+
await Promise.race([close(), new Promise<void>((resolve) => setTimeout(resolve, timeoutMs))]);
|
|
97
|
+
} finally {
|
|
98
|
+
removeEphemeralProfileDir(profileDir);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Detect whether the launcher wrapper + pinned shell are reachable.
|
|
104
|
+
* Bounded by a 2s timeout so a hung unshare does not freeze the
|
|
105
|
+
* verifier; the verdict path runs this once per cold-start.
|
|
106
|
+
*/
|
|
107
|
+
export async function probeLauncherAvailability(
|
|
108
|
+
overrides: { readonly version?: string } = {},
|
|
109
|
+
): Promise<{ available: boolean; reason: string | null }> {
|
|
110
|
+
let launcher: ResolvedLauncher;
|
|
111
|
+
try {
|
|
112
|
+
launcher = resolveLauncher(
|
|
113
|
+
overrides.version !== undefined ? { version: overrides.version } : {},
|
|
114
|
+
);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
117
|
+
return { available: false, reason: message };
|
|
118
|
+
}
|
|
119
|
+
return new Promise((resolve) => {
|
|
120
|
+
const child: ChildProcess = spawn(launcher.executablePath, ["--version"], {
|
|
121
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
122
|
+
detached: false,
|
|
123
|
+
});
|
|
124
|
+
const timer = setTimeout(() => {
|
|
125
|
+
try {
|
|
126
|
+
child.kill("SIGKILL");
|
|
127
|
+
} catch {
|
|
128
|
+
// already dead
|
|
129
|
+
}
|
|
130
|
+
resolve({ available: false, reason: "launcher probe timed out" });
|
|
131
|
+
}, 2_000);
|
|
132
|
+
child.once("exit", (code) => {
|
|
133
|
+
clearTimeout(timer);
|
|
134
|
+
if (code === 0) {
|
|
135
|
+
resolve({ available: true, reason: null });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
resolve({ available: false, reason: `launcher exited with code ${code ?? "null"}` });
|
|
139
|
+
});
|
|
140
|
+
child.once("error", (error) => {
|
|
141
|
+
clearTimeout(timer);
|
|
142
|
+
resolve({ available: false, reason: `launcher spawn failed: ${error.message}` });
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
void FacetError;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Puppeteer-core adapter for the Tier 1 verifier.
|
|
3
|
+
*
|
|
4
|
+
* The verifier never imports puppeteer-core directly — it goes
|
|
5
|
+
* through `Tier1Browser` (declared in `browser-process.ts`) so a
|
|
6
|
+
* future raw-CDP impl can replace this module without changing the
|
|
7
|
+
* runner. This file is the ONLY place puppeteer-core appears.
|
|
8
|
+
*
|
|
9
|
+
* CDP transport: `--remote-debugging-pipe`. Puppeteer-core with
|
|
10
|
+
* `pipe: true` spawns the browser with the pipe inherited from
|
|
11
|
+
* the parent and exchanges a single JSON-encoded handshake on
|
|
12
|
+
* stdin/stdout to discover the page target. The verifier then
|
|
13
|
+
* opens a per-page CDPSession and uses it for the protocol probes.
|
|
14
|
+
*
|
|
15
|
+
* The pipe transport is REQUIRED inside the netns: loopback is
|
|
16
|
+
* DOWN so a TCP CDP port cannot accept connections. The
|
|
17
|
+
* `scripts/launch-netns.sh` wrapper execs unshare + the pinned
|
|
18
|
+
* shell, inheriting the pipe through the chain.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import puppeteer, { type Browser, type CDPSession, type Page } from "puppeteer-core";
|
|
22
|
+
|
|
23
|
+
import { buildBrowserArgs, resolveLauncher, type ResolvedLauncher } from "./launcher";
|
|
24
|
+
import { TIER1_CDP_CALL_WATCHDOG_MS } from "./limits";
|
|
25
|
+
import {
|
|
26
|
+
closeAndRemoveEphemeralProfile,
|
|
27
|
+
createEphemeralProfileDir,
|
|
28
|
+
removeEphemeralProfileDir,
|
|
29
|
+
type VerifierCdpSession,
|
|
30
|
+
type VerifierTarget,
|
|
31
|
+
} from "./browser-process";
|
|
32
|
+
import { readPidStartTimeTicks } from "../../shared/util/process";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Typed signal for a dead CDP pipe transport: the browser process is
|
|
36
|
+
* alive but every protocol call pends forever (observed when a browser
|
|
37
|
+
* is spawned immediately after another browser's teardown — the new
|
|
38
|
+
* child's devtools pipe is torn down out from under it). The runner
|
|
39
|
+
* retries once with a fresh browser; anything else would hang the
|
|
40
|
+
* verification past every barrier deadline.
|
|
41
|
+
*/
|
|
42
|
+
export class Tier1TransportWedgeError extends Error {
|
|
43
|
+
constructor(where: string) {
|
|
44
|
+
super(`tier1: CDP transport wedged at ${where}`);
|
|
45
|
+
this.name = "Tier1TransportWedgeError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const TIER1_TRACE = process.env.FACET_TIER1_TRACE === "1";
|
|
50
|
+
|
|
51
|
+
function traceLaunch(stage: string, startedAt: number): void {
|
|
52
|
+
if (!TIER1_TRACE) return;
|
|
53
|
+
process.stderr.write(`[tier1] +${Date.now() - startedAt}ms browser:${stage}\n`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class PuppeteerCdpSessionAdapter implements VerifierCdpSession {
|
|
57
|
+
constructor(private readonly inner: CDPSession) {}
|
|
58
|
+
|
|
59
|
+
async send<T = unknown>(method: string, params: Record<string, unknown> = {}): Promise<T> {
|
|
60
|
+
// Watchdog every call: a wedged pipe transport leaves the browser
|
|
61
|
+
// alive but every send pending forever — the run would hang past
|
|
62
|
+
// every barrier deadline because the deadline loops only advance
|
|
63
|
+
// when a send resolves. A typed rejection lets the runner retry
|
|
64
|
+
// with a fresh browser instead of hanging the verification.
|
|
65
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
66
|
+
try {
|
|
67
|
+
return await Promise.race([
|
|
68
|
+
this.inner.send(method as never, params as never) as Promise<T>,
|
|
69
|
+
new Promise<never>((_resolve, reject) => {
|
|
70
|
+
timer = setTimeout(
|
|
71
|
+
() => reject(new Tier1TransportWedgeError(method)),
|
|
72
|
+
TIER1_CDP_CALL_WATCHDOG_MS,
|
|
73
|
+
);
|
|
74
|
+
}),
|
|
75
|
+
]);
|
|
76
|
+
} finally {
|
|
77
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
on(event: string, listener: (params: unknown) => void): void {
|
|
82
|
+
this.inner.on(event as never, listener as never);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
off(event: string, listener: (params: unknown) => void): void {
|
|
86
|
+
this.inner.off(event as never, listener as never);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async detach(): Promise<void> {
|
|
90
|
+
try {
|
|
91
|
+
await this.inner.detach();
|
|
92
|
+
} catch {
|
|
93
|
+
// session already torn down
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class PuppeteerVerifierTarget implements VerifierTarget {
|
|
99
|
+
private readonly browser: Browser;
|
|
100
|
+
private readonly page: Page;
|
|
101
|
+
readonly session: VerifierCdpSession;
|
|
102
|
+
private readonly profileDir: string;
|
|
103
|
+
private closed = false;
|
|
104
|
+
|
|
105
|
+
constructor(browser: Browser, page: Page, session: CDPSession, profileDir: string) {
|
|
106
|
+
this.browser = browser;
|
|
107
|
+
this.page = page;
|
|
108
|
+
this.session = new PuppeteerCdpSessionAdapter(session);
|
|
109
|
+
this.profileDir = profileDir;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get pid(): number {
|
|
113
|
+
const proc = this.browser.process();
|
|
114
|
+
return proc?.pid ?? -1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get startTime(): number {
|
|
118
|
+
// Read the OS-recorded monotonic start time (field 22 of
|
|
119
|
+
// /proc/<pid>/stat) so this record is byte-identical with the
|
|
120
|
+
// canonical lock-metadata start time in
|
|
121
|
+
// `service/lifecycle/process-lock.ts`. A future orphan-cleanup
|
|
122
|
+
// hook that cross-references the two with `isPidAlive` and
|
|
123
|
+
// start-time ticks will see the same value here as the lock
|
|
124
|
+
// metadata recorded at the parent's startup. Falls back to
|
|
125
|
+
// `Date.now()` only when the browser process is unreachable
|
|
126
|
+
// (e.g. closed before probe); the runner treats the
|
|
127
|
+
// (pid, startTime) pair as a best-effort identity key.
|
|
128
|
+
const proc = this.browser.process();
|
|
129
|
+
const pid = proc?.pid;
|
|
130
|
+
if (pid === undefined || !Number.isInteger(pid) || pid <= 0) return Date.now();
|
|
131
|
+
return readPidStartTimeTicks(pid) ?? Date.now();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async getFrameTree(): Promise<unknown> {
|
|
135
|
+
return this.session.send("Page.getFrameTree");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async close(): Promise<void> {
|
|
139
|
+
if (this.closed) return;
|
|
140
|
+
this.closed = true;
|
|
141
|
+
await closeAndRemoveEphemeralProfile(async () => {
|
|
142
|
+
await this.session.detach().catch(() => {});
|
|
143
|
+
await this.page.close().catch(() => {});
|
|
144
|
+
await this.browser.close().catch(() => {});
|
|
145
|
+
}, this.profileDir);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface PuppeteerTier1BrowserOptions {
|
|
150
|
+
readonly launcher?: ResolvedLauncher;
|
|
151
|
+
readonly logger?: (message: string) => void;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Production Tier1Browser impl backed by puppeteer-core. Constructs
|
|
156
|
+
* a fresh ephemeral profile under `os.tmpdir()` per launch, opens
|
|
157
|
+
* the launcher's executable as the netns wrapper, and returns a
|
|
158
|
+
* `VerifierTarget` the runner can drive.
|
|
159
|
+
*/
|
|
160
|
+
export class PuppeteerTier1Browser {
|
|
161
|
+
constructor(private readonly options: PuppeteerTier1BrowserOptions = {}) {}
|
|
162
|
+
|
|
163
|
+
async launch(): Promise<VerifierTarget> {
|
|
164
|
+
const startedAt = Date.now();
|
|
165
|
+
const launcher = this.options.launcher ?? resolveLauncher();
|
|
166
|
+
traceLaunch("launcher-resolved", startedAt);
|
|
167
|
+
const profileDir = createEphemeralProfileDir();
|
|
168
|
+
traceLaunch("profile-created", startedAt);
|
|
169
|
+
let browser: Browser | undefined;
|
|
170
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
171
|
+
let gaveUp = false;
|
|
172
|
+
try {
|
|
173
|
+
const launchPromise = puppeteer.launch({
|
|
174
|
+
executablePath: launcher.executablePath,
|
|
175
|
+
pipe: true,
|
|
176
|
+
headless: true,
|
|
177
|
+
userDataDir: profileDir,
|
|
178
|
+
// Bound the phases puppeteer's own timeout covers (spawn,
|
|
179
|
+
// endpoint wait, initial page target).
|
|
180
|
+
timeout: TIER1_CDP_CALL_WATCHDOG_MS,
|
|
181
|
+
args: buildBrowserArgs(profileDir) as string[],
|
|
182
|
+
});
|
|
183
|
+
// A launch that outlives the watchdog is abandoned; should the
|
|
184
|
+
// handshake settle late anyway, close the orphan immediately AND
|
|
185
|
+
// remove its profile dir. The watchdog's catch block already
|
|
186
|
+
// removed `profileDir` on the abandon path, but a Chromium
|
|
187
|
+
// process that only resolves afterward can recreate/write into
|
|
188
|
+
// that same directory on its way up — closing without a second
|
|
189
|
+
// (idempotent) removal would strand it again.
|
|
190
|
+
void launchPromise.then(
|
|
191
|
+
(late) => {
|
|
192
|
+
if (gaveUp) {
|
|
193
|
+
void late
|
|
194
|
+
.close()
|
|
195
|
+
.catch(() => {})
|
|
196
|
+
.finally(() => removeEphemeralProfileDir(profileDir));
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
() => {},
|
|
200
|
+
);
|
|
201
|
+
// Separate watchdog around the WHOLE launch: with pipe transport
|
|
202
|
+
// puppeteer's own timeout does not cover the spawn/handshake
|
|
203
|
+
// phase, which can pend forever (observed: spawn after another
|
|
204
|
+
// browser's teardown never produces a child process).
|
|
205
|
+
browser = await Promise.race([
|
|
206
|
+
launchPromise,
|
|
207
|
+
new Promise<never>((_resolve, reject) => {
|
|
208
|
+
timer = setTimeout(() => {
|
|
209
|
+
gaveUp = true;
|
|
210
|
+
reject(new Tier1TransportWedgeError("launch handshake"));
|
|
211
|
+
}, TIER1_CDP_CALL_WATCHDOG_MS);
|
|
212
|
+
}),
|
|
213
|
+
]);
|
|
214
|
+
traceLaunch("puppeteer-launch-complete", startedAt);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
removeEphemeralProfileDir(profileDir);
|
|
217
|
+
if (error instanceof Tier1TransportWedgeError) throw error;
|
|
218
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
219
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
220
|
+
throw new Tier1TransportWedgeError(`launch: ${message}`);
|
|
221
|
+
}
|
|
222
|
+
throw new Error(`tier1: puppeteer launch failed: ${message}`, { cause: error });
|
|
223
|
+
} finally {
|
|
224
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
225
|
+
}
|
|
226
|
+
let page: Page;
|
|
227
|
+
try {
|
|
228
|
+
page = await browser.newPage();
|
|
229
|
+
traceLaunch("page-created", startedAt);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
await browser.close().catch(() => {});
|
|
232
|
+
removeEphemeralProfileDir(profileDir);
|
|
233
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
234
|
+
throw new Error(`tier1: failed to open page: ${message}`, { cause: error });
|
|
235
|
+
}
|
|
236
|
+
let session: CDPSession;
|
|
237
|
+
try {
|
|
238
|
+
session = await page.createCDPSession();
|
|
239
|
+
traceLaunch("cdp-session-created", startedAt);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
await page.close().catch(() => {});
|
|
242
|
+
await browser.close().catch(() => {});
|
|
243
|
+
removeEphemeralProfileDir(profileDir);
|
|
244
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
245
|
+
throw new Error(`tier1: failed to open CDP session: ${message}`, { cause: error });
|
|
246
|
+
}
|
|
247
|
+
traceLaunch("ready", startedAt);
|
|
248
|
+
return new PuppeteerVerifierTarget(browser, page, session, profileDir);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async probeAvailability(): Promise<{ available: boolean; reason: string | null }> {
|
|
252
|
+
const launcher = this.options.launcher ?? resolveLauncher();
|
|
253
|
+
let browser: Browser | undefined;
|
|
254
|
+
const profileDir = createEphemeralProfileDir();
|
|
255
|
+
try {
|
|
256
|
+
browser = await puppeteer.launch({
|
|
257
|
+
executablePath: launcher.executablePath,
|
|
258
|
+
pipe: true,
|
|
259
|
+
headless: true,
|
|
260
|
+
userDataDir: profileDir,
|
|
261
|
+
args: buildBrowserArgs(profileDir) as string[],
|
|
262
|
+
});
|
|
263
|
+
const page = await browser.newPage();
|
|
264
|
+
await page.close();
|
|
265
|
+
await browser.close();
|
|
266
|
+
return { available: true, reason: null };
|
|
267
|
+
} catch (error) {
|
|
268
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
269
|
+
return { available: false, reason: message };
|
|
270
|
+
} finally {
|
|
271
|
+
if (browser !== undefined) {
|
|
272
|
+
try {
|
|
273
|
+
await browser.close();
|
|
274
|
+
} catch {
|
|
275
|
+
// already closed
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
removeEphemeralProfileDir(profileDir);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { renderChart } from "../../../gallery-web/frame/renderers/chart";
|
|
2
|
+
import { createRendererRegistry } from "../../../gallery-web/frame/renderers/registry";
|
|
3
|
+
import { startTier1Harness } from "../harness-entry";
|
|
4
|
+
|
|
5
|
+
startTier1Harness(createRendererRegistry([["chart", renderChart]]));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import "../../../gallery-web/frame/styles/artifact.css";
|
|
2
|
+
import { renderHtml } from "../../../gallery-web/frame/renderers/html";
|
|
3
|
+
import { createRendererRegistry } from "../../../gallery-web/frame/renderers/registry";
|
|
4
|
+
import { startTier1Harness } from "../harness-entry";
|
|
5
|
+
|
|
6
|
+
startTier1Harness(createRendererRegistry([["html", renderHtml]]));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { renderMarkdown } from "../../../gallery-web/frame/renderers/markdown";
|
|
2
|
+
import { createRendererRegistry } from "../../../gallery-web/frame/renderers/registry";
|
|
3
|
+
import { startTier1Harness } from "../harness-entry";
|
|
4
|
+
|
|
5
|
+
startTier1Harness(createRendererRegistry([["markdown", renderMarkdown]]));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { renderMermaidDocument } from "../../../gallery-web/frame/renderers/mermaid";
|
|
2
|
+
import { createRendererRegistry } from "../../../gallery-web/frame/renderers/registry";
|
|
3
|
+
import { startTier1Harness } from "../harness-entry";
|
|
4
|
+
|
|
5
|
+
startTier1Harness(createRendererRegistry([["mermaid", renderMermaidDocument]]));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createRendererRegistry } from "../../../gallery-web/frame/renderers/registry";
|
|
2
|
+
import { renderSvgDocument } from "../../../gallery-web/frame/renderers/svg";
|
|
3
|
+
import { startTier1Harness } from "../harness-entry";
|
|
4
|
+
|
|
5
|
+
startTier1Harness(createRendererRegistry([["svg", renderSvgDocument]]));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { renderTsx } from "../../../gallery-web/frame/renderers/tsx";
|
|
2
|
+
import { createRendererRegistry } from "../../../gallery-web/frame/renderers/registry";
|
|
3
|
+
import { startTier1Harness } from "../harness-entry";
|
|
4
|
+
|
|
5
|
+
startTier1Harness(createRendererRegistry([["tsx", renderTsx]]));
|