@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,501 @@
1
+ import { isTsxExecutionMode, type TsxExecutionMode } from "../../shared/tsx/execution";
2
+ import { isResolvedGalleryTheme } from "../theme";
3
+
4
+ import { validateRenderer } from "./renderer-validation";
5
+ import {
6
+ appendRenderError,
7
+ countPageShim,
8
+ dispatchRender,
9
+ FacetRenderError,
10
+ type PageShimCounts,
11
+ type RendererRegistry,
12
+ } from "./renderers/registry";
13
+ import {
14
+ decodePayloadBytes,
15
+ isFrameViewState,
16
+ isGestureMode,
17
+ isUint8Array,
18
+ parseViewBox,
19
+ type FrameRenderPayload,
20
+ type FrameViewState,
21
+ type GestureMode,
22
+ } from "./frame-payload";
23
+ import type { SvgViewBox } from "./view-box";
24
+ import {
25
+ clampZoom,
26
+ EMPTY_VIEW_STATE,
27
+ nextViewStateForKey,
28
+ normalizeViewState,
29
+ zoomAtPoint,
30
+ } from "../view-state";
31
+
32
+ export type { FrameRenderPayload, FrameViewState, GestureMode };
33
+
34
+ export interface RenderResult {
35
+ readonly observed: PageShimCounts;
36
+ readonly viewMode: "native" | "css";
37
+ readonly applyViewState: (state: FrameViewState) => void;
38
+ readonly readViewState: () => FrameViewState;
39
+ /** Gesture mode a fresh render started in — restored by reset. */
40
+ readonly defaultGestureMode: GestureMode;
41
+ readonly gestureMode: () => GestureMode;
42
+ readonly setGestureMode: (mode: GestureMode) => void;
43
+ readonly resetDiagramRegions: () => void;
44
+ }
45
+
46
+ /** Artifact types where the whole document IS one diagram — wheel/drag gestures are the natural interaction with no competing text-selection or click behavior to protect. */
47
+ const STANDALONE_DIAGRAM_TYPES = new Set(["mermaid", "svg", "chart"]);
48
+ const DIAGRAM_REGION_SELECTOR = "[data-facet-diagram-region]";
49
+
50
+ export interface DiagramRegionEngagementState {
51
+ readonly activeRegion: string | null;
52
+ readonly armedRegion: string | null;
53
+ }
54
+
55
+ export type DiagramRegionEngagementEvent =
56
+ | { readonly type: "pointerenter" | "pointerleave" | "activate"; readonly region: string }
57
+ | { readonly type: "dismiss" };
58
+
59
+ export function nextDiagramRegionEngagement(
60
+ state: DiagramRegionEngagementState,
61
+ event: DiagramRegionEngagementEvent,
62
+ ): DiagramRegionEngagementState {
63
+ if (event.type === "dismiss") return { activeRegion: null, armedRegion: null };
64
+ if (event.type === "pointerenter") {
65
+ return state.activeRegion === event.region ? state : { ...state, armedRegion: event.region };
66
+ }
67
+ if (event.type === "pointerleave") {
68
+ return state.armedRegion === event.region ? { ...state, armedRegion: null } : state;
69
+ }
70
+ return state.armedRegion === event.region
71
+ ? { activeRegion: event.region, armedRegion: null }
72
+ : state;
73
+ }
74
+
75
+ interface DiagramRegionGestures {
76
+ readonly setEnabled: (enabled: boolean) => void;
77
+ readonly dismiss: () => boolean;
78
+ readonly reset: () => void;
79
+ }
80
+
81
+ export interface GalleryFrameApi {
82
+ render(payload: FrameRenderPayload): Promise<RenderResult>;
83
+ }
84
+
85
+ declare global {
86
+ interface Window {
87
+ __facetFrame?: GalleryFrameApi;
88
+ }
89
+ }
90
+
91
+ /** Duck-typed `.style` guard — covers HTMLElement and SVGElement alike without requiring SVGElement as a global (the frame bundle and this file's unit-test DOM shim don't both provide it). */
92
+ function asStylable(node: Element | null): (Element & { style: CSSStyleDeclaration }) | null {
93
+ return node !== null && "style" in node
94
+ ? (node as Element & { style: CSSStyleDeclaration })
95
+ : null;
96
+ }
97
+
98
+ function setPointerEventsIfStylable(node: Element | null, value: string): void {
99
+ const stylable = asStylable(node);
100
+ if (stylable !== null) stylable.style.pointerEvents = value;
101
+ }
102
+
103
+ /**
104
+ * Markdown appends its rendered fragment directly into the container,
105
+ * which can leave MULTIPLE top-level siblings (e.g. a paragraph
106
+ * followed by a standalone link). Suppressing pointer-events on only
107
+ * `firstElementChild` left every later sibling interactive during a
108
+ * panzoom drag, so every top-level child gets the same treatment —
109
+ * the centering CSS keys on the first child specifically and is
110
+ * untouched by also styling the rest.
111
+ */
112
+ function setPointerEventsForAllChildren(container: HTMLElement, value: string): void {
113
+ for (const child of Array.from(container.children)) {
114
+ setPointerEventsIfStylable(child, value);
115
+ }
116
+ }
117
+
118
+ function renderedSvg(
119
+ container: HTMLElement,
120
+ ): { readonly svg: SVGSVGElement; readonly viewBox: SvgViewBox } | null {
121
+ const root = container.children.length === 1 ? container.firstElementChild : null;
122
+ if (root?.nodeName.toLowerCase() !== "svg") return null;
123
+ const viewBox = parseViewBox(root.getAttribute("viewBox"));
124
+ if (viewBox === null) return null;
125
+ return { svg: root as SVGSVGElement, viewBox };
126
+ }
127
+
128
+ function installDiagramRegionGestures(container: HTMLElement): DiagramRegionGestures {
129
+ const regions = Array.from(container.querySelectorAll<HTMLElement>(DIAGRAM_REGION_SELECTOR));
130
+ let enabled = false;
131
+ let engagement: DiagramRegionEngagementState = { activeRegion: null, armedRegion: null };
132
+ interface RegionControl {
133
+ readonly region: HTMLElement;
134
+ readonly regionId: string;
135
+ readonly sync: () => void;
136
+ readonly dismiss: () => void;
137
+ readonly reset: () => void;
138
+ }
139
+ let controls: readonly RegionControl[] = [];
140
+ const transition = (event: DiagramRegionEngagementEvent): void => {
141
+ engagement = nextDiagramRegionEngagement(engagement, event);
142
+ controls.forEach((control) => control.sync());
143
+ };
144
+ controls = regions.flatMap((region, index) => {
145
+ const svg = region.querySelector<SVGSVGElement>(":scope > svg");
146
+ if (svg === null) return [];
147
+ const rect = svg.getBoundingClientRect();
148
+ const naturalWidth = rect.width;
149
+ const naturalHeight = rect.height;
150
+ if (naturalWidth <= 0 || naturalHeight <= 0) return [];
151
+ region.style.width = `${Math.ceil(naturalWidth)}px`;
152
+ region.style.height = `${Math.ceil(naturalHeight)}px`;
153
+ region.style.overflow = "hidden";
154
+ const regionId = String(index);
155
+ let zoom = 1;
156
+ let drag: { x: number; y: number; pointerId: number } | null = null;
157
+ const sync = (): void => {
158
+ if (engagement.activeRegion === regionId)
159
+ region.setAttribute("data-facet-diagram-engaged", "true");
160
+ else region.removeAttribute("data-facet-diagram-engaged");
161
+ };
162
+ const eventRegion = (event: Event): HTMLElement | null => {
163
+ const target = event.target;
164
+ return target instanceof Element
165
+ ? target.closest<HTMLElement>(DIAGRAM_REGION_SELECTOR)
166
+ : null;
167
+ };
168
+ region.addEventListener("pointerenter", () => {
169
+ if (enabled) transition({ type: "pointerenter", region: regionId });
170
+ });
171
+ region.addEventListener("pointerleave", () => {
172
+ if (enabled) transition({ type: "pointerleave", region: regionId });
173
+ });
174
+ region.addEventListener("pointerdown", (event) => {
175
+ if (!enabled || eventRegion(event) !== region) return;
176
+ transition({ type: "activate", region: regionId });
177
+ if (engagement.activeRegion !== regionId) return;
178
+ region.focus({ preventScroll: true });
179
+ drag = { x: event.clientX, y: event.clientY, pointerId: event.pointerId };
180
+ region.setPointerCapture(event.pointerId);
181
+ region.style.cursor = "grabbing";
182
+ event.preventDefault();
183
+ });
184
+ region.addEventListener("pointermove", (event) => {
185
+ if (drag === null || event.pointerId !== drag.pointerId || eventRegion(event) !== region)
186
+ return;
187
+ region.scrollLeft = Math.max(0, region.scrollLeft - (event.clientX - drag.x));
188
+ region.scrollTop = Math.max(0, region.scrollTop - (event.clientY - drag.y));
189
+ drag = { ...drag, x: event.clientX, y: event.clientY };
190
+ event.preventDefault();
191
+ });
192
+ const endDrag = (event: PointerEvent): void => {
193
+ if (drag === null || event.pointerId !== drag.pointerId) return;
194
+ releaseDrag();
195
+ };
196
+ const releaseDrag = (): void => {
197
+ const activeDrag = drag;
198
+ drag = null;
199
+ if (
200
+ activeDrag !== null &&
201
+ region.isConnected &&
202
+ region.hasPointerCapture(activeDrag.pointerId)
203
+ ) {
204
+ region.releasePointerCapture(activeDrag.pointerId);
205
+ }
206
+ region.style.cursor = "";
207
+ };
208
+ region.addEventListener("pointerup", endDrag);
209
+ region.addEventListener("pointercancel", endDrag);
210
+ region.addEventListener(
211
+ "wheel",
212
+ (event) => {
213
+ if (!enabled || engagement.activeRegion !== regionId || eventRegion(event) !== region)
214
+ return;
215
+ event.preventDefault();
216
+ const nextZoom = clampZoom(zoom * Math.exp(-event.deltaY * 0.001));
217
+ const regionRect = region.getBoundingClientRect();
218
+ const cursorX = event.clientX - regionRect.left;
219
+ const cursorY = event.clientY - regionRect.top;
220
+ const scale = nextZoom / zoom;
221
+ zoom = nextZoom;
222
+ svg.style.width = `${Math.ceil(naturalWidth * zoom)}px`;
223
+ svg.style.maxWidth = "none";
224
+ svg.style.height = "auto";
225
+ region.scrollLeft = Math.max(0, (region.scrollLeft + cursorX) * scale - cursorX);
226
+ region.scrollTop = Math.max(0, (region.scrollTop + cursorY) * scale - cursorY);
227
+ },
228
+ { passive: false },
229
+ );
230
+ return [
231
+ {
232
+ region,
233
+ regionId,
234
+ sync,
235
+ dismiss: (): void => {
236
+ releaseDrag();
237
+ },
238
+ reset: (): void => {
239
+ zoom = 1;
240
+ svg.style.width = `${Math.ceil(naturalWidth)}px`;
241
+ svg.style.maxWidth = "none";
242
+ svg.style.height = "auto";
243
+ region.scrollLeft = 0;
244
+ region.scrollTop = 0;
245
+ releaseDrag();
246
+ },
247
+ },
248
+ ];
249
+ });
250
+ return {
251
+ setEnabled(nextEnabled: boolean): void {
252
+ enabled = nextEnabled;
253
+ if (!enabled) {
254
+ controls.forEach((control) => control.dismiss());
255
+ transition({ type: "dismiss" });
256
+ }
257
+ },
258
+ dismiss(): boolean {
259
+ const wasEngaged = engagement.activeRegion !== null;
260
+ controls.forEach((control) => control.dismiss());
261
+ transition({ type: "dismiss" });
262
+ return wasEngaged;
263
+ },
264
+ reset(): void {
265
+ controls.forEach((control) => control.reset());
266
+ transition({ type: "dismiss" });
267
+ },
268
+ };
269
+ }
270
+
271
+ function validatePayload(value: unknown): {
272
+ readonly artifactType: string;
273
+ readonly renderer: ReturnType<typeof validateRenderer>;
274
+ readonly bytes: Uint8Array;
275
+ readonly theme: FrameRenderPayload["theme"];
276
+ readonly execution?: TsxExecutionMode;
277
+ } {
278
+ if (value === null || typeof value !== "object") {
279
+ throw new FacetRenderError("artifact payload is missing", "invalid_request");
280
+ }
281
+ const payload = value as Record<string, unknown>;
282
+ if (typeof payload.artifactType !== "string") {
283
+ throw new FacetRenderError("artifact payload is missing artifactType", "invalid_request");
284
+ }
285
+ if (!isResolvedGalleryTheme(payload.theme)) {
286
+ throw new FacetRenderError("artifact payload has invalid theme", "invalid_request");
287
+ }
288
+ if (typeof payload.bytes !== "string" && !isUint8Array(payload.bytes)) {
289
+ throw new FacetRenderError("artifact payload is missing bytes", "invalid_request");
290
+ }
291
+ if (payload.execution !== undefined && !isTsxExecutionMode(payload.execution)) {
292
+ throw new FacetRenderError("artifact payload has invalid execution", "invalid_request");
293
+ }
294
+ return {
295
+ artifactType: payload.artifactType,
296
+ renderer: validateRenderer(payload.renderer),
297
+ bytes: decodePayloadBytes(payload.bytes),
298
+ theme: payload.theme,
299
+ ...(payload.execution === undefined ? {} : { execution: payload.execution }),
300
+ };
301
+ }
302
+
303
+ export function installGalleryFrameApi(registry: RendererRegistry): void {
304
+ const container = document.getElementById("artifact");
305
+ if (container === null) throw new Error("frame runtime: #artifact container missing");
306
+ let rendered = false;
307
+
308
+ document.documentElement.style.height = "100%";
309
+ document.documentElement.style.overflow = "hidden";
310
+ document.body.style.height = "100%";
311
+ document.body.style.margin = "0";
312
+ document.body.style.padding = "0";
313
+ document.body.style.overflow = "hidden";
314
+ container.style.height = "100%";
315
+ container.style.width = "100%";
316
+ container.style.overflow = "auto";
317
+ container.style.boxSizing = "border-box";
318
+
319
+ let currentRenderResult: RenderResult | null = null;
320
+ let diagramRegions: DiagramRegionGestures | null = null;
321
+ let gestureMode: GestureMode = "native";
322
+ let drag: { x: number; y: number } | null = null;
323
+ let capturedPointerId: number | null = null;
324
+
325
+ const onWheel = (event: WheelEvent): void => {
326
+ event.preventDefault();
327
+ const rect = container.getBoundingClientRect();
328
+ const state = currentRenderResult?.readViewState() ?? EMPTY_VIEW_STATE;
329
+ const factor = Math.exp(-event.deltaY * 0.001);
330
+ const nextZoom = clampZoom(state.zoom * factor);
331
+ const cursorX = event.clientX - rect.left;
332
+ const cursorY = event.clientY - rect.top;
333
+ const next = zoomAtPoint(state, nextZoom, cursorX, cursorY);
334
+ currentRenderResult?.applyViewState(normalizeViewState(next));
335
+ };
336
+ const onPointerDown = (event: PointerEvent): void => {
337
+ drag = { x: event.clientX, y: event.clientY };
338
+ container.setPointerCapture(event.pointerId);
339
+ capturedPointerId = event.pointerId;
340
+ container.style.cursor = "grabbing";
341
+ };
342
+ const onPointerMove = (event: PointerEvent): void => {
343
+ if (drag === null) return;
344
+ const dx = event.clientX - drag.x;
345
+ const dy = event.clientY - drag.y;
346
+ drag = { x: event.clientX, y: event.clientY };
347
+ const state = normalizeViewState(currentRenderResult?.readViewState() ?? EMPTY_VIEW_STATE);
348
+ currentRenderResult?.applyViewState({
349
+ ...state,
350
+ panX: state.panX + dx,
351
+ panY: state.panY + dy,
352
+ });
353
+ };
354
+ const onPointerEnd = (event: PointerEvent): void => {
355
+ drag = null;
356
+ container.releasePointerCapture(event.pointerId);
357
+ capturedPointerId = null;
358
+ container.style.cursor = "auto";
359
+ };
360
+
361
+ // Frame-wide gesture listeners exist only while panzoom mode is active.
362
+ let gesturesInstalled = false;
363
+ const installGestureListeners = (): void => {
364
+ if (gesturesInstalled) return;
365
+ gesturesInstalled = true;
366
+ container.addEventListener("wheel", onWheel, { passive: false });
367
+ container.addEventListener("pointerdown", onPointerDown);
368
+ container.addEventListener("pointermove", onPointerMove);
369
+ container.addEventListener("pointerup", onPointerEnd);
370
+ container.addEventListener("pointercancel", onPointerEnd);
371
+ };
372
+ const removeGestureListeners = (): void => {
373
+ if (!gesturesInstalled) return;
374
+ gesturesInstalled = false;
375
+ container.removeEventListener("wheel", onWheel);
376
+ container.removeEventListener("pointerdown", onPointerDown);
377
+ container.removeEventListener("pointermove", onPointerMove);
378
+ container.removeEventListener("pointerup", onPointerEnd);
379
+ container.removeEventListener("pointercancel", onPointerEnd);
380
+ if (capturedPointerId !== null) {
381
+ container.releasePointerCapture(capturedPointerId);
382
+ capturedPointerId = null;
383
+ }
384
+ };
385
+
386
+ const setGestureMode = (mode: GestureMode): void => {
387
+ if (!isGestureMode(mode)) {
388
+ throw new FacetRenderError("gesture mode is invalid", "invalid_request");
389
+ }
390
+ gestureMode = mode;
391
+ if (mode === "panzoom") {
392
+ installGestureListeners();
393
+ } else {
394
+ removeGestureListeners();
395
+ drag = null;
396
+ container.style.cursor = "auto";
397
+ }
398
+ diagramRegions?.setEnabled(mode === "native");
399
+ // Suppress the artifact's own pointer interaction while dragging
400
+ // pans it — events fall through the (now pointer-events:none) root
401
+ // to the container, which is what pointerdown/move/up listen on.
402
+ // A raw SVG root (mermaid/svg/chart, the STANDALONE_DIAGRAM_TYPES
403
+ // that default straight into panzoom) is an SVGSVGElement, not an
404
+ // HTMLElement — an `instanceof HTMLElement` guard here silently
405
+ // skips exactly the default-panzoom path and only ever suppressed
406
+ // the html/tsx CSS-fallback root reached via the toggle button.
407
+ // `.style` duck-typing covers both without depending on SVGElement
408
+ // being a global in every runtime (frame bundle + unit-test shims).
409
+ setPointerEventsForAllChildren(container, mode === "panzoom" ? "none" : "");
410
+ };
411
+
412
+ const api: GalleryFrameApi = {
413
+ async render(payload: FrameRenderPayload): Promise<RenderResult> {
414
+ if (rendered) throw new FacetRenderError("frame already rendered", "invalid_request");
415
+ const validated = validatePayload(payload);
416
+ rendered = true;
417
+ try {
418
+ await dispatchRender(registry, { container, theme: validated.theme }, validated);
419
+ } catch (error) {
420
+ appendRenderError(container, error instanceof Error ? error.message : String(error));
421
+ throw error;
422
+ }
423
+
424
+ diagramRegions = installDiagramRegionGestures(container);
425
+
426
+ const svg = renderedSvg(container);
427
+ const viewMode = svg === null ? "css" : "native";
428
+ let viewState: FrameViewState = { ...EMPTY_VIEW_STATE };
429
+ const applyViewState = (state: FrameViewState): void => {
430
+ if (!isFrameViewState(state)) {
431
+ throw new FacetRenderError("view state is invalid", "invalid_request");
432
+ }
433
+ viewState = { zoom: state.zoom, panX: state.panX, panY: state.panY };
434
+ if (svg !== null) {
435
+ svg.svg.style.width = `${Math.ceil(svg.viewBox.width * state.zoom)}px`;
436
+ svg.svg.style.maxWidth = "none";
437
+ svg.svg.style.height = "auto";
438
+ } else {
439
+ // A viewBox-less SVG root (`renderedSvg` returned null, so this
440
+ // is the CSS-fallback branch) is still an SVGElement, not an
441
+ // HTMLElement — the same class of guard miss `setGestureMode`
442
+ // had for pointer-events, reachable here on the zoom path
443
+ // instead.
444
+ const root = asStylable(container.firstElementChild);
445
+ if (root !== null) {
446
+ root.style.transformOrigin = "top left";
447
+ root.style.transform = `scale(${state.zoom})`;
448
+ }
449
+ }
450
+ container.scrollLeft = Math.max(0, -state.panX);
451
+ container.scrollTop = Math.max(0, -state.panY);
452
+ };
453
+ const defaultGestureMode: GestureMode = STANDALONE_DIAGRAM_TYPES.has(validated.artifactType)
454
+ ? "panzoom"
455
+ : "native";
456
+ setGestureMode(defaultGestureMode);
457
+ currentRenderResult = {
458
+ observed: countPageShim(),
459
+ viewMode,
460
+ applyViewState,
461
+ readViewState: () => ({ ...viewState }),
462
+ defaultGestureMode,
463
+ gestureMode: () => gestureMode,
464
+ setGestureMode,
465
+ resetDiagramRegions: () => diagramRegions?.reset(),
466
+ };
467
+ return currentRenderResult;
468
+ },
469
+ };
470
+ Reflect.set(window, "__facetFrame", api);
471
+
472
+ document.addEventListener("pointerdown", (event) => {
473
+ const target = event.target;
474
+ if (target instanceof Element && target.closest(DIAGRAM_REGION_SELECTOR) !== null) return;
475
+ diagramRegions?.dismiss();
476
+ });
477
+
478
+ // Frame-side listener: this is the frame's own document, distinct
479
+ // from the shell's (app.ts keydown listener attaches to the parent
480
+ // document). Keyboard focus can legitimately sit in either realm, so
481
+ // both listeners stay; both route through `nextViewStateForKey` so
482
+ // the key-to-state mapping has one tested home instead of two.
483
+ document.addEventListener("keydown", (event) => {
484
+ const result = currentRenderResult;
485
+ if (!result) return;
486
+ if (event.key === "Escape" && diagramRegions?.dismiss()) {
487
+ event.preventDefault();
488
+ return;
489
+ }
490
+ const next = nextViewStateForKey(
491
+ result.readViewState(),
492
+ event.key,
493
+ event.shiftKey,
494
+ container.getBoundingClientRect(),
495
+ );
496
+ if (next === null) return;
497
+ event.preventDefault();
498
+ if (event.key === "0") result.setGestureMode(result.defaultGestureMode);
499
+ result.applyViewState(normalizeViewState(next));
500
+ });
501
+ }