@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,61 @@
1
+ /**
2
+ * Bun.build plugins shared by every frame-bundle build (gallery frame
3
+ * bootstrap + Tier 1 verifier harness).
4
+ *
5
+ * The dompurify alias points `import "dompurify"` at the frame shim,
6
+ * which re-instantiates DOMPurify against the frame's own window. The
7
+ * import-time default instance captures unbound Document methods from
8
+ * whatever global the bundler resolved; inlined into a srcdoc bundle
9
+ * those throw `Illegal invocation` at sanitize time. The shim import
10
+ * itself is excluded from the alias so it resolves the real package.
11
+ */
12
+
13
+ import { basename, join } from "node:path";
14
+
15
+ /**
16
+ * Minimal shape of `Bun.build`'s plugin object — the public Bun type
17
+ * does not export `BunBuildPlugin` (only `BunPlugin` and the inferred
18
+ * shape from the bundler overloads), so we declare the surface this
19
+ * module uses. The `setup` callback receives the live build handle and
20
+ * a per-resolution args bag; both are inferred from the API.
21
+ */
22
+ interface BunBuildPlugin {
23
+ readonly name: string;
24
+ setup(build: {
25
+ onResolve(
26
+ filter: { readonly filter: RegExp },
27
+ callback: (args: {
28
+ readonly path: string;
29
+ readonly importer: string;
30
+ }) => { readonly path: string } | undefined,
31
+ ): void;
32
+ }): void;
33
+ }
34
+
35
+ const DOMPURIFY_SHIM_PATH = join(
36
+ import.meta.dir,
37
+ "..",
38
+ "..",
39
+ "gallery-web",
40
+ "frame",
41
+ "renderers",
42
+ "dompurify-shim.ts",
43
+ );
44
+
45
+ export function frameBundlePlugins(): BunBuildPlugin[] {
46
+ return [
47
+ {
48
+ name: "facet-dom-purify-frame-alias",
49
+ setup(build) {
50
+ build.onResolve({ filter: /^dompurify$/ }, (args) => {
51
+ // The shim's OWN import must resolve to the real package.
52
+ // Bun reports the importer through symlink-copied paths
53
+ // (e.g. /tmp/node_modules/…), so match by basename, not by
54
+ // exact path.
55
+ if (basename(args.importer) === "dompurify-shim.ts") return undefined;
56
+ return { path: DOMPURIFY_SHIM_PATH };
57
+ });
58
+ },
59
+ },
60
+ ];
61
+ }
@@ -0,0 +1,28 @@
1
+ export type ServiceLaunch =
2
+ | {
3
+ readonly mode: "source";
4
+ readonly entrypoint: string;
5
+ readonly tier0RunnerPath: string;
6
+ readonly tier1RunnerPath: string;
7
+ }
8
+ | { readonly mode: "compiled" };
9
+
10
+ export type Tier0WorkerCommand =
11
+ | { readonly mode: "source"; readonly entrypoint: string }
12
+ | { readonly mode: "compiled" };
13
+
14
+ const BUN_COMPILED_FILESYSTEM_PREFIX = "/$bunfs/";
15
+
16
+ export function isCompiledRuntime(): boolean {
17
+ return Bun.main.startsWith(BUN_COMPILED_FILESYSTEM_PREFIX);
18
+ }
19
+
20
+ export function isCompiledEntrypointArg(argv: readonly string[]): boolean {
21
+ return argv[0] === "--facet-internal-service" || argv[0] === "--facet-internal-tier0-worker";
22
+ }
23
+
24
+ export function buildTier0WorkerArgs(command: Tier0WorkerCommand): string[] {
25
+ return command.mode === "compiled"
26
+ ? ["--facet-internal-tier0-worker"]
27
+ : ["run", command.entrypoint];
28
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Evidence-root read resolution.
3
+ *
4
+ * The service is the only writer of evidence; the CLI and the export path
5
+ * are readers. After explicit evidence-root threading, both agree on ONE
6
+ * canonical root. But default-XDG installs that ran before the threading
7
+ * wrote evidence under a legacy child-derived root
8
+ * (`legacyXdgEvidenceRoot`), so readers must tolerate the divergence:
9
+ * when the canonical root holds no evidence AND the legacy root does, reads
10
+ * ALSO fall back to the legacy root. Writes never go there — the fallback is
11
+ * read-only and the canonical root is authoritative for everything new.
12
+ */
13
+
14
+ import { readdirSync, statSync } from "node:fs";
15
+
16
+ export interface EvidenceReadRoot {
17
+ readonly path: string;
18
+ /** True when this root is the legacy fallback, false for the canonical root. */
19
+ readonly legacy: boolean;
20
+ }
21
+
22
+ export interface EvidenceFs {
23
+ /** True when `path` exists and holds at least one non-empty file (recursively). */
24
+ hasEvidence(path: string): boolean;
25
+ }
26
+
27
+ /**
28
+ * Recursively true when `path` exists and contains at least one file with
29
+ * non-zero size. An empty or missing directory is "no evidence".
30
+ */
31
+ export function directoryHasEvidence(path: string): boolean {
32
+ let stat;
33
+ try {
34
+ stat = statSync(path);
35
+ } catch {
36
+ return false;
37
+ }
38
+ if (stat.isFile()) return stat.size > 0;
39
+ if (!stat.isDirectory()) return false;
40
+ let entries: string[];
41
+ try {
42
+ entries = readdirSync(path);
43
+ } catch {
44
+ return false;
45
+ }
46
+ for (const entry of entries) {
47
+ if (directoryHasEvidence(`${path}/${entry}`)) return true;
48
+ }
49
+ return false;
50
+ }
51
+
52
+ /**
53
+ * Ordered roots a reader should consult. The canonical root is always first;
54
+ * the legacy root is appended only when the canonical root holds no evidence
55
+ * while the legacy root does (the pre-threading migration case). Callers that
56
+ * see a `legacy: true` root should log once and must never write to it.
57
+ */
58
+ export function resolveEvidenceReadRoots(
59
+ canonical: string,
60
+ legacy: string | null,
61
+ fs: EvidenceFs = { hasEvidence: directoryHasEvidence },
62
+ ): EvidenceReadRoot[] {
63
+ const roots: EvidenceReadRoot[] = [{ path: canonical, legacy: false }];
64
+ if (
65
+ legacy !== null &&
66
+ legacy !== canonical &&
67
+ !fs.hasEvidence(canonical) &&
68
+ fs.hasEvidence(legacy)
69
+ ) {
70
+ roots.push({ path: legacy, legacy: true });
71
+ }
72
+ return roots;
73
+ }
74
+
75
+ /** Total byte count of evidence across the resolved read roots. */
76
+ export function evidenceBytesAcross(roots: readonly EvidenceReadRoot[]): number {
77
+ let total = 0;
78
+ for (const root of roots) total += directoryBytes(root.path);
79
+ return total;
80
+ }
81
+
82
+ function directoryBytes(path: string): number {
83
+ let stat;
84
+ try {
85
+ stat = statSync(path);
86
+ } catch {
87
+ return 0;
88
+ }
89
+ if (stat.isFile()) return stat.size;
90
+ if (!stat.isDirectory()) return 0;
91
+ let total = 0;
92
+ try {
93
+ for (const entry of readdirSync(path)) total += directoryBytes(`${path}/${entry}`);
94
+ } catch {
95
+ // unreadable directory counts as zero
96
+ }
97
+ return total;
98
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Hard cap on artifact source bytes per ADR 0001 D2. Benchmarks measured
3
+ * 20/20 clean renders through 5 MB inside the 200 ms visibility budget;
4
+ * 10 MB breaches it. Renderer-complexity guards are separate from this
5
+ * cap and live alongside it (see below).
6
+ */
7
+ export const SOURCE_CAP_BYTES = 5 * 1024 * 1024;
8
+
9
+ /**
10
+ * Pinned Chrome for Testing build used by the Tier 1 verifier. Browser-pin
11
+ * changes must re-check opaque-origin storage isolation, sandbox navigation
12
+ * limits, `connect-src 'none'`, and nonce-only script execution.
13
+ */
14
+ export const TIER1_PINNED_VERSION = "151.0.7922.77";
15
+
16
+ /** Acceptance commands must outlive the Tier 1 verifier and return its typed verdict. */
17
+ export const ACCEPTANCE_TEST_BUDGET_MS = 90_000;
18
+
19
+ /**
20
+ * Lexical guard: at most this many ```mermaid fenced blocks in one
21
+ * artifact. Larger graphs hit the mermaid parser's own limits and
22
+ * produce unreadable SVGs.
23
+ */
24
+ export const MAX_MERMAID_BLOCKS = 64;
25
+
26
+ /**
27
+ * Lexical guard: at most this many `N<digits>[...]` node declarations
28
+ * across all mermaid blocks in one artifact. Bounds the rendered DOM
29
+ * size independent of source byte count.
30
+ */
31
+ export const MAX_MERMAID_NODES = 10_000;
32
+
33
+ /**
34
+ * Bounds HTML token nesting before parse5 tree construction. At 10,000 nested
35
+ * elements, Tier 0 measured about 100 ms locally, leaving wide margin below
36
+ * the five-second worker timeout.
37
+ */
38
+ export const MAX_HTML_NESTING_DEPTH = 10_000;
39
+
40
+ /** Default in-memory cap for list responses. */
41
+ export const DEFAULT_LIST_LIMIT = 50;
42
+ export const MAX_LIST_LIMIT = 500;
43
+
44
+ /**
45
+ * Lexical guard: max bytes in a single SVG artifact. Bounds the render
46
+ * time on the Tier 1 verifier independent of the source cap (an SVG
47
+ * can be small in bytes but enormous in DOM).
48
+ */
49
+ export const MAX_SVG_BYTES = 1 * 1024 * 1024;
50
+
51
+ /**
52
+ * Lexical guard: max top-level `<svg>` elements in a single SVG
53
+ * artifact. Forged pages cannot add more than this without inflating
54
+ * the verifier's own SVG count past the lexical expectation.
55
+ */
56
+ export const MAX_SVG_ROOTS = 16;
@@ -0,0 +1,96 @@
1
+ import { accessSync, constants } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ /**
5
+ * All runtime paths the service writes to. Pure path computation — no
6
+ * filesystem effects. Callers (the database opener, the validation
7
+ * harness) own the actual mkdir/chmod calls so the path layer can be
8
+ * exercised in tests without touching the disk.
9
+ */
10
+ export interface FacetRuntimePaths {
11
+ /** Persistent state: the SQLite database (with its WAL/SHM sidecars). */
12
+ readonly database: string;
13
+ /** Per-run evidence: screenshots, console captures, request logs. */
14
+ readonly evidence: string;
15
+ /** Long-lived secret material: the promote-capability token. */
16
+ readonly token: string;
17
+ /** Cross-process lock guarding single-writer sections. */
18
+ readonly lock: string;
19
+ /** Free-form metadata (project name, gallery title, etc.). */
20
+ readonly metadata: string;
21
+ }
22
+
23
+ export interface FacetPathEnvironment {
24
+ readonly facetHome?: string;
25
+ readonly xdgDataHome?: string;
26
+ readonly xdgStateHome?: string;
27
+ readonly xdgConfigHome?: string;
28
+ readonly xdgCacheHome?: string;
29
+ }
30
+
31
+ const FALLBACK_XDG_DATA = `${process.env.HOME ?? ""}/.local/share`;
32
+ const FALLBACK_XDG_STATE = `${process.env.HOME ?? ""}/.local/state`;
33
+ const FALLBACK_XDG_CONFIG = `${process.env.HOME ?? ""}/.config`;
34
+ const FALLBACK_XDG_CACHE = `${process.env.HOME ?? ""}/.cache`;
35
+
36
+ /**
37
+ * Resolve generated gallery assets to the checkout when it can be written,
38
+ * otherwise use the operator-owned cache so installed packages stay immutable.
39
+ */
40
+ export function resolveGalleryRoot(packageRoot: string, env: FacetPathEnvironment = {}): string {
41
+ try {
42
+ accessSync(packageRoot, constants.W_OK);
43
+ return join(packageRoot, "dist", "gallery");
44
+ } catch {
45
+ const facetHome = env.facetHome ?? process.env.FACET_HOME;
46
+ if (facetHome) return join(facetHome, "cache", "gallery");
47
+ const cacheHome = env.xdgCacheHome ?? process.env.XDG_CACHE_HOME ?? FALLBACK_XDG_CACHE;
48
+ return join(cacheHome, "facet", "gallery");
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Compute the five runtime paths. XDG-style: data/state/configurable
54
+ * each have their own override; `facetHome` (or `FACET_HOME`) wins
55
+ * outright when set so operators can run a fully self-contained
56
+ * install without polluting their home directory.
57
+ */
58
+ export function computeFacetPaths(env: FacetPathEnvironment = {}): FacetRuntimePaths {
59
+ const facetHome = env.facetHome ?? process.env.FACET_HOME;
60
+ if (facetHome) {
61
+ return {
62
+ database: join(facetHome, "db", "facet.sqlite"),
63
+ evidence: join(facetHome, "evidence"),
64
+ token: join(facetHome, "secrets", "promote.token"),
65
+ lock: join(facetHome, "run", "facet.lock"),
66
+ metadata: join(facetHome, "metadata.json"),
67
+ };
68
+ }
69
+ const dataHome = env.xdgDataHome ?? process.env.XDG_DATA_HOME ?? FALLBACK_XDG_DATA;
70
+ const stateHome = env.xdgStateHome ?? process.env.XDG_STATE_HOME ?? FALLBACK_XDG_STATE;
71
+ const configHome = env.xdgConfigHome ?? process.env.XDG_CONFIG_HOME ?? FALLBACK_XDG_CONFIG;
72
+ return {
73
+ database: join(dataHome, "facet", "db", "facet.sqlite"),
74
+ evidence: join(stateHome, "facet", "evidence"),
75
+ token: join(dataHome, "facet", "secrets", "promote.token"),
76
+ lock: join(stateHome, "facet", "run", "facet.lock"),
77
+ metadata: join(configHome, "facet", "metadata.json"),
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Legacy evidence root for XDG-default installs predating explicit
83
+ * evidence-root threading. Before the parent CLI passed its evidence root
84
+ * to the service child, the child derived its paths from
85
+ * `FACET_HOME = <dataHome>/facet` (see `spawnChild`) and wrote evidence to
86
+ * `<dataHome>/facet/evidence`, while the parent read
87
+ * `<stateHome>/facet/evidence`. Returns that child-derived root in XDG mode
88
+ * so readers can tolerate the divergence; returns null when `FACET_HOME` is
89
+ * set (that mode never diverged — parent and child always agreed).
90
+ */
91
+ export function legacyXdgEvidenceRoot(env: FacetPathEnvironment = {}): string | null {
92
+ const facetHome = env.facetHome ?? process.env.FACET_HOME;
93
+ if (facetHome) return null;
94
+ const dataHome = env.xdgDataHome ?? process.env.XDG_DATA_HOME ?? FALLBACK_XDG_DATA;
95
+ return join(dataHome, "facet", "evidence");
96
+ }
@@ -0,0 +1,2 @@
1
+ export const ARTIFACT_TYPES = ["markdown", "mermaid", "svg", "chart", "html", "tsx"] as const;
2
+ export type ArtifactType = (typeof ARTIFACT_TYPES)[number];
@@ -0,0 +1,98 @@
1
+ import { z } from "zod";
2
+
3
+ import { TSX_EXECUTION_MODES } from "../tsx/execution";
4
+ import { ARTIFACT_TYPES } from "./artifact-types";
5
+ import { RENDERERS } from "./renderers";
6
+ import { EvidenceImageFormatSchema } from "../evidence-image";
7
+
8
+ export { ARTIFACT_TYPES, type ArtifactType } from "./artifact-types";
9
+ export { RENDERERS, type Renderer } from "./renderers";
10
+ export const ArtifactTypeSchema = z.enum(ARTIFACT_TYPES);
11
+ export const RendererSchema = z.enum(RENDERERS);
12
+
13
+ const IdSchema = z.string().min(1);
14
+ const IsoTimestampSchema = z.string().datetime({ offset: true });
15
+ const Sha256Schema = z.string().regex(/^[a-f0-9]{64}$/);
16
+ const ByteSourceSchema = z.instanceof(Uint8Array);
17
+
18
+ export const ProjectSchema = z.object({
19
+ id: IdSchema,
20
+ projectRoot: z.string().min(1),
21
+ createdAt: IsoTimestampSchema,
22
+ });
23
+ export type Project = z.infer<typeof ProjectSchema>;
24
+
25
+ export const ArtifactSchema = z.object({
26
+ id: IdSchema,
27
+ projectId: IdSchema,
28
+ slug: z.string().min(1),
29
+ title: z.string().min(1),
30
+ createdAt: IsoTimestampSchema,
31
+ updatedAt: IsoTimestampSchema,
32
+ });
33
+ export type Artifact = z.infer<typeof ArtifactSchema>;
34
+
35
+ export const RevisionSchema = z.object({
36
+ id: IdSchema,
37
+ artifactId: IdSchema,
38
+ revisionNumber: z.number().int().positive(),
39
+ parentRevisionId: IdSchema.nullable(),
40
+ artifactType: ArtifactTypeSchema,
41
+ renderer: RendererSchema,
42
+ source: ByteSourceSchema,
43
+ sha256: Sha256Schema,
44
+ note: z.string().nullable(),
45
+ pinned: z.boolean(),
46
+ createdAt: IsoTimestampSchema,
47
+ /**
48
+ * TSX execution mode (D2). Only present for `artifactType === "tsx"`
49
+ * — every other type stores `'static'` on disk but the field is
50
+ * absent from the wire form (the dispatch is explicit: `execution`
51
+ * must be absent, not null, for non-TSX artifacts). Derived from
52
+ * the canonical `TSX_EXECUTION_MODES` array so a new mode lands in
53
+ * one place.
54
+ */
55
+ execution: z.enum(TSX_EXECUTION_MODES).optional(),
56
+ });
57
+ export type Revision = z.infer<typeof RevisionSchema>;
58
+
59
+ export const RenderRunSchema = z.object({
60
+ id: IdSchema,
61
+ revisionId: IdSchema,
62
+ tier: z.union([z.literal(0), z.literal(1)]),
63
+ status: z.string().min(1),
64
+ expectedJson: z.string(),
65
+ observedJson: z.string(),
66
+ screenshotPath: z.string().nullable(),
67
+ screenshotFormat: EvidenceImageFormatSchema.nullable(),
68
+ consolePath: z.string().nullable(),
69
+ /**
70
+ * TSX compiled-bundle evidence (D7). The compiled bundle is derived
71
+ * output stored alongside the run, not a wire form — `null` for
72
+ * non-TSX runs and for TSX runs whose compilation did not produce
73
+ * a retained file.
74
+ */
75
+ compiledPath: z.string().nullable().optional(),
76
+ screenshotErrorJson: z.string().nullable(),
77
+ insecureJson: z.string().nullable(),
78
+ /**
79
+ * Retained-evidence carve-out: `true` exempts the row from the
80
+ * last-N retention eviction. Pin/template call sites mark rows
81
+ * retained; the cleanup policy skips them.
82
+ */
83
+ retained: z.boolean(),
84
+ startedAt: IsoTimestampSchema,
85
+ finishedAt: IsoTimestampSchema,
86
+ });
87
+ export type RenderRun = z.infer<typeof RenderRunSchema>;
88
+
89
+ export const TemplateSchema = z.object({
90
+ id: IdSchema,
91
+ artifactId: IdSchema,
92
+ revisionId: IdSchema,
93
+ name: z.string().min(1),
94
+ description: z.string().nullable(),
95
+ promotedBy: z.string().min(1),
96
+ promotedAt: IsoTimestampSchema,
97
+ });
98
+ export type Template = z.infer<typeof TemplateSchema>;
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+
3
+ import { TSX_EXECUTION_MODES } from "../../tsx/execution";
4
+ import { ArtifactTypeSchema, RendererSchema, TemplateSchema } from "../artifact";
5
+
6
+ /**
7
+ * Shared wire-envelope schemas reused by every command verb's request
8
+ * and result. Kept in one place so a typo in `requestId` or a SHA
9
+ * regex drift is fixed in a single file.
10
+ */
11
+ export const ArtifactEnvelopeSchema = z.object({
12
+ id: z.string().min(1),
13
+ projectId: z.string().min(1),
14
+ slug: z.string().min(1),
15
+ title: z.string().min(1),
16
+ createdAt: z.string().datetime({ offset: true }),
17
+ updatedAt: z.string().datetime({ offset: true }),
18
+ });
19
+ export type ArtifactEnvelope = z.infer<typeof ArtifactEnvelopeSchema>;
20
+
21
+ export const RevisionEnvelopeSchema = z.object({
22
+ id: z.string().min(1),
23
+ artifactId: z.string().min(1),
24
+ revisionNumber: z.number().int().positive(),
25
+ parentRevisionId: z.string().min(1).nullable(),
26
+ artifactType: ArtifactTypeSchema,
27
+ renderer: RendererSchema,
28
+ sha256: z.string().regex(/^[a-f0-9]{64}$/),
29
+ note: z.string().nullable(),
30
+ pinned: z.boolean(),
31
+ createdAt: z.string().datetime({ offset: true }),
32
+ /**
33
+ * TSX execution mode (D2). Omitted from the wire for every non-TSX
34
+ * artifact type so existing wire fixtures stay byte-identical after
35
+ * the TSX arc lands. The service emits this key only when the
36
+ * revision's `artifactType` is `"tsx"`. Derived from the canonical
37
+ * `TSX_EXECUTION_MODES` array so a new mode lands in one place.
38
+ */
39
+ execution: z.enum(TSX_EXECUTION_MODES).optional(),
40
+ });
41
+ export type RevisionEnvelope = z.infer<typeof RevisionEnvelopeSchema>;
42
+
43
+ export const TemplateEnvelopeSchema = TemplateSchema;
44
+ export type TemplateEnvelope = z.infer<typeof TemplateEnvelopeSchema>;
45
+
46
+ /** Every request carries a `requestId` for correlation with its result. */
47
+ export const BaseRequestSchema = z.object({ requestId: z.string().min(1) });
48
+
49
+ /** Every result echoes the request's `requestId`. */
50
+ export const BaseResultSchema = z.object({ requestId: z.string().min(1) });
51
+
52
+ /** Public read-back tier. `0` and `1` are the literal verifier tiers; `"visual"` is sugar for `1`. */
53
+ export const ReadBackTierSchema = z.union([z.literal(0), z.literal(1), z.literal("visual")]);
54
+ export type ReadBackTier = z.infer<typeof ReadBackTierSchema>;
55
+
56
+ /** Normalize a public read-back tier to a numeric validation tier. */
57
+ export function normalizeReadBackTier(tier: ReadBackTier): 0 | 1 {
58
+ return tier === "visual" ? 1 : tier;
59
+ }
@@ -0,0 +1,86 @@
1
+ import { z } from "zod";
2
+
3
+ import type { Renderer } from "../renderers";
4
+ import { FacetError } from "../../errors/facet-error";
5
+
6
+ import {
7
+ CommandNameSchema,
8
+ IMPLEMENTED_COMMANDS,
9
+ RESERVED_COMMANDS,
10
+ type CommandName,
11
+ } from "./names";
12
+ import { PublishArtifactTypeSchema } from "./requests";
13
+
14
+ export {
15
+ CommandNameSchema,
16
+ IMPLEMENTED_COMMANDS,
17
+ RESERVED_COMMANDS,
18
+ type CommandName,
19
+ PublishArtifactTypeSchema,
20
+ };
21
+
22
+ /**
23
+ * Returns a `reserved_not_implemented` FacetError for any reserved verb and
24
+ * `null` for every implemented verb. The command dispatcher calls this before
25
+ * running any handler.
26
+ */
27
+ export function checkCommandImplemented(name: CommandName): FacetError | null {
28
+ if ((RESERVED_COMMANDS as readonly CommandName[]).includes(name)) {
29
+ return new FacetError(
30
+ "reserved_not_implemented",
31
+ `Command '${name}' is reserved and not implemented in this build`,
32
+ { retryable: false, details: { command: name } },
33
+ );
34
+ }
35
+ return null;
36
+ }
37
+
38
+ /**
39
+ * Future reserved artifact types return `unsupported_reserved_type`; every
40
+ * known type currently returns `null`. The publish dispatcher calls this
41
+ * before passing bytes into the store.
42
+ */
43
+ export function checkArtifactTypeSupported(_type: string): FacetError | null {
44
+ return null;
45
+ }
46
+
47
+ /** Returns `invalid_request` when a renderer is incompatible with the artifact type. */
48
+ export function checkRendererSupported(
49
+ artifactType: string,
50
+ renderer: Renderer,
51
+ ): FacetError | null {
52
+ if (renderer === "canvas" && artifactType !== "chart") {
53
+ return new FacetError(
54
+ "invalid_request",
55
+ `Renderer '${renderer}' is only supported for artifact type 'chart'`,
56
+ { retryable: false, details: { artifactType, renderer } },
57
+ );
58
+ }
59
+ return null;
60
+ }
61
+
62
+ /**
63
+ * Returns `invalid_request` when an `interactive` execution is set on a
64
+ * non-TSX artifact. `static` is the default and is silently accepted on
65
+ * every type (mirroring how `renderer: "svg"` is accepted on every
66
+ * artifact type — it is the canonical default). Callers see a typed
67
+ * error before any service round-trip or storage write; the publish
68
+ * dispatcher calls this before passing the request into the store.
69
+ */
70
+ export function checkExecutionSupported(
71
+ artifactType: string,
72
+ execution: string | undefined,
73
+ ): FacetError | null {
74
+ if (execution === "interactive" && artifactType !== "tsx") {
75
+ return new FacetError(
76
+ "invalid_request",
77
+ `Execution mode 'interactive' is only supported for artifact type 'tsx' (got '${artifactType}')`,
78
+ { retryable: false, details: { artifactType, execution } },
79
+ );
80
+ }
81
+ return null;
82
+ }
83
+
84
+ // Re-export zod here so the barrel's `import { z } from "zod"` callers
85
+ // keep working without re-importing.
86
+ export { z };