@forgeax/engine-pack 0.1.2

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 (228) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +370 -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__/pack-v2-fixture-migration.test.d.ts +2 -0
  51. package/dist/__tests__/pack-v2-fixture-migration.test.d.ts.map +1 -0
  52. package/dist/__tests__/pack-v2-schema.test.d.ts +2 -0
  53. package/dist/__tests__/pack-v2-schema.test.d.ts.map +1 -0
  54. package/dist/__tests__/pack.unit.test.d.ts +2 -0
  55. package/dist/__tests__/pack.unit.test.d.ts.map +1 -0
  56. package/dist/__tests__/producer-contract-schema.unit.test.d.ts +2 -0
  57. package/dist/__tests__/producer-contract-schema.unit.test.d.ts.map +1 -0
  58. package/dist/__tests__/resolve-asset-source.test.d.ts +2 -0
  59. package/dist/__tests__/resolve-asset-source.test.d.ts.map +1 -0
  60. package/dist/__tests__/runtime-projection.unit.test.d.ts +2 -0
  61. package/dist/__tests__/runtime-projection.unit.test.d.ts.map +1 -0
  62. package/dist/__tests__/scanner-inventory.test.d.ts +2 -0
  63. package/dist/__tests__/scanner-inventory.test.d.ts.map +1 -0
  64. package/dist/__tests__/scanner-mount-cycle.test.d.ts +2 -0
  65. package/dist/__tests__/scanner-mount-cycle.test.d.ts.map +1 -0
  66. package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts +2 -0
  67. package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts.map +1 -0
  68. package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts +2 -0
  69. package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts.map +1 -0
  70. package/dist/__tests__/scriptable-pack.test-d.d.ts +2 -0
  71. package/dist/__tests__/scriptable-pack.test-d.d.ts.map +1 -0
  72. package/dist/__tests__/scriptable-pack.unit.test.d.ts +2 -0
  73. package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +1 -0
  74. package/dist/__tests__/topology-actions.unit.test.d.ts +2 -0
  75. package/dist/__tests__/topology-actions.unit.test.d.ts.map +1 -0
  76. package/dist/__tests__/topology.unit.test.d.ts +2 -0
  77. package/dist/__tests__/topology.unit.test.d.ts.map +1 -0
  78. package/dist/artifact-path.d.ts +9 -0
  79. package/dist/artifact-path.d.ts.map +1 -0
  80. package/dist/atlas/run-atlas.d.ts +8 -0
  81. package/dist/atlas/run-atlas.d.ts.map +1 -0
  82. package/dist/atlas/shelf-pack.d.ts +55 -0
  83. package/dist/atlas/shelf-pack.d.ts.map +1 -0
  84. package/dist/bridge.d.ts +2 -0
  85. package/dist/bridge.d.ts.map +1 -0
  86. package/dist/bridge.mjs +3 -0
  87. package/dist/bridge.mjs.map +1 -0
  88. package/dist/builtin.d.ts +24 -0
  89. package/dist/builtin.d.ts.map +1 -0
  90. package/dist/cli-asset.d.ts +22 -0
  91. package/dist/cli-asset.d.ts.map +1 -0
  92. package/dist/cli-asset.mjs +9950 -0
  93. package/dist/cli-asset.mjs.map +1 -0
  94. package/dist/config.d.ts +6 -0
  95. package/dist/config.d.ts.map +1 -0
  96. package/dist/config.mjs +29 -0
  97. package/dist/config.mjs.map +1 -0
  98. package/dist/deriveAssetName.d.ts +15 -0
  99. package/dist/deriveAssetName.d.ts.map +1 -0
  100. package/dist/errors.d.ts +30 -0
  101. package/dist/errors.d.ts.map +1 -0
  102. package/dist/errors.mjs +19 -0
  103. package/dist/errors.mjs.map +1 -0
  104. package/dist/evidence/material-cook.d.ts +70 -0
  105. package/dist/evidence/material-cook.d.ts.map +1 -0
  106. package/dist/evidence/offline-evidence.d.ts +25 -0
  107. package/dist/evidence/offline-evidence.d.ts.map +1 -0
  108. package/dist/evidence/source-inventory.d.ts +29 -0
  109. package/dist/evidence/source-inventory.d.ts.map +1 -0
  110. package/dist/guid.d.ts +45 -0
  111. package/dist/guid.d.ts.map +1 -0
  112. package/dist/guid.mjs +94 -0
  113. package/dist/guid.mjs.map +1 -0
  114. package/dist/index.d.ts +15 -0
  115. package/dist/index.d.ts.map +1 -0
  116. package/dist/index.mjs +8475 -0
  117. package/dist/index.mjs.map +1 -0
  118. package/dist/material/artifact-writer.d.ts +14 -0
  119. package/dist/material/artifact-writer.d.ts.map +1 -0
  120. package/dist/mesh-bin-contract.d.ts +36 -0
  121. package/dist/mesh-bin-contract.d.ts.map +1 -0
  122. package/dist/name.mjs +22 -0
  123. package/dist/name.mjs.map +1 -0
  124. package/dist/native-cooker-registry.d.ts +28 -0
  125. package/dist/native-cooker-registry.d.ts.map +1 -0
  126. package/dist/native-cooker.d.ts +3 -0
  127. package/dist/native-cooker.d.ts.map +1 -0
  128. package/dist/native-cooker.mjs +105 -0
  129. package/dist/native-cooker.mjs.map +1 -0
  130. package/dist/producer-contract.d.ts +19 -0
  131. package/dist/producer-contract.d.ts.map +1 -0
  132. package/dist/resolve-asset-source.d.ts +4 -0
  133. package/dist/resolve-asset-source.d.ts.map +1 -0
  134. package/dist/resolve-asset-source.mjs +99 -0
  135. package/dist/resolve-asset-source.mjs.map +1 -0
  136. package/dist/runtime-projection.d.ts +14 -0
  137. package/dist/runtime-projection.d.ts.map +1 -0
  138. package/dist/scanner.d.ts +80 -0
  139. package/dist/scanner.d.ts.map +1 -0
  140. package/dist/scanner.mjs +9015 -0
  141. package/dist/scanner.mjs.map +1 -0
  142. package/dist/schema-compiled.d.ts +12 -0
  143. package/dist/schema-compiled.d.ts.map +1 -0
  144. package/dist/schema.mjs +7369 -0
  145. package/dist/schema.mjs.map +1 -0
  146. package/dist/scriptable-pack-node.d.ts +67 -0
  147. package/dist/scriptable-pack-node.d.ts.map +1 -0
  148. package/dist/scriptable-pack-node.mjs +1590 -0
  149. package/dist/scriptable-pack-node.mjs.map +1 -0
  150. package/dist/scriptable-pack-worker.d.ts +2 -0
  151. package/dist/scriptable-pack-worker.d.ts.map +1 -0
  152. package/dist/scriptable-pack-worker.mjs +223 -0
  153. package/dist/scriptable-pack-worker.mjs.map +1 -0
  154. package/dist/scriptable-pack.d.ts +525 -0
  155. package/dist/scriptable-pack.d.ts.map +1 -0
  156. package/dist/scriptable-pack.mjs +583 -0
  157. package/dist/scriptable-pack.mjs.map +1 -0
  158. package/dist/topology.d.ts +10 -0
  159. package/dist/topology.d.ts.map +1 -0
  160. package/package.json +129 -0
  161. package/schema/meta.schema.json +173 -0
  162. package/schema/pack.schema.json +232 -0
  163. package/src/__tests__/ai-recovery.unit.test.ts +114 -0
  164. package/src/__tests__/artifact-path.test.ts +41 -0
  165. package/src/__tests__/asset-evidence-error-hints.test.ts +46 -0
  166. package/src/__tests__/asset-evidence-lifecycle.test.ts +104 -0
  167. package/src/__tests__/asset-evidence-schema.test.ts +86 -0
  168. package/src/__tests__/atlas-cli-region-mismatch.test.ts +134 -0
  169. package/src/__tests__/atlas-error-code-owner.test-d.ts +23 -0
  170. package/src/__tests__/cli-asset-evidence.integration.test.ts +172 -0
  171. package/src/__tests__/cli.unit.test.ts +448 -0
  172. package/src/__tests__/evidence-source-inventory.unit.test.ts +37 -0
  173. package/src/__tests__/guid-collision.unit.test.ts +112 -0
  174. package/src/__tests__/guid-validator-surface.unit.test.ts +20 -0
  175. package/src/__tests__/load-asset-config.test.ts +121 -0
  176. package/src/__tests__/material-cook-dependencies.unit.test.ts +45 -0
  177. package/src/__tests__/material-cook-receipt.unit.test.ts +91 -0
  178. package/src/__tests__/material-cook-schema.unit.test.ts +98 -0
  179. package/src/__tests__/mesh-bin-consumer-surface.unit.test.ts +21 -0
  180. package/src/__tests__/mesh-bin-contract.unit.test.ts +32 -0
  181. package/src/__tests__/meta-schema-optional.test.ts +116 -0
  182. package/src/__tests__/meta-source-overrides.unit.test.ts +71 -0
  183. package/src/__tests__/native-cooker-registry.test.ts +64 -0
  184. package/src/__tests__/offline-evidence-artifact-verification-owner.test-d.ts +64 -0
  185. package/src/__tests__/offline-evidence.integration.test.ts +106 -0
  186. package/src/__tests__/pack-v2-fixture-migration.test.ts +64 -0
  187. package/src/__tests__/pack-v2-schema.test.ts +142 -0
  188. package/src/__tests__/pack.unit.test.ts +2218 -0
  189. package/src/__tests__/producer-contract-schema.unit.test.ts +92 -0
  190. package/src/__tests__/resolve-asset-source.test.ts +121 -0
  191. package/src/__tests__/runtime-projection.unit.test.ts +87 -0
  192. package/src/__tests__/scanner-inventory.test.ts +72 -0
  193. package/src/__tests__/scanner-mount-cycle.test.ts +232 -0
  194. package/src/__tests__/scriptable-pack-cli.integration.test.ts +47 -0
  195. package/src/__tests__/scriptable-pack-ddc-key.unit.test.ts +63 -0
  196. package/src/__tests__/scriptable-pack.test-d.ts +131 -0
  197. package/src/__tests__/scriptable-pack.unit.test.ts +747 -0
  198. package/src/__tests__/topology-actions.unit.test.ts +94 -0
  199. package/src/__tests__/topology.unit.test.ts +85 -0
  200. package/src/artifact-path.ts +69 -0
  201. package/src/atlas/run-atlas.ts +358 -0
  202. package/src/atlas/shelf-pack.ts +176 -0
  203. package/src/atlas/upng-ambient.d.ts +13 -0
  204. package/src/bridge.ts +2 -0
  205. package/src/builtin.ts +79 -0
  206. package/src/cli-asset.ts +590 -0
  207. package/src/config.ts +36 -0
  208. package/src/deriveAssetName.ts +42 -0
  209. package/src/errors.ts +47 -0
  210. package/src/evidence/material-cook.ts +332 -0
  211. package/src/evidence/offline-evidence.ts +90 -0
  212. package/src/evidence/source-inventory.ts +193 -0
  213. package/src/guid.ts +99 -0
  214. package/src/index.ts +104 -0
  215. package/src/material/artifact-writer.ts +26 -0
  216. package/src/mesh-bin-contract.ts +150 -0
  217. package/src/native-cooker-registry.ts +163 -0
  218. package/src/native-cooker.ts +2 -0
  219. package/src/producer-contract.ts +277 -0
  220. package/src/resolve-asset-source.ts +93 -0
  221. package/src/runtime-projection.ts +100 -0
  222. package/src/scanner.ts +716 -0
  223. package/src/schema/material-cook.schema.json +79 -0
  224. package/src/schema-compiled.ts +238 -0
  225. package/src/scriptable-pack-node.ts +1390 -0
  226. package/src/scriptable-pack-worker.ts +306 -0
  227. package/src/scriptable-pack.ts +884 -0
  228. package/src/topology.ts +156 -0
@@ -0,0 +1,64 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import type {
3
+ ArtifactDescriptor,
4
+ ArtifactVerificationStatus,
5
+ AssetEvidenceArtifact,
6
+ AssetEvidencePackageInput,
7
+ } from '@forgeax/engine-types';
8
+ import { describe, expect, expectTypeOf, it } from 'vitest';
9
+ import type { OfflineArtifactInput, OfflinePackageInput } from '../evidence/offline-evidence.js';
10
+ import type { buildOfflineAssetEvidence } from '../index.js';
11
+
12
+ const guid = '11111111-1111-4111-8111-111111111111';
13
+ const projectionSource = readFileSync(
14
+ new URL('../evidence/offline-evidence.ts', import.meta.url),
15
+ 'utf8',
16
+ );
17
+
18
+ type SharedOptionalVerification = AssetEvidencePackageInput['artifacts'][string]['verification'];
19
+ type PublicOfflineArtifact = NonNullable<
20
+ Parameters<typeof buildOfflineAssetEvidence>[0]['package']
21
+ >['artifacts'][string];
22
+
23
+ describe('offline artifact verification owner', () => {
24
+ it('derives the optional offline projection from the shared status owner', () => {
25
+ expectTypeOf<ArtifactVerificationStatus>().toEqualTypeOf<'notChecked' | 'passed' | 'failed'>();
26
+ expectTypeOf<OfflineArtifactInput['verification']>().toEqualTypeOf<
27
+ ArtifactVerificationStatus | undefined
28
+ >();
29
+ expectTypeOf<
30
+ OfflineArtifactInput['verification']
31
+ >().toEqualTypeOf<SharedOptionalVerification>();
32
+ expectTypeOf<
33
+ AssetEvidenceArtifact['verification']
34
+ >().toEqualTypeOf<ArtifactVerificationStatus>();
35
+ expectTypeOf<PublicOfflineArtifact['verification']>().toEqualTypeOf<
36
+ ArtifactVerificationStatus | undefined
37
+ >();
38
+ expectTypeOf<'unknown'>().not.toExtend<ArtifactVerificationStatus>();
39
+ });
40
+
41
+ it('keeps artifact verification optional at the offline input boundary', () => {
42
+ const descriptor = {
43
+ path: 'artifacts/data.bin',
44
+ mediaType: 'application/octet-stream',
45
+ } satisfies ArtifactDescriptor;
46
+ const input = {
47
+ guid,
48
+ artifacts: { data: descriptor },
49
+ } satisfies OfflinePackageInput;
50
+ const publicInput = {
51
+ guid,
52
+ package: input,
53
+ } satisfies Parameters<typeof buildOfflineAssetEvidence>[0];
54
+ void publicInput;
55
+ });
56
+
57
+ it('keeps the source projection tied to the shared type instead of a second ledger', () => {
58
+ expect(projectionSource).toContain('ArtifactVerificationStatus');
59
+ expect(projectionSource).toContain('readonly verification?: ArtifactVerificationStatus;');
60
+ expect(projectionSource).not.toContain(
61
+ "readonly verification?: 'notChecked' | 'passed' | 'failed';",
62
+ );
63
+ });
64
+ });
@@ -0,0 +1,106 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { buildOfflineAssetEvidence } from '../evidence/offline-evidence.js';
4
+
5
+ const guid = '11111111-1111-4111-8111-111111111111';
6
+
7
+ describe('offline evidence adapter', () => {
8
+ it('joins source inventory, catalog, receipt, and package verification', async () => {
9
+ const result = await buildOfflineAssetEvidence({
10
+ guid,
11
+ source: { origin: 'sourceMeta', inputFingerprint: 'fp-1' },
12
+ locator: { packageUrl: '/assets/config.pack.json', cookReceiptUrl: '/receipts/config.json' },
13
+ receipt: {
14
+ guid,
15
+ origin: 'sourceMeta',
16
+ status: 'succeeded',
17
+ inputFingerprint: 'fp-1',
18
+ outputDigest: 'digest-1',
19
+ },
20
+ package: {
21
+ guid,
22
+ digest: 'digest-1',
23
+ artifacts: {
24
+ data: {
25
+ path: 'artifacts/data.bin',
26
+ mediaType: 'application/octet-stream',
27
+ },
28
+ },
29
+ },
30
+ });
31
+
32
+ expect(result.ok).toBe(true);
33
+ if (!result.ok) return;
34
+ expect(result.value.packageUrl).toBe('/assets/config.pack.json');
35
+ expect(result.value.cook.freshness).toBe('current');
36
+ expect(result.value.package?.status).toBe('passed');
37
+ });
38
+
39
+ it('keeps runtime and WS outside the offline adapter boundary', () => {
40
+ expect(buildOfflineAssetEvidence.toString()).not.toContain('assets.load');
41
+ expect(buildOfflineAssetEvidence.toString()).not.toContain('WebSocket');
42
+ expect(buildOfflineAssetEvidence.toString()).not.toContain('AssetRegistry');
43
+ });
44
+
45
+ it('keeps the static Pack boundary gate live for runtime package imports', async () => {
46
+ const gate = await readFile(
47
+ new URL('../../../../packages/pack/scripts/check-no-assets-load.mjs', import.meta.url),
48
+ 'utf8',
49
+ );
50
+
51
+ expect(gate).toContain('@forgeax/engine-(assets-runtime|runtime)');
52
+ expect(gate).toContain(String.raw`engine\\.assets\\.load(`);
53
+ expect(gate).not.toContain("registry.load('");
54
+ });
55
+
56
+ it('projects a verified cook product without requiring DDC storage', async () => {
57
+ const result = await buildOfflineAssetEvidence({
58
+ guid,
59
+ product: {
60
+ guid,
61
+ payload: { kind: 'direct-asset' },
62
+ refs: [],
63
+ artifacts: {
64
+ data: { path: 'assets/data.bin', mediaType: 'application/octet-stream' },
65
+ },
66
+ digest: 'digest-direct',
67
+ receipt: {
68
+ guid,
69
+ origin: 'authoredPack',
70
+ status: 'succeeded',
71
+ inputFingerprint: 'direct-input',
72
+ outputDigest: 'digest-direct',
73
+ },
74
+ },
75
+ });
76
+
77
+ expect(result.ok).toBe(true);
78
+ if (!result.ok) return;
79
+ expect(result.value.cook.freshness).toBe('notApplicable');
80
+ expect(result.value.package?.status).toBe('passed');
81
+ });
82
+
83
+ it('keeps a failed artifact in the offline evidence boundary', async () => {
84
+ const result = await buildOfflineAssetEvidence({
85
+ guid,
86
+ source: { origin: 'sourceMeta', inputFingerprint: 'fp-failed' },
87
+ locator: { packageUrl: '/assets/failed.pack.json' },
88
+ package: {
89
+ guid,
90
+ digest: 'digest-failed',
91
+ artifacts: {
92
+ data: {
93
+ path: 'assets/data.bin',
94
+ mediaType: 'application/octet-stream',
95
+ verification: 'failed',
96
+ },
97
+ },
98
+ },
99
+ });
100
+
101
+ expect(result.ok).toBe(true);
102
+ if (!result.ok) return;
103
+ expect(result.value.package?.status).toBe('failed');
104
+ expect(result.value.cook.freshness).toBe('unknown');
105
+ });
106
+ });
@@ -0,0 +1,64 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { parsePackV2 } from '../index.js';
5
+
6
+ const root = resolve(import.meta.dirname, '../../../../');
7
+
8
+ const fixtures = [
9
+ {
10
+ path: 'apps/hello/cube/assets/cube-mesh.pack.json',
11
+ assets: [{ guid: 'cbe42beb-8975-5096-b3a1-3dda4cb4c077', refs: [] }],
12
+ },
13
+ {
14
+ path: 'apps/hello/room/assets/room.pack.json',
15
+ assets: [
16
+ {
17
+ guid: '019e2808-d3ba-735f-811f-ae7bbb465392',
18
+ refs: [
19
+ 'cbe42beb-8975-5096-b3a1-3dda4cb4c077',
20
+ 'f6af7007-158f-4d92-9e47-93bf2f213e1f',
21
+ '008e4f75-e7a3-4715-b05b-b93a9ec12074',
22
+ ],
23
+ },
24
+ ],
25
+ },
26
+ {
27
+ path: 'apps/hello/scene-nesting/assets/outer-scene.pack.json',
28
+ assets: [
29
+ {
30
+ guid: 'd07a7b8e-9c12-4f6b-a8e1-3d4f5a6b7c8d',
31
+ refs: ['f47ac10b-58cc-4372-a567-0e02b2c3d479', '008e4f75-e7a3-4715-b05b-b93a9ec12074'],
32
+ },
33
+ ],
34
+ },
35
+ ];
36
+
37
+ describe('Pack v2 fixture migration', () => {
38
+ it('preserves logical GUID and refs while requiring local artifact maps', () => {
39
+ for (const fixture of fixtures) {
40
+ const parsed = parsePackV2(JSON.parse(readFileSync(resolve(root, fixture.path), 'utf8')));
41
+ expect(parsed.ok, fixture.path).toBe(true);
42
+ if (!parsed.ok) continue;
43
+ expect(
44
+ parsed.value.assets.map(({ guid, refs, artifacts }) => ({ guid, refs, artifacts })),
45
+ ).toEqual(
46
+ fixture.assets.map((asset) => ({ guid: asset.guid, refs: asset.refs, artifacts: {} })),
47
+ );
48
+ }
49
+ });
50
+
51
+ it('rejects the empty legacy envelope and package-global payload arms', () => {
52
+ expect(
53
+ parsePackV2({ schemaVersion: '1.0.0', kind: 'internal-text-package', assets: [] }).ok,
54
+ ).toBe(false);
55
+ expect(
56
+ parsePackV2({
57
+ schemaVersion: '2.0.0',
58
+ kind: 'internal-text-package',
59
+ assets: [],
60
+ artifacts: [{ path: 'shared.bin', bytes: 'base64' }],
61
+ }).ok,
62
+ ).toBe(false);
63
+ });
64
+ });
@@ -0,0 +1,142 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { validatePack, validatePackV2 } from '../schema-compiled.js';
3
+ import { SCRIPTABLE_PACK_ASSET_KINDS } from '../scriptable-pack.js';
4
+
5
+ const GUID_A = '11111111-1111-4111-8111-111111111111';
6
+ const GUID_B = '22222222-2222-4222-8222-222222222222';
7
+
8
+ function validPack() {
9
+ return {
10
+ schemaVersion: '2.0.0',
11
+ kind: 'internal-text-package',
12
+ packageId: 'pkg/fixture',
13
+ assets: [
14
+ {
15
+ guid: GUID_A,
16
+ kind: 'texture',
17
+ execution: 'cooked',
18
+ payload: { kind: 'texture', width: 1, height: 1 },
19
+ refs: [GUID_B],
20
+ artifacts: {
21
+ image: {
22
+ path: 'artifacts/texture.bin',
23
+ mediaType: 'image/ktx2',
24
+ assetCodec: { name: 'basis', profile: 'uastc', version: '1' },
25
+ contentEncoding: 'zstd',
26
+ byteLength: 4,
27
+ integrity: {
28
+ algorithm: 'sha256',
29
+ digest: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=',
30
+ },
31
+ },
32
+ },
33
+ },
34
+ {
35
+ guid: GUID_B,
36
+ kind: 'material',
37
+ execution: 'cooked',
38
+ payload: { kind: 'material' },
39
+ refs: [],
40
+ artifacts: {},
41
+ },
42
+ ],
43
+ };
44
+ }
45
+
46
+ function mutablePack() {
47
+ return JSON.parse(JSON.stringify(validPack())) as {
48
+ schemaVersion: string;
49
+ kind: string;
50
+ assets: Array<Record<string, unknown>>;
51
+ };
52
+ }
53
+
54
+ describe('Pack v2 schema', () => {
55
+ it('accepts a v2 envelope with asset-local artifacts and separated encoding facts', () => {
56
+ const pack = validPack();
57
+
58
+ expect(validatePack(pack)).toBe(true);
59
+ expect(validatePackV2(pack)).toBe(true);
60
+ });
61
+
62
+ it('rejects a non-v2 schema version', () => {
63
+ const pack = { ...validPack(), schemaVersion: '1.0.0' };
64
+
65
+ expect(validatePackV2(pack)).toBe(false);
66
+ });
67
+
68
+ it('rejects an artifact descriptor without required content facts', () => {
69
+ const pack = mutablePack();
70
+ const asset = pack.assets[0];
71
+ if (!asset) throw new Error('fixture asset is missing');
72
+ const artifacts = asset.artifacts as Record<string, Record<string, unknown>>;
73
+ const image = artifacts.image;
74
+ if (!image) throw new Error('fixture artifact is missing');
75
+ delete image.mediaType;
76
+
77
+ expect(validatePackV2(pack)).toBe(false);
78
+ });
79
+
80
+ it('rejects integrity that is not a structured algorithm and digest object', () => {
81
+ const pack = mutablePack();
82
+ const asset = pack.assets[0];
83
+ if (!asset) throw new Error('fixture asset is missing');
84
+ const artifacts = asset.artifacts as Record<string, Record<string, unknown>>;
85
+ const image = artifacts.image;
86
+ if (!image) throw new Error('fixture artifact is missing');
87
+ image.integrity = 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=';
88
+
89
+ expect(validatePackV2(pack)).toBe(false);
90
+ });
91
+
92
+ it('rejects duplicate asset GUIDs and duplicate artifact keys', () => {
93
+ const duplicateGuidPack = mutablePack();
94
+ const secondAsset = duplicateGuidPack.assets[1];
95
+ if (!secondAsset) throw new Error('fixture asset is missing');
96
+ secondAsset.guid = GUID_A;
97
+
98
+ const duplicateArtifactKeyPack = mutablePack();
99
+ const firstAsset = duplicateArtifactKeyPack.assets[0];
100
+ if (!firstAsset) throw new Error('fixture asset is missing');
101
+ const imageArtifact = (firstAsset.artifacts as Record<string, unknown>).image as Record<
102
+ string,
103
+ unknown
104
+ >;
105
+ firstAsset.artifacts = [
106
+ { key: 'image', ...imageArtifact },
107
+ { key: 'image', ...imageArtifact },
108
+ ];
109
+
110
+ expect(validatePackV2(duplicateGuidPack)).toBe(false);
111
+ expect(validatePackV2(duplicateArtifactKeyPack)).toBe(false);
112
+ });
113
+
114
+ it('rejects missing descriptor fields instead of silently skipping the asset', () => {
115
+ const pack = mutablePack();
116
+ const asset = pack.assets[0];
117
+ if (!asset) throw new Error('fixture asset is missing');
118
+ delete asset.refs;
119
+
120
+ expect(validatePackV2(pack)).toBe(false);
121
+ });
122
+
123
+ it('rejects a v2 asset with no explicit execution mode', () => {
124
+ const pack = mutablePack();
125
+ const asset = pack.assets[0];
126
+ if (!asset) throw new Error('fixture asset is missing');
127
+ delete asset.execution;
128
+
129
+ expect(validatePackV2(pack)).toBe(false);
130
+ });
131
+
132
+ it('accepts every published durable kind without closing producer extensions', () => {
133
+ for (const kind of SCRIPTABLE_PACK_ASSET_KINDS) {
134
+ const pack = validPack();
135
+ const asset = pack.assets[0];
136
+ if (!asset) throw new Error('fixture asset is missing');
137
+ asset.kind = kind;
138
+ asset.payload = { kind };
139
+ expect(validatePackV2(pack), kind).toBe(true);
140
+ }
141
+ });
142
+ });