@pixel-point/toolcraft 0.0.18 → 0.0.19

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 (384) hide show
  1. package/package.json +1 -1
  2. package/src/generate-demand-only-lifecycle.test.mjs +9 -1
  3. package/src/generate-performance-iteration-execution.test.mjs +3 -3
  4. package/src/generate-test-delivery-architecture-assertions.mjs +11 -73
  5. package/src/generate-test-demand-only-lifecycle.mjs +34 -36
  6. package/src/generate-test-dependency-sandbox.mjs +4 -1
  7. package/src/generate-test-doc-assertions.mjs +55 -53
  8. package/src/generate-test-doc-assertions.test.mjs +1 -1
  9. package/src/generate-test-doc-policy-assertions.mjs +1 -1
  10. package/src/generate-test-export-doc-assertions.mjs +53 -0
  11. package/src/generate-test-file-assertions.mjs +51 -8
  12. package/src/generate-test-integrity-assertions.mjs +5 -2
  13. package/src/generate-test-integrity-manifest-assertions.mjs +16 -5
  14. package/src/generate-test-integrity-ownership-assertions.mjs +36 -4
  15. package/src/generate-test-package-assertions.mjs +4 -0
  16. package/src/generate-test-product-fixture.mjs +1 -1
  17. package/src/generate-test-product-fixture.test.mjs +35 -38
  18. package/src/generate-test-product-iteration.mjs +0 -1
  19. package/src/generate-test-product-iteration.test.mjs +0 -1
  20. package/src/generate-test-runtime-boundary-assertions.mjs +20 -0
  21. package/src/generate-test-standalone-assertions.mjs +0 -16
  22. package/src/generate-test-standalone-delivery-stages.mjs +2 -86
  23. package/src/generate.test.mjs +4 -0
  24. package/src/generated-integrity-manifest.test.mjs +5 -2
  25. package/src/generated-source-policy.test.mjs +0 -4
  26. package/src/package-json.mjs +5 -0
  27. package/src/package-json.test.mjs +21 -0
  28. package/src/package-manager.mjs +2 -0
  29. package/src/package-manager.test.mjs +8 -0
  30. package/src/packaged-template-fallback-helpers.mjs +254 -0
  31. package/src/packaged-template-fallback.test.mjs +121 -46
  32. package/src/packaged-template-runtime-assertions.mjs +150 -0
  33. package/templates/runtime/contracts/component-contracts.choices.test.ts +18 -3
  34. package/templates/runtime/contracts/component-contracts.choices.ts +8 -3
  35. package/templates/runtime/contracts/component-contracts.inputs.ts +2 -1
  36. package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +18 -4
  37. package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +1 -1
  38. package/templates/runtime/contracts/component-contracts.runtime.ts +7 -4
  39. package/templates/runtime/contracts/component-contracts.visual.test.ts +20 -0
  40. package/templates/runtime/contracts/component-contracts.visual.ts +4 -0
  41. package/templates/runtime/contracts/decision-contracts.test.ts +18 -12
  42. package/templates/runtime/contracts/decision-contracts.ts +4 -4
  43. package/templates/runtime/contracts/performance-verification-policy.test.ts +12 -12
  44. package/templates/runtime/contracts/performance-verification-policy.ts +8 -8
  45. package/templates/runtime/export/artifact-download.ts +1 -0
  46. package/templates/runtime/export/artifact-frame-renderer.ts +2 -15
  47. package/templates/runtime/export/artifact-frame-state.ts +16 -0
  48. package/templates/runtime/export/artifact-scene-frame.test.ts +3 -3
  49. package/templates/runtime/export/artifact-scene-frame.ts +3 -3
  50. package/templates/runtime/export/export-error.ts +5 -0
  51. package/templates/runtime/export/export-renderer-coverage.ts +54 -0
  52. package/templates/runtime/export/image-artifact-export.ts +2 -2
  53. package/templates/runtime/export/index.ts +13 -0
  54. package/templates/runtime/export/product-export-renderer.test.ts +38 -4
  55. package/templates/runtime/export/product-export-renderer.ts +0 -41
  56. package/templates/runtime/export/product-svg-export-renderer.ts +21 -0
  57. package/templates/runtime/export/svg-artifact-export.test.ts +98 -0
  58. package/templates/runtime/export/svg-artifact-export.ts +120 -0
  59. package/templates/runtime/export/svg-document.test.ts +221 -0
  60. package/templates/runtime/export/svg-document.ts +400 -0
  61. package/templates/runtime/export/svg-policy.ts +47 -0
  62. package/templates/runtime/index.ts +1 -0
  63. package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +10 -0
  64. package/templates/runtime/react/app-shell/settings-transfer.test.ts +21 -0
  65. package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +46 -1
  66. package/templates/runtime/react/app-shell/toolcraft-app.tsx +7 -2
  67. package/templates/runtime/react/canvas/canvas-shell-interactions.test.tsx +30 -0
  68. package/templates/runtime/react/canvas/canvas-shell-pinch-interactions.test.tsx +155 -0
  69. package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +1 -1
  70. package/templates/runtime/react/canvas/canvas-shell.tsx +16 -5
  71. package/templates/runtime/react/canvas/canvas-viewport-geometry.ts +133 -0
  72. package/templates/runtime/react/canvas/use-canvas-viewport-interactions.ts +285 -36
  73. package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-applicability-layout.test.tsx +134 -0
  74. package/templates/runtime/react/controls-panel/__tests__/controls-panel.color-layout.test.tsx +16 -1
  75. package/templates/runtime/react/controls-panel/__tests__/controls-panel.render-isolation.test.tsx +2 -0
  76. package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +39 -0
  77. package/templates/runtime/react/controls-panel/__tests__/controls-panel.setup-controls.test.tsx +3 -3
  78. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +1 -4
  79. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +2 -2
  80. package/templates/runtime/react/controls-panel/__tests__/controls-panel.vector-interaction.test.tsx +6 -6
  81. package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +2 -2
  82. package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +40 -11
  83. package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +31 -2
  84. package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +4 -1
  85. package/templates/runtime/react/controls-panel/renderers/controls-panel-action-renderer.tsx +2 -1
  86. package/templates/runtime/react/controls-panel/renderers/controls-panel-basic-renderers.tsx +2 -52
  87. package/templates/runtime/react/controls-panel/values/controls-panel-color-values.ts +1 -9
  88. package/templates/runtime/react/controls-panel/values/controls-panel-spatial-values.ts +7 -2
  89. package/templates/runtime/react/orientation-gizmo/orientation-gizmo-math.ts +9 -69
  90. package/templates/runtime/schema/artifact-export-actions.test.ts +47 -0
  91. package/templates/runtime/schema/artifact-export-actions.ts +49 -0
  92. package/templates/runtime/schema/types.ts +1 -0
  93. package/templates/runtime/state/canvas-background-state.ts +1 -16
  94. package/templates/runtime/state/canvas-state.ts +32 -20
  95. package/templates/runtime/state/control-value-codecs.test.ts +98 -0
  96. package/templates/runtime/state/control-value-codecs.ts +700 -0
  97. package/templates/runtime/state/control-value-normalization.test.ts +1 -1
  98. package/templates/runtime/state/control-value-normalization.ts +127 -135
  99. package/templates/runtime/state/controls-reducer.ts +26 -3
  100. package/templates/runtime/state/create-template-state.test.ts +30 -0
  101. package/templates/runtime/state/create-template-state.ts +52 -31
  102. package/templates/runtime/state/orientation-pose.ts +80 -0
  103. package/templates/runtime/state/persistence-control-values.test.ts +59 -0
  104. package/templates/runtime/state/reducer-controls.test.ts +45 -0
  105. package/templates/runtime/state/reducer-media-reset.test.ts +1 -1
  106. package/templates/runtime/state/reducer-timeline.test.ts +70 -0
  107. package/templates/runtime/state/timeline-reducer.ts +40 -2
  108. package/templates/runtime/testing/performance-schema-queries.ts +2 -2
  109. package/templates/starter/AGENTS.md +21 -22
  110. package/templates/starter/docs/toolcraft/README.md +6 -6
  111. package/templates/starter/docs/toolcraft/acceptance-testing.md +20 -20
  112. package/templates/starter/docs/toolcraft/agent-worklog.md +88 -1
  113. package/templates/starter/docs/toolcraft/assembly-workflow.md +7 -6
  114. package/templates/starter/docs/toolcraft/component-rules.md +2 -2
  115. package/templates/starter/docs/toolcraft/core/layout.md +6 -1
  116. package/templates/starter/docs/toolcraft/core/media-upload.md +1 -1
  117. package/templates/starter/docs/toolcraft/core/performance.md +8 -10
  118. package/templates/starter/docs/toolcraft/core/reference-study.md +80 -19
  119. package/templates/starter/docs/toolcraft/core/runtime-boundary.md +2 -1
  120. package/templates/starter/docs/toolcraft/core/setup-export.md +43 -13
  121. package/templates/starter/docs/toolcraft/decision-contract.md +2 -2
  122. package/templates/starter/docs/toolcraft/performance.md +8 -10
  123. package/templates/starter/docs/toolcraft/renderer-technique.md +2 -2
  124. package/templates/starter/docs/toolcraft/schema-reference.md +8 -8
  125. package/templates/starter/docs/toolcraft/workflow.md +14 -14
  126. package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +38 -0
  127. package/templates/starter/e2e/app-browser-motion-reference-requirements.spec.ts +411 -0
  128. package/templates/starter/e2e/app-browser-proof-session.spec.ts +85 -0
  129. package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +62 -0
  130. package/templates/starter/e2e/app-browser-selection-scope-evidence.spec.ts +137 -0
  131. package/templates/starter/e2e/browser-infinity-canvas-svg-evidence.ts +42 -0
  132. package/templates/starter/e2e/browser-layer-evidence-helpers.ts +15 -36
  133. package/templates/starter/e2e/browser-product-raster-snapshot.ts +213 -0
  134. package/templates/starter/e2e/browser-proof-session.ts +43 -15
  135. package/templates/starter/e2e/browser-proof-surface-action.ts +79 -0
  136. package/templates/starter/e2e/browser-proof-types.ts +15 -0
  137. package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +22 -6
  138. package/templates/starter/e2e/browser-selection-scope-evidence.ts +269 -0
  139. package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +97 -0
  140. package/templates/starter/e2e/browser-svg-export-evidence.spec.ts +79 -0
  141. package/templates/starter/e2e/browser-svg-export-evidence.ts +152 -0
  142. package/templates/starter/e2e/export-artifact-helpers.ts +70 -0
  143. package/templates/starter/e2e/product-observable-helpers.ts +8 -129
  144. package/templates/starter/e2e/svg-artifact-inspection.spec.ts +136 -0
  145. package/templates/starter/e2e/svg-artifact-inspection.ts +331 -0
  146. package/templates/starter/e2e/svg-download-test-fixtures.ts +34 -0
  147. package/templates/starter/e2e/toolcraft-delivery-catalog-reporter.ts +198 -15
  148. package/templates/starter/e2e/toolcraft-feature-verification-reporter.spec.ts +149 -0
  149. package/templates/starter/e2e/toolcraft-feature-verification-reporter.ts +223 -0
  150. package/templates/starter/package.json +2 -0
  151. package/templates/starter/scripts/check-toolcraft-docs.mjs +1 -0
  152. package/templates/starter/scripts/create-toolcraft-motion-reference-study.mjs +233 -0
  153. package/templates/starter/scripts/create-toolcraft-motion-reference-study.test.mjs +459 -0
  154. package/templates/starter/scripts/playwright-test-title-selection.d.mts +45 -0
  155. package/templates/starter/scripts/run-browser-performance-test-helpers.mjs +0 -27
  156. package/templates/starter/scripts/run-browser-performance.mjs +0 -6
  157. package/templates/starter/scripts/run-browser-performance.test.mjs +5 -13
  158. package/templates/starter/scripts/run-delivery-verification-test-helpers.mjs +0 -34
  159. package/templates/starter/scripts/run-delivery-verification.test.mjs +135 -31
  160. package/templates/starter/scripts/run-feature-verification.mjs +212 -0
  161. package/templates/starter/scripts/run-feature-verification.test.mjs +226 -0
  162. package/templates/starter/scripts/toolcraft-checkpoint-delivery-state.mjs +21 -8
  163. package/templates/starter/scripts/toolcraft-checkpoint-delivery-state.test.mjs +32 -92
  164. package/templates/starter/scripts/toolcraft-checkpoint-transaction.mjs +28 -6
  165. package/templates/starter/scripts/toolcraft-checkpoint-transaction.test.mjs +8 -63
  166. package/templates/starter/scripts/toolcraft-delivery-anchor.mjs +28 -8
  167. package/templates/starter/scripts/toolcraft-delivery-anchor.test.mjs +50 -61
  168. package/templates/starter/scripts/toolcraft-delivery-architecture-inventory.mjs +0 -4
  169. package/templates/starter/scripts/toolcraft-delivery-architecture-policy.mjs +0 -1
  170. package/templates/starter/scripts/toolcraft-delivery-evidence.test.mjs +2 -3
  171. package/templates/starter/scripts/toolcraft-delivery-executor.mjs +5 -1
  172. package/templates/starter/scripts/toolcraft-delivery-executor.test.mjs +5 -12
  173. package/templates/starter/scripts/toolcraft-delivery-functional-context.d.mts +1 -48
  174. package/templates/starter/scripts/toolcraft-delivery-functional-context.mjs +2 -30
  175. package/templates/starter/scripts/toolcraft-delivery-functional-context.test.mjs +4 -166
  176. package/templates/starter/scripts/toolcraft-delivery-functional-initial.test.mjs +4 -20
  177. package/templates/starter/scripts/toolcraft-delivery-lifecycle.mjs +43 -192
  178. package/templates/starter/scripts/toolcraft-delivery-performance-iteration-lifecycle.test.mjs +79 -440
  179. package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +102 -297
  180. package/templates/starter/scripts/toolcraft-delivery-plan-construction.mjs +46 -97
  181. package/templates/starter/scripts/toolcraft-delivery-plan-test-helpers.mjs +125 -0
  182. package/templates/starter/scripts/toolcraft-delivery-plan-validation.test.mjs +58 -0
  183. package/templates/starter/scripts/toolcraft-delivery-plan.d.mts +19 -31
  184. package/templates/starter/scripts/toolcraft-delivery-plan.mjs +254 -252
  185. package/templates/starter/scripts/toolcraft-delivery-plan.test.mjs +29 -457
  186. package/templates/starter/scripts/toolcraft-delivery-receipt-performance.test.mjs +1 -1
  187. package/templates/starter/scripts/toolcraft-delivery-receipt-strictness.test.mjs +3 -72
  188. package/templates/starter/scripts/toolcraft-delivery-receipt-test-helpers.mjs +7 -36
  189. package/templates/starter/scripts/toolcraft-delivery-receipt.d.mts +3 -5
  190. package/templates/starter/scripts/toolcraft-delivery-receipt.mjs +15 -31
  191. package/templates/starter/scripts/toolcraft-delivery-receipt.test.mjs +28 -20
  192. package/templates/starter/scripts/toolcraft-delivery-receipt.types.test.mts +22 -30
  193. package/templates/starter/scripts/toolcraft-feature-verification-plan.d.mts +20 -0
  194. package/templates/starter/scripts/toolcraft-feature-verification-plan.mjs +146 -0
  195. package/templates/starter/scripts/toolcraft-feature-verification-plan.test.mjs +96 -0
  196. package/templates/starter/scripts/toolcraft-functional-proof-model.d.mts +3 -18
  197. package/templates/starter/scripts/toolcraft-functional-proof-model.mjs +39 -127
  198. package/templates/starter/scripts/toolcraft-functional-proof-model.test.mjs +25 -362
  199. package/templates/starter/scripts/toolcraft-functional-proof-model.types.test.mts +6 -83
  200. package/templates/starter/scripts/toolcraft-functional-proof-primitives.d.mts +11 -0
  201. package/templates/starter/scripts/toolcraft-functional-proof-primitives.mjs +61 -0
  202. package/templates/starter/scripts/toolcraft-functional-proof-primitives.test.mjs +43 -0
  203. package/templates/starter/scripts/toolcraft-integrity-policy.mjs +13 -0
  204. package/templates/starter/scripts/toolcraft-integrity-test-utils.mjs +1 -1
  205. package/templates/starter/scripts/toolcraft-layer-evidence-facade.test.mjs +58 -22
  206. package/templates/starter/scripts/toolcraft-motion-reference-analysis-protocol.mjs +111 -0
  207. package/templates/starter/scripts/toolcraft-motion-reference-analysis-protocol.test.mjs +116 -0
  208. package/templates/starter/scripts/toolcraft-motion-reference-artifacts.d.mts +37 -0
  209. package/templates/starter/scripts/toolcraft-motion-reference-artifacts.mjs +308 -0
  210. package/templates/starter/scripts/toolcraft-motion-reference-artifacts.test.mjs +420 -0
  211. package/templates/starter/scripts/toolcraft-motion-reference-artifacts.types.test.mts +50 -0
  212. package/templates/starter/scripts/toolcraft-motion-reference-command-options.mjs +195 -0
  213. package/templates/starter/scripts/toolcraft-motion-reference-command-options.test.mjs +224 -0
  214. package/templates/starter/scripts/toolcraft-motion-reference-contact-sheet.mjs +122 -0
  215. package/templates/starter/scripts/toolcraft-motion-reference-contact-sheet.test.mjs +153 -0
  216. package/templates/starter/scripts/toolcraft-motion-reference-evidence-assembly.mjs +270 -0
  217. package/templates/starter/scripts/toolcraft-motion-reference-evidence.test.mjs +432 -0
  218. package/templates/starter/scripts/toolcraft-motion-reference-ffmpeg-output.mjs +275 -0
  219. package/templates/starter/scripts/toolcraft-motion-reference-ffmpeg-output.test.mjs +294 -0
  220. package/templates/starter/scripts/toolcraft-motion-reference-group-inventory.mjs +148 -0
  221. package/templates/starter/scripts/toolcraft-motion-reference-image-filter-graph.mjs +101 -0
  222. package/templates/starter/scripts/toolcraft-motion-reference-image-filter-graph.test.mjs +124 -0
  223. package/templates/starter/scripts/toolcraft-motion-reference-image-source.mjs +259 -0
  224. package/templates/starter/scripts/toolcraft-motion-reference-image-source.test.mjs +338 -0
  225. package/templates/starter/scripts/toolcraft-motion-reference-image-timing.mjs +96 -0
  226. package/templates/starter/scripts/toolcraft-motion-reference-image-timing.test.mjs +81 -0
  227. package/templates/starter/scripts/toolcraft-motion-reference-lock-recovery.test.mjs +144 -0
  228. package/templates/starter/scripts/toolcraft-motion-reference-media-source.mjs +165 -0
  229. package/templates/starter/scripts/toolcraft-motion-reference-media-source.test.mjs +299 -0
  230. package/templates/starter/scripts/toolcraft-motion-reference-publication-discovery.test.mjs +142 -0
  231. package/templates/starter/scripts/toolcraft-motion-reference-publication-isolation.test.mjs +234 -0
  232. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-schema.mjs +122 -0
  233. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-store.mjs +89 -0
  234. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal-store.test.mjs +74 -0
  235. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal.mjs +264 -0
  236. package/templates/starter/scripts/toolcraft-motion-reference-publication-journal.test.mjs +377 -0
  237. package/templates/starter/scripts/toolcraft-motion-reference-publication-lease.mjs +254 -0
  238. package/templates/starter/scripts/toolcraft-motion-reference-publication-lease.test.mjs +130 -0
  239. package/templates/starter/scripts/toolcraft-motion-reference-publication-staging.mjs +128 -0
  240. package/templates/starter/scripts/toolcraft-motion-reference-publication-staging.test.mjs +209 -0
  241. package/templates/starter/scripts/toolcraft-motion-reference-publication.fixtures.mjs +88 -0
  242. package/templates/starter/scripts/toolcraft-motion-reference-publication.mjs +249 -0
  243. package/templates/starter/scripts/toolcraft-motion-reference-publication.test.mjs +433 -0
  244. package/templates/starter/scripts/toolcraft-motion-reference-review-output.mjs +38 -0
  245. package/templates/starter/scripts/toolcraft-motion-reference-review-output.test.mjs +29 -0
  246. package/templates/starter/scripts/toolcraft-motion-reference-root-safety.test.mjs +84 -0
  247. package/templates/starter/scripts/toolcraft-motion-reference-roots.mjs +83 -0
  248. package/templates/starter/scripts/toolcraft-motion-reference-sampling.mjs +283 -0
  249. package/templates/starter/scripts/toolcraft-motion-reference-sampling.test.mjs +480 -0
  250. package/templates/starter/scripts/toolcraft-motion-reference-stage-registry.mjs +39 -0
  251. package/templates/starter/scripts/toolcraft-motion-reference-stage-registry.test.mjs +59 -0
  252. package/templates/starter/scripts/toolcraft-motion-reference-stage.mjs +71 -0
  253. package/templates/starter/scripts/toolcraft-motion-reference-stage.test.mjs +61 -0
  254. package/templates/starter/scripts/toolcraft-motion-review-numeric.mjs +74 -0
  255. package/templates/starter/scripts/toolcraft-motion-review-numeric.test.mjs +65 -0
  256. package/templates/starter/scripts/toolcraft-motion-review-partitioning.mjs +268 -0
  257. package/templates/starter/scripts/toolcraft-motion-review-partitioning.test.mjs +329 -0
  258. package/templates/starter/scripts/toolcraft-motion-review-protected-units.mjs +120 -0
  259. package/templates/starter/scripts/toolcraft-motion-review-protected-units.test.mjs +111 -0
  260. package/templates/starter/scripts/toolcraft-performance-escalation-policy.test.mjs +77 -345
  261. package/templates/starter/scripts/toolcraft-performance-receipt-validation.test.mjs +19 -21
  262. package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +1 -1
  263. package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +10 -1
  264. package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +5 -1
  265. package/templates/starter/scripts/toolcraft-product-observable-facade.test.mjs +46 -1
  266. package/templates/starter/scripts/toolcraft-receipt-boundary.test.mjs +0 -1
  267. package/templates/starter/scripts/toolcraft-source-inventory.d.mts +5 -0
  268. package/templates/starter/scripts/toolcraft-source-inventory.mjs +25 -14
  269. package/templates/starter/scripts/toolcraft-source-inventory.test.mjs +48 -0
  270. package/templates/starter/scripts/toolcraft-source-ownership.mjs +0 -1
  271. package/templates/starter/scripts/toolcraft-source-ownership.test.mjs +30 -5
  272. package/templates/starter/scripts/toolcraft-verification-receipt-test-helpers.mjs +5 -26
  273. package/templates/starter/scripts/toolcraft-verification-receipt.d.mts +0 -7
  274. package/templates/starter/scripts/toolcraft-verification-receipt.mjs +3 -47
  275. package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +6 -87
  276. package/templates/starter/scripts/toolcraft-vitest-runtime-contract.d.mts +19 -0
  277. package/templates/starter/scripts/toolcraft-vitest-runtime-contract.mjs +107 -0
  278. package/templates/starter/scripts/toolcraft-vitest-runtime-contract.test.mjs +114 -0
  279. package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.mjs +13 -2
  280. package/templates/starter/scripts/toolcraft-vitest-runtime-evidence-reporter.test.mjs +110 -0
  281. package/templates/starter/src/app/acceptance/artifact-export-intent.ts +51 -20
  282. package/templates/starter/src/app/acceptance/canvas-coverage.ts +1 -0
  283. package/templates/starter/src/app/acceptance/color-bank-labels.ts +11 -15
  284. package/templates/starter/src/app/acceptance/color-row-grouping.ts +33 -0
  285. package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +0 -11
  286. package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -3
  287. package/templates/starter/src/app/acceptance/control-component-rules.ts +0 -10
  288. package/templates/starter/src/app/acceptance/control-labels.ts +30 -1
  289. package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +18 -0
  290. package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +25 -17
  291. package/templates/starter/src/app/acceptance/feature-verification-selection.test.ts +295 -0
  292. package/templates/starter/src/app/acceptance/feature-verification-selection.ts +161 -0
  293. package/templates/starter/src/app/acceptance/infinity-canvas.ts +10 -0
  294. package/templates/starter/src/app/acceptance/interaction-ownership-intent.ts +19 -1
  295. package/templates/starter/src/app/acceptance/interaction-ownership.ts +18 -2
  296. package/templates/starter/src/app/acceptance/motion-reference-evidence-validation.mjs +65 -0
  297. package/templates/starter/src/app/acceptance/motion-reference-evidence-windows.mjs +301 -0
  298. package/templates/starter/src/app/acceptance/motion-reference-evidence.d.mts +11 -0
  299. package/templates/starter/src/app/acceptance/motion-reference-evidence.mjs +145 -0
  300. package/templates/starter/src/app/acceptance/motion-reference-study-behaviors.ts +162 -0
  301. package/templates/starter/src/app/acceptance/motion-reference-study-coverage.ts +206 -0
  302. package/templates/starter/src/app/acceptance/motion-reference-study-identity.ts +129 -0
  303. package/templates/starter/src/app/acceptance/motion-reference-study-partitions.ts +186 -0
  304. package/templates/starter/src/app/acceptance/motion-reference-study.ts +90 -0
  305. package/templates/starter/src/app/acceptance/output-export-actions.test.ts +9 -1
  306. package/templates/starter/src/app/acceptance/output-export-actions.ts +14 -22
  307. package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +5 -1
  308. package/templates/starter/src/app/acceptance/output-export-model.ts +4 -0
  309. package/templates/starter/src/app/acceptance/output-export.ts +4 -1
  310. package/templates/starter/src/app/acceptance/reference-study-types.ts +167 -17
  311. package/templates/starter/src/app/acceptance/runtime-coverage.ts +6 -1
  312. package/templates/starter/src/app/acceptance/selection-scope.test.ts +200 -0
  313. package/templates/starter/src/app/acceptance/selection-scope.ts +151 -0
  314. package/templates/starter/src/app/acceptance/transfer-mode-types.ts +114 -0
  315. package/templates/starter/src/app/acceptance/types.ts +43 -118
  316. package/templates/starter/src/app/acceptance/validate-coverage.ts +14 -3
  317. package/templates/starter/src/app/starter-acceptance-data.ts +1 -0
  318. package/templates/starter/src/app/starter-acceptance.animation-intent.test.ts +3 -0
  319. package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +81 -8
  320. package/templates/starter/src/app/starter-acceptance.background-export.test.ts +2 -0
  321. package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +2 -0
  322. package/templates/starter/src/app/starter-acceptance.color-row-grouping.test.ts +118 -0
  323. package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +1 -1
  324. package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +1 -0
  325. package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +1 -0
  326. package/templates/starter/src/app/starter-acceptance.control-label-context.test.ts +1 -0
  327. package/templates/starter/src/app/starter-acceptance.control-labels.test.ts +78 -3
  328. package/templates/starter/src/app/starter-acceptance.control-naming-rules.test.ts +77 -1
  329. package/templates/starter/src/app/starter-acceptance.custom-control-fit-check.test.ts +1 -1
  330. package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +74 -3
  331. package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +5 -1
  332. package/templates/starter/src/app/starter-acceptance.image-export-settings.test.ts +2 -0
  333. package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +19 -3
  334. package/templates/starter/src/app/starter-acceptance.layers.test.ts +2 -1
  335. package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
  336. package/templates/starter/src/app/starter-acceptance.motion-reference-evidence.test.ts +17 -0
  337. package/templates/starter/src/app/starter-acceptance.motion-reference-identity.test.ts +163 -0
  338. package/templates/starter/src/app/starter-acceptance.motion-reference-mapping.test.ts +133 -0
  339. package/templates/starter/src/app/starter-acceptance.motion-reference-ownership.test.ts +121 -0
  340. package/templates/starter/src/app/starter-acceptance.motion-reference-partitions.test.ts +210 -0
  341. package/templates/starter/src/app/starter-acceptance.motion-reference-test-utils.ts +313 -0
  342. package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +78 -4
  343. package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +6 -1
  344. package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +18 -1
  345. package/templates/starter/src/app/starter-acceptance.product-worklog.test.ts +123 -2
  346. package/templates/starter/src/app/starter-acceptance.reference-custom-timeline.test.ts +2 -0
  347. package/templates/starter/src/app/starter-acceptance.reference-foundation.test.ts +8 -0
  348. package/templates/starter/src/app/starter-acceptance.reference-timeline.test.ts +7 -0
  349. package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +2 -0
  350. package/templates/starter/src/app/starter-acceptance.timeline-playback.test.ts +5 -0
  351. package/templates/starter/src/app/starter-acceptance.ts +34 -8
  352. package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +2 -0
  353. package/templates/starter/src/app/starter-acceptance.video-reference-study.test.ts +34 -236
  354. package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +4 -0
  355. package/templates/starter/src/app/starter-acceptance.worklog-test-utils.ts +122 -6
  356. package/templates/starter/src/app/starter-acceptance.worklog.test.ts +152 -16
  357. package/templates/starter/src/app/starter-performance.gates.test.ts +20 -18
  358. package/templates/starter/src/app/starter-performance.lifecycle.test.ts +22 -6
  359. package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +3 -0
  360. package/templates/starter/tsconfig.json +1 -0
  361. package/templates/starter/scripts/toolcraft-browser-impact-selection.mjs +0 -26
  362. package/templates/starter/scripts/toolcraft-delivery-framework-refresh.test.mjs +0 -141
  363. package/templates/starter/scripts/toolcraft-delivery-functional-changed.test.mjs +0 -468
  364. package/templates/starter/scripts/toolcraft-delivery-functional-model-lifecycle.test.mjs +0 -254
  365. package/templates/starter/scripts/toolcraft-delivery-functional-semantic-regression.test.mjs +0 -135
  366. package/templates/starter/scripts/toolcraft-delivery-performance-iteration-durability.test.mjs +0 -329
  367. package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +0 -430
  368. package/templates/starter/scripts/toolcraft-delivery-plan-functional-model.test.mjs +0 -358
  369. package/templates/starter/scripts/toolcraft-functional-proof-regression-fixtures.mjs +0 -298
  370. package/templates/starter/scripts/toolcraft-verification-impact-inventory.d.mts +0 -62
  371. package/templates/starter/scripts/toolcraft-verification-impact-inventory.mjs +0 -344
  372. package/templates/starter/scripts/toolcraft-verification-impact-inventory.test.mjs +0 -204
  373. package/templates/starter/scripts/toolcraft-verification-impact-performance-candidates.test.mjs +0 -210
  374. package/templates/starter/scripts/toolcraft-verification-impact-resolution.mjs +0 -344
  375. package/templates/starter/scripts/toolcraft-verification-impact-resolution.test.mjs +0 -270
  376. package/templates/starter/scripts/toolcraft-verification-impact-semantic-resolution.test.mjs +0 -449
  377. package/templates/starter/scripts/toolcraft-verification-impact-support-resolution.test.mjs +0 -195
  378. package/templates/starter/scripts/toolcraft-verification-impact.d.mts +0 -132
  379. package/templates/starter/scripts/toolcraft-verification-impact.mjs +0 -169
  380. package/templates/starter/scripts/toolcraft-verification-impact.test.mjs +0 -300
  381. package/templates/starter/scripts/toolcraft-verification-input-roles.mjs +0 -197
  382. package/templates/starter/scripts/toolcraft-verification-input-roles.test.mjs +0 -225
  383. package/templates/starter/src/app/acceptance/video-reference-study.ts +0 -217
  384. package/templates/starter/src/app/starter-verification-impact.json +0 -15
@@ -0,0 +1,221 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { ToolcraftArtifactExportError } from "./export-error";
4
+ import {
5
+ createToolcraftSvgDocument,
6
+ createToolcraftSvgProductContainer,
7
+ serializeToolcraftSvgDocument,
8
+ } from "./svg-document";
9
+ import { TOOLCRAFT_SVG_NAMESPACE } from "./svg-policy";
10
+
11
+ const frame = Object.freeze({ height: 80, width: 120, x: -10, y: 5 });
12
+
13
+ function createSvgElement(name: string): Readonly<{
14
+ container: SVGGElement;
15
+ element: Element;
16
+ }> {
17
+ const { container } = createToolcraftSvgProductContainer();
18
+ const element = container.ownerDocument.createElementNS(
19
+ TOOLCRAFT_SVG_NAMESPACE,
20
+ name,
21
+ );
22
+ container.append(element);
23
+ return { container, element };
24
+ }
25
+
26
+ describe("Toolcraft SVG document", () => {
27
+ it("serializes valued attributes as strict standalone XML", () => {
28
+ const { document } = createToolcraftSvgDocument(frame);
29
+ const { container } = createToolcraftSvgProductContainer();
30
+ const rect = container.ownerDocument.createElementNS(
31
+ TOOLCRAFT_SVG_NAMESPACE,
32
+ "rect",
33
+ );
34
+ rect.setAttribute("data-column-track", "true");
35
+ rect.setAttribute("height", "40");
36
+ rect.setAttribute("width", "20");
37
+ container.append(rect);
38
+
39
+ const artifact = serializeToolcraftSvgDocument({
40
+ backgroundColor: "#123456",
41
+ container,
42
+ document,
43
+ frame,
44
+ });
45
+ const parsed = new DOMParser().parseFromString(
46
+ artifact.source,
47
+ "image/svg+xml",
48
+ );
49
+
50
+ expect(artifact.source).toContain('data-column-track="true"');
51
+ expect(parsed.querySelector("parsererror")).toBeNull();
52
+ expect(parsed.documentElement.namespaceURI).toBe(TOOLCRAFT_SVG_NAMESPACE);
53
+ expect(parsed.documentElement.getAttribute("viewBox")).toBe("-10 5 120 80");
54
+ expect(
55
+ parsed.documentElement.children[0]?.getAttribute(
56
+ "data-toolcraft-background",
57
+ ),
58
+ ).toBe("true");
59
+ expect(
60
+ parsed.documentElement.children[1]?.getAttribute(
61
+ "data-toolcraft-product-scene",
62
+ ),
63
+ ).toBe("true");
64
+ expect(artifact.vectorElementCount).toBe(1);
65
+ });
66
+
67
+ it.each([
68
+ "animate",
69
+ "discard",
70
+ "foreignObject",
71
+ "image",
72
+ "script",
73
+ "style",
74
+ ])("rejects forbidden <%s> content", (elementName) => {
75
+ const { container } = createSvgElement(elementName);
76
+
77
+ expect(() =>
78
+ serializeToolcraftSvgDocument({
79
+ backgroundColor: null,
80
+ container,
81
+ document: createToolcraftSvgDocument(frame).document,
82
+ frame,
83
+ }),
84
+ ).toThrow(ToolcraftArtifactExportError);
85
+ });
86
+
87
+ it.each([
88
+ ["onclick", "alert(1)", "event attribute"],
89
+ ["href", "https://example.com/shape.svg#icon", "external href"],
90
+ ["fill", "url(https://example.com/fill.svg#paint)", "external url()"],
91
+ ])("rejects unsafe %s attributes", (name, value, message) => {
92
+ const { container } = createToolcraftSvgProductContainer();
93
+ const use = container.ownerDocument.createElementNS(
94
+ TOOLCRAFT_SVG_NAMESPACE,
95
+ "use",
96
+ );
97
+ use.setAttribute(name, value);
98
+ container.append(use);
99
+
100
+ expect(() =>
101
+ serializeToolcraftSvgDocument({
102
+ backgroundColor: null,
103
+ container,
104
+ document: createToolcraftSvgDocument(frame).document,
105
+ frame,
106
+ }),
107
+ ).toThrow(message);
108
+ });
109
+
110
+ it("rejects documents without a visible product vector", () => {
111
+ const { container } = createToolcraftSvgProductContainer();
112
+ const defs = container.ownerDocument.createElementNS(
113
+ TOOLCRAFT_SVG_NAMESPACE,
114
+ "defs",
115
+ );
116
+ const path = container.ownerDocument.createElementNS(
117
+ TOOLCRAFT_SVG_NAMESPACE,
118
+ "path",
119
+ );
120
+ defs.append(path);
121
+ container.append(defs);
122
+
123
+ expect(() =>
124
+ serializeToolcraftSvgDocument({
125
+ backgroundColor: null,
126
+ container,
127
+ document: createToolcraftSvgDocument(frame).document,
128
+ frame,
129
+ }),
130
+ ).toThrow("visible vector primitive");
131
+ });
132
+
133
+ it("rejects product attempts to mutate runtime-owned markers", () => {
134
+ const { container, element } = createSvgElement("rect");
135
+ element.setAttribute("data-toolcraft-background", "true");
136
+
137
+ expect(() =>
138
+ serializeToolcraftSvgDocument({
139
+ backgroundColor: null,
140
+ container,
141
+ document: createToolcraftSvgDocument(frame).document,
142
+ frame,
143
+ }),
144
+ ).toThrow("runtime-owned marker attributes");
145
+
146
+ element.removeAttribute("data-toolcraft-background");
147
+ container.removeAttribute("data-toolcraft-product-scene");
148
+ expect(() =>
149
+ serializeToolcraftSvgDocument({
150
+ backgroundColor: null,
151
+ container,
152
+ document: createToolcraftSvgDocument(frame).document,
153
+ frame,
154
+ }),
155
+ ).toThrow("runtime-owned SVG group");
156
+ });
157
+
158
+ it("requires unique ids and resolved local references", () => {
159
+ const { container } = createToolcraftSvgProductContainer();
160
+ const defs = container.ownerDocument.createElementNS(
161
+ TOOLCRAFT_SVG_NAMESPACE,
162
+ "defs",
163
+ );
164
+ const shape = container.ownerDocument.createElementNS(
165
+ TOOLCRAFT_SVG_NAMESPACE,
166
+ "path",
167
+ );
168
+ shape.setAttribute("id", "shape");
169
+ defs.append(shape);
170
+ const use = container.ownerDocument.createElementNS(
171
+ TOOLCRAFT_SVG_NAMESPACE,
172
+ "use",
173
+ );
174
+ use.setAttribute("href", "#missing");
175
+ container.append(defs, use);
176
+
177
+ expect(() =>
178
+ serializeToolcraftSvgDocument({
179
+ backgroundColor: null,
180
+ container,
181
+ document: createToolcraftSvgDocument(frame).document,
182
+ frame,
183
+ }),
184
+ ).toThrow("#missing has no matching element");
185
+
186
+ use.setAttribute("href", "#shape");
187
+ const duplicate = container.ownerDocument.createElementNS(
188
+ TOOLCRAFT_SVG_NAMESPACE,
189
+ "rect",
190
+ );
191
+ duplicate.setAttribute("id", "shape");
192
+ container.append(duplicate);
193
+ expect(() =>
194
+ serializeToolcraftSvgDocument({
195
+ backgroundColor: null,
196
+ container,
197
+ document: createToolcraftSvgDocument(frame).document,
198
+ frame,
199
+ }),
200
+ ).toThrow("non-empty and unique");
201
+ });
202
+
203
+ it("rejects non-content XML nodes", () => {
204
+ const { container } = createSvgElement("rect");
205
+ container.append(
206
+ container.ownerDocument.createProcessingInstruction(
207
+ "xml-stylesheet",
208
+ 'href="https://example.com/styles.css"',
209
+ ),
210
+ );
211
+
212
+ expect(() =>
213
+ serializeToolcraftSvgDocument({
214
+ backgroundColor: null,
215
+ container,
216
+ document: createToolcraftSvgDocument(frame).document,
217
+ frame,
218
+ }),
219
+ ).toThrow("only elements and text nodes");
220
+ });
221
+ });
@@ -0,0 +1,400 @@
1
+ import type { ToolcraftExportFrame } from "./export-frame";
2
+ import { ToolcraftArtifactExportError } from "./export-error";
3
+ import {
4
+ TOOLCRAFT_FORBIDDEN_SVG_ELEMENTS,
5
+ TOOLCRAFT_SVG_BACKGROUND_ATTRIBUTE,
6
+ TOOLCRAFT_SVG_NAMESPACE,
7
+ TOOLCRAFT_SVG_NON_RENDERING_CONTAINERS,
8
+ TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE,
9
+ TOOLCRAFT_SVG_VECTOR_PRIMITIVES,
10
+ } from "./svg-policy";
11
+
12
+ const forbiddenElementNames = new Set<string>(
13
+ TOOLCRAFT_FORBIDDEN_SVG_ELEMENTS,
14
+ );
15
+ const vectorPrimitiveNames = new Set<string>(
16
+ TOOLCRAFT_SVG_VECTOR_PRIMITIVES,
17
+ );
18
+ const nonRenderingContainerNames = new Set<string>(
19
+ TOOLCRAFT_SVG_NON_RENDERING_CONTAINERS,
20
+ );
21
+
22
+ type ToolcraftSvgDocument = Readonly<{ document: XMLDocument }>;
23
+ type ToolcraftSvgProductContainer = Readonly<{ container: SVGGElement }>;
24
+
25
+ export type ToolcraftSerializedSvgDocument = Readonly<{
26
+ source: string;
27
+ vectorElementCount: number;
28
+ }>;
29
+
30
+ function getDocumentImplementation(): DOMImplementation {
31
+ if (typeof document === "undefined") {
32
+ throw new ToolcraftArtifactExportError({
33
+ code: "svg-document-invalid",
34
+ message: "SVG export requires browser XML document support.",
35
+ });
36
+ }
37
+ return document.implementation;
38
+ }
39
+
40
+ function createSvgXmlDocument(rootName: "g" | "svg"): XMLDocument {
41
+ return getDocumentImplementation().createDocument(
42
+ TOOLCRAFT_SVG_NAMESPACE,
43
+ rootName,
44
+ null,
45
+ );
46
+ }
47
+
48
+ function setFrameAttributes(
49
+ root: SVGSVGElement,
50
+ frame: ToolcraftExportFrame,
51
+ ): void {
52
+ root.setAttribute("width", String(frame.width));
53
+ root.setAttribute("height", String(frame.height));
54
+ root.setAttribute(
55
+ "viewBox",
56
+ `${frame.x} ${frame.y} ${frame.width} ${frame.height}`,
57
+ );
58
+ }
59
+
60
+ export function createToolcraftSvgDocument(
61
+ frame: ToolcraftExportFrame,
62
+ ): ToolcraftSvgDocument {
63
+ const svgDocument = createSvgXmlDocument("svg");
64
+ const root = svgDocument.querySelector("svg");
65
+ if (!root) {
66
+ throw new ToolcraftArtifactExportError({
67
+ code: "svg-document-invalid",
68
+ message: "Runtime could not create the SVG document root.",
69
+ });
70
+ }
71
+ setFrameAttributes(root, frame);
72
+ return Object.freeze({ document: svgDocument });
73
+ }
74
+
75
+ export function createToolcraftSvgProductContainer(): ToolcraftSvgProductContainer {
76
+ const productDocument = createSvgXmlDocument("g");
77
+ const container = productDocument.querySelector("g");
78
+ if (!container) {
79
+ throw new ToolcraftArtifactExportError({
80
+ code: "svg-document-invalid",
81
+ message: "Runtime could not create the SVG product container.",
82
+ });
83
+ }
84
+ container.setAttribute(TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE, "true");
85
+ return Object.freeze({ container });
86
+ }
87
+
88
+ function hasNonRenderingAncestor(
89
+ element: Element,
90
+ container: Element,
91
+ ): boolean {
92
+ let current = element.parentElement;
93
+ while (current && !current.isSameNode(container)) {
94
+ if (nonRenderingContainerNames.has(current.localName.toLowerCase())) {
95
+ return true;
96
+ }
97
+ current = current.parentElement;
98
+ }
99
+ return false;
100
+ }
101
+
102
+ function validateUrlReferences(
103
+ attribute: Attr,
104
+ referencedIds?: Set<string>,
105
+ ): void {
106
+ const attributeName = attribute.localName.toLowerCase();
107
+ const value = attribute.value.trim();
108
+ if (attributeName === "href" || attributeName === "src") {
109
+ if (!value.startsWith("#")) {
110
+ throw new ToolcraftArtifactExportError({
111
+ code: "svg-content-invalid",
112
+ message: `SVG export cannot contain external ${attribute.name} references.`,
113
+ });
114
+ }
115
+ const referencedId = value.slice(1).trim();
116
+ if (!referencedId) {
117
+ throw new ToolcraftArtifactExportError({
118
+ code: "svg-content-invalid",
119
+ message: `SVG export ${attribute.name} references must name a local element.`,
120
+ });
121
+ }
122
+ referencedIds?.add(referencedId);
123
+ }
124
+
125
+ const urlPattern = /url\(\s*(['"]?)(.*?)\1\s*\)/giu;
126
+ for (const match of value.matchAll(urlPattern)) {
127
+ const reference = match[2]?.trim() ?? "";
128
+ if (!reference.startsWith("#")) {
129
+ throw new ToolcraftArtifactExportError({
130
+ code: "svg-content-invalid",
131
+ message: `SVG export cannot contain external url() references in ${attribute.name}.`,
132
+ });
133
+ }
134
+ const referencedId = reference.slice(1).trim();
135
+ if (!referencedId) {
136
+ throw new ToolcraftArtifactExportError({
137
+ code: "svg-content-invalid",
138
+ message: `SVG export url() in ${attribute.name} must name a local element.`,
139
+ });
140
+ }
141
+ referencedIds?.add(referencedId);
142
+ }
143
+ }
144
+
145
+ function validateProductContainer(container: Element): number {
146
+ if (
147
+ container.namespaceURI !== TOOLCRAFT_SVG_NAMESPACE ||
148
+ container.localName !== "g" ||
149
+ container.getAttribute(TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE) !== "true"
150
+ ) {
151
+ throw new ToolcraftArtifactExportError({
152
+ code: "svg-content-invalid",
153
+ message: "SVG product content must use the runtime-owned SVG group.",
154
+ });
155
+ }
156
+
157
+ let vectorElementCount = 0;
158
+ const definedIds = new Set<string>();
159
+ const referencedIds = new Set<string>();
160
+ const elements = [container, ...container.querySelectorAll("*")];
161
+ for (const element of elements) {
162
+ for (const node of element.childNodes) {
163
+ if (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE) {
164
+ throw new ToolcraftArtifactExportError({
165
+ code: "svg-content-invalid",
166
+ message: "SVG export content may contain only elements and text nodes.",
167
+ });
168
+ }
169
+ }
170
+ if (element.namespaceURI !== TOOLCRAFT_SVG_NAMESPACE) {
171
+ throw new ToolcraftArtifactExportError({
172
+ code: "svg-content-invalid",
173
+ message: "SVG export content must use the SVG namespace.",
174
+ });
175
+ }
176
+
177
+ const elementName = element.localName.toLowerCase();
178
+ if (
179
+ (element !== container &&
180
+ element.hasAttribute(TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE)) ||
181
+ element.hasAttribute(TOOLCRAFT_SVG_BACKGROUND_ATTRIBUTE)
182
+ ) {
183
+ throw new ToolcraftArtifactExportError({
184
+ code: "svg-content-invalid",
185
+ message: "SVG product content cannot use runtime-owned marker attributes.",
186
+ });
187
+ }
188
+ if (forbiddenElementNames.has(elementName)) {
189
+ throw new ToolcraftArtifactExportError({
190
+ code: "svg-content-invalid",
191
+ message: `SVG export cannot contain <${element.localName}> content.`,
192
+ });
193
+ }
194
+ if (
195
+ vectorPrimitiveNames.has(elementName) &&
196
+ !hasNonRenderingAncestor(element, container)
197
+ ) {
198
+ vectorElementCount += 1;
199
+ }
200
+
201
+ for (const attribute of element.attributes) {
202
+ if (attribute.localName.toLowerCase() === "id") {
203
+ const id = attribute.value.trim();
204
+ if (!id || definedIds.has(id)) {
205
+ throw new ToolcraftArtifactExportError({
206
+ code: "svg-content-invalid",
207
+ message: "SVG export element ids must be non-empty and unique.",
208
+ });
209
+ }
210
+ definedIds.add(id);
211
+ }
212
+ if (attribute.localName.toLowerCase().startsWith("on")) {
213
+ throw new ToolcraftArtifactExportError({
214
+ code: "svg-content-invalid",
215
+ message: `SVG export cannot contain event attribute ${attribute.name}.`,
216
+ });
217
+ }
218
+ validateUrlReferences(attribute, referencedIds);
219
+ }
220
+ }
221
+
222
+ for (const referencedId of referencedIds) {
223
+ if (!definedIds.has(referencedId)) {
224
+ throw new ToolcraftArtifactExportError({
225
+ code: "svg-content-invalid",
226
+ message: `SVG export local reference #${referencedId} has no matching element.`,
227
+ });
228
+ }
229
+ }
230
+
231
+ if (vectorElementCount === 0) {
232
+ throw new ToolcraftArtifactExportError({
233
+ code: "svg-content-empty",
234
+ message: "SVG export must contain at least one visible vector primitive.",
235
+ });
236
+ }
237
+ return vectorElementCount;
238
+ }
239
+
240
+ function createBackground(
241
+ svgDocument: XMLDocument,
242
+ frame: ToolcraftExportFrame,
243
+ color: string,
244
+ ): SVGRectElement {
245
+ if (!color.trim()) {
246
+ throw new ToolcraftArtifactExportError({
247
+ code: "svg-content-invalid",
248
+ message: "SVG export background color must not be blank.",
249
+ });
250
+ }
251
+ const background = svgDocument.createElementNS(
252
+ TOOLCRAFT_SVG_NAMESPACE,
253
+ "rect",
254
+ );
255
+ background.setAttribute(TOOLCRAFT_SVG_BACKGROUND_ATTRIBUTE, "true");
256
+ background.setAttribute("fill", color);
257
+ background.setAttribute("x", String(frame.x));
258
+ background.setAttribute("y", String(frame.y));
259
+ background.setAttribute("width", String(frame.width));
260
+ background.setAttribute("height", String(frame.height));
261
+ const fill = background.getAttributeNode("fill");
262
+ if (fill) validateUrlReferences(fill);
263
+ return background;
264
+ }
265
+
266
+ function requireStrictSvgSource(
267
+ source: string,
268
+ frame: ToolcraftExportFrame,
269
+ backgroundColor: string | null,
270
+ expectedVectorElementCount: number,
271
+ ): void {
272
+ const parsed = new DOMParser().parseFromString(source, "image/svg+xml");
273
+ if (parsed.getElementsByTagName("parsererror").length > 0) {
274
+ throw new ToolcraftArtifactExportError({
275
+ code: "svg-serialization-failed",
276
+ message: "Serialized SVG export is not valid XML.",
277
+ });
278
+ }
279
+ const root = parsed.documentElement;
280
+ if (
281
+ root.localName !== "svg" ||
282
+ root.namespaceURI !== TOOLCRAFT_SVG_NAMESPACE ||
283
+ root.getAttribute("width") !== String(frame.width) ||
284
+ root.getAttribute("height") !== String(frame.height) ||
285
+ root.getAttribute("viewBox") !==
286
+ `${frame.x} ${frame.y} ${frame.width} ${frame.height}`
287
+ ) {
288
+ throw new ToolcraftArtifactExportError({
289
+ code: "svg-serialization-failed",
290
+ message: "Serialized SVG export lost its runtime-owned frame metadata.",
291
+ });
292
+ }
293
+
294
+ const productContainers = root.querySelectorAll(
295
+ `[${TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE}]`,
296
+ );
297
+ const backgrounds = root.querySelectorAll(
298
+ `[${TOOLCRAFT_SVG_BACKGROUND_ATTRIBUTE}]`,
299
+ );
300
+ const productContainer = productContainers[0];
301
+ const expectedChildCount = backgroundColor === null ? 1 : 2;
302
+ if (
303
+ productContainers.length !== 1 ||
304
+ !productContainer ||
305
+ productContainer.parentElement !== root ||
306
+ root.children.length !== expectedChildCount ||
307
+ root.children[expectedChildCount - 1] !== productContainer ||
308
+ validateProductContainer(productContainer) !== expectedVectorElementCount
309
+ ) {
310
+ throw new ToolcraftArtifactExportError({
311
+ code: "svg-serialization-failed",
312
+ message: "Serialized SVG export lost its runtime-owned product structure.",
313
+ });
314
+ }
315
+
316
+ if (backgroundColor === null) {
317
+ if (backgrounds.length > 0) {
318
+ throw new ToolcraftArtifactExportError({
319
+ code: "svg-serialization-failed",
320
+ message: "Serialized SVG export gained an unexpected background.",
321
+ });
322
+ }
323
+ return;
324
+ }
325
+
326
+ const background = backgrounds[0];
327
+ if (
328
+ backgrounds.length !== 1 ||
329
+ !background ||
330
+ background.localName !== "rect" ||
331
+ background.parentElement !== root ||
332
+ root.children[0] !== background ||
333
+ background.getAttribute("fill") !== backgroundColor ||
334
+ background.getAttribute("x") !== String(frame.x) ||
335
+ background.getAttribute("y") !== String(frame.y) ||
336
+ background.getAttribute("width") !== String(frame.width) ||
337
+ background.getAttribute("height") !== String(frame.height)
338
+ ) {
339
+ throw new ToolcraftArtifactExportError({
340
+ code: "svg-serialization-failed",
341
+ message: "Serialized SVG export lost its runtime-owned background.",
342
+ });
343
+ }
344
+ }
345
+
346
+ export function serializeToolcraftSvgDocument({
347
+ backgroundColor,
348
+ container,
349
+ document: svgDocument,
350
+ frame,
351
+ }: Readonly<{
352
+ backgroundColor: string | null;
353
+ container: SVGGElement;
354
+ document: XMLDocument;
355
+ frame: ToolcraftExportFrame;
356
+ }>): ToolcraftSerializedSvgDocument {
357
+ try {
358
+ const vectorElementCount = validateProductContainer(container);
359
+ const root = svgDocument.querySelector("svg");
360
+ if (!root) {
361
+ throw new ToolcraftArtifactExportError({
362
+ code: "svg-document-invalid",
363
+ message: "Runtime SVG document is missing its root.",
364
+ });
365
+ }
366
+ if (root.childNodes.length > 0) {
367
+ throw new ToolcraftArtifactExportError({
368
+ code: "svg-document-invalid",
369
+ message: "Runtime SVG document must be assembled exactly once.",
370
+ });
371
+ }
372
+
373
+ const productContent = svgDocument.importNode(container, true);
374
+ const nodes: Node[] =
375
+ backgroundColor === null
376
+ ? [productContent]
377
+ : [createBackground(svgDocument, frame, backgroundColor), productContent];
378
+ root.append(...nodes);
379
+
380
+ const source = `<?xml version="1.0" encoding="UTF-8"?>\n${new XMLSerializer().serializeToString(svgDocument)}`;
381
+ requireStrictSvgSource(
382
+ source,
383
+ frame,
384
+ backgroundColor,
385
+ vectorElementCount,
386
+ );
387
+ return Object.freeze({ source, vectorElementCount });
388
+ } catch (error) {
389
+ if (error instanceof ToolcraftArtifactExportError) {
390
+ throw error;
391
+ }
392
+ throw new ToolcraftArtifactExportError(
393
+ {
394
+ code: "svg-serialization-failed",
395
+ message: "Toolcraft could not serialize the SVG export.",
396
+ },
397
+ { cause: error },
398
+ );
399
+ }
400
+ }
@@ -0,0 +1,47 @@
1
+ export const TOOLCRAFT_SVG_NAMESPACE = "http://www.w3.org/2000/svg" as const;
2
+ export const TOOLCRAFT_SVG_BACKGROUND_ATTRIBUTE =
3
+ "data-toolcraft-background" as const;
4
+ export const TOOLCRAFT_SVG_PRODUCT_SCENE_ATTRIBUTE =
5
+ "data-toolcraft-product-scene" as const;
6
+
7
+ export const TOOLCRAFT_FORBIDDEN_SVG_ELEMENTS = Object.freeze([
8
+ "animate",
9
+ "animatecolor",
10
+ "animatemotion",
11
+ "animatetransform",
12
+ "audio",
13
+ "canvas",
14
+ "discard",
15
+ "embed",
16
+ "feimage",
17
+ "foreignobject",
18
+ "handler",
19
+ "iframe",
20
+ "image",
21
+ "object",
22
+ "script",
23
+ "set",
24
+ "style",
25
+ "video",
26
+ ] as const);
27
+
28
+ export const TOOLCRAFT_SVG_VECTOR_PRIMITIVES = Object.freeze([
29
+ "circle",
30
+ "ellipse",
31
+ "line",
32
+ "path",
33
+ "polygon",
34
+ "polyline",
35
+ "rect",
36
+ "text",
37
+ "use",
38
+ ] as const);
39
+
40
+ export const TOOLCRAFT_SVG_NON_RENDERING_CONTAINERS = Object.freeze([
41
+ "clippath",
42
+ "defs",
43
+ "marker",
44
+ "mask",
45
+ "pattern",
46
+ "symbol",
47
+ ] as const);
@@ -15,6 +15,7 @@ export * from "./schema/define-toolcraft";
15
15
  export { TOOLCRAFT_CANVAS_RENDER_SCALE } from "./schema/canvas-render-scale";
16
16
  export * from "./schema/collection-item-controls";
17
17
  export * from "./schema/control-applicability";
18
+ export * from "./schema/artifact-export-actions";
18
19
  export * from "./schema/keyframe-capability";
19
20
  export * from "./schema/runtime-targets";
20
21
  export * from "./schema/types";
@@ -40,6 +40,14 @@ describe("Toolcraft template runtime public API", () => {
40
40
  expect(ToolcraftRuntime).not.toHaveProperty(
41
41
  "toolcraftControlsResetHistorySource",
42
42
  );
43
+ expect(ToolcraftRuntime.TOOLCRAFT_SVG_NAMESPACE).toBe(
44
+ "http://www.w3.org/2000/svg",
45
+ );
46
+ expect(ToolcraftRuntime).not.toHaveProperty("exportToolcraftSvgArtifact");
47
+ expect(ToolcraftRuntime).not.toHaveProperty("createToolcraftSvgDocument");
48
+ expect(ToolcraftRuntime).not.toHaveProperty(
49
+ "serializeToolcraftSvgDocument",
50
+ );
43
51
  expect(ToolcraftReact.useToolcraftDispatch).toBe(useToolcraftDispatch);
44
52
  expect(ToolcraftReact.useToolcraftSelector).toBe(useToolcraftSelector);
45
53
  expect(ToolcraftReact.useToolcraftPipeline).toBe(useToolcraftPipeline);
@@ -83,6 +91,7 @@ describe("Toolcraft template runtime public API", () => {
83
91
  | "rendererPipelineRegistration"
84
92
  | "sceneBoundsProvider"
85
93
  | "schema"
94
+ | "svgExportRenderer"
86
95
  >();
87
96
  expectTypeOf<
88
97
  keyof React.ComponentProps<typeof ToolcraftApp>
@@ -99,6 +108,7 @@ describe("Toolcraft template runtime public API", () => {
99
108
  | "sceneBoundsProvider"
100
109
  | "schema"
101
110
  | "style"
111
+ | "svgExportRenderer"
102
112
  >();
103
113
  expectTypeOf<
104
114
  keyof React.ComponentProps<typeof ToolcraftRoot>