@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.
Files changed (327) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/LICENSE-MIT +23 -0
  3. package/README.md +158 -0
  4. package/dist/gallery/chunk-4g8rem85.css +656 -0
  5. package/dist/gallery/chunk-c6s9k9ty.js +5970 -0
  6. package/dist/gallery/frame/artifact.css +8424 -0
  7. package/dist/gallery/frame/chunks/abnfDiagram-N423BO3Z-jj74szbm.js +155 -0
  8. package/dist/gallery/frame/chunks/architecture-TIHT7OUA-cvdjn7dt.js +10 -0
  9. package/dist/gallery/frame/chunks/architectureDiagram-T3A2C74G-n4z77eg6.js +8675 -0
  10. package/dist/gallery/frame/chunks/blockDiagram-VBNYF7ZC-7xsqzrd1.js +3529 -0
  11. package/dist/gallery/frame/chunks/c4Diagram-5PPSVZJV-z2zjs7wx.js +2413 -0
  12. package/dist/gallery/frame/chunks/classDiagram-JCYQIIEL-ve8rngqp.js +47 -0
  13. package/dist/gallery/frame/chunks/classDiagram-v2-OCEON4UE-8cfaxb8z.js +47 -0
  14. package/dist/gallery/frame/chunks/cose-bilkent-JH36ORCC-sn92qexx.js +4822 -0
  15. package/dist/gallery/frame/chunks/cynefin-VYW2F7L2-tqjv913d.js +10 -0
  16. package/dist/gallery/frame/chunks/cynefinDiagram-MW4NZA55-c1c0dj12.js +500 -0
  17. package/dist/gallery/frame/chunks/dagre-VZM6K2ZE-7tyg4yt2.js +476 -0
  18. package/dist/gallery/frame/chunks/diagram-7IWD3JNH-y4eng9sc.js +526 -0
  19. package/dist/gallery/frame/chunks/diagram-B4RE2ZJO-rn3p7j5h.js +673 -0
  20. package/dist/gallery/frame/chunks/diagram-LBJQPF4R-w4a2cm9q.js +255 -0
  21. package/dist/gallery/frame/chunks/diagram-Q27KOJAE-aqc2v7wy.js +575 -0
  22. package/dist/gallery/frame/chunks/diagram-UB23O5K3-xncy8nyd.js +355 -0
  23. package/dist/gallery/frame/chunks/ebnfDiagram-BXEA7PRR-acwgxz16.js +175 -0
  24. package/dist/gallery/frame/chunks/erDiagram-JOGREHBK-mzp5nswt.js +1332 -0
  25. package/dist/gallery/frame/chunks/eventmodeling-45OFAUF4-sg07qr0c.js +10 -0
  26. package/dist/gallery/frame/chunks/flowDiagram-UKHOOZJN-2e5ygs44.js +29 -0
  27. package/dist/gallery/frame/chunks/ganttDiagram-PKOTCBZU-p2hhh7hr.js +2620 -0
  28. package/dist/gallery/frame/chunks/gitGraph-TEB2WS4Q-j6kw0naa.js +10 -0
  29. package/dist/gallery/frame/chunks/gitGraphDiagram-DS77QQ5N-4z9z8wjm.js +1347 -0
  30. package/dist/gallery/frame/chunks/info-DKCQHKI2-mpt80jd3.js +10 -0
  31. package/dist/gallery/frame/chunks/infoDiagram-6WML65LV-5rqm3mgg.js +65 -0
  32. package/dist/gallery/frame/chunks/ishikawaDiagram-WSZJBQD7-b51z96jy.js +975 -0
  33. package/dist/gallery/frame/chunks/journeyDiagram-NVQOT4AX-9jbfp0pk.js +1272 -0
  34. package/dist/gallery/frame/chunks/kanban-definition-27J2QSJJ-4x0vx4se.js +1116 -0
  35. package/dist/gallery/frame/chunks/katex-xfgk95tv.js +14034 -0
  36. package/dist/gallery/frame/chunks/markdown-1s5vwkf2.js +9 -0
  37. package/dist/gallery/frame/chunks/markdown-2h6bwsms.js +3239 -0
  38. package/dist/gallery/frame/chunks/markdown-2x113yh8.js +107 -0
  39. package/dist/gallery/frame/chunks/markdown-30b7rwmp.js +1943 -0
  40. package/dist/gallery/frame/chunks/markdown-356bfj4g.js +2031 -0
  41. package/dist/gallery/frame/chunks/markdown-44n9ea69.js +2406 -0
  42. package/dist/gallery/frame/chunks/markdown-56enrf2r.js +39 -0
  43. package/dist/gallery/frame/chunks/markdown-5avymm8t.js +725 -0
  44. package/dist/gallery/frame/chunks/markdown-6r4svars.js +33 -0
  45. package/dist/gallery/frame/chunks/markdown-6zz8efx2.js +24 -0
  46. package/dist/gallery/frame/chunks/markdown-7jp6prnb.js +19 -0
  47. package/dist/gallery/frame/chunks/markdown-809k7q41.js +55 -0
  48. package/dist/gallery/frame/chunks/markdown-82xmzhzd.js +1076 -0
  49. package/dist/gallery/frame/chunks/markdown-92n37gd4.js +7627 -0
  50. package/dist/gallery/frame/chunks/markdown-ag2sssf8.js +36 -0
  51. package/dist/gallery/frame/chunks/markdown-b4s7ntq2.js +19 -0
  52. package/dist/gallery/frame/chunks/markdown-c9k0fvp1.js +100 -0
  53. package/dist/gallery/frame/chunks/markdown-ceefymn8.js +125 -0
  54. package/dist/gallery/frame/chunks/markdown-d1wfbs70.js +243 -0
  55. package/dist/gallery/frame/chunks/markdown-dy5sgjhc.js +11022 -0
  56. package/dist/gallery/frame/chunks/markdown-dz3z5w30.js +30868 -0
  57. package/dist/gallery/frame/chunks/markdown-eg6ngz7y.js +30340 -0
  58. package/dist/gallery/frame/chunks/markdown-ff5wvb43.js +1149 -0
  59. package/dist/gallery/frame/chunks/markdown-ffg06nfz.js +86 -0
  60. package/dist/gallery/frame/chunks/markdown-fvfncadf.js +472 -0
  61. package/dist/gallery/frame/chunks/markdown-gvyszqpv.js +19 -0
  62. package/dist/gallery/frame/chunks/markdown-gx1p0ew0.js +88 -0
  63. package/dist/gallery/frame/chunks/markdown-gxye5frz.js +65 -0
  64. package/dist/gallery/frame/chunks/markdown-h7pzv34e.js +105 -0
  65. package/dist/gallery/frame/chunks/markdown-hab789t9.js +47 -0
  66. package/dist/gallery/frame/chunks/markdown-hjp6sj8t.js +125 -0
  67. package/dist/gallery/frame/chunks/markdown-jka64qgp.js +1993 -0
  68. package/dist/gallery/frame/chunks/markdown-k8xkn8mw.js +56 -0
  69. package/dist/gallery/frame/chunks/markdown-m7frq8x8.js +112 -0
  70. package/dist/gallery/frame/chunks/markdown-p740v4qm.js +36 -0
  71. package/dist/gallery/frame/chunks/markdown-pxfeh231.js +57 -0
  72. package/dist/gallery/frame/chunks/markdown-pzgd8z3s.js +87 -0
  73. package/dist/gallery/frame/chunks/markdown-qx3cngz2.js +1673 -0
  74. package/dist/gallery/frame/chunks/markdown-rawhfvvc.js +85 -0
  75. package/dist/gallery/frame/chunks/markdown-rd8w0ng9.js +49 -0
  76. package/dist/gallery/frame/chunks/markdown-rm2yxfjj.js +2773 -0
  77. package/dist/gallery/frame/chunks/markdown-s1jatrnk.js +993 -0
  78. package/dist/gallery/frame/chunks/markdown-sgfg5vvq.js +59 -0
  79. package/dist/gallery/frame/chunks/markdown-t4ethv9d.js +409 -0
  80. package/dist/gallery/frame/chunks/markdown-tnmabqvz.js +36 -0
  81. package/dist/gallery/frame/chunks/markdown-tvdpppw8.js +5292 -0
  82. package/dist/gallery/frame/chunks/markdown-v0x27twr.js +22 -0
  83. package/dist/gallery/frame/chunks/markdown-v14td68x.js +36 -0
  84. package/dist/gallery/frame/chunks/markdown-w6sgd54z.js +36 -0
  85. package/dist/gallery/frame/chunks/markdown-xw8dsk8b.js +2125 -0
  86. package/dist/gallery/frame/chunks/markdown-y18mve73.js +85 -0
  87. package/dist/gallery/frame/chunks/markdown-yengsms5.js +427 -0
  88. package/dist/gallery/frame/chunks/markdown-z1wc0v0d.js +1826 -0
  89. package/dist/gallery/frame/chunks/markdown-zazx9h4n.js +329 -0
  90. package/dist/gallery/frame/chunks/mermaid-de7p4v0x.js +30 -0
  91. package/dist/gallery/frame/chunks/mindmap-definition-FAOFIHXS-ra44cbem.js +1216 -0
  92. package/dist/gallery/frame/chunks/packet-7NZHBO7P-xppm5913.js +10 -0
  93. package/dist/gallery/frame/chunks/pegDiagram-VL7TDLO6-a2a4wg03.js +163 -0
  94. package/dist/gallery/frame/chunks/pie-RZYD4A2V-hnbxk152.js +10 -0
  95. package/dist/gallery/frame/chunks/pieDiagram-7S7Q4E2Y-f1vxjtmx.js +311 -0
  96. package/dist/gallery/frame/chunks/quadrantDiagram-CIZ2JOQS-ywafa6kh.js +1390 -0
  97. package/dist/gallery/frame/chunks/radar-I7S5WNFK-ewqd27se.js +10 -0
  98. package/dist/gallery/frame/chunks/railroad-3IZDKUUU-5hy4bdx3.js +10 -0
  99. package/dist/gallery/frame/chunks/railroad-abnf-AHOZXSZD-bh5sxz0d.js +10 -0
  100. package/dist/gallery/frame/chunks/railroad-ebnf-EBAXGLYW-6b9c3zeh.js +10 -0
  101. package/dist/gallery/frame/chunks/railroad-peg-LSFZ7HO6-avc8v39s.js +10 -0
  102. package/dist/gallery/frame/chunks/railroadDiagram-AXF67PYL-qc61wax4.js +131 -0
  103. package/dist/gallery/frame/chunks/requirementDiagram-LRYGKXZP-9bs4786c.js +1295 -0
  104. package/dist/gallery/frame/chunks/sankeyDiagram-W5VNT64P-awt1em8b.js +1339 -0
  105. package/dist/gallery/frame/chunks/sequenceDiagram-SI44F4Z6-qaf9mz3t.js +4324 -0
  106. package/dist/gallery/frame/chunks/sizeCapture-X5ZJPWSS-9fq685xd.js +64 -0
  107. package/dist/gallery/frame/chunks/stateDiagram-OKZ733FA-vx7a7hh0.js +456 -0
  108. package/dist/gallery/frame/chunks/stateDiagram-v2-UEYNNEHI-x4m6ws5t.js +46 -0
  109. package/dist/gallery/frame/chunks/swimlanes-SLNWSIFB-e4mxm1gh.js +8287 -0
  110. package/dist/gallery/frame/chunks/swimlanesDiagram-ULZ7WXOC-0h93e2qq.js +42 -0
  111. package/dist/gallery/frame/chunks/timeline-definition-Z64GVDOM-wp0bhbzg.js +1549 -0
  112. package/dist/gallery/frame/chunks/treeView-QDETBFTQ-hcx7n0vt.js +10 -0
  113. package/dist/gallery/frame/chunks/treemap-6X3UGDF4-av02skdw.js +10 -0
  114. package/dist/gallery/frame/chunks/vennDiagram-T6HMQDX7-pmvk1p7n.js +2566 -0
  115. package/dist/gallery/frame/chunks/wardley-OPB4EBWU-njsp6e40.js +10 -0
  116. package/dist/gallery/frame/chunks/wardleyDiagram-T6FBY63Y-eggmnv4z.js +979 -0
  117. package/dist/gallery/frame/chunks/xychartDiagram-ELKLHX3M-q40tnpe5.js +1954 -0
  118. package/dist/gallery/frame/frame.css +183 -0
  119. package/dist/gallery/frame/runtime/chart.js +42744 -0
  120. package/dist/gallery/frame/runtime/html.js +14 -0
  121. package/dist/gallery/frame/runtime/markdown.js +1403 -0
  122. package/dist/gallery/frame/runtime/mermaid.js +32 -0
  123. package/dist/gallery/frame/runtime/svg.js +14 -0
  124. package/dist/gallery/frame/runtime/tsx.js +52 -0
  125. package/dist/gallery/index.html +104 -0
  126. package/docs/reference/cli.md +163 -0
  127. package/docs/reference/export.md +106 -0
  128. package/docs/reference/html.md +215 -0
  129. package/docs/reference/http.md +85 -0
  130. package/docs/reference/mcp.md +78 -0
  131. package/docs/reference/security.md +119 -0
  132. package/docs/reference/storage.md +93 -0
  133. package/docs/reference/tsx.md +101 -0
  134. package/docs/reference/validation.md +212 -0
  135. package/package.json +89 -0
  136. package/scripts/build-gallery.ts +127 -0
  137. package/scripts/launch-netns.sh +67 -0
  138. package/skills/facet/SKILL.md +100 -0
  139. package/src/cli/client.ts +311 -0
  140. package/src/cli/commands/create.ts +49 -0
  141. package/src/cli/commands/doctor.ts +249 -0
  142. package/src/cli/commands/export.ts +179 -0
  143. package/src/cli/commands/instantiate.ts +35 -0
  144. package/src/cli/commands/list.ts +49 -0
  145. package/src/cli/commands/open.ts +75 -0
  146. package/src/cli/commands/pin.ts +39 -0
  147. package/src/cli/commands/promote.ts +49 -0
  148. package/src/cli/commands/publish.ts +122 -0
  149. package/src/cli/commands/read-back.ts +56 -0
  150. package/src/cli/commands/status.ts +114 -0
  151. package/src/cli/commands/watch.ts +146 -0
  152. package/src/cli/main.ts +522 -0
  153. package/src/cli/output.ts +106 -0
  154. package/src/cli/parser.ts +376 -0
  155. package/src/cli/presenter.ts +212 -0
  156. package/src/cli/service-metadata.ts +134 -0
  157. package/src/cli/spawn-service.ts +326 -0
  158. package/src/css.d.ts +4 -0
  159. package/src/gallery-web/app.ts +1581 -0
  160. package/src/gallery-web/export-menu.ts +121 -0
  161. package/src/gallery-web/export.ts +112 -0
  162. package/src/gallery-web/favicon.ts +54 -0
  163. package/src/gallery-web/frame/entries/chart.ts +6 -0
  164. package/src/gallery-web/frame/entries/html.ts +6 -0
  165. package/src/gallery-web/frame/entries/markdown.ts +6 -0
  166. package/src/gallery-web/frame/entries/mermaid.ts +6 -0
  167. package/src/gallery-web/frame/entries/svg.ts +6 -0
  168. package/src/gallery-web/frame/entries/tsx.ts +6 -0
  169. package/src/gallery-web/frame/frame-payload.ts +91 -0
  170. package/src/gallery-web/frame/renderer-validation.ts +13 -0
  171. package/src/gallery-web/frame/renderers/chart.ts +182 -0
  172. package/src/gallery-web/frame/renderers/dompurify-shim.ts +112 -0
  173. package/src/gallery-web/frame/renderers/html.ts +48 -0
  174. package/src/gallery-web/frame/renderers/markdown.ts +81 -0
  175. package/src/gallery-web/frame/renderers/mermaid.ts +120 -0
  176. package/src/gallery-web/frame/renderers/registry.ts +260 -0
  177. package/src/gallery-web/frame/renderers/svg.ts +393 -0
  178. package/src/gallery-web/frame/renderers/tsx.ts +61 -0
  179. package/src/gallery-web/frame/runtime.ts +501 -0
  180. package/src/gallery-web/frame/styles/artifact.css +8424 -0
  181. package/src/gallery-web/frame/styles/frame.css +183 -0
  182. package/src/gallery-web/frame/styles/html-source.css +23 -0
  183. package/src/gallery-web/frame/view-box.ts +33 -0
  184. package/src/gallery-web/frame-html.ts +72 -0
  185. package/src/gallery-web/index.html +104 -0
  186. package/src/gallery-web/session.ts +107 -0
  187. package/src/gallery-web/sse-client.ts +212 -0
  188. package/src/gallery-web/styles/app.css +420 -0
  189. package/src/gallery-web/styles/tokens.css +112 -0
  190. package/src/gallery-web/styles/verdict.css +186 -0
  191. package/src/gallery-web/swap.ts +64 -0
  192. package/src/gallery-web/theme.ts +40 -0
  193. package/src/gallery-web/view-state.ts +93 -0
  194. package/src/harness-adapters/claude-code/README.md +10 -0
  195. package/src/harness-adapters/claude-code/facet.sh +4 -0
  196. package/src/harness-adapters/codex/README.md +10 -0
  197. package/src/harness-adapters/codex/facet.sh +4 -0
  198. package/src/harness-adapters/mcp/README.md +13 -0
  199. package/src/harness-adapters/mcp/cli-bridge.ts +216 -0
  200. package/src/harness-adapters/mcp/main.ts +15 -0
  201. package/src/harness-adapters/mcp/server.ts +164 -0
  202. package/src/harness-adapters/mcp/tool-schemas.ts +53 -0
  203. package/src/harness-adapters/opencode/README.md +10 -0
  204. package/src/harness-adapters/opencode/facet.sh +4 -0
  205. package/src/runtime/compiled-entrypoints.ts +69 -0
  206. package/src/service/dispatcher.ts +717 -0
  207. package/src/service/export.ts +218 -0
  208. package/src/service/http-utils.ts +55 -0
  209. package/src/service/lexical/expectations.ts +210 -0
  210. package/src/service/lifecycle/idle-controller.ts +125 -0
  211. package/src/service/lifecycle/orphan-cleanup.ts +128 -0
  212. package/src/service/lifecycle/process-lock.ts +235 -0
  213. package/src/service/main.ts +136 -0
  214. package/src/service/process.ts +240 -0
  215. package/src/service/router-guards.ts +239 -0
  216. package/src/service/router.ts +627 -0
  217. package/src/service/security/auth.ts +168 -0
  218. package/src/service/security/host-origin.ts +138 -0
  219. package/src/service/security/http-guards.ts +13 -0
  220. package/src/service/security/leases.ts +182 -0
  221. package/src/service/security/token-store.ts +138 -0
  222. package/src/service/server.ts +356 -0
  223. package/src/service/store/database.ts +61 -0
  224. package/src/service/store/evidence-retention.ts +234 -0
  225. package/src/service/store/migrations.ts +167 -0
  226. package/src/service/store/repository-lifecycle.ts +121 -0
  227. package/src/service/store/repository.ts +732 -0
  228. package/src/service/store/schema.ts +218 -0
  229. package/src/service/stored-verdict.ts +97 -0
  230. package/src/service/stream.ts +301 -0
  231. package/src/service/verdict-enrichment.ts +45 -0
  232. package/src/shared/build/frame-bundle-plugins.ts +61 -0
  233. package/src/shared/build-mode.ts +28 -0
  234. package/src/shared/config/evidence-read.ts +98 -0
  235. package/src/shared/config/limits.ts +56 -0
  236. package/src/shared/config/paths.ts +96 -0
  237. package/src/shared/contracts/artifact-types.ts +2 -0
  238. package/src/shared/contracts/artifact.ts +98 -0
  239. package/src/shared/contracts/commands/_shared.ts +59 -0
  240. package/src/shared/contracts/commands/guards.ts +86 -0
  241. package/src/shared/contracts/commands/index.ts +204 -0
  242. package/src/shared/contracts/commands/names.ts +35 -0
  243. package/src/shared/contracts/commands/requests.ts +149 -0
  244. package/src/shared/contracts/commands/results.ts +200 -0
  245. package/src/shared/contracts/envelope.ts +140 -0
  246. package/src/shared/contracts/events.ts +41 -0
  247. package/src/shared/contracts/observed-counts.ts +19 -0
  248. package/src/shared/contracts/renderers.ts +6 -0
  249. package/src/shared/contracts/validation.ts +296 -0
  250. package/src/shared/errors/facet-error.ts +127 -0
  251. package/src/shared/errors/store-error.ts +68 -0
  252. package/src/shared/evidence-image.ts +37 -0
  253. package/src/shared/export.ts +31 -0
  254. package/src/shared/html/policy.ts +108 -0
  255. package/src/shared/html/style-vocabulary.ts +352 -0
  256. package/src/shared/logging/logger.ts +108 -0
  257. package/src/shared/logging/redact.ts +49 -0
  258. package/src/shared/security/frozen-csp.ts +13 -0
  259. package/src/shared/storage-version.ts +2 -0
  260. package/src/shared/tsx/execution.ts +29 -0
  261. package/src/shared/tsx/import-policy.ts +94 -0
  262. package/src/shared/util/dir-permissions.ts +94 -0
  263. package/src/shared/util/mermaid-nodes.ts +230 -0
  264. package/src/shared/util/process.ts +67 -0
  265. package/src/shared/util/time.ts +21 -0
  266. package/src/shared/version.ts +3 -0
  267. package/src/validation/sandbox/limits.ts +41 -0
  268. package/src/validation/sandbox/netns.ts +177 -0
  269. package/src/validation/tier0/chart.ts +177 -0
  270. package/src/validation/tier0/dom-shim.ts +54 -0
  271. package/src/validation/tier0/html.ts +408 -0
  272. package/src/validation/tier0/markdown.ts +285 -0
  273. package/src/validation/tier0/mermaid.ts +98 -0
  274. package/src/validation/tier0/runner.ts +504 -0
  275. package/src/validation/tier0/svg.ts +321 -0
  276. package/src/validation/tier0/tsx/allowlist-resolver.ts +77 -0
  277. package/src/validation/tier0/tsx/ast-policy-capabilities.ts +238 -0
  278. package/src/validation/tier0/tsx/ast-policy-shared.ts +71 -0
  279. package/src/validation/tier0/tsx/ast-policy.ts +181 -0
  280. package/src/validation/tier0/tsx/compiler.ts +156 -0
  281. package/src/validation/tier0/worker-dispatch.ts +211 -0
  282. package/src/validation/tier0/worker-entry.ts +96 -0
  283. package/src/validation/tier0/worker-input.ts +75 -0
  284. package/src/validation/tier1/browser-process.ts +147 -0
  285. package/src/validation/tier1/cdp-pipe.ts +281 -0
  286. package/src/validation/tier1/entries/chart.ts +5 -0
  287. package/src/validation/tier1/entries/html.ts +6 -0
  288. package/src/validation/tier1/entries/markdown.ts +5 -0
  289. package/src/validation/tier1/entries/mermaid.ts +5 -0
  290. package/src/validation/tier1/entries/svg.ts +5 -0
  291. package/src/validation/tier1/entries/tsx.ts +5 -0
  292. package/src/validation/tier1/frame-target.ts +162 -0
  293. package/src/validation/tier1/harness-entry.ts +261 -0
  294. package/src/validation/tier1/harness.ts +215 -0
  295. package/src/validation/tier1/isolated-probe.ts +94 -0
  296. package/src/validation/tier1/launcher.ts +177 -0
  297. package/src/validation/tier1/limits.ts +80 -0
  298. package/src/validation/tier1/nonce.ts +17 -0
  299. package/src/validation/tier1/protocol-probe.ts +519 -0
  300. package/src/validation/tier1/runner.ts +1122 -0
  301. package/src/validation/tier1/verdict.ts +246 -0
  302. package/src/validation/tier1/webp.ts +69 -0
  303. package/templates/README.md +88 -0
  304. package/templates/bar-compare.vl.json +63 -0
  305. package/templates/capacity-report.tsx +102 -0
  306. package/templates/decision-record.md +24 -0
  307. package/templates/deployment-state.mmd +40 -0
  308. package/templates/exemplar.md +91 -0
  309. package/templates/facet-story.tsx +799 -0
  310. package/templates/fleet-dashboard.html +141 -0
  311. package/templates/html-release-ledger.html +91 -0
  312. package/templates/html-status-report.html +114 -0
  313. package/templates/incident-console.tsx +147 -0
  314. package/templates/legion-boundaries.mmd +23 -0
  315. package/templates/legion-flow.mmd +15 -0
  316. package/templates/legion-sequence.mmd +14 -0
  317. package/templates/legion-state.mmd +7 -0
  318. package/templates/metric-card.svg +29 -0
  319. package/templates/observability-map.svg +101 -0
  320. package/templates/pipeline-audit.md +108 -0
  321. package/templates/release-metrics.vl.json +98 -0
  322. package/templates/service-topology.mmd +47 -0
  323. package/templates/status-report.md +32 -0
  324. package/templates/system-map.svg +53 -0
  325. package/templates/timeseries.vl.json +71 -0
  326. package/templates/tsx-interactive-counter.tsx +19 -0
  327. package/templates/tsx-status-report.tsx +31 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Tier 0 mermaid parser.
3
+ *
4
+ * `mermaid.parse()` is the canonical mermaid grammar check: it runs
5
+ * the JISON parser over the input and returns `{ diagramType, config }`
6
+ * for valid sources or THROWS a parse error for invalid ones. No DOM,
7
+ * no rendering — but the library itself depends on a browser-shaped
8
+ * DOM (DOMPurify, etc.) at IMPORT TIME.
9
+ *
10
+ * `dom-shim.ts` installs a linkedom-based structural DOM stub BEFORE
11
+ * this module imports mermaid, so the library's import-time
12
+ * DOMPurify check succeeds. The shim is structural: it never executes
13
+ * artifact source against the DOM. netns ensures any library-initiated
14
+ * network egress cannot reach a host.
15
+ */
16
+
17
+ import "./dom-shim";
18
+ import mermaid from "mermaid";
19
+
20
+ import type { DiscriminativeError, VerdictObserved } from "../../shared/contracts/validation";
21
+ import { countMermaidNodeDeclarations } from "../../shared/util/mermaid-nodes";
22
+
23
+ export interface MermaidParseOk {
24
+ readonly status: "ok";
25
+ readonly observed: VerdictObserved;
26
+ }
27
+
28
+ export interface MermaidParseFail {
29
+ readonly status: "error";
30
+ readonly observed: VerdictObserved;
31
+ readonly errors: readonly DiscriminativeError[];
32
+ }
33
+
34
+ export type MermaidParseResult = MermaidParseOk | MermaidParseFail;
35
+
36
+ /**
37
+ * Count node declarations across the source. This is the lexical
38
+ * counter the parser agrees with when the parse succeeds and disagrees
39
+ * with when a hostile artifact lies in its mermaid nodes; the canonical
40
+ * prediction lives in `shared/util/mermaid-nodes.ts` (the service-side
41
+ * expectations import the same one).
42
+ */
43
+ function countMermaidNodes(bytes: Uint8Array): number {
44
+ const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
45
+ return countMermaidNodeDeclarations(text) ?? 0;
46
+ }
47
+
48
+ interface MermaidResolved {
49
+ readonly diagramType: string;
50
+ }
51
+
52
+ /**
53
+ * Parse the source bytes as a mermaid diagram. On success, returns
54
+ * the lexical mermaid-node count. On failure, returns the parser's
55
+ * error text wrapped as a discriminative error.
56
+ */
57
+ export async function parseMermaid(bytes: Uint8Array): Promise<MermaidParseResult> {
58
+ const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
59
+ const lexicalNodes = countMermaidNodes(bytes);
60
+ try {
61
+ const resolved = (await (mermaid as { parse: (s: string) => Promise<MermaidResolved> }).parse(
62
+ text,
63
+ )) as MermaidResolved;
64
+ void resolved;
65
+ return {
66
+ status: "ok",
67
+ observed: {
68
+ rendererRootSvgCount: 1,
69
+ graphCount: 1,
70
+ mermaidNodeCount: lexicalNodes,
71
+ visibleSvgCount: 1,
72
+ errorCount: 0,
73
+ opaqueRegionCount: 0,
74
+ externalImageCount: 0,
75
+ },
76
+ };
77
+ } catch (error) {
78
+ const message = error instanceof Error ? error.message : String(error);
79
+ return {
80
+ status: "error",
81
+ observed: {
82
+ rendererRootSvgCount: 0,
83
+ graphCount: 0,
84
+ mermaidNodeCount: 0,
85
+ visibleSvgCount: 0,
86
+ errorCount: 1,
87
+ opaqueRegionCount: 0,
88
+ externalImageCount: 0,
89
+ },
90
+ errors: [
91
+ {
92
+ code: "mermaid_parse_error",
93
+ message,
94
+ },
95
+ ],
96
+ };
97
+ }
98
+ }
@@ -0,0 +1,504 @@
1
+ /**
2
+ * Tier 0 runner — the PARENT side of the Tier 0 worker protocol.
3
+ *
4
+ * Spawns the worker subprocess inside a rootless network namespace
5
+ * (via `sandbox/netns.ts`), writes the input envelope to the worker's
6
+ * STDIN, reads STDOUT with a hard byte cap, enforces a wall-clock
7
+ * timeout, kills the worker on overrun, and parses the STDOUT payload
8
+ * via a STRICT zod schema.
9
+ *
10
+ * Worker stdout is treated as UNTRUSTED protocol input. Anything
11
+ * beyond a single well-formed Tier0Result is mapped to a typed
12
+ * `FacetError`:
13
+ *
14
+ * - `tier0_unavailable` — unshare / userns not available.
15
+ * - `tier0_timeout` — wall-clock budget exceeded.
16
+ * - `tier0_output_cap` — stdout exceeded the byte cap.
17
+ * - `tier0_worker_died` — worker exited via signal.
18
+ * - `tier0_protocol_error` — non-JSON, schema mismatch, or extra
19
+ * trailing bytes.
20
+ *
21
+ * Parser-level outcomes (status="error") are returned as Tier0WorkerResult
22
+ * with discriminativeErrors populated; the caller persists them as a
23
+ * render_run bound to (artifactId, revisionSha).
24
+ */
25
+
26
+ import type { ChildProcess } from "node:child_process";
27
+ import { resolve as resolvePath } from "node:path";
28
+
29
+ import type { ArtifactType } from "../../shared/contracts/artifact";
30
+ import {
31
+ buildTier0WorkerArgs,
32
+ isCompiledRuntime,
33
+ type Tier0WorkerCommand,
34
+ } from "../../shared/build-mode";
35
+ import { FacetError } from "../../shared/errors/facet-error";
36
+ import {
37
+ LexicalCountersSchema,
38
+ Tier0WorkerResultSchema,
39
+ type LexicalCounters,
40
+ type InsecureLevel,
41
+ type Tier0Input,
42
+ type Tier0Runner,
43
+ type Tier0WorkerResult,
44
+ } from "../../shared/contracts/validation";
45
+
46
+ import {
47
+ probeNetnsSupport,
48
+ resolveTier0Isolation,
49
+ spawnDirectWorker,
50
+ spawnNetnsWorker,
51
+ } from "../sandbox/netns";
52
+
53
+ export { resolveTier0Isolation } from "../sandbox/netns";
54
+
55
+ /** Probe the host capability used by Tier 0 without bypassing its cache. */
56
+ export function probeTier0Isolation() {
57
+ return probeNetnsSupport();
58
+ }
59
+
60
+ import { TIER0_OUTPUT_CAP_BYTES, TIER0_TIMEOUT_MS, TIER0_TSX_TIMEOUT_MS } from "../sandbox/limits";
61
+
62
+ const TIER0_WORKER_ENTRY = resolvePath(import.meta.dir, "worker-entry.ts");
63
+
64
+ /**
65
+ * The wire envelope the parent writes to the worker's STDIN. The
66
+ * source bytes are base64-encoded so JSON can carry them; the worker
67
+ * decodes them back into a Uint8Array before invoking the parser.
68
+ */
69
+ interface WorkerInputEnvelope {
70
+ readonly schemaVersion: "facet.tier0.v2";
71
+ readonly requestId: string;
72
+ readonly revisionSha: string;
73
+ readonly artifactType: ArtifactType;
74
+ readonly renderer: Tier0Input["renderer"];
75
+ readonly sourceBase64: string;
76
+ readonly lexical: LexicalCounters;
77
+ readonly execution?: Tier0Input["execution"];
78
+ }
79
+
80
+ export interface Tier0RunnerTestHooks {
81
+ readonly workerEntry?: string;
82
+ readonly timeoutMs?: number;
83
+ readonly tsxTimeoutMs?: number;
84
+ readonly outputCap?: number;
85
+ readonly onWorkerSpawn?: (pid: number) => void;
86
+ }
87
+
88
+ /**
89
+ * Build the STDIN envelope from a `Tier0Input`. The lexical counters
90
+ * are validated against the closed schema here so a misbehaving
91
+ * caller cannot smuggle extra fields into the worker.
92
+ */
93
+ function buildInputEnvelope(input: Tier0Input, requestId: string): WorkerInputEnvelope {
94
+ // The contract says input.source is Uint8Array; we accept anything
95
+ // `instanceof Uint8Array` and base64-encode it.
96
+ const sourceBytes =
97
+ input.source instanceof Uint8Array ? input.source : new Uint8Array(input.source);
98
+ return {
99
+ schemaVersion: "facet.tier0.v2",
100
+ requestId,
101
+ revisionSha: input.revisionSha,
102
+ artifactType: input.artifactType,
103
+ renderer: input.renderer,
104
+ sourceBase64: Buffer.from(sourceBytes).toString("base64"),
105
+ lexical: LexicalCountersSchema.parse(input.lexical),
106
+ ...(input.execution === undefined ? {} : { execution: input.execution }),
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Strip the worker's trailing newline (if any) and parse the JSON.
112
+ * A non-JSON payload, a schema mismatch, or extra trailing bytes
113
+ * produces a typed `tier0_protocol_error`.
114
+ *
115
+ * Exported (with a test-only prefix) so the strict-zod guard is
116
+ * directly testable; production code only calls it through
117
+ * `runTier0`.
118
+ */
119
+ // oxlint-disable-next-line no-underscore-dangle
120
+ export function _parseWorkerStdout(stdout: string, outputCap: number): Tier0WorkerResult {
121
+ const trimmed = stdout.trim();
122
+ if (trimmed.length === 0) {
123
+ throw new FacetError("tier0_protocol_error", "Worker emitted empty stdout", {
124
+ retryable: false,
125
+ });
126
+ }
127
+ let parsedJson: unknown;
128
+ try {
129
+ parsedJson = JSON.parse(trimmed);
130
+ } catch (error) {
131
+ const message = error instanceof Error ? error.message : String(error);
132
+ throw new FacetError("tier0_protocol_error", `Worker stdout is not valid JSON: ${message}`, {
133
+ retryable: false,
134
+ });
135
+ }
136
+ const result = Tier0WorkerResultSchema.safeParse(parsedJson);
137
+ if (!result.success) {
138
+ throw new FacetError(
139
+ "tier0_protocol_error",
140
+ `Worker stdout does not match Tier0WorkerResultSchema: ${result.error.issues
141
+ .slice(0, 3)
142
+ .map((issue) => issue.message)
143
+ .join("; ")}`,
144
+ { retryable: false },
145
+ );
146
+ }
147
+ // Reject extra trailing bytes — the worker MUST emit exactly one
148
+ // JSON object. A trailing newline is allowed (we trimmed) but any
149
+ // additional content after the closing brace is a protocol violation.
150
+ // The output-cap check above bounds the total size so this is a
151
+ // final correctness gate.
152
+ void outputCap;
153
+ return result.data;
154
+ }
155
+
156
+ interface PendingRequest {
157
+ readonly requestId: string;
158
+ readonly worker: ChildProcess;
159
+ readonly resolve: (result: Tier0WorkerResult) => void;
160
+ readonly reject: (error: FacetError) => void;
161
+ readonly timer: ReturnType<typeof setTimeout>;
162
+ }
163
+
164
+ interface RunnerOptions {
165
+ readonly workerCommand: Tier0WorkerCommand;
166
+ readonly timeoutMs: number;
167
+ readonly tsxTimeoutMs: number;
168
+ readonly outputCap: number;
169
+ readonly onWorkerSpawn?: (pid: number) => void;
170
+ }
171
+
172
+ function workerDiedError(
173
+ code: number | null,
174
+ signal: NodeJS.Signals | null,
175
+ stderr: Buffer,
176
+ ): FacetError {
177
+ const stderrText = stderr.toString("utf8").trim() || null;
178
+ if (signal !== null) {
179
+ return new FacetError("tier0_worker_died", `Worker terminated by signal ${signal}`, {
180
+ retryable: false,
181
+ details: { signal, stderr: stderrText },
182
+ });
183
+ }
184
+ if (code === null) {
185
+ return new FacetError("tier0_worker_died", "Worker exited without code or signal", {
186
+ retryable: false,
187
+ details: { stderr: stderrText },
188
+ });
189
+ }
190
+ return new FacetError("tier0_worker_died", `Worker exited with non-zero code ${code}`, {
191
+ retryable: false,
192
+ details: { exitCode: code, stderr: stderrText },
193
+ });
194
+ }
195
+
196
+ function workerStartError(error: unknown, stderr: Buffer): FacetError {
197
+ const fields =
198
+ error !== null && typeof error === "object" ? (error as Record<string, unknown>) : {};
199
+ const message = error instanceof Error ? error.message : String(error);
200
+ const code = typeof fields.code === "string" ? fields.code : null;
201
+ const errno =
202
+ typeof fields.errno === "string" || typeof fields.errno === "number" ? fields.errno : null;
203
+ const syscall = typeof fields.syscall === "string" ? fields.syscall : null;
204
+ const stderrText = stderr.toString("utf8").trim() || null;
205
+ const diagnostics = [
206
+ `error=${message}`,
207
+ ...(code === null ? [] : [`code=${code}`]),
208
+ ...(errno === null ? [] : [`errno=${errno}`]),
209
+ ...(syscall === null ? [] : [`syscall=${syscall}`]),
210
+ ...(stderrText === null ? [] : [`stderr=${stderrText}`]),
211
+ ];
212
+ return new FacetError(
213
+ "tier0_worker_died",
214
+ `Worker process failed to start: ${diagnostics.join("; ")}`,
215
+ {
216
+ retryable: false,
217
+ details: {
218
+ code,
219
+ errno,
220
+ syscall,
221
+ stderr: stderrText,
222
+ },
223
+ },
224
+ );
225
+ }
226
+
227
+ function parseWorkerResponse(
228
+ line: Buffer,
229
+ requestId: string,
230
+ outputCap: number,
231
+ ): Tier0WorkerResult {
232
+ let rawEnvelope: unknown;
233
+ try {
234
+ rawEnvelope = JSON.parse(line.toString("utf8"));
235
+ } catch (error) {
236
+ const message = error instanceof Error ? error.message : String(error);
237
+ throw new FacetError("tier0_protocol_error", `Worker stdout is not valid JSON: ${message}`, {
238
+ retryable: false,
239
+ });
240
+ }
241
+ if (typeof rawEnvelope !== "object" || rawEnvelope === null || Array.isArray(rawEnvelope)) {
242
+ throw new FacetError("tier0_protocol_error", "Worker response is not an object envelope", {
243
+ retryable: false,
244
+ });
245
+ }
246
+ const envelope = rawEnvelope as { requestId?: unknown; result?: unknown };
247
+ if (envelope.requestId !== requestId) {
248
+ throw new FacetError("tier0_protocol_error", "Worker response requestId did not match", {
249
+ retryable: false,
250
+ });
251
+ }
252
+ if (envelope.result === undefined) {
253
+ throw new FacetError("tier0_protocol_error", "Worker response omitted result", {
254
+ retryable: false,
255
+ });
256
+ }
257
+ return _parseWorkerStdout(JSON.stringify(envelope.result), outputCap);
258
+ }
259
+
260
+ function createRunner(level: InsecureLevel, options: RunnerOptions): Tier0Runner {
261
+ let child: ChildProcess | null = null;
262
+ let pending: PendingRequest | null = null;
263
+ let stdoutBuffer = Buffer.alloc(0);
264
+ let stderrBuffer = Buffer.alloc(0);
265
+ let queued: Promise<void> = Promise.resolve();
266
+ let requestSequence = 0;
267
+ let closed = false;
268
+
269
+ function rejectPending(target: PendingRequest, error: FacetError): void {
270
+ if (pending !== target) return;
271
+ clearTimeout(target.timer);
272
+ pending = null;
273
+ target.reject(error);
274
+ }
275
+
276
+ function resolvePending(target: PendingRequest, result: Tier0WorkerResult): void {
277
+ if (pending !== target) return;
278
+ clearTimeout(target.timer);
279
+ pending = null;
280
+ target.resolve(result);
281
+ }
282
+
283
+ function clearWorker(target: ChildProcess, kill: boolean): void {
284
+ if (child !== target) return;
285
+ child = null;
286
+ stdoutBuffer = Buffer.alloc(0);
287
+ stderrBuffer = Buffer.alloc(0);
288
+ if (!kill) return;
289
+ try {
290
+ target.kill("SIGKILL");
291
+ } catch {
292
+ // The worker may have exited between protocol failure and teardown.
293
+ }
294
+ }
295
+
296
+ function failWorker(target: ChildProcess, error: FacetError, kill: boolean): void {
297
+ if (pending?.worker === target) rejectPending(pending, error);
298
+ clearWorker(target, kill);
299
+ }
300
+
301
+ function handleStdout(target: ChildProcess, chunk: Buffer): void {
302
+ if (child !== target || pending === null || pending.worker !== target) {
303
+ failWorker(
304
+ target,
305
+ new FacetError("tier0_protocol_error", "Worker emitted stdout without an active request", {
306
+ retryable: false,
307
+ }),
308
+ true,
309
+ );
310
+ return;
311
+ }
312
+ stdoutBuffer = Buffer.concat([stdoutBuffer, chunk]);
313
+ const newline = stdoutBuffer.indexOf(0x0a);
314
+ if (newline < 0) {
315
+ if (stdoutBuffer.byteLength > options.outputCap) {
316
+ failWorker(
317
+ target,
318
+ new FacetError("tier0_output_cap", "Worker stdout exceeded the byte cap", {
319
+ retryable: false,
320
+ details: { capBytes: options.outputCap },
321
+ }),
322
+ true,
323
+ );
324
+ }
325
+ return;
326
+ }
327
+ const line = stdoutBuffer.subarray(0, newline);
328
+ const trailing = stdoutBuffer.subarray(newline + 1);
329
+ stdoutBuffer = trailing;
330
+ if (trailing.byteLength > 0 && trailing[0] !== 0x7b) {
331
+ failWorker(
332
+ target,
333
+ new FacetError("tier0_protocol_error", "Worker emitted invalid trailing stdout bytes", {
334
+ retryable: false,
335
+ }),
336
+ true,
337
+ );
338
+ return;
339
+ }
340
+ if (line.byteLength > options.outputCap) {
341
+ failWorker(
342
+ target,
343
+ new FacetError("tier0_output_cap", "Worker stdout exceeded the byte cap", {
344
+ retryable: false,
345
+ details: { capBytes: options.outputCap },
346
+ }),
347
+ true,
348
+ );
349
+ return;
350
+ }
351
+ const active = pending;
352
+ try {
353
+ resolvePending(active, parseWorkerResponse(line, active.requestId, options.outputCap));
354
+ } catch (error) {
355
+ const facet = FacetError.from(error);
356
+ failWorker(target, facet, true);
357
+ }
358
+ }
359
+
360
+ function spawnWorker(): ChildProcess {
361
+ const isolation = resolveTier0Isolation(level);
362
+ if (isolation === "netns") {
363
+ const probe = probeNetnsSupport();
364
+ if (!probe.available) {
365
+ throw new FacetError(
366
+ "tier0_unavailable",
367
+ `Tier 0 cannot run: netns unavailable (${probe.reason ?? "unknown"})`,
368
+ { retryable: false, details: { reason: probe.reason ?? "unknown" } },
369
+ );
370
+ }
371
+ }
372
+ const started =
373
+ isolation === "netns"
374
+ ? spawnNetnsWorker(buildTier0WorkerArgs(options.workerCommand))
375
+ : spawnDirectWorker(buildTier0WorkerArgs(options.workerCommand));
376
+ child = started;
377
+ options.onWorkerSpawn?.(started.pid!);
378
+ started.stderr?.on("data", (chunk: Buffer) => {
379
+ stderrBuffer = Buffer.concat([stderrBuffer, Buffer.from(chunk)]).subarray(
380
+ 0,
381
+ options.outputCap,
382
+ );
383
+ });
384
+ started.stderr?.pipe(process.stderr, { end: false });
385
+ started.stdout?.on("data", (chunk: Buffer) => handleStdout(started, Buffer.from(chunk)));
386
+ started.once("error", (error) => {
387
+ failWorker(started, workerStartError(error, stderrBuffer), false);
388
+ });
389
+ started.once("exit", (code, signal) => {
390
+ failWorker(started, workerDiedError(code, signal, stderrBuffer), false);
391
+ });
392
+ return started;
393
+ }
394
+
395
+ async function runRequest(input: Tier0Input): Promise<Tier0WorkerResult> {
396
+ if (closed) {
397
+ throw new FacetError("tier0_worker_died", "Tier 0 runner is closed", { retryable: false });
398
+ }
399
+ const worker = child ?? spawnWorker();
400
+ if (child !== worker)
401
+ throw new FacetError("tier0_worker_died", "Worker exited before request", {
402
+ retryable: false,
403
+ });
404
+ const requestId = String(++requestSequence);
405
+ const envelopeJson = `${JSON.stringify(buildInputEnvelope(input, requestId))}\n`;
406
+ return new Promise<Tier0WorkerResult>((resolve, reject) => {
407
+ const timeoutMs = input.artifactType === "tsx" ? options.tsxTimeoutMs : options.timeoutMs;
408
+ const target: PendingRequest = {
409
+ requestId,
410
+ worker,
411
+ resolve,
412
+ reject,
413
+ timer: setTimeout(() => {
414
+ failWorker(
415
+ worker,
416
+ new FacetError("tier0_timeout", `Tier 0 worker timed out after ${timeoutMs}ms`, {
417
+ retryable: false,
418
+ details: { timeoutMs },
419
+ }),
420
+ true,
421
+ );
422
+ }, timeoutMs),
423
+ };
424
+ pending = target;
425
+ if (stdoutBuffer.byteLength > 0) handleStdout(worker, Buffer.alloc(0));
426
+ if (pending !== target) return;
427
+ try {
428
+ worker.stdin?.write(envelopeJson);
429
+ } catch {
430
+ failWorker(
431
+ worker,
432
+ new FacetError("tier0_worker_died", "Failed to write to Tier 0 worker", {
433
+ retryable: false,
434
+ }),
435
+ true,
436
+ );
437
+ }
438
+ });
439
+ }
440
+
441
+ const run = (input: Tier0Input): Promise<Tier0WorkerResult> => {
442
+ const request = queued.then(() => runRequest(input));
443
+ queued = request.then(
444
+ () => undefined,
445
+ () => undefined,
446
+ );
447
+ return request;
448
+ };
449
+
450
+ const close = (): void => {
451
+ if (closed) return;
452
+ closed = true;
453
+ const active = child;
454
+ if (active === null) return;
455
+ failWorker(
456
+ active,
457
+ new FacetError("tier0_worker_died", "Tier 0 runner closed", { retryable: false }),
458
+ true,
459
+ );
460
+ };
461
+
462
+ return Object.assign(run, { close });
463
+ }
464
+
465
+ /**
466
+ * Run the Tier 0 worker against an artifact's immutable bytes. The
467
+ * returned worker result ALWAYS conforms to `Tier0WorkerResultSchema`; the
468
+ * `status` field distinguishes a clean parse (`ok`) from a parser
469
+ * rejection (`error`). System-level failures throw `FacetError` with
470
+ * a typed `tier0_*` code.
471
+ */
472
+ export async function runTier0(input: Tier0Input): Promise<Tier0WorkerResult> {
473
+ const runner = createTier0Runner(0);
474
+ try {
475
+ return await runner(input);
476
+ } finally {
477
+ runner.close?.();
478
+ }
479
+ }
480
+
481
+ /** Create a Tier 0 runner using the requested isolation level. */
482
+ export function createTier0Runner(level: InsecureLevel): Tier0Runner {
483
+ return createRunner(level, {
484
+ workerCommand: isCompiledRuntime()
485
+ ? { mode: "compiled" }
486
+ : { mode: "source", entrypoint: TIER0_WORKER_ENTRY },
487
+ timeoutMs: TIER0_TIMEOUT_MS,
488
+ tsxTimeoutMs: TIER0_TSX_TIMEOUT_MS,
489
+ outputCap: TIER0_OUTPUT_CAP_BYTES,
490
+ });
491
+ }
492
+
493
+ export function createTier0RunnerForTests(
494
+ level: InsecureLevel,
495
+ hooks: Tier0RunnerTestHooks,
496
+ ): Tier0Runner {
497
+ return createRunner(level, {
498
+ workerCommand: { mode: "source", entrypoint: hooks.workerEntry ?? TIER0_WORKER_ENTRY },
499
+ timeoutMs: hooks.timeoutMs ?? TIER0_TIMEOUT_MS,
500
+ tsxTimeoutMs: hooks.tsxTimeoutMs ?? TIER0_TSX_TIMEOUT_MS,
501
+ outputCap: hooks.outputCap ?? TIER0_OUTPUT_CAP_BYTES,
502
+ ...(hooks.onWorkerSpawn === undefined ? {} : { onWorkerSpawn: hooks.onWorkerSpawn }),
503
+ });
504
+ }