@forgeax/engine-pack 0.0.0-dev.8d955ade1c79

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 (276) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +429 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/ai-recovery.unit.test.d.ts +2 -0
  5. package/dist/__tests__/ai-recovery.unit.test.d.ts.map +1 -0
  6. package/dist/__tests__/artifact-path.test.d.ts +2 -0
  7. package/dist/__tests__/artifact-path.test.d.ts.map +1 -0
  8. package/dist/__tests__/asset-evidence-error-hints.test.d.ts +2 -0
  9. package/dist/__tests__/asset-evidence-error-hints.test.d.ts.map +1 -0
  10. package/dist/__tests__/asset-evidence-lifecycle.test.d.ts +2 -0
  11. package/dist/__tests__/asset-evidence-lifecycle.test.d.ts.map +1 -0
  12. package/dist/__tests__/asset-evidence-schema.test.d.ts +2 -0
  13. package/dist/__tests__/asset-evidence-schema.test.d.ts.map +1 -0
  14. package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts +2 -0
  15. package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts.map +1 -0
  16. package/dist/__tests__/atlas-error-code-owner.test-d.d.ts +2 -0
  17. package/dist/__tests__/atlas-error-code-owner.test-d.d.ts.map +1 -0
  18. package/dist/__tests__/cli-asset-evidence.integration.test.d.ts +2 -0
  19. package/dist/__tests__/cli-asset-evidence.integration.test.d.ts.map +1 -0
  20. package/dist/__tests__/cli.unit.test.d.ts +2 -0
  21. package/dist/__tests__/cli.unit.test.d.ts.map +1 -0
  22. package/dist/__tests__/evidence-source-inventory.unit.test.d.ts +2 -0
  23. package/dist/__tests__/evidence-source-inventory.unit.test.d.ts.map +1 -0
  24. package/dist/__tests__/guid-collision.unit.test.d.ts +2 -0
  25. package/dist/__tests__/guid-collision.unit.test.d.ts.map +1 -0
  26. package/dist/__tests__/guid-validator-surface.unit.test.d.ts +2 -0
  27. package/dist/__tests__/guid-validator-surface.unit.test.d.ts.map +1 -0
  28. package/dist/__tests__/load-asset-config.test.d.ts +2 -0
  29. package/dist/__tests__/load-asset-config.test.d.ts.map +1 -0
  30. package/dist/__tests__/material-cook-dependencies.unit.test.d.ts +2 -0
  31. package/dist/__tests__/material-cook-dependencies.unit.test.d.ts.map +1 -0
  32. package/dist/__tests__/material-cook-receipt.unit.test.d.ts +2 -0
  33. package/dist/__tests__/material-cook-receipt.unit.test.d.ts.map +1 -0
  34. package/dist/__tests__/material-cook-schema.unit.test.d.ts +2 -0
  35. package/dist/__tests__/material-cook-schema.unit.test.d.ts.map +1 -0
  36. package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts +2 -0
  37. package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts.map +1 -0
  38. package/dist/__tests__/mesh-bin-contract.unit.test.d.ts +2 -0
  39. package/dist/__tests__/mesh-bin-contract.unit.test.d.ts.map +1 -0
  40. package/dist/__tests__/meta-schema-optional.test.d.ts +2 -0
  41. package/dist/__tests__/meta-schema-optional.test.d.ts.map +1 -0
  42. package/dist/__tests__/meta-source-overrides.unit.test.d.ts +2 -0
  43. package/dist/__tests__/meta-source-overrides.unit.test.d.ts.map +1 -0
  44. package/dist/__tests__/native-cooker-registry.test.d.ts +2 -0
  45. package/dist/__tests__/native-cooker-registry.test.d.ts.map +1 -0
  46. package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts +2 -0
  47. package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts.map +1 -0
  48. package/dist/__tests__/offline-evidence.integration.test.d.ts +2 -0
  49. package/dist/__tests__/offline-evidence.integration.test.d.ts.map +1 -0
  50. package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
  51. package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
  52. package/dist/__tests__/pack-v2-fixture-migration.test.d.ts +2 -0
  53. package/dist/__tests__/pack-v2-fixture-migration.test.d.ts.map +1 -0
  54. package/dist/__tests__/pack-v2-schema.test.d.ts +2 -0
  55. package/dist/__tests__/pack-v2-schema.test.d.ts.map +1 -0
  56. package/dist/__tests__/pack.unit.test.d.ts +2 -0
  57. package/dist/__tests__/pack.unit.test.d.ts.map +1 -0
  58. package/dist/__tests__/package-finalizer.contract.test.d.ts +2 -0
  59. package/dist/__tests__/package-finalizer.contract.test.d.ts.map +1 -0
  60. package/dist/__tests__/producer-contract-schema.unit.test.d.ts +2 -0
  61. package/dist/__tests__/producer-contract-schema.unit.test.d.ts.map +1 -0
  62. package/dist/__tests__/resolve-asset-source.test.d.ts +2 -0
  63. package/dist/__tests__/resolve-asset-source.test.d.ts.map +1 -0
  64. package/dist/__tests__/runtime-projection.unit.test.d.ts +2 -0
  65. package/dist/__tests__/runtime-projection.unit.test.d.ts.map +1 -0
  66. package/dist/__tests__/runtime.browser.test.d.ts +2 -0
  67. package/dist/__tests__/runtime.browser.test.d.ts.map +1 -0
  68. package/dist/__tests__/scanner-inventory.contract.test.d.ts +2 -0
  69. package/dist/__tests__/scanner-inventory.contract.test.d.ts.map +1 -0
  70. package/dist/__tests__/scanner-inventory.test.d.ts +2 -0
  71. package/dist/__tests__/scanner-inventory.test.d.ts.map +1 -0
  72. package/dist/__tests__/scanner-mount-cycle.test.d.ts +2 -0
  73. package/dist/__tests__/scanner-mount-cycle.test.d.ts.map +1 -0
  74. package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts +2 -0
  75. package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts.map +1 -0
  76. package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts +2 -0
  77. package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts.map +1 -0
  78. package/dist/__tests__/scriptable-pack.test-d.d.ts +2 -0
  79. package/dist/__tests__/scriptable-pack.test-d.d.ts.map +1 -0
  80. package/dist/__tests__/scriptable-pack.unit.test.d.ts +2 -0
  81. package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +1 -0
  82. package/dist/__tests__/topology-actions.unit.test.d.ts +2 -0
  83. package/dist/__tests__/topology-actions.unit.test.d.ts.map +1 -0
  84. package/dist/__tests__/topology.unit.test.d.ts +2 -0
  85. package/dist/__tests__/topology.unit.test.d.ts.map +1 -0
  86. package/dist/artifact-path.d.ts +9 -0
  87. package/dist/artifact-path.d.ts.map +1 -0
  88. package/dist/artifact-path.mjs +53 -0
  89. package/dist/artifact-path.mjs.map +1 -0
  90. package/dist/atlas/run-atlas.d.ts +8 -0
  91. package/dist/atlas/run-atlas.d.ts.map +1 -0
  92. package/dist/atlas/shelf-pack.d.ts +55 -0
  93. package/dist/atlas/shelf-pack.d.ts.map +1 -0
  94. package/dist/bridge.d.ts +2 -0
  95. package/dist/bridge.d.ts.map +1 -0
  96. package/dist/bridge.mjs +3 -0
  97. package/dist/bridge.mjs.map +1 -0
  98. package/dist/build.d.ts +19 -0
  99. package/dist/build.d.ts.map +1 -0
  100. package/dist/build.mjs +10203 -0
  101. package/dist/build.mjs.map +1 -0
  102. package/dist/builtin.d.ts +44 -0
  103. package/dist/builtin.d.ts.map +1 -0
  104. package/dist/builtin.mjs +141 -0
  105. package/dist/builtin.mjs.map +1 -0
  106. package/dist/catalog-builder.d.ts +43 -0
  107. package/dist/catalog-builder.d.ts.map +1 -0
  108. package/dist/catalog-delta.d.ts +11 -0
  109. package/dist/catalog-delta.d.ts.map +1 -0
  110. package/dist/catalog-projection.d.ts +91 -0
  111. package/dist/catalog-projection.d.ts.map +1 -0
  112. package/dist/cli-asset.d.ts +22 -0
  113. package/dist/cli-asset.d.ts.map +1 -0
  114. package/dist/cli-asset.mjs +9697 -0
  115. package/dist/cli-asset.mjs.map +1 -0
  116. package/dist/config.d.ts +6 -0
  117. package/dist/config.d.ts.map +1 -0
  118. package/dist/config.mjs +29 -0
  119. package/dist/config.mjs.map +1 -0
  120. package/dist/deriveAssetName.d.ts +15 -0
  121. package/dist/deriveAssetName.d.ts.map +1 -0
  122. package/dist/errors.d.ts +30 -0
  123. package/dist/errors.d.ts.map +1 -0
  124. package/dist/errors.mjs +19 -0
  125. package/dist/errors.mjs.map +1 -0
  126. package/dist/evidence/material-cook.d.ts +92 -0
  127. package/dist/evidence/material-cook.d.ts.map +1 -0
  128. package/dist/evidence/offline-evidence.d.ts +25 -0
  129. package/dist/evidence/offline-evidence.d.ts.map +1 -0
  130. package/dist/evidence/source-inventory.d.ts +29 -0
  131. package/dist/evidence/source-inventory.d.ts.map +1 -0
  132. package/dist/guid.d.ts +45 -0
  133. package/dist/guid.d.ts.map +1 -0
  134. package/dist/guid.mjs +94 -0
  135. package/dist/guid.mjs.map +1 -0
  136. package/dist/index.d.ts +15 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.mjs +8612 -0
  139. package/dist/index.mjs.map +1 -0
  140. package/dist/material/artifact-writer.d.ts +14 -0
  141. package/dist/material/artifact-writer.d.ts.map +1 -0
  142. package/dist/material-cook.d.ts +2 -0
  143. package/dist/material-cook.d.ts.map +1 -0
  144. package/dist/material-cook.mjs +248 -0
  145. package/dist/material-cook.mjs.map +1 -0
  146. package/dist/mesh-bin-contract.d.ts +36 -0
  147. package/dist/mesh-bin-contract.d.ts.map +1 -0
  148. package/dist/mesh-bin-contract.mjs +96 -0
  149. package/dist/mesh-bin-contract.mjs.map +1 -0
  150. package/dist/name.mjs +22 -0
  151. package/dist/name.mjs.map +1 -0
  152. package/dist/native-cooker-registry.d.ts +47 -0
  153. package/dist/native-cooker-registry.d.ts.map +1 -0
  154. package/dist/native-cooker.d.ts +3 -0
  155. package/dist/native-cooker.d.ts.map +1 -0
  156. package/dist/native-cooker.mjs +142 -0
  157. package/dist/native-cooker.mjs.map +1 -0
  158. package/dist/package-finalizer.d.ts +121 -0
  159. package/dist/package-finalizer.d.ts.map +1 -0
  160. package/dist/producer-contract.d.ts +19 -0
  161. package/dist/producer-contract.d.ts.map +1 -0
  162. package/dist/resolve-asset-source.d.ts +4 -0
  163. package/dist/resolve-asset-source.d.ts.map +1 -0
  164. package/dist/resolve-asset-source.mjs +109 -0
  165. package/dist/resolve-asset-source.mjs.map +1 -0
  166. package/dist/runtime-projection.d.ts +14 -0
  167. package/dist/runtime-projection.d.ts.map +1 -0
  168. package/dist/runtime-publication.d.ts +44 -0
  169. package/dist/runtime-publication.d.ts.map +1 -0
  170. package/dist/runtime.d.ts +8 -0
  171. package/dist/runtime.d.ts.map +1 -0
  172. package/dist/runtime.mjs +7261 -0
  173. package/dist/runtime.mjs.map +1 -0
  174. package/dist/scanner.d.ts +113 -0
  175. package/dist/scanner.d.ts.map +1 -0
  176. package/dist/scanner.mjs +8819 -0
  177. package/dist/scanner.mjs.map +1 -0
  178. package/dist/schema-compiled.d.ts +12 -0
  179. package/dist/schema-compiled.d.ts.map +1 -0
  180. package/dist/schema.mjs +7369 -0
  181. package/dist/schema.mjs.map +1 -0
  182. package/dist/scriptable-pack-node.d.ts +62 -0
  183. package/dist/scriptable-pack-node.d.ts.map +1 -0
  184. package/dist/scriptable-pack-node.mjs +1674 -0
  185. package/dist/scriptable-pack-node.mjs.map +1 -0
  186. package/dist/scriptable-pack-worker.d.ts +2 -0
  187. package/dist/scriptable-pack-worker.d.ts.map +1 -0
  188. package/dist/scriptable-pack-worker.mjs +224 -0
  189. package/dist/scriptable-pack-worker.mjs.map +1 -0
  190. package/dist/scriptable-pack.d.ts +558 -0
  191. package/dist/scriptable-pack.d.ts.map +1 -0
  192. package/dist/scriptable-pack.mjs +672 -0
  193. package/dist/scriptable-pack.mjs.map +1 -0
  194. package/dist/topology.d.ts +10 -0
  195. package/dist/topology.d.ts.map +1 -0
  196. package/package.json +154 -0
  197. package/schema/meta.schema.json +173 -0
  198. package/schema/pack.schema.json +232 -0
  199. package/src/__tests__/ai-recovery.unit.test.ts +114 -0
  200. package/src/__tests__/artifact-path.test.ts +41 -0
  201. package/src/__tests__/asset-evidence-error-hints.test.ts +46 -0
  202. package/src/__tests__/asset-evidence-lifecycle.test.ts +104 -0
  203. package/src/__tests__/asset-evidence-schema.test.ts +86 -0
  204. package/src/__tests__/atlas-cli-region-mismatch.test.ts +134 -0
  205. package/src/__tests__/atlas-error-code-owner.test-d.ts +23 -0
  206. package/src/__tests__/cli-asset-evidence.integration.test.ts +172 -0
  207. package/src/__tests__/cli.unit.test.ts +448 -0
  208. package/src/__tests__/evidence-source-inventory.unit.test.ts +37 -0
  209. package/src/__tests__/guid-collision.unit.test.ts +112 -0
  210. package/src/__tests__/guid-validator-surface.unit.test.ts +20 -0
  211. package/src/__tests__/load-asset-config.test.ts +121 -0
  212. package/src/__tests__/material-cook-dependencies.unit.test.ts +112 -0
  213. package/src/__tests__/material-cook-receipt.unit.test.ts +148 -0
  214. package/src/__tests__/material-cook-schema.unit.test.ts +152 -0
  215. package/src/__tests__/mesh-bin-consumer-surface.unit.test.ts +22 -0
  216. package/src/__tests__/mesh-bin-contract.unit.test.ts +32 -0
  217. package/src/__tests__/meta-schema-optional.test.ts +116 -0
  218. package/src/__tests__/meta-source-overrides.unit.test.ts +71 -0
  219. package/src/__tests__/native-cooker-registry.test.ts +64 -0
  220. package/src/__tests__/offline-evidence-artifact-verification-owner.test-d.ts +64 -0
  221. package/src/__tests__/offline-evidence.integration.test.ts +106 -0
  222. package/src/__tests__/owner-chain.integration.test.ts +78 -0
  223. package/src/__tests__/pack-v2-fixture-migration.test.ts +87 -0
  224. package/src/__tests__/pack-v2-schema.test.ts +142 -0
  225. package/src/__tests__/pack.unit.test.ts +2218 -0
  226. package/src/__tests__/package-finalizer.contract.test.ts +105 -0
  227. package/src/__tests__/producer-contract-schema.unit.test.ts +92 -0
  228. package/src/__tests__/resolve-asset-source.test.ts +121 -0
  229. package/src/__tests__/runtime-projection.unit.test.ts +87 -0
  230. package/src/__tests__/runtime.browser.test.ts +15 -0
  231. package/src/__tests__/scanner-inventory.contract.test.ts +167 -0
  232. package/src/__tests__/scanner-inventory.test.ts +76 -0
  233. package/src/__tests__/scanner-mount-cycle.test.ts +232 -0
  234. package/src/__tests__/scriptable-pack-cli.integration.test.ts +47 -0
  235. package/src/__tests__/scriptable-pack-ddc-key.unit.test.ts +63 -0
  236. package/src/__tests__/scriptable-pack.test-d.ts +131 -0
  237. package/src/__tests__/scriptable-pack.unit.test.ts +789 -0
  238. package/src/__tests__/topology-actions.unit.test.ts +94 -0
  239. package/src/__tests__/topology.unit.test.ts +85 -0
  240. package/src/artifact-path.ts +69 -0
  241. package/src/atlas/run-atlas.ts +358 -0
  242. package/src/atlas/shelf-pack.ts +172 -0
  243. package/src/atlas/upng-ambient.d.ts +13 -0
  244. package/src/bridge.ts +2 -0
  245. package/src/build.ts +139 -0
  246. package/src/builtin.ts +117 -0
  247. package/src/catalog-builder.ts +313 -0
  248. package/src/catalog-delta.ts +189 -0
  249. package/src/catalog-projection.ts +344 -0
  250. package/src/cli-asset.ts +586 -0
  251. package/src/config.ts +36 -0
  252. package/src/deriveAssetName.ts +42 -0
  253. package/src/errors.ts +47 -0
  254. package/src/evidence/material-cook.ts +411 -0
  255. package/src/evidence/offline-evidence.ts +90 -0
  256. package/src/evidence/source-inventory.ts +193 -0
  257. package/src/guid.ts +99 -0
  258. package/src/index.ts +108 -0
  259. package/src/material/artifact-writer.ts +26 -0
  260. package/src/material-cook.ts +15 -0
  261. package/src/mesh-bin-contract.ts +150 -0
  262. package/src/native-cooker-registry.ts +235 -0
  263. package/src/native-cooker.ts +2 -0
  264. package/src/package-finalizer.ts +394 -0
  265. package/src/producer-contract.ts +277 -0
  266. package/src/resolve-asset-source.ts +88 -0
  267. package/src/runtime-projection.ts +100 -0
  268. package/src/runtime-publication.ts +190 -0
  269. package/src/runtime.ts +23 -0
  270. package/src/scanner.ts +715 -0
  271. package/src/schema/material-cook.schema.json +119 -0
  272. package/src/schema-compiled.ts +238 -0
  273. package/src/scriptable-pack-node.ts +1394 -0
  274. package/src/scriptable-pack-worker.ts +308 -0
  275. package/src/scriptable-pack.ts +1006 -0
  276. package/src/topology.ts +157 -0
@@ -0,0 +1,121 @@
1
+ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
5
+ import { loadAssetConfig } from '../config.js';
6
+
7
+ describe('loadAssetConfig SSOT (w8 / AC-13)', () => {
8
+ let tmpDir: string;
9
+
10
+ beforeAll(async () => {
11
+ tmpDir = await mkdtemp(join(tmpdir(), 'forgeax-test-loadAssetConfig-'));
12
+ });
13
+
14
+ afterAll(async () => {
15
+ await rm(tmpDir, { recursive: true, force: true });
16
+ });
17
+
18
+ it('missing package.json returns default roots + empty paths', () => {
19
+ const result = loadAssetConfig(join(tmpDir, 'nonexistent'));
20
+ expect(result.roots).toEqual([join(join(tmpDir, 'nonexistent'), 'assets')]);
21
+ expect(result.paths).toEqual({});
22
+ });
23
+
24
+ it('package.json without forgeax.assets returns default', async () => {
25
+ const dir = join(tmpDir, 'no-forgeax');
26
+ await mkdir(dir);
27
+ await writeFile(join(dir, 'package.json'), JSON.stringify({ name: 'test' }));
28
+ const result = loadAssetConfig(dir);
29
+ expect(result.roots).toEqual([join(dir, 'assets')]);
30
+ expect(result.paths).toEqual({});
31
+ });
32
+
33
+ it('reads forgeax.assets.roots from package.json', async () => {
34
+ const dir = join(tmpDir, 'with-roots');
35
+ await mkdir(dir);
36
+ await writeFile(
37
+ join(dir, 'package.json'),
38
+ JSON.stringify({
39
+ name: 'test',
40
+ forgeax: { assets: { roots: ['my-assets', 'more'] } },
41
+ }),
42
+ );
43
+ const result = loadAssetConfig(dir);
44
+ expect(result.roots).toEqual([join(dir, 'my-assets'), join(dir, 'more')]);
45
+ expect(result.paths).toEqual({});
46
+ });
47
+
48
+ it('empty forgeax.assets.roots array falls back to default', async () => {
49
+ const dir = join(tmpDir, 'empty-roots');
50
+ await mkdir(dir);
51
+ await writeFile(
52
+ join(dir, 'package.json'),
53
+ JSON.stringify({
54
+ name: 'test',
55
+ forgeax: { assets: { roots: [] } },
56
+ }),
57
+ );
58
+ const result = loadAssetConfig(dir);
59
+ expect(result.roots).toEqual([join(dir, 'assets')]);
60
+ });
61
+
62
+ it('reads forgeax.assets.paths with cwd-relative resolution', async () => {
63
+ const dir = join(tmpDir, 'with-paths');
64
+ await mkdir(dir);
65
+ await writeFile(
66
+ join(dir, 'package.json'),
67
+ JSON.stringify({
68
+ name: 'test',
69
+ forgeax: {
70
+ assets: {
71
+ paths: {
72
+ shared: 'lib/assets',
73
+ vendor: '../vendor-assets',
74
+ },
75
+ },
76
+ },
77
+ }),
78
+ );
79
+ const result = loadAssetConfig(dir);
80
+ expect(result.paths).toEqual({
81
+ shared: join(dir, 'lib/assets'),
82
+ vendor: join(dir, '../vendor-assets'),
83
+ });
84
+ });
85
+
86
+ it('paths absent defaults to empty object', async () => {
87
+ const dir = join(tmpDir, 'roots-only');
88
+ await mkdir(dir);
89
+ await writeFile(
90
+ join(dir, 'package.json'),
91
+ JSON.stringify({
92
+ name: 'test',
93
+ forgeax: { assets: { roots: ['a'] } },
94
+ }),
95
+ );
96
+ const result = loadAssetConfig(dir);
97
+ expect(result.paths).toEqual({});
98
+ });
99
+
100
+ it('non-string path values are skipped', async () => {
101
+ const dir = join(tmpDir, 'bad-paths');
102
+ await mkdir(dir);
103
+ await writeFile(
104
+ join(dir, 'package.json'),
105
+ JSON.stringify({
106
+ name: 'test',
107
+ forgeax: {
108
+ assets: {
109
+ paths: {
110
+ good: 'lib/a',
111
+ badNum: 42,
112
+ badBool: true,
113
+ },
114
+ },
115
+ },
116
+ }),
117
+ );
118
+ const result = loadAssetConfig(dir);
119
+ expect(result.paths).toEqual({ good: join(dir, 'lib/a') });
120
+ });
121
+ });
@@ -0,0 +1,112 @@
1
+ import type { AssetGuid } from '@forgeax/engine-types';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { collectMaterialCookRefs } from '../evidence/material-cook.js';
4
+ import { NativeCookerRegistry } from '../native-cooker-registry.js';
5
+ import { createRuntimePackPublication } from '../runtime-publication.js';
6
+
7
+ describe('material cook dependency closure', () => {
8
+ it('collects parent, texture, sampler, and module references', () => {
9
+ expect(
10
+ collectMaterialCookRefs({
11
+ parent: 'mat-parent' as unknown as AssetGuid,
12
+ passes: [{ name: 'forward', program: { module: 'module/pbr' } }],
13
+ values: {
14
+ baseColor: {
15
+ texture: 'texture/albedo' as unknown as AssetGuid,
16
+ sampler: 'sampler/linear' as unknown as AssetGuid,
17
+ },
18
+ },
19
+ }),
20
+ ).toEqual({
21
+ parent: ['mat-parent'],
22
+ textures: ['texture/albedo'],
23
+ samplers: ['sampler/linear'],
24
+ modules: ['module/pbr'],
25
+ });
26
+ });
27
+
28
+ it('collects string shorthands only from declared texture parameters', () => {
29
+ expect(
30
+ collectMaterialCookRefs({
31
+ parameters: [
32
+ { name: 'surface', type: 'texture' },
33
+ { name: 'label', type: 'f32' },
34
+ ],
35
+ values: { surface: 'texture/albedo', label: 'not-a-texture-ref' },
36
+ }),
37
+ ).toEqual({ parent: [], textures: ['texture/albedo'], samplers: [], modules: [] });
38
+ });
39
+
40
+ it('treats a string value as a texture shorthand when parameters are omitted', () => {
41
+ expect(
42
+ collectMaterialCookRefs({
43
+ values: { baseColorTexture: 'texture/albedo' },
44
+ }),
45
+ ).toEqual({ parent: [], textures: ['texture/albedo'], samplers: [], modules: [] });
46
+ });
47
+
48
+ it('keeps the last known good candidate when a generation fails before publication', async () => {
49
+ const registry = new NativeCookerRegistry();
50
+ let fail = false;
51
+ registry.register({
52
+ key: 'material',
53
+ cook: () => {
54
+ if (fail) throw new Error('candidate rejected');
55
+ return {
56
+ guid: 'material-child',
57
+ payload: { kind: 'material' },
58
+ refs: ['texture/albedo'],
59
+ artifacts: {
60
+ 'material-child/shader.wgsl': {
61
+ mediaType: 'text/wgsl',
62
+ bytes: new TextEncoder().encode('shader'),
63
+ },
64
+ },
65
+ inputFingerprint: 'sha256:material-candidate',
66
+ };
67
+ },
68
+ });
69
+
70
+ const committed = await registry.runTransaction({ key: 'material', input: {} });
71
+ expect(committed.ok).toBe(true);
72
+ if (!committed.ok) throw new Error(committed.error.hint);
73
+ fail = true;
74
+ const recovered = await registry.runTransaction({
75
+ key: 'material',
76
+ input: {},
77
+ previous: committed.value,
78
+ });
79
+
80
+ expect(recovered).toMatchObject({
81
+ ok: true,
82
+ value: {
83
+ status: 'recovered',
84
+ generation: 1,
85
+ candidateGeneration: 2,
86
+ lastKnownGoodGeneration: 1,
87
+ lastKnownGood: committed.value.draft,
88
+ },
89
+ });
90
+ });
91
+
92
+ it('keeps runtime publication identity independent from source-path diagnostics', () => {
93
+ const input = {
94
+ pack: {
95
+ assets: [{ guid: 'material-child', kind: 'material', payload: { roughness: 0.5 } }],
96
+ },
97
+ scopeId: 'project',
98
+ sourceRevision: 'rev-1',
99
+ packageUrl: '/packs/materials.json',
100
+ };
101
+ const first = createRuntimePackPublication({ ...input, sourcePath: '/src/materials.json' });
102
+ const moved = createRuntimePackPublication({ ...input, sourcePath: '/moved/materials.json' });
103
+
104
+ expect(moved.pack.digest).toBe(first.pack.digest);
105
+ expect(moved.publication.digest).toBe(first.publication.digest);
106
+ expect(moved.publication.outputSetDigest).toBe(first.publication.outputSetDigest);
107
+ expect(moved.publication.receipt.inputFingerprint).toBe(
108
+ first.publication.receipt.inputFingerprint,
109
+ );
110
+ expect(moved.publication.sourcePath).toBe('/moved/materials.json');
111
+ });
112
+ });
@@ -0,0 +1,148 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ type MaterialCookReceipt,
4
+ serializeMaterialCookReceipt,
5
+ validateMaterialCookReceipt,
6
+ } from '../evidence/material-cook.js';
7
+
8
+ const layoutIdentity = 'sha256:layout-identity';
9
+ const artifactDigest = 'sha256:output';
10
+
11
+ function receipt(overrides: Record<string, unknown> = {}): MaterialCookReceipt {
12
+ return {
13
+ schemaVersion: 'material-cook/3',
14
+ sourceClosure: ['b.wgsl', 'a.material.json'],
15
+ profile: 'webgpu/v1',
16
+ compilerVersion: 'compiler/1',
17
+ identity: {
18
+ materialContractDigest: 'sha256:material-contract',
19
+ sourceRevision: 'sha256:source-revision',
20
+ sourceClosureDigest: 'sha256:source-closure',
21
+ layoutIdentity,
22
+ programIdentity: 'sha256:program',
23
+ pipelineIdentity: 'sha256:pipeline',
24
+ materialPublicationIdentity: 'sha256:publication',
25
+ cookIdentity: 'sha256:input',
26
+ compilerFingerprint: 'sha256:compiler',
27
+ wasm: {
28
+ sourceContentKey: 'sha256:wasm-source',
29
+ artifactSha256: 'sha256:wasm-artifact',
30
+ glueSha256: 'sha256:wasm-glue',
31
+ },
32
+ artifactDigest,
33
+ valueGeneration: 1,
34
+ dependencyGeneration: 1,
35
+ cookGeneration: 1,
36
+ },
37
+ derivedInterface: { layoutIdentity },
38
+ ...overrides,
39
+ } as unknown as MaterialCookReceipt;
40
+ }
41
+
42
+ describe('material cook receipt', () => {
43
+ it('requires the v3 identity and provenance tuple', () => {
44
+ const v3 = {
45
+ schemaVersion: 'material-cook/3',
46
+ sourceClosure: ['materials/mat.material.json', 'shaders/pbr.wgsl'],
47
+ profile: 'webgpu/v1',
48
+ compilerVersion: 'compiler/1',
49
+ identity: {
50
+ materialContractDigest: 'sha256:material-contract',
51
+ sourceRevision: 'sha256:source-revision',
52
+ sourceClosureDigest: 'sha256:source-closure',
53
+ layoutIdentity,
54
+ programIdentity: 'sha256:program',
55
+ pipelineIdentity: 'sha256:pipeline',
56
+ materialPublicationIdentity: 'sha256:publication',
57
+ cookIdentity: 'sha256:cook',
58
+ compilerFingerprint: 'sha256:compiler',
59
+ wasm: {
60
+ sourceContentKey: 'sha256:wasm-source',
61
+ artifactSha256: 'sha256:wasm-artifact',
62
+ glueSha256: 'sha256:wasm-glue',
63
+ },
64
+ artifactDigest,
65
+ valueGeneration: 3,
66
+ dependencyGeneration: 2,
67
+ cookGeneration: 7,
68
+ },
69
+ derivedInterface: { layoutIdentity },
70
+ };
71
+ expect(validateMaterialCookReceipt(v3)).toMatchObject({ ok: true, value: v3 });
72
+ expect(validateMaterialCookReceipt(receipt())).toMatchObject({ ok: true });
73
+ expect(
74
+ validateMaterialCookReceipt({
75
+ ...v3,
76
+ identity: { ...v3.identity, compilerFingerprint: undefined },
77
+ }),
78
+ ).toMatchObject({
79
+ ok: false,
80
+ error: { code: 'material-cook-record-invalid' },
81
+ });
82
+ });
83
+
84
+ it('serializes a material-cook/3 receipt stably for equivalent cooks', () => {
85
+ const first = serializeMaterialCookReceipt(receipt());
86
+ const second = serializeMaterialCookReceipt(
87
+ receipt({ sourceClosure: ['a.material.json', 'b.wgsl'] }),
88
+ );
89
+ expect(first).toBe(second);
90
+ expect(JSON.parse(first)).toMatchObject({
91
+ schemaVersion: 'material-cook/3',
92
+ identity: { layoutIdentity },
93
+ derivedInterface: { layoutIdentity },
94
+ });
95
+ });
96
+
97
+ it('accepts the expected identity and rejects schema or interface mutations', () => {
98
+ const expected = { layoutIdentity, artifactDigest, inputDigest: 'sha256:input' };
99
+ expect(validateMaterialCookReceipt(receipt(), expected)).toEqual({
100
+ ok: true,
101
+ value: receipt(),
102
+ });
103
+ expect(
104
+ validateMaterialCookReceipt(
105
+ receipt({ identity: { ...receipt().identity, layoutIdentity: 'sha256:changed-layout' } }),
106
+ expected,
107
+ ),
108
+ ).toMatchObject({ ok: false, error: { code: 'material-cook-record-invalid' } });
109
+ expect(
110
+ validateMaterialCookReceipt(
111
+ receipt({ derivedInterface: { layoutIdentity: 'sha256:changed-layout' } }),
112
+ expected,
113
+ ),
114
+ ).toMatchObject({ ok: false, error: { code: 'material-cook-record-invalid' } });
115
+ });
116
+
117
+ it('rejects missing, stale, mismatched, and material-cook/1 receipts fail-fast', () => {
118
+ const expected = { layoutIdentity, artifactDigest, inputDigest: 'sha256:input' };
119
+ for (const value of [
120
+ receipt({ identity: { ...receipt().identity, layoutIdentity: undefined } }),
121
+ receipt({ identity: { ...receipt().identity, artifactDigest: 'sha256:stale-artifact' } }),
122
+ receipt({ identity: { ...receipt().identity, cookIdentity: 'sha256:stale-input' } }),
123
+ {
124
+ sourceClosure: [],
125
+ profile: 'webgpu/v1',
126
+ compilerVersion: 'compiler/1',
127
+ identity: receipt().identity,
128
+ },
129
+ ]) {
130
+ expect(validateMaterialCookReceipt(value as MaterialCookReceipt, expected)).toMatchObject({
131
+ ok: false,
132
+ error: { code: 'material-cook-record-invalid' },
133
+ });
134
+ }
135
+ });
136
+
137
+ it('records the coordinator-owned publication generation', () => {
138
+ const generationReceipt = {
139
+ sourceClosure: ['materials/mat-root.material.json'],
140
+ profile: 'webgpu/v1',
141
+ compilerVersion: 'compiler/1',
142
+ identity: { ...receipt().identity, cookGeneration: 11 },
143
+ };
144
+ expect(generationReceipt).toMatchObject({
145
+ identity: { cookGeneration: 11 },
146
+ });
147
+ });
148
+ });
@@ -0,0 +1,152 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { describe, expect, it } from 'vitest';
4
+ import {
5
+ type CookedMaterialRecord,
6
+ createMaterialArtifactDigest,
7
+ projectCookedMaterialRecord,
8
+ serializeCookedMaterialRecord,
9
+ validateCookedMaterialRecord,
10
+ } from '../evidence/material-cook.js';
11
+
12
+ const record: CookedMaterialRecord = {
13
+ schemaVersion: 'material-cook/3',
14
+ guid: 'mat-root',
15
+ authored: {
16
+ kind: 'material',
17
+ passes: [{ name: 'forward', program: { module: 'core/pbr' } }],
18
+ parameters: [{ name: 'roughness', type: 'f32' }],
19
+ values: { roughness: 0.5 },
20
+ },
21
+ resolved: {
22
+ passes: [{ name: 'forward', program: { module: 'core/pbr' } }],
23
+ parameters: [{ name: 'roughness', type: 'f32' }],
24
+ values: { roughness: 0.5 },
25
+ },
26
+ refs: { parent: [], textures: [], samplers: [], modules: ['core/pbr'] },
27
+ artifact: {
28
+ mediaType: 'text/wgsl',
29
+ path: 'materials/mat-root/forward.wgsl',
30
+ digest: 'sha256:artifact',
31
+ bytes: new TextEncoder().encode('shader'),
32
+ },
33
+ receipt: {
34
+ schemaVersion: 'material-cook/3',
35
+ sourceClosure: ['materials/mat-root.material.json'],
36
+ profile: 'webgpu/v1',
37
+ compilerVersion: 'compiler/1',
38
+ identity: {
39
+ materialContractDigest: 'sha256:material-contract',
40
+ sourceRevision: 'sha256:source-revision',
41
+ sourceClosureDigest: 'sha256:source-closure',
42
+ layoutIdentity: 'sha256:layout',
43
+ programIdentity: 'sha256:program',
44
+ pipelineIdentity: 'sha256:pipeline',
45
+ materialPublicationIdentity: 'sha256:publication',
46
+ cookIdentity: 'sha256:input',
47
+ compilerFingerprint: 'sha256:compiler',
48
+ wasm: {
49
+ sourceContentKey: 'sha256:wasm-source',
50
+ artifactSha256: 'sha256:wasm-artifact',
51
+ glueSha256: 'sha256:wasm-glue',
52
+ },
53
+ artifactDigest: 'sha256:artifact',
54
+ valueGeneration: 1,
55
+ dependencyGeneration: 1,
56
+ cookGeneration: 1,
57
+ },
58
+ derivedInterface: { layoutIdentity: 'sha256:layout' },
59
+ },
60
+ };
61
+
62
+ describe('cooked material schema', () => {
63
+ it('requires material-cook/3 identity and derived interface association', () => {
64
+ const schema = JSON.parse(
65
+ readFileSync(
66
+ fileURLToPath(new URL('../schema/material-cook.schema.json', import.meta.url)),
67
+ 'utf8',
68
+ ),
69
+ ) as {
70
+ properties: Record<
71
+ string,
72
+ { const?: string; required?: string[]; properties?: Record<string, unknown> }
73
+ >;
74
+ required: string[];
75
+ };
76
+ expect(schema.properties.schemaVersion?.const).toBe('material-cook/3');
77
+ expect(schema.properties.receipt?.required).toEqual(
78
+ expect.arrayContaining(['identity', 'derivedInterface']),
79
+ );
80
+ expect(schema.properties.receipt?.properties?.derivedInterface).toBeDefined();
81
+ });
82
+
83
+ it('defines the v3 identity, provenance, and generation contract', () => {
84
+ const schema = JSON.parse(
85
+ readFileSync(
86
+ fileURLToPath(new URL('../schema/material-cook.schema.json', import.meta.url)),
87
+ 'utf8',
88
+ ),
89
+ ) as {
90
+ properties: Record<
91
+ string,
92
+ { const?: string; required?: string[]; properties?: Record<string, unknown> }
93
+ >;
94
+ };
95
+ expect(schema.properties.schemaVersion?.const).toBe('material-cook/3');
96
+ expect(schema.properties.receipt?.properties?.identity).toMatchObject({
97
+ type: 'object',
98
+ required: expect.arrayContaining([
99
+ 'materialContractDigest',
100
+ 'sourceRevision',
101
+ 'sourceClosureDigest',
102
+ 'layoutIdentity',
103
+ 'programIdentity',
104
+ 'pipelineIdentity',
105
+ 'materialPublicationIdentity',
106
+ 'cookIdentity',
107
+ 'compilerFingerprint',
108
+ 'wasm',
109
+ 'artifactDigest',
110
+ 'valueGeneration',
111
+ 'dependencyGeneration',
112
+ 'cookGeneration',
113
+ ]),
114
+ });
115
+ expect(schema.properties.receipt?.required).toEqual(
116
+ expect.arrayContaining(['identity', 'derivedInterface']),
117
+ );
118
+ });
119
+
120
+ it('serializes a stable cooked DTO and derives an artifact digest', () => {
121
+ const first = serializeCookedMaterialRecord(record);
122
+ const second = serializeCookedMaterialRecord({ ...record });
123
+
124
+ expect(first).toBe(second);
125
+ expect(createMaterialArtifactDigest(record.artifact.bytes)).toBe(
126
+ 'sha256:e137e75a5e0e7a623ca39de480667a55b43e0eedec58767634ebaef07c33383a',
127
+ );
128
+ expect(validateCookedMaterialRecord(JSON.parse(first))).toEqual({ ok: true, value: record });
129
+ });
130
+
131
+ it('projects cooked data without exposing an authored GUID entry', () => {
132
+ const projection = projectCookedMaterialRecord(record);
133
+
134
+ expect(projection.resolved.values).toEqual({ roughness: 0.5 });
135
+ expect(projection.artifact.digest).toBe('sha256:artifact');
136
+ expect(projection).not.toHaveProperty('guid');
137
+ expect(projection).not.toHaveProperty('authored');
138
+ });
139
+
140
+ it('rejects missing fields and unsupported schema versions', () => {
141
+ expect(validateCookedMaterialRecord({ ...record, refs: undefined })).toMatchObject({
142
+ ok: false,
143
+ error: { code: 'material-cook-record-invalid' },
144
+ });
145
+ expect(
146
+ validateCookedMaterialRecord({ ...record, schemaVersion: 'material-cook/1' }),
147
+ ).toMatchObject({
148
+ ok: false,
149
+ error: { code: 'material-cook-record-invalid' },
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,22 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ const encoderSource = readFileSync(
5
+ new URL('../../../import/src/mesh-bin.ts', import.meta.url),
6
+ 'utf8',
7
+ );
8
+ const decoderSource = readFileSync(
9
+ new URL('../../../geometry/src/assets/mesh-binary.ts', import.meta.url),
10
+ 'utf8',
11
+ );
12
+
13
+ describe('mesh-bin consumer owner surface', () => {
14
+ it('routes both production halves through Pack-owned facts', () => {
15
+ expect(encoderSource).toContain("from '@forgeax/engine-pack/mesh-bin-contract'");
16
+ expect(decoderSource).toContain("from '@forgeax/engine-pack'");
17
+ for (const source of [encoderSource, decoderSource]) {
18
+ expect(source).toContain('MESH_BIN_HEADER_V4_BYTES');
19
+ expect(source).not.toContain('const HEADER_V2_BYTES = 28');
20
+ }
21
+ });
22
+ });
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ decodeMeshBinHeader,
4
+ MESH_BIN_HEADER_V4_BYTES,
5
+ MESH_BIN_VERSION,
6
+ } from '../mesh-bin-contract.js';
7
+
8
+ describe('mesh-bin v4 wire contract', () => {
9
+ it('owns a fixed v4 header and rejects legacy versions at the contract boundary', () => {
10
+ expect(MESH_BIN_VERSION).toBe(4);
11
+ expect(MESH_BIN_HEADER_V4_BYTES).toBeGreaterThan(28);
12
+ expect(decodeMeshBinHeader(new Uint8Array(MESH_BIN_HEADER_V4_BYTES))).toMatchObject({
13
+ ok: false,
14
+ error: { code: 'mesh-bin-version-unsupported' },
15
+ });
16
+ });
17
+
18
+ it('reports truncated headers as structured failures with recovery context', () => {
19
+ const result = decodeMeshBinHeader(new Uint8Array(MESH_BIN_HEADER_V4_BYTES - 1), 'mesh/source');
20
+ expect(result).toMatchObject({
21
+ ok: false,
22
+ error: {
23
+ code: 'mesh-bin-header-truncated',
24
+ subject: 'mesh-bin',
25
+ sourceKey: 'mesh/source',
26
+ expected: expect.any(String),
27
+ actual: expect.any(String),
28
+ recovery: expect.stringContaining('re-cook'),
29
+ },
30
+ });
31
+ });
32
+ });