@pixel-point/toolcraft 0.0.17 → 0.0.18

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 (196) hide show
  1. package/package.json +1 -1
  2. package/src/generate-test-doc-assertions.mjs +58 -68
  3. package/src/generate-test-doc-assertions.test.mjs +43 -0
  4. package/src/generate-test-doc-policy-assertions.mjs +92 -0
  5. package/src/generate-test-file-assertions.mjs +26 -75
  6. package/src/generate-test-product-iteration.test.mjs +17 -0
  7. package/src/generate-test-runtime-boundary-assertions.mjs +158 -0
  8. package/src/generate-test-standalone-assertions.mjs +10 -0
  9. package/templates/runtime/contracts/component-contracts.choices.test.ts +47 -3
  10. package/templates/runtime/contracts/component-contracts.choices.ts +48 -3
  11. package/templates/runtime/contracts/component-contracts.inputs.test.ts +6 -6
  12. package/templates/runtime/contracts/component-contracts.inputs.ts +6 -6
  13. package/templates/runtime/contracts/component-contracts.media-custom.test.ts +8 -2
  14. package/templates/runtime/contracts/component-contracts.media-custom.ts +8 -2
  15. package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +4 -4
  16. package/templates/runtime/contracts/component-contracts.runtime-labels.test.ts +7 -1
  17. package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +3 -0
  18. package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +3 -0
  19. package/templates/runtime/contracts/component-contracts.runtime.ts +12 -8
  20. package/templates/runtime/contracts/component-contracts.test.ts +2 -0
  21. package/templates/runtime/contracts/decision-contracts.test.ts +18 -0
  22. package/templates/runtime/contracts/decision-contracts.ts +10 -10
  23. package/templates/runtime/export/export-background.ts +0 -19
  24. package/templates/runtime/export/export.test.ts +45 -42
  25. package/templates/runtime/export/index.ts +44 -16
  26. package/templates/runtime/index.ts +2 -0
  27. package/templates/runtime/model-import/model-import-limits.ts +15 -4
  28. package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +11 -1
  29. package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +29 -0
  30. package/templates/runtime/react/app-shell/toolcraft-app.tsx +18 -8
  31. package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +76 -0
  32. package/templates/runtime/react/canvas/canvas-shell-test-utils.tsx +13 -1
  33. package/templates/runtime/react/canvas/canvas-shell.tsx +16 -1
  34. package/templates/runtime/react/canvas/product-scene-surface.test.tsx +195 -0
  35. package/templates/runtime/react/canvas/product-scene-surface.tsx +155 -0
  36. package/templates/runtime/react/canvas/runtime-scene-export.ts +1 -1
  37. package/templates/runtime/react/controls-panel/__tests__/control-conditions.test.ts +61 -0
  38. package/templates/runtime/react/controls-panel/__tests__/controls-panel.collection-actions.test.tsx +89 -0
  39. package/templates/runtime/react/controls-panel/__tests__/controls-panel.control-visibility.test.tsx +40 -19
  40. package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +1 -1
  41. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +62 -0
  42. package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +67 -0
  43. package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +2 -4
  44. package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +10 -4
  45. package/templates/runtime/react/controls-panel/conditions/control-conditions.ts +32 -119
  46. package/templates/runtime/react/controls-panel/layout/controls-panel-control-group.tsx +5 -2
  47. package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +10 -8
  48. package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +3 -3
  49. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.test.tsx +63 -0
  50. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.tsx +138 -0
  51. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-items.tsx +104 -0
  52. package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-renderer.tsx +115 -236
  53. package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.test.tsx +95 -0
  54. package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.tsx +82 -0
  55. package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.test.tsx +73 -1
  56. package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.tsx +50 -1
  57. package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.test.ts +1 -1
  58. package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.ts +2 -1
  59. package/templates/runtime/react/controls-panel/values/controls-panel-collection-values.ts +38 -6
  60. package/templates/runtime/react/controls-panel/values/controls-panel-value-labels.ts +1 -0
  61. package/templates/runtime/react/index.ts +4 -0
  62. package/templates/runtime/react/model-rendering/model-export.ts +1 -1
  63. package/templates/runtime/react/orientation-gizmo/orientation-gizmo-selection.ts +5 -5
  64. package/templates/runtime/react/orientation-gizmo/use-toolcraft-orientation-control-selection.ts +2 -2
  65. package/templates/runtime/scene/scene-bounds.test.ts +23 -0
  66. package/templates/runtime/scene/scene-bounds.ts +31 -17
  67. package/templates/runtime/schema/app-capabilities.test.ts +4 -0
  68. package/templates/runtime/schema/collection-item-controls.ts +29 -0
  69. package/templates/runtime/schema/control-applicability.test.ts +127 -0
  70. package/templates/runtime/schema/control-applicability.ts +433 -0
  71. package/templates/runtime/schema/control-schema-normalization.ts +177 -11
  72. package/templates/runtime/schema/controls-panel-actions.ts +2 -2
  73. package/templates/runtime/schema/controls-panel-section-layout.ts +14 -9
  74. package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +59 -0
  75. package/templates/runtime/schema/define-toolcraft.collection-actions-item-controls.test.ts +127 -0
  76. package/templates/runtime/schema/define-toolcraft.control-sections.test.ts +10 -4
  77. package/templates/runtime/schema/define-toolcraft.file-drop-item-controls.test.ts +98 -0
  78. package/templates/runtime/schema/define-toolcraft.file-drop-normalization.test.ts +27 -0
  79. package/templates/runtime/schema/define-toolcraft.segmented-control-fit.test.ts +20 -2
  80. package/templates/runtime/schema/define-toolcraft.setup-canvas.test.ts +16 -3
  81. package/templates/runtime/schema/define-toolcraft.source-collection.test.ts +79 -0
  82. package/templates/runtime/schema/runtime-setup-section.ts +24 -5
  83. package/templates/runtime/schema/types.ts +68 -13
  84. package/templates/starter/AGENTS.md +12 -10
  85. package/templates/starter/docs/toolcraft/acceptance-testing.md +17 -31
  86. package/templates/starter/docs/toolcraft/agent-worklog.md +15 -0
  87. package/templates/starter/docs/toolcraft/assembly-workflow.md +10 -3
  88. package/templates/starter/docs/toolcraft/component-rules.md +8 -6
  89. package/templates/starter/docs/toolcraft/core/control-selection.md +9 -3
  90. package/templates/starter/docs/toolcraft/core/layout.md +7 -7
  91. package/templates/starter/docs/toolcraft/core/media-upload.md +2 -0
  92. package/templates/starter/docs/toolcraft/core/runtime-boundary.md +13 -2
  93. package/templates/starter/docs/toolcraft/core/setup-export.md +33 -7
  94. package/templates/starter/docs/toolcraft/custom-controls.md +6 -4
  95. package/templates/starter/docs/toolcraft/decision-contract.md +3 -1
  96. package/templates/starter/docs/toolcraft/renderer-technique.md +4 -0
  97. package/templates/starter/docs/toolcraft/schema-reference.md +21 -19
  98. package/templates/starter/e2e/app-browser-control-applicability-requirements.spec.ts +163 -0
  99. package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +7 -2
  100. package/templates/starter/e2e/app-browser-orientation-evidence.spec.ts +174 -21
  101. package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +1 -33
  102. package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +61 -15
  103. package/templates/starter/e2e/{browser-conditional-output-evidence-helpers.ts → browser-background-output-evidence.ts} +0 -68
  104. package/templates/starter/e2e/browser-control-applicability-evidence.ts +114 -0
  105. package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +20 -11
  106. package/templates/starter/e2e/browser-orientation-gizmo-evidence-helpers.ts +4 -0
  107. package/templates/starter/e2e/browser-orientation-gizmo-live-preconditions.ts +86 -0
  108. package/templates/starter/e2e/browser-runtime-applicability-requirements.ts +84 -0
  109. package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +9 -2
  110. package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +29 -26
  111. package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +25 -0
  112. package/templates/starter/scripts/toolcraft-contract-manifest.json +1 -0
  113. package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +85 -115
  114. package/templates/starter/scripts/toolcraft-product-boundary-module-policy.mjs +101 -0
  115. package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +0 -72
  116. package/templates/starter/scripts/toolcraft-product-control-bindings.mjs +182 -0
  117. package/templates/starter/scripts/toolcraft-product-control-boundary.mjs +146 -0
  118. package/templates/starter/scripts/toolcraft-product-control-boundary.test.mjs +186 -0
  119. package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +6 -17
  120. package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +196 -0
  121. package/templates/starter/scripts/toolcraft-typescript-source-evidence-structure.test.mjs +20 -0
  122. package/templates/starter/scripts/toolcraft-typescript-source-evidence.mjs +1 -0
  123. package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +2 -0
  124. package/templates/starter/src/app/acceptance/artifact-export-intent.ts +130 -0
  125. package/templates/starter/src/app/acceptance/conditions.ts +1 -1
  126. package/templates/starter/src/app/acceptance/control-acceptance-context.ts +2 -2
  127. package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +6 -20
  128. package/templates/starter/src/app/acceptance/control-acceptance-kind-rules.ts +24 -5
  129. package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -13
  130. package/templates/starter/src/app/acceptance/control-applicability-cases.test.ts +321 -0
  131. package/templates/starter/src/app/acceptance/control-applicability-cases.ts +290 -0
  132. package/templates/starter/src/app/acceptance/control-applicability.ts +332 -0
  133. package/templates/starter/src/app/acceptance/control-layout-dependency-rules.ts +20 -9
  134. package/templates/starter/src/app/acceptance/control-layout-entity-rules.ts +4 -6
  135. package/templates/starter/src/app/acceptance/control-layout-model.ts +4 -4
  136. package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +3 -10
  137. package/templates/starter/src/app/acceptance/control-parts.ts +8 -0
  138. package/templates/starter/src/app/acceptance/control-section-entity-cohesion.test.ts +215 -0
  139. package/templates/starter/src/app/acceptance/control-section-entity-cohesion.ts +132 -0
  140. package/templates/starter/src/app/acceptance/control-section-inventory.test.ts +87 -0
  141. package/templates/starter/src/app/acceptance/control-section-inventory.ts +74 -44
  142. package/templates/starter/src/app/acceptance/custom-controls.ts +11 -3
  143. package/templates/starter/src/app/acceptance/inline-layout.ts +64 -9
  144. package/templates/starter/src/app/acceptance/media-upload.ts +5 -1
  145. package/templates/starter/src/app/acceptance/orientation-gizmo.ts +18 -11
  146. package/templates/starter/src/app/acceptance/output-background-rules.ts +2 -2
  147. package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +18 -10
  148. package/templates/starter/src/app/acceptance/output-export-model.ts +54 -11
  149. package/templates/starter/src/app/acceptance/output-export.ts +22 -10
  150. package/templates/starter/src/app/acceptance/output-video-export-rules.ts +99 -0
  151. package/templates/starter/src/app/acceptance/types.ts +23 -8
  152. package/templates/starter/src/app/acceptance/validate-coverage.ts +19 -2
  153. package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +239 -0
  154. package/templates/starter/src/app/starter-acceptance.background-export.test.ts +33 -0
  155. package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +16 -1
  156. package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +65 -0
  157. package/templates/starter/src/app/starter-acceptance.contract-fixtures.test.ts +41 -0
  158. package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +112 -16
  159. package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +224 -0
  160. package/templates/starter/src/app/starter-acceptance.control-order.test.ts +58 -3
  161. package/templates/starter/src/app/starter-acceptance.control-state.test.ts +0 -111
  162. package/templates/starter/src/app/starter-acceptance.custom-control-collections.test.ts +3 -1
  163. package/templates/starter/src/app/starter-acceptance.custom-control-valid.test.ts +1 -1
  164. package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +22 -0
  165. package/templates/starter/src/app/{starter-acceptance.image-video-export-settings.test.ts → starter-acceptance.image-export-settings.test.ts} +37 -9
  166. package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +12 -0
  167. package/templates/starter/src/app/starter-acceptance.media-upload.fixtures.ts +11 -1
  168. package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
  169. package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +384 -0
  170. package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +4 -0
  171. package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +112 -105
  172. package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +12 -0
  173. package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +1 -1
  174. package/templates/starter/src/app/starter-acceptance.section-dependencies.test.ts +35 -24
  175. package/templates/starter/src/app/starter-acceptance.section-size.test.ts +72 -0
  176. package/templates/starter/src/app/starter-acceptance.ts +11 -2
  177. package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +336 -0
  178. package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +8 -0
  179. package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +2 -2
  180. package/templates/starter/src/routes/index.tsx +1 -0
  181. package/templates/ui/components/composites/tabs.tsx +2 -2
  182. package/templates/ui/components/controls/collection-actions/collection-item-groups.test.tsx +38 -0
  183. package/templates/ui/components/controls/collection-actions/collection-item-groups.tsx +36 -0
  184. package/templates/ui/components/controls/collection-actions/index.ts +2 -0
  185. package/templates/ui/components/controls/file-drop/file-drop-collection-control.tsx +137 -0
  186. package/templates/ui/components/controls/file-drop/file-drop-control.test.tsx +189 -0
  187. package/templates/ui/components/controls/file-drop/file-drop-control.tsx +81 -88
  188. package/templates/ui/components/controls/file-drop/file-drop-presentation.tsx +131 -9
  189. package/templates/ui/components/controls/file-drop/file-drop-types.ts +36 -22
  190. package/templates/ui/components/controls/file-drop/index.ts +1 -0
  191. package/templates/ui/components/controls/index.ts +3 -0
  192. package/templates/ui/components/controls/range-slider/range-slider-control.tsx +1 -1
  193. package/templates/ui/components/controls/range-slider/range-slider-value.test.ts +14 -0
  194. package/templates/ui/components/controls/range-slider/range-slider-value.ts +3 -2
  195. package/templates/starter/src/app/acceptance/condition-exclusivity.test.ts +0 -60
  196. package/templates/starter/src/app/acceptance/condition-exclusivity.ts +0 -221
@@ -0,0 +1,196 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { evaluateToolcraftProductBoundary } from "./toolcraft-product-boundary.mjs";
5
+ import { createToolcraftProductBoundaryFixture as createFixture } from "./toolcraft-product-boundary-test-fixtures.mjs";
6
+
7
+ test("keeps artifact encoding and download mechanics runtime-owned", async (context) => {
8
+ const workspaceRuntimeExportSpecifier = [
9
+ "@repo",
10
+ "toolcraft-runtime",
11
+ "export",
12
+ "image-artifact-export",
13
+ ].join("/");
14
+ const rootDir = await createFixture(context, {
15
+ "src/features/export-mechanics.ts": `
16
+ import { createToolcraftVideoEncoderBackend as createAliasBackend } from "@/toolcraft/runtime/export/video-encoding-backend";
17
+ import { exportToolcraftImageArtifact } from "${workspaceRuntimeExportSpecifier}";
18
+ import { downloadToolcraftArtifact } from "../toolcraft/runtime/export/artifact-download";
19
+ import { Output } from "mediabunny";
20
+ export function exportArtifact(canvas: HTMLCanvasElement) {
21
+ const recorder = new MediaRecorder(canvas.captureStream());
22
+ const encoder = new VideoEncoder({ error() {}, output() {} });
23
+ canvas.toBlob(() => undefined);
24
+ canvas.toDataURL();
25
+ URL.createObjectURL(new Blob());
26
+ void createAliasBackend;
27
+ void exportToolcraftImageArtifact;
28
+ void downloadToolcraftArtifact;
29
+ void Output;
30
+ void recorder;
31
+ void encoder;
32
+ }
33
+ `,
34
+ "src/features/safe-renderer.ts": `
35
+ import * as Runtime from "@/toolcraft/runtime";
36
+ import {
37
+ getToolcraftImageExportSize,
38
+ shouldIncludeToolcraftPreviewBackground,
39
+ validateToolcraftArtifactSize,
40
+ } from "@/toolcraft/runtime";
41
+ import type {
42
+ ToolcraftExportFrame,
43
+ ToolcraftProductExportRenderer,
44
+ } from "@/toolcraft/runtime";
45
+ export const renderFrame = ({ context }) => context.fillRect(0, 0, 1, 1);
46
+ void Runtime;
47
+ void getToolcraftImageExportSize;
48
+ void shouldIncludeToolcraftPreviewBackground;
49
+ void validateToolcraftArtifactSize;
50
+ void (null as unknown as ToolcraftExportFrame);
51
+ void (null as unknown as ToolcraftProductExportRenderer);
52
+ `,
53
+ "src/features/shadowed-globals.ts": `
54
+ export function localNames(MediaRecorder, VideoEncoder, URL) {
55
+ void new MediaRecorder();
56
+ void new VideoEncoder();
57
+ URL.createObjectURL();
58
+ }
59
+ `,
60
+ });
61
+
62
+ const result = await evaluateToolcraftProductBoundary({ rootDir });
63
+
64
+ assert.equal(result.violations.length, 10);
65
+ assert.equal(
66
+ result.violations.every(
67
+ (violation) => violation.kind === "runtime-export-ownership",
68
+ ),
69
+ true,
70
+ );
71
+ assert.equal(
72
+ result.violations.some((violation) =>
73
+ violation.repoPath.endsWith("shadowed-globals.ts"),
74
+ ),
75
+ false,
76
+ );
77
+ assert.equal(
78
+ result.violations.some((violation) =>
79
+ violation.repoPath.endsWith("safe-renderer.ts"),
80
+ ),
81
+ false,
82
+ );
83
+ });
84
+
85
+ test("classifies every runtime export mechanics module reference once", async (context) => {
86
+ const workspaceRuntimeExportRoot = [
87
+ "@repo",
88
+ "toolcraft-runtime",
89
+ "export",
90
+ ].join("/");
91
+ const rootDir = await createFixture(context, {
92
+ "src/features/dynamic-import.ts": `
93
+ export async function loadMedia() {
94
+ return import("mediabunny");
95
+ }
96
+ `,
97
+ "src/features/import-equals.ts": `
98
+ import Backend = require("@/toolcraft/runtime/export/video-encoding-backend");
99
+ void Backend;
100
+ `,
101
+ "src/features/import-type.ts": `
102
+ type Backend = import("@/toolcraft/runtime/export/video-encoding-backend").ToolcraftVideoEncoderBackend;
103
+ void (null as unknown as Backend);
104
+ `,
105
+ "src/features/regular-export.ts": `
106
+ export {
107
+ createToolcraftVideoEncoderBackend as createFirstBackend,
108
+ createToolcraftVideoEncoderBackend as createSecondBackend,
109
+ } from "@/toolcraft/runtime/export/video-encoding-backend";
110
+ `,
111
+ "src/features/regular-import.ts": `
112
+ import {
113
+ createToolcraftVideoEncoderBackend as createFirstBackend,
114
+ createToolcraftVideoEncoderBackend as createSecondBackend,
115
+ } from "@/toolcraft/runtime/export/video-encoding-backend";
116
+ void createFirstBackend;
117
+ void createSecondBackend;
118
+ `,
119
+ "src/features/require.ts": `
120
+ export const backend = require("${workspaceRuntimeExportRoot}/video-encoding-backend");
121
+ `,
122
+ "src/features/safe-root-type-import.ts": `
123
+ import type { ToolcraftExportFrame } from "@/toolcraft/runtime";
124
+ void (null as unknown as ToolcraftExportFrame);
125
+ `,
126
+ "src/features/side-effect-import.ts": `import "mediabunny";`,
127
+ "src/features/type-only-export.ts": `
128
+ export type {
129
+ ToolcraftImageArtifactExportRequest,
130
+ ToolcraftImageArtifactExportResult,
131
+ } from "${workspaceRuntimeExportRoot}/image-artifact-export";
132
+ `,
133
+ "src/features/type-only-import-equals.ts": `
134
+ import type Backend = require("@/toolcraft/runtime/export/video-encoding-backend");
135
+ void (null as unknown as Backend);
136
+ `,
137
+ "src/features/type-only-import.ts": `
138
+ import type {
139
+ ToolcraftVideoArtifactExportRequest,
140
+ ToolcraftVideoArtifactExportResult,
141
+ } from "${workspaceRuntimeExportRoot}/video-artifact-export";
142
+ void (null as unknown as ToolcraftVideoArtifactExportRequest);
143
+ void (null as unknown as ToolcraftVideoArtifactExportResult);
144
+ `,
145
+ "src/features/typeof-import-type.ts": `
146
+ type MediaBunny = typeof import("mediabunny");
147
+ void (null as unknown as MediaBunny);
148
+ `,
149
+ "src/features/export-boundary.test.ts": `
150
+ import type { ToolcraftVideoEncoderBackend } from "@/toolcraft/runtime/export/video-encoding-backend";
151
+ void (null as unknown as ToolcraftVideoEncoderBackend);
152
+ `,
153
+ "e2e/test-support/export-fixture.ts": `
154
+ export type { ToolcraftVideoEncoderBackend } from "@/toolcraft/runtime/export/video-encoding-backend";
155
+ `,
156
+ });
157
+
158
+ const result = await evaluateToolcraftProductBoundary({ rootDir });
159
+
160
+ assert.deepEqual(
161
+ result.violations.map(({ repoPath }) => repoPath).sort(),
162
+ [
163
+ "src/features/dynamic-import.ts",
164
+ "src/features/import-equals.ts",
165
+ "src/features/import-type.ts",
166
+ "src/features/regular-export.ts",
167
+ "src/features/regular-import.ts",
168
+ "src/features/require.ts",
169
+ "src/features/side-effect-import.ts",
170
+ "src/features/type-only-export.ts",
171
+ "src/features/type-only-import-equals.ts",
172
+ "src/features/type-only-import.ts",
173
+ "src/features/typeof-import-type.ts",
174
+ ],
175
+ );
176
+ assert.equal(
177
+ new Set(result.violations.map(({ repoPath }) => repoPath)).size,
178
+ result.violations.length,
179
+ );
180
+ assert.equal(
181
+ result.violations.every(
182
+ (violation) => violation.kind === "runtime-export-ownership",
183
+ ),
184
+ true,
185
+ );
186
+ assert.equal(
187
+ result.violations.some(({ repoPath }) =>
188
+ [
189
+ "src/features/export-boundary.test.ts",
190
+ "src/features/safe-root-type-import.ts",
191
+ "e2e/test-support/export-fixture.ts",
192
+ ].includes(repoPath),
193
+ ),
194
+ false,
195
+ );
196
+ });
@@ -41,4 +41,24 @@ test("keeps TypeScript parsing and boundary facts on one canonical path", async
41
41
  assert.deepEqual(checkerOwners, [
42
42
  "toolcraft-typescript-analysis.mjs",
43
43
  ]);
44
+
45
+ const productBoundarySource = await fs.readFile(
46
+ path.join(scriptsDir, "toolcraft-product-boundary-ast.mjs"),
47
+ "utf8",
48
+ );
49
+ const controlBoundarySource = await fs.readFile(
50
+ path.join(scriptsDir, "toolcraft-product-control-boundary.mjs"),
51
+ "utf8",
52
+ );
53
+ const modulePolicySource = await fs.readFile(
54
+ path.join(scriptsDir, "toolcraft-product-boundary-module-policy.mjs"),
55
+ "utf8",
56
+ );
57
+
58
+ assert.match(productBoundarySource, /createToolcraftProductControlInspector/u);
59
+ assert.match(
60
+ modulePolicySource,
61
+ /isToolcraftPrivateControlImplementationModule/u,
62
+ );
63
+ assert.doesNotMatch(controlBoundarySource, /(?:ts\.)?createSourceFile\(/u);
44
64
  });
@@ -300,6 +300,7 @@ export async function createToolcraftTypeScriptSourceRecord({
300
300
  inspectors.createToolcraftStaticStringResolver(sourceFile, checker);
301
301
  const inspectorInput = {
302
302
  absolutePath,
303
+ checker,
303
304
  getNodeLocation,
304
305
  repoPath,
305
306
  resolveStaticString,
@@ -29,6 +29,7 @@ function createFiniteObservation(width, height) {
29
29
  finiteControlSize: { height, width },
30
30
  finiteControlsPresent: true,
31
31
  overflow: "hidden",
32
+ productSceneStatus: null,
32
33
  sceneRect: null,
33
34
  viewportOffset: { x: 0, y: 0 },
34
35
  };
@@ -42,6 +43,7 @@ function createInfiniteObservation() {
42
43
  finiteControlSize: null,
43
44
  finiteControlsPresent: false,
44
45
  overflow: "visible",
46
+ productSceneStatus: "ready",
45
47
  sceneRect: { height: 400, width: 640, x: -320, y: -200 },
46
48
  viewportOffset: { x: 0, y: 0 },
47
49
  };
@@ -0,0 +1,130 @@
1
+ import type { ToolcraftArtifactExportIntent } from "./types";
2
+
3
+ export type ToolcraftArtifactExportDelivery = Readonly<{
4
+ imageEnabled: boolean;
5
+ videoEnabled: boolean;
6
+ }>;
7
+
8
+ export type ToolcraftArtifactExportSchemaFacts = Readonly<{
9
+ hasImageExportAction: boolean;
10
+ hasImageExportSection: boolean;
11
+ hasVideoExportAction: boolean;
12
+ hasVideoExportSection: boolean;
13
+ }>;
14
+
15
+ export type ToolcraftArtifactExportIntentValidation = Readonly<{
16
+ delivery: ToolcraftArtifactExportDelivery;
17
+ errors: readonly string[];
18
+ }>;
19
+
20
+ export function resolveToolcraftArtifactExportIntent(
21
+ intent: ToolcraftArtifactExportIntent,
22
+ ): ToolcraftArtifactExportDelivery {
23
+ return Object.freeze({
24
+ imageEnabled: intent.image.mode !== "user-removed",
25
+ videoEnabled: intent.video.mode === "user-requested",
26
+ });
27
+ }
28
+
29
+ export function getToolcraftArtifactExportIntentEvidenceErrors(
30
+ intent: ToolcraftArtifactExportIntent,
31
+ ): readonly string[] {
32
+ const errors: string[] = [];
33
+
34
+ if (
35
+ intent.image.mode !== "toolcraft-default" &&
36
+ intent.image.evidence.trim() === ""
37
+ ) {
38
+ errors.push(
39
+ `Image export ${intent.image.mode} intent requires non-empty evidence.`,
40
+ );
41
+ }
42
+
43
+ if (
44
+ intent.video.mode === "user-requested" &&
45
+ intent.video.evidence.trim() === ""
46
+ ) {
47
+ errors.push(
48
+ "Video export user-requested intent requires non-empty evidence.",
49
+ );
50
+ }
51
+
52
+ return errors;
53
+ }
54
+
55
+ function getToolcraftArtifactCorrespondenceErrors({
56
+ actionRole,
57
+ artifact,
58
+ enabled,
59
+ hasAction,
60
+ hasSection,
61
+ intentMode,
62
+ sectionArticle,
63
+ sectionTitle,
64
+ }: Readonly<{
65
+ actionRole: "export-image" | "export-video";
66
+ artifact: "Image" | "Video";
67
+ enabled: boolean;
68
+ hasAction: boolean;
69
+ hasSection: boolean;
70
+ intentMode:
71
+ | ToolcraftArtifactExportIntent["image"]["mode"]
72
+ | ToolcraftArtifactExportIntent["video"]["mode"];
73
+ sectionArticle: "a" | "an";
74
+ sectionTitle: "Image Export" | "Video Export";
75
+ }>): string[] {
76
+ const errors: string[] = [];
77
+ const requirement = enabled ? "requires" : "must not have";
78
+
79
+ if (hasAction !== enabled) {
80
+ errors.push(
81
+ `${artifact} export intent "${intentMode}" ${requirement} an ${actionRole} panel action.`,
82
+ );
83
+ }
84
+
85
+ if (hasSection !== enabled) {
86
+ errors.push(
87
+ `${artifact} export intent "${intentMode}" ${requirement} ${sectionArticle} "${sectionTitle}" section.`,
88
+ );
89
+ }
90
+
91
+ return errors;
92
+ }
93
+
94
+ export function validateToolcraftArtifactExportIntentCorrespondence({
95
+ hasImageExportAction,
96
+ hasImageExportSection,
97
+ hasVideoExportAction,
98
+ hasVideoExportSection,
99
+ intent,
100
+ }: ToolcraftArtifactExportSchemaFacts &
101
+ Readonly<{
102
+ intent: ToolcraftArtifactExportIntent;
103
+ }>): ToolcraftArtifactExportIntentValidation {
104
+ const delivery = resolveToolcraftArtifactExportIntent(intent);
105
+ const errors = [
106
+ ...getToolcraftArtifactExportIntentEvidenceErrors(intent),
107
+ ...getToolcraftArtifactCorrespondenceErrors({
108
+ actionRole: "export-image",
109
+ artifact: "Image",
110
+ enabled: delivery.imageEnabled,
111
+ hasAction: hasImageExportAction,
112
+ hasSection: hasImageExportSection,
113
+ intentMode: intent.image.mode,
114
+ sectionArticle: "an",
115
+ sectionTitle: "Image Export",
116
+ }),
117
+ ...getToolcraftArtifactCorrespondenceErrors({
118
+ actionRole: "export-video",
119
+ artifact: "Video",
120
+ enabled: delivery.videoEnabled,
121
+ hasAction: hasVideoExportAction,
122
+ hasSection: hasVideoExportSection,
123
+ intentMode: intent.video.mode,
124
+ sectionArticle: "a",
125
+ sectionTitle: "Video Export",
126
+ }),
127
+ ];
128
+
129
+ return { delivery, errors };
130
+ }
@@ -26,7 +26,7 @@ export function getConditionValidationErrors({
26
26
  label,
27
27
  }: {
28
28
  condition: ToolcraftControlConditionSchema;
29
- conditionName: "disabledWhen" | "visibleWhen";
29
+ conditionName: "disabledWhen";
30
30
  controlTargets: ReadonlySet<string>;
31
31
  label: string;
32
32
  }): string[] {
@@ -1,6 +1,6 @@
1
1
  import type {
2
2
  ResolvedToolcraftAppSchema,
3
- ToolcraftControlSchema,
3
+ ResolvedToolcraftControlSchema,
4
4
  ToolcraftTimelineMode,
5
5
  } from "@repo/toolcraft-runtime";
6
6
 
@@ -21,7 +21,7 @@ export type ToolcraftControlAcceptanceContext = {
21
21
  };
22
22
 
23
23
  export type ToolcraftControlRuleContext = {
24
- control: ToolcraftControlSchema;
24
+ control: ResolvedToolcraftControlSchema;
25
25
  controlId: string;
26
26
  controlTargets: ReadonlySet<string>;
27
27
  label: string;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getToolcraftControlKeyframeCapability,
3
3
  type ResolvedToolcraftAppSchema,
4
- type ToolcraftControlSchema,
4
+ type ResolvedToolcraftControlSchema,
5
5
  type ToolcraftTimelineMode,
6
6
  } from "@repo/toolcraft-runtime";
7
7
 
@@ -21,7 +21,6 @@ import {
21
21
  import type {
22
22
  ToolcraftBackgroundOutputCoverage,
23
23
  ToolcraftComponentAcceptance,
24
- ToolcraftConditionalVisibilityCoverage,
25
24
  ToolcraftOrientationGizmoCoverage,
26
25
  ToolcraftProductReadiness,
27
26
  } from "./types";
@@ -53,7 +52,7 @@ function getControlAcceptanceEntryErrors({
53
52
  entry,
54
53
  label,
55
54
  }: {
56
- control: ToolcraftControlSchema;
55
+ control: ResolvedToolcraftControlSchema;
57
56
  entry: ToolcraftComponentAcceptance;
58
57
  label: string;
59
58
  }): string[] {
@@ -98,7 +97,7 @@ function getControlLifecycleAndCustomErrors({
98
97
  schema,
99
98
  }: {
100
99
  acceptance: readonly ToolcraftComponentAcceptance[];
101
- control: ToolcraftControlSchema;
100
+ control: ResolvedToolcraftControlSchema;
102
101
  entry: ToolcraftComponentAcceptance;
103
102
  label: string;
104
103
  layersEnabled: boolean;
@@ -155,7 +154,7 @@ function getControlEvidenceErrors({
155
154
  schema,
156
155
  sectionTitle,
157
156
  }: {
158
- control: ToolcraftControlSchema;
157
+ control: ResolvedToolcraftControlSchema;
159
158
  controlId: string;
160
159
  entry: ToolcraftComponentAcceptance;
161
160
  label: string;
@@ -177,19 +176,6 @@ function getControlEvidenceErrors({
177
176
  );
178
177
  }
179
178
 
180
- if (
181
- control.visibleWhen &&
182
- !hasTypedCoverage<ToolcraftConditionalVisibilityCoverage>(
183
- entry.visibilityCoverage,
184
- "all-conditional-visibility",
185
- ["hidden", "visible"],
186
- )
187
- ) {
188
- errors.push(
189
- `${label} uses visibleWhen and must declare visibilityCoverage for both "hidden" and "visible" states reached through ${control.visibleWhen.target}.`,
190
- );
191
- }
192
-
193
179
  if (
194
180
  schemaHasPngExportPanelAction(schema) &&
195
181
  isOutputBackgroundToggleControl({ control, controlId, sectionTitle })
@@ -228,7 +214,7 @@ function getControlPartAndRuntimeCoverageErrors({
228
214
  layersEnabled,
229
215
  timelineMode,
230
216
  }: {
231
- control: ToolcraftControlSchema;
217
+ control: ResolvedToolcraftControlSchema;
232
218
  entry: ToolcraftComponentAcceptance;
233
219
  label: string;
234
220
  layersEnabled: boolean;
@@ -281,7 +267,7 @@ export function getControlAcceptanceCoverageErrors({
281
267
  timelineMode,
282
268
  }: {
283
269
  acceptance: readonly ToolcraftComponentAcceptance[];
284
- control: ToolcraftControlSchema;
270
+ control: ResolvedToolcraftControlSchema;
285
271
  controlId: string;
286
272
  entry: ToolcraftComponentAcceptance;
287
273
  label: string;
@@ -1,5 +1,8 @@
1
1
  import { getToolcraftSegmentedStaticFitError } from "@repo/toolcraft-runtime";
2
- import type { ToolcraftControlSchema } from "@repo/toolcraft-runtime";
2
+ import type {
3
+ ToolcraftCollectionItemControlSchema,
4
+ ToolcraftControlSchema,
5
+ } from "@repo/toolcraft-runtime";
3
6
 
4
7
  import { getActionValue, isResetPanelAction } from "./actions";
5
8
  import {
@@ -103,13 +106,29 @@ function getOptionControlAcceptanceErrors({
103
106
  }
104
107
  }
105
108
 
106
- if (control.type === "collectionActions" && control.itemControl) {
107
- const segmentedLayoutError = getToolcraftSegmentedStaticFitError(
108
- control.itemControl,
109
+ const nestedItemControls: Array<
110
+ [string, ToolcraftCollectionItemControlSchema]
111
+ > = [];
112
+
113
+ if (control.itemControl) {
114
+ nestedItemControls.push(["itemControl", control.itemControl]);
115
+ }
116
+
117
+ if (control.itemControls) {
118
+ nestedItemControls.push(
119
+ ...Object.entries(control.itemControls).map(
120
+ ([itemId, itemControl]): [string, ToolcraftCollectionItemControlSchema] =>
121
+ [`itemControls.${itemId}`, itemControl],
122
+ ),
109
123
  );
124
+ }
125
+
126
+ for (const [itemId, itemControl] of nestedItemControls) {
127
+ const segmentedLayoutError =
128
+ getToolcraftSegmentedStaticFitError(itemControl);
110
129
 
111
130
  if (segmentedLayoutError) {
112
- errors.push(`${label} itemControl ${segmentedLayoutError}`);
131
+ errors.push(`${label} ${itemId} ${segmentedLayoutError}`);
113
132
  }
114
133
  }
115
134
 
@@ -140,17 +140,6 @@ function getControlStatePolicyErrors({
140
140
  >): string[] {
141
141
  const errors: string[] = [];
142
142
 
143
- if (control.visibleWhen) {
144
- errors.push(
145
- ...getConditionValidationErrors({
146
- condition: control.visibleWhen,
147
- conditionName: "visibleWhen",
148
- controlTargets,
149
- label,
150
- }),
151
- );
152
- }
153
-
154
143
  if (control.disabledWhen) {
155
144
  errors.push(
156
145
  ...getConditionValidationErrors({
@@ -164,13 +153,13 @@ function getControlStatePolicyErrors({
164
153
 
165
154
  if (control.disabled === true) {
166
155
  errors.push(
167
- `${label} sets disabled: true. Generated product panels should show only controls usable in the current state; hide unavailable product controls with visibleWhen instead of rendering disabled controls.`,
156
+ `${label} sets disabled: true. Generated product panels should show only controls usable in the current state; declare conditional applicability instead of rendering disabled controls.`,
168
157
  );
169
158
  }
170
159
 
171
160
  if (control.disabledWhen) {
172
161
  errors.push(
173
- `${label} uses disabledWhen. Generated product panels should show only controls usable in the current state; hide unavailable product controls with visibleWhen instead of rendering disabled controls.`,
162
+ `${label} uses disabledWhen. Generated product panels should show only controls usable in the current state; declare conditional applicability instead of rendering disabled controls.`,
174
163
  );
175
164
  }
176
165