@pixel-point/toolcraft 0.0.16 → 0.0.17

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 (170) hide show
  1. package/package.json +1 -1
  2. package/src/generate-performance-iteration-execution.test.mjs +2 -2
  3. package/src/generate-test-demand-only-lifecycle.mjs +14 -3
  4. package/src/generate-test-dependency-sandbox.mjs +42 -5
  5. package/src/generate-test-doc-assertions.mjs +68 -18
  6. package/src/generate-test-doc-assertions.test.mjs +22 -0
  7. package/src/generate-test-file-assertions.mjs +43 -76
  8. package/src/generate-test-output-assertions.mjs +95 -0
  9. package/src/generate-test-package-assertions.mjs +6 -0
  10. package/src/generate-test-performance-boundary-assertions.mjs +45 -0
  11. package/src/generate-test-product-fixture.mjs +1 -0
  12. package/src/generate-test-product-fixture.test.mjs +18 -0
  13. package/src/generate-test-product-iteration.mjs +1 -0
  14. package/src/generate-test-product-iteration.test.mjs +1 -0
  15. package/src/generate-test-standalone-delivery-stages.mjs +14 -6
  16. package/src/generate.test.mjs +14 -0
  17. package/templates/runtime/contracts/component-contracts.choices.test.ts +12 -9
  18. package/templates/runtime/contracts/component-contracts.choices.ts +10 -9
  19. package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +17 -2
  20. package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +1 -1
  21. package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +1 -1
  22. package/templates/runtime/contracts/component-contracts.runtime.ts +3 -2
  23. package/templates/runtime/contracts/decision-contracts.test.ts +25 -4
  24. package/templates/runtime/contracts/decision-contracts.ts +3 -3
  25. package/templates/runtime/contracts/performance-verification-policy.test.ts +59 -6
  26. package/templates/runtime/contracts/performance-verification-policy.ts +5 -2
  27. package/templates/runtime/export/artifact-download.test.ts +71 -0
  28. package/templates/runtime/export/artifact-download.ts +80 -0
  29. package/templates/runtime/export/artifact-export-request.ts +23 -0
  30. package/templates/runtime/export/artifact-export-settings.test.ts +75 -0
  31. package/templates/runtime/export/artifact-export-settings.ts +96 -0
  32. package/templates/runtime/export/artifact-frame-renderer.test.ts +107 -0
  33. package/templates/runtime/export/artifact-frame-renderer.ts +96 -0
  34. package/templates/runtime/export/artifact-frame-state.test.ts +54 -0
  35. package/templates/runtime/export/artifact-frame-state.ts +44 -0
  36. package/templates/runtime/export/artifact-scene-frame.test.ts +84 -0
  37. package/templates/runtime/export/artifact-scene-frame.ts +118 -0
  38. package/templates/runtime/export/export-background.ts +65 -0
  39. package/templates/runtime/export/export-error.ts +43 -0
  40. package/templates/runtime/export/export.test.ts +6 -186
  41. package/templates/runtime/export/image-artifact-export.test.ts +111 -0
  42. package/templates/runtime/export/image-artifact-export.ts +145 -0
  43. package/templates/runtime/export/index.ts +14 -1
  44. package/templates/runtime/export/product-export-renderer.test.ts +77 -0
  45. package/templates/runtime/export/product-export-renderer.ts +63 -0
  46. package/templates/runtime/export/video-artifact-export.test.ts +166 -0
  47. package/templates/runtime/export/video-artifact-export.ts +164 -0
  48. package/templates/runtime/export/video-encoding-backend.ts +124 -0
  49. package/templates/runtime/export/video-encoding-policy.test.ts +68 -0
  50. package/templates/runtime/export/video-encoding-policy.ts +86 -0
  51. package/templates/runtime/export/video-frame-schedule.test.ts +58 -0
  52. package/templates/runtime/export/video-frame-schedule.ts +42 -0
  53. package/templates/runtime/index.ts +1 -0
  54. package/templates/runtime/react/app-shell/runtime-public-api-runtime.test.tsx +291 -0
  55. package/templates/runtime/react/app-shell/runtime-public-api-test-support.ts +21 -0
  56. package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +4 -303
  57. package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +12 -27
  58. package/templates/runtime/react/app-shell/toolcraft-app.tsx +22 -4
  59. package/templates/runtime/react/app-shell/toolcraft-source-asset-context.test.tsx +16 -57
  60. package/templates/runtime/react/app-shell/toolcraft-source-asset-context.tsx +75 -434
  61. package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.test.ts +194 -0
  62. package/templates/runtime/react/app-shell/toolcraft-source-asset-owner.ts +227 -0
  63. package/templates/runtime/react/app-shell/toolcraft-source-asset-provider-ownership.test.tsx +243 -0
  64. package/templates/runtime/react/app-shell/toolcraft-source-asset-test-support.ts +51 -0
  65. package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.test.ts +138 -0
  66. package/templates/runtime/react/app-shell/toolcraft-unavailable-resource-proof.ts +378 -0
  67. package/templates/runtime/react/controls-panel/__tests__/controls-panel.footer-actions.test.tsx +7 -12
  68. package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +17 -83
  69. package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +42 -84
  70. package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +62 -0
  71. package/templates/runtime/react/panel-host/panel-host-runtime-placement.test.tsx +30 -0
  72. package/templates/runtime/react/panel-host/panel-host.tsx +4 -0
  73. package/templates/runtime/scene/scene-bounds.ts +1 -2
  74. package/templates/runtime/schema/canvas-render-scale.ts +62 -0
  75. package/templates/runtime/schema/controls-panel-section-id.test.ts +1 -1
  76. package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +3 -3
  77. package/templates/runtime/schema/define-toolcraft.setup-background.test.ts +1 -1
  78. package/templates/runtime/schema/define-toolcraft.setup-render-scale-timeline.test.ts +71 -3
  79. package/templates/runtime/schema/define-toolcraft.ts +1 -1
  80. package/templates/runtime/schema/schema-resolvers.ts +0 -56
  81. package/templates/runtime/schema/types.ts +4 -4
  82. package/templates/runtime/state/canvas-background-state.ts +6 -0
  83. package/templates/runtime/testing/performance-render-plan-integration.test.ts +64 -0
  84. package/templates/runtime/testing/performance-renderer-pipeline-validation.ts +18 -1
  85. package/templates/starter/AGENTS.md +8 -7
  86. package/templates/starter/NOTICE.md +6 -0
  87. package/templates/starter/docs/toolcraft/README.md +2 -1
  88. package/templates/starter/docs/toolcraft/acceptance-testing.md +8 -6
  89. package/templates/starter/docs/toolcraft/agent-worklog.md +3 -3
  90. package/templates/starter/docs/toolcraft/assembly-workflow.md +4 -3
  91. package/templates/starter/docs/toolcraft/core/media-upload.md +2 -2
  92. package/templates/starter/docs/toolcraft/core/performance.md +14 -3
  93. package/templates/starter/docs/toolcraft/core/runtime-boundary.md +3 -1
  94. package/templates/starter/docs/toolcraft/core/setup-export.md +16 -12
  95. package/templates/starter/docs/toolcraft/core/timeline-animation.md +4 -4
  96. package/templates/starter/docs/toolcraft/performance.md +20 -6
  97. package/templates/starter/docs/toolcraft/renderer-technique.md +1 -1
  98. package/templates/starter/docs/toolcraft/schema-reference.md +2 -6
  99. package/templates/starter/docs/toolcraft/workflow.md +7 -2
  100. package/templates/starter/e2e/app-browser-control-coverage.spec.ts +33 -0
  101. package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +43 -1
  102. package/templates/starter/e2e/app-browser-render-scale-evidence.spec.ts +60 -6
  103. package/templates/starter/e2e/app-browser-render-scale-requirements.spec.ts +77 -1
  104. package/templates/starter/e2e/app-browser-runtime-provenance.spec.ts +8 -0
  105. package/templates/starter/e2e/app-browser-runtime-reporter.spec.ts +35 -0
  106. package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +4 -1
  107. package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +0 -1
  108. package/templates/starter/e2e/app-browser-shell.spec.ts +13 -5
  109. package/templates/starter/e2e/app-performance-path-adapters.ts +8 -1
  110. package/templates/starter/e2e/app-performance.spec.ts +16 -9
  111. package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +9 -330
  112. package/templates/starter/e2e/browser-infinity-canvas-unavailable-image-evidence.ts +370 -0
  113. package/templates/starter/e2e/browser-media-export-evidence.spec.ts +77 -0
  114. package/templates/starter/e2e/browser-media-export-evidence.ts +208 -0
  115. package/templates/starter/e2e/browser-render-scale-evidence.ts +37 -29
  116. package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +4 -1
  117. package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +43 -9
  118. package/templates/starter/e2e/browser-standard-timeline-evidence.ts +106 -0
  119. package/templates/starter/e2e/decoded-pixel-observation.spec.ts +74 -0
  120. package/templates/starter/e2e/decoded-pixel-observation.ts +227 -0
  121. package/templates/starter/e2e/export-artifact-helpers.ts +206 -25
  122. package/templates/starter/e2e/image-artifact-inspection.spec.ts +41 -0
  123. package/templates/starter/e2e/image-artifact-inspection.ts +99 -0
  124. package/templates/starter/e2e/performance-canvas-helpers.ts +22 -21
  125. package/templates/starter/e2e/performance-canvas-quality-guard.spec.ts +312 -0
  126. package/templates/starter/e2e/performance-canvas-quality-guard.ts +196 -0
  127. package/templates/starter/e2e/performance-canvas-quality-observer.ts +351 -0
  128. package/templates/starter/e2e/performance-control-layout-helpers.ts +2 -2
  129. package/templates/starter/e2e/performance-path-adapter-contract.ts +4 -4
  130. package/templates/starter/e2e/performance-path-helpers.spec.ts +203 -70
  131. package/templates/starter/e2e/performance-path-helpers.ts +210 -91
  132. package/templates/starter/e2e/performance-pipeline-evidence-continuity.spec.ts +76 -0
  133. package/templates/starter/e2e/performance-pipeline-evidence-test-fixtures.ts +11 -2
  134. package/templates/starter/e2e/performance-pipeline-invariants.ts +47 -4
  135. package/templates/starter/e2e/performance-render-scale-runner.spec.ts +140 -38
  136. package/templates/starter/e2e/video-artifact-inspection.spec.ts +64 -0
  137. package/templates/starter/e2e/video-artifact-inspection.ts +281 -0
  138. package/templates/starter/gitignore +1 -0
  139. package/templates/starter/package.json +1 -0
  140. package/templates/starter/scripts/check-toolcraft-docs.mjs +17 -0
  141. package/templates/starter/scripts/toolcraft-delivery-plan-authority.test.mjs +60 -0
  142. package/templates/starter/scripts/toolcraft-delivery-plan-errors.test.mjs +7 -2
  143. package/templates/starter/scripts/toolcraft-delivery-plan.mjs +7 -3
  144. package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +24 -2
  145. package/templates/starter/scripts/toolcraft-product-boundary.mjs +12 -0
  146. package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +46 -0
  147. package/templates/starter/scripts/toolcraft-product-evidence-boundary.test.mjs +85 -20
  148. package/templates/starter/scripts/toolcraft-product-evidence-import-policy.mjs +43 -0
  149. package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +168 -0
  150. package/templates/starter/scripts/toolcraft-typescript-analysis.d.mts +73 -0
  151. package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade-test-helpers.mjs +318 -0
  152. package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +155 -206
  153. package/templates/starter/scripts/toolcraft-verification-inventory.mjs +1 -0
  154. package/templates/starter/scripts/toolcraft-verification-receipt.test.mjs +51 -0
  155. package/templates/starter/scripts/toolcraft-vite-env-activation.test.mjs +128 -59
  156. package/templates/starter/src/app/acceptance/export-artifact-coverage.ts +113 -0
  157. package/templates/starter/src/app/acceptance/output-background-rules.ts +1 -1
  158. package/templates/starter/src/app/acceptance/output-export.ts +13 -2
  159. package/templates/starter/src/app/acceptance/types.ts +7 -0
  160. package/templates/starter/src/app/acceptance/validate-coverage.ts +2 -1
  161. package/templates/starter/src/app/starter-acceptance.export-artifact.test.ts +131 -0
  162. package/templates/starter/src/app/starter-acceptance.keyframes.test.ts +14 -4
  163. package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +42 -1
  164. package/templates/starter/src/app/starter-acceptance.ts +2 -0
  165. package/templates/starter/src/app/starter-performance-test-utils.ts +103 -0
  166. package/templates/starter/src/app/starter-performance.gates.test.ts +231 -278
  167. package/templates/starter/src/app/starter-performance.lifecycle.test.ts +261 -0
  168. package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +2 -0
  169. package/templates/starter/src/routes/index.tsx +1 -0
  170. package/templates/runtime/export/export.ts +0 -166
@@ -1,6 +1,9 @@
1
1
  import { expect, test } from "@playwright/test";
2
2
 
3
- import type { ToolcraftPerformancePath } from "@repo/toolcraft-runtime";
3
+ import {
4
+ defineToolcraft,
5
+ type ToolcraftPerformancePath,
6
+ } from "@repo/toolcraft-runtime";
4
7
 
5
8
  import {
6
9
  compileToolcraftPerformancePathAdapterMatrix,
@@ -9,6 +12,16 @@ import {
9
12
  } from "./performance-path-helpers";
10
13
  import type { ToolcraftPerformancePathAdapter } from "./performance-path-adapter-contract";
11
14
 
15
+ const rasterSchema = defineToolcraft({
16
+ canvas: { enabled: true, renderScale: true },
17
+ panels: { controls: { sections: [], title: "Controls" } },
18
+ });
19
+
20
+ const vectorSchema = defineToolcraft({
21
+ canvas: { enabled: true },
22
+ panels: { controls: { sections: [], title: "Controls" } },
23
+ });
24
+
12
25
  const paths = [
13
26
  {
14
27
  id: "performance-path:b",
@@ -40,10 +53,12 @@ function adapter(pathId: string): ToolcraftPerformancePathAdapter {
40
53
  }
41
54
 
42
55
  test("browser perf: path adapter matrix is canonical and deterministic", () => {
43
- const matrix = compileToolcraftPerformancePathAdapterMatrix(paths, [
44
- adapter(paths[0].id),
45
- adapter(paths[1].id),
46
- ]);
56
+ const matrix = compileToolcraftPerformancePathAdapterMatrix({
57
+ adapters: [adapter(paths[0].id), adapter(paths[1].id)],
58
+ canvasBacking: undefined,
59
+ paths,
60
+ schema: vectorSchema,
61
+ });
47
62
 
48
63
  expect(matrix.map((entry) => entry.path.id)).toEqual([
49
64
  "performance-path:a",
@@ -58,50 +73,81 @@ test("browser perf: path adapter matrix is canonical and deterministic", () => {
58
73
  ]);
59
74
  });
60
75
 
61
- test("browser perf: initial render collects enough frames for a real p95", () => {
76
+ test("browser perf: interaction measurements collect enough frames for a real p95", () => {
62
77
  expect(
63
78
  getToolcraftPerformancePathSettleFrames({ interaction: "initial-render" }),
64
79
  ).toBe(20);
65
80
  expect(
66
81
  getToolcraftPerformancePathSettleFrames({ interaction: "control-change" }),
82
+ ).toBe(20);
83
+ expect(
84
+ getToolcraftPerformancePathSettleFrames({ interaction: "viewport-zoom" }),
85
+ ).toBe(20);
86
+ expect(
87
+ getToolcraftPerformancePathSettleFrames({ interaction: "animation-frame" }),
88
+ ).toBeUndefined();
89
+ expect(
90
+ getToolcraftPerformancePathSettleFrames({ interaction: "export" }),
67
91
  ).toBeUndefined();
68
92
  });
69
93
 
70
94
  test("browser perf: path adapter matrix rejects missing duplicate and orphan adapters", () => {
71
95
  expect(() =>
72
- compileToolcraftPerformancePathAdapterMatrix(paths, [adapter(paths[0].id)]),
96
+ compileToolcraftPerformancePathAdapterMatrix({
97
+ adapters: [adapter(paths[0].id)],
98
+ canvasBacking: undefined,
99
+ paths,
100
+ schema: vectorSchema,
101
+ }),
73
102
  ).toThrow(/missing adapter.*performance-path:a/iu);
74
103
 
75
104
  expect(() =>
76
- compileToolcraftPerformancePathAdapterMatrix(paths, [
77
- adapter(paths[0].id),
78
- adapter(paths[0].id),
79
- adapter(paths[1].id),
80
- ]),
105
+ compileToolcraftPerformancePathAdapterMatrix({
106
+ adapters: [
107
+ adapter(paths[0].id),
108
+ adapter(paths[0].id),
109
+ adapter(paths[1].id),
110
+ ],
111
+ canvasBacking: undefined,
112
+ paths,
113
+ schema: vectorSchema,
114
+ }),
81
115
  ).toThrow(/duplicate adapter.*performance-path:b/iu);
82
116
 
83
117
  expect(() =>
84
- compileToolcraftPerformancePathAdapterMatrix(paths, [
85
- adapter(paths[0].id),
86
- adapter(paths[1].id),
87
- adapter("performance-path:orphan"),
88
- ]),
118
+ compileToolcraftPerformancePathAdapterMatrix({
119
+ adapters: [
120
+ adapter(paths[0].id),
121
+ adapter(paths[1].id),
122
+ adapter("performance-path:orphan"),
123
+ ],
124
+ canvasBacking: undefined,
125
+ paths,
126
+ schema: vectorSchema,
127
+ }),
89
128
  ).toThrow(/orphan adapter.*performance-path:orphan/iu);
90
129
 
91
130
  expect(() =>
92
- compileToolcraftPerformancePathAdapterMatrix(
93
- [paths[0], paths[0]],
94
- [adapter(paths[0].id)],
95
- ),
131
+ compileToolcraftPerformancePathAdapterMatrix({
132
+ adapters: [adapter(paths[0].id)],
133
+ canvasBacking: undefined,
134
+ paths: [paths[0], paths[0]],
135
+ schema: vectorSchema,
136
+ }),
96
137
  ).toThrow(/duplicate canonical path ids/iu);
97
138
  });
98
139
 
99
140
  test("browser perf: path adapter matrix enforces outcome and output semantics", () => {
100
141
  expect(() =>
101
- compileToolcraftPerformancePathAdapterMatrix(paths, [
102
- { ...adapter(paths[0].id), observeOutcome: undefined },
103
- adapter(paths[1].id),
104
- ]),
142
+ compileToolcraftPerformancePathAdapterMatrix({
143
+ adapters: [
144
+ { ...adapter(paths[0].id), observeOutcome: undefined },
145
+ adapter(paths[1].id),
146
+ ],
147
+ canvasBacking: undefined,
148
+ paths,
149
+ schema: vectorSchema,
150
+ }),
105
151
  ).toThrow(/requires an observable product outcome/iu);
106
152
 
107
153
  const exportPath = {
@@ -114,58 +160,145 @@ test("browser perf: path adapter matrix enforces outcome and output semantics",
114
160
  workloadDimensions: [],
115
161
  } as const satisfies ToolcraftPerformancePath;
116
162
  expect(() =>
117
- compileToolcraftPerformancePathAdapterMatrix(
118
- [exportPath],
119
- [adapter(exportPath.id)],
120
- ),
163
+ compileToolcraftPerformancePathAdapterMatrix({
164
+ adapters: [adapter(exportPath.id)],
165
+ canvasBacking: undefined,
166
+ paths: [exportPath],
167
+ schema: vectorSchema,
168
+ }),
121
169
  ).toThrow(/requires a protected output completion adapter/iu);
122
170
  });
123
171
 
124
- test("browser perf: render-scale paths require real backing-pixel proof configuration", () => {
125
- const renderScalePath = {
126
- id: "performance-path:render-scale",
127
- interaction: "control-change",
128
- invalidates: ["preview"],
129
- profile: "interactive-discrete",
130
- runsOn: ["main"],
131
- targets: ["canvas.renderScale"],
132
- workloadDimensions: [],
133
- } as const satisfies ToolcraftPerformancePath;
172
+ test("browser perf: raster catalogs require one product canvas backing proof", () => {
173
+ expect(() =>
174
+ compileToolcraftPerformancePathAdapterMatrix({
175
+ adapters: [adapter(paths[0].id), adapter(paths[1].id)],
176
+ canvasBacking: undefined,
177
+ paths,
178
+ schema: rasterSchema,
179
+ }),
180
+ ).toThrow(/render-scale-enabled.*product canvas selector/iu);
134
181
 
135
182
  expect(() =>
136
- compileToolcraftPerformancePathAdapterMatrix(
137
- [renderScalePath],
138
- [adapter(renderScalePath.id)],
139
- ),
140
- ).toThrow(/render-scale.*backing-pixel proof/iu);
183
+ compileToolcraftPerformancePathAdapterMatrix({
184
+ adapters: [adapter(paths[0].id), adapter(paths[1].id)],
185
+ canvasBacking: { canvasSelector: "[data-product-canvas]" },
186
+ paths,
187
+ schema: rasterSchema,
188
+ }),
189
+ ).not.toThrow();
141
190
 
142
191
  expect(() =>
143
- compileToolcraftPerformancePathAdapterMatrix(
144
- [renderScalePath],
145
- [
146
- {
147
- ...adapter(renderScalePath.id),
148
- renderScaleBacking: (async () => ({
149
- canvasSelector: "[data-product-canvas]",
150
- selectedScale: 2,
151
- })) as never,
152
- },
153
- ],
154
- ),
155
- ).toThrow(/render-scale backing-pixel proof.*non-null object/iu);
192
+ compileToolcraftPerformancePathAdapterMatrix({
193
+ adapters: [adapter(paths[0].id), adapter(paths[1].id)],
194
+ canvasBacking: { canvasSelector: "[data-product-canvas]" },
195
+ paths,
196
+ schema: vectorSchema,
197
+ }),
198
+ ).toThrow(/must be omitted.*render scale is disabled/iu);
199
+ });
200
+
201
+ test("browser perf: canvas backing config is an exact selector object", () => {
202
+ for (const canvasBacking of [
203
+ null,
204
+ [],
205
+ "[data-product-canvas]",
206
+ { canvasSelector: "" },
207
+ { canvasSelector: " [data-product-canvas]" },
208
+ { canvasSelector: "[data-product-canvas] " },
209
+ { canvasSelector: "[data-product-canvas]", extra: true },
210
+ ]) {
211
+ expect(() =>
212
+ compileToolcraftPerformancePathAdapterMatrix({
213
+ adapters: [adapter(paths[0].id), adapter(paths[1].id)],
214
+ canvasBacking: canvasBacking as never,
215
+ paths,
216
+ schema: rasterSchema,
217
+ }),
218
+ ).toThrow(/performance (?:canvas backing|catalogs require)/iu);
219
+ }
220
+ });
221
+
222
+ test("browser perf: vector catalogs reject every present falsy canvas backing value", () => {
223
+ for (const canvasBacking of [null, false, 0, ""]) {
224
+ expect(() =>
225
+ compileToolcraftPerformancePathAdapterMatrix({
226
+ adapters: [adapter(paths[0].id), adapter(paths[1].id)],
227
+ canvasBacking: canvasBacking as never,
228
+ paths,
229
+ schema: vectorSchema,
230
+ }),
231
+ ).toThrow(/must be omitted.*render scale is disabled/iu);
232
+ }
233
+ });
234
+
235
+ test("browser perf: canvas backing rejects accessors without invoking them", () => {
236
+ let getterReads = 0;
237
+ const canvasBacking = Object.defineProperty({}, "canvasSelector", {
238
+ enumerable: true,
239
+ get: () => {
240
+ getterReads += 1;
241
+ return getterReads === 1
242
+ ? "[data-product-canvas]"
243
+ : "[data-mutated-canvas]";
244
+ },
245
+ });
156
246
 
157
247
  expect(() =>
158
- compileToolcraftPerformancePathAdapterMatrix(
159
- [renderScalePath],
160
- [
161
- {
162
- ...adapter(renderScalePath.id),
163
- renderScaleBacking: {
164
- canvasSelector: "[data-product-canvas]",
165
- selectedScale: 2,
166
- },
167
- },
168
- ],
169
- ),
170
- ).not.toThrow();
248
+ compileToolcraftPerformancePathAdapterMatrix({
249
+ adapters: [adapter(paths[0].id), adapter(paths[1].id)],
250
+ canvasBacking: canvasBacking as never,
251
+ paths,
252
+ schema: rasterSchema,
253
+ }),
254
+ ).toThrow(/canvasSelector.*data property/iu);
255
+ expect(getterReads).toBe(0);
256
+ });
257
+
258
+ test("browser perf: canvas backing snapshots proxy data without property reads", () => {
259
+ let propertyReads = 0;
260
+ const canvasBacking = new Proxy(
261
+ { canvasSelector: "[data-product-canvas]" },
262
+ {
263
+ get(target, property, receiver) {
264
+ if (property === "canvasSelector") {
265
+ propertyReads += 1;
266
+ return propertyReads === 1
267
+ ? Reflect.get(target, property, receiver)
268
+ : "[data-mutated-canvas]";
269
+ }
270
+ return Reflect.get(target, property, receiver);
271
+ },
272
+ },
273
+ );
274
+
275
+ const matrix = compileToolcraftPerformancePathAdapterMatrix({
276
+ adapters: [adapter(paths[0].id), adapter(paths[1].id)],
277
+ canvasBacking,
278
+ paths,
279
+ schema: rasterSchema,
280
+ });
281
+
282
+ expect(propertyReads).toBe(0);
283
+ expect(matrix[0]?.canvasBacking?.canvasSelector).toBe(
284
+ "[data-product-canvas]",
285
+ );
286
+ });
287
+
288
+ test("browser perf: compiled canvas backing is one immutable snapshot", () => {
289
+ const canvasBacking = { canvasSelector: "[data-product-canvas]" };
290
+ const matrix = compileToolcraftPerformancePathAdapterMatrix({
291
+ adapters: [adapter(paths[0].id), adapter(paths[1].id)],
292
+ canvasBacking,
293
+ paths,
294
+ schema: rasterSchema,
295
+ });
296
+
297
+ canvasBacking.canvasSelector = "[data-mutated-canvas]";
298
+
299
+ expect(matrix[0]?.canvasBacking).toBe(matrix[1]?.canvasBacking);
300
+ expect(matrix[0]?.canvasBacking).toEqual({
301
+ canvasSelector: "[data-product-canvas]",
302
+ });
303
+ expect(Object.isFrozen(matrix[0]?.canvasBacking)).toBe(true);
171
304
  });