@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.
- package/LICENSE +202 -0
- package/README.md +429 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/ai-recovery.unit.test.d.ts +2 -0
- package/dist/__tests__/ai-recovery.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-path.test.d.ts +2 -0
- package/dist/__tests__/artifact-path.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-error-hints.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-error-hints.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-lifecycle.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-lifecycle.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-schema.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-schema.test.d.ts.map +1 -0
- package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts +2 -0
- package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts.map +1 -0
- package/dist/__tests__/atlas-error-code-owner.test-d.d.ts +2 -0
- package/dist/__tests__/atlas-error-code-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/cli-asset-evidence.integration.test.d.ts +2 -0
- package/dist/__tests__/cli-asset-evidence.integration.test.d.ts.map +1 -0
- package/dist/__tests__/cli.unit.test.d.ts +2 -0
- package/dist/__tests__/cli.unit.test.d.ts.map +1 -0
- package/dist/__tests__/evidence-source-inventory.unit.test.d.ts +2 -0
- package/dist/__tests__/evidence-source-inventory.unit.test.d.ts.map +1 -0
- package/dist/__tests__/guid-collision.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-collision.unit.test.d.ts.map +1 -0
- package/dist/__tests__/guid-validator-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-validator-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-asset-config.test.d.ts +2 -0
- package/dist/__tests__/load-asset-config.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-dependencies.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-dependencies.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-receipt.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-receipt.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-schema.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-schema.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/meta-schema-optional.test.d.ts +2 -0
- package/dist/__tests__/meta-schema-optional.test.d.ts.map +1 -0
- package/dist/__tests__/meta-source-overrides.unit.test.d.ts +2 -0
- package/dist/__tests__/meta-source-overrides.unit.test.d.ts.map +1 -0
- package/dist/__tests__/native-cooker-registry.test.d.ts +2 -0
- package/dist/__tests__/native-cooker-registry.test.d.ts.map +1 -0
- package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts +2 -0
- package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/offline-evidence.integration.test.d.ts +2 -0
- package/dist/__tests__/offline-evidence.integration.test.d.ts.map +1 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
- package/dist/__tests__/pack-v2-fixture-migration.test.d.ts +2 -0
- package/dist/__tests__/pack-v2-fixture-migration.test.d.ts.map +1 -0
- package/dist/__tests__/pack-v2-schema.test.d.ts +2 -0
- package/dist/__tests__/pack-v2-schema.test.d.ts.map +1 -0
- package/dist/__tests__/pack.unit.test.d.ts +2 -0
- package/dist/__tests__/pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/package-finalizer.contract.test.d.ts +2 -0
- package/dist/__tests__/package-finalizer.contract.test.d.ts.map +1 -0
- package/dist/__tests__/producer-contract-schema.unit.test.d.ts +2 -0
- package/dist/__tests__/producer-contract-schema.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-source.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-source.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-projection.unit.test.d.ts +2 -0
- package/dist/__tests__/runtime-projection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/runtime.browser.test.d.ts +2 -0
- package/dist/__tests__/runtime.browser.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-inventory.contract.test.d.ts +2 -0
- package/dist/__tests__/scanner-inventory.contract.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-inventory.test.d.ts +2 -0
- package/dist/__tests__/scanner-inventory.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-mount-cycle.test.d.ts +2 -0
- package/dist/__tests__/scanner-mount-cycle.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack.test-d.d.ts +2 -0
- package/dist/__tests__/scriptable-pack.test-d.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/topology-actions.unit.test.d.ts +2 -0
- package/dist/__tests__/topology-actions.unit.test.d.ts.map +1 -0
- package/dist/__tests__/topology.unit.test.d.ts +2 -0
- package/dist/__tests__/topology.unit.test.d.ts.map +1 -0
- package/dist/artifact-path.d.ts +9 -0
- package/dist/artifact-path.d.ts.map +1 -0
- package/dist/artifact-path.mjs +53 -0
- package/dist/artifact-path.mjs.map +1 -0
- package/dist/atlas/run-atlas.d.ts +8 -0
- package/dist/atlas/run-atlas.d.ts.map +1 -0
- package/dist/atlas/shelf-pack.d.ts +55 -0
- package/dist/atlas/shelf-pack.d.ts.map +1 -0
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.mjs +3 -0
- package/dist/bridge.mjs.map +1 -0
- package/dist/build.d.ts +19 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.mjs +10203 -0
- package/dist/build.mjs.map +1 -0
- package/dist/builtin.d.ts +44 -0
- package/dist/builtin.d.ts.map +1 -0
- package/dist/builtin.mjs +141 -0
- package/dist/builtin.mjs.map +1 -0
- package/dist/catalog-builder.d.ts +43 -0
- package/dist/catalog-builder.d.ts.map +1 -0
- package/dist/catalog-delta.d.ts +11 -0
- package/dist/catalog-delta.d.ts.map +1 -0
- package/dist/catalog-projection.d.ts +91 -0
- package/dist/catalog-projection.d.ts.map +1 -0
- package/dist/cli-asset.d.ts +22 -0
- package/dist/cli-asset.d.ts.map +1 -0
- package/dist/cli-asset.mjs +9697 -0
- package/dist/cli-asset.mjs.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.mjs +29 -0
- package/dist/config.mjs.map +1 -0
- package/dist/deriveAssetName.d.ts +15 -0
- package/dist/deriveAssetName.d.ts.map +1 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.mjs +19 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/evidence/material-cook.d.ts +92 -0
- package/dist/evidence/material-cook.d.ts.map +1 -0
- package/dist/evidence/offline-evidence.d.ts +25 -0
- package/dist/evidence/offline-evidence.d.ts.map +1 -0
- package/dist/evidence/source-inventory.d.ts +29 -0
- package/dist/evidence/source-inventory.d.ts.map +1 -0
- package/dist/guid.d.ts +45 -0
- package/dist/guid.d.ts.map +1 -0
- package/dist/guid.mjs +94 -0
- package/dist/guid.mjs.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +8612 -0
- package/dist/index.mjs.map +1 -0
- package/dist/material/artifact-writer.d.ts +14 -0
- package/dist/material/artifact-writer.d.ts.map +1 -0
- package/dist/material-cook.d.ts +2 -0
- package/dist/material-cook.d.ts.map +1 -0
- package/dist/material-cook.mjs +248 -0
- package/dist/material-cook.mjs.map +1 -0
- package/dist/mesh-bin-contract.d.ts +36 -0
- package/dist/mesh-bin-contract.d.ts.map +1 -0
- package/dist/mesh-bin-contract.mjs +96 -0
- package/dist/mesh-bin-contract.mjs.map +1 -0
- package/dist/name.mjs +22 -0
- package/dist/name.mjs.map +1 -0
- package/dist/native-cooker-registry.d.ts +47 -0
- package/dist/native-cooker-registry.d.ts.map +1 -0
- package/dist/native-cooker.d.ts +3 -0
- package/dist/native-cooker.d.ts.map +1 -0
- package/dist/native-cooker.mjs +142 -0
- package/dist/native-cooker.mjs.map +1 -0
- package/dist/package-finalizer.d.ts +121 -0
- package/dist/package-finalizer.d.ts.map +1 -0
- package/dist/producer-contract.d.ts +19 -0
- package/dist/producer-contract.d.ts.map +1 -0
- package/dist/resolve-asset-source.d.ts +4 -0
- package/dist/resolve-asset-source.d.ts.map +1 -0
- package/dist/resolve-asset-source.mjs +109 -0
- package/dist/resolve-asset-source.mjs.map +1 -0
- package/dist/runtime-projection.d.ts +14 -0
- package/dist/runtime-projection.d.ts.map +1 -0
- package/dist/runtime-publication.d.ts +44 -0
- package/dist/runtime-publication.d.ts.map +1 -0
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.mjs +7261 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/scanner.d.ts +113 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.mjs +8819 -0
- package/dist/scanner.mjs.map +1 -0
- package/dist/schema-compiled.d.ts +12 -0
- package/dist/schema-compiled.d.ts.map +1 -0
- package/dist/schema.mjs +7369 -0
- package/dist/schema.mjs.map +1 -0
- package/dist/scriptable-pack-node.d.ts +62 -0
- package/dist/scriptable-pack-node.d.ts.map +1 -0
- package/dist/scriptable-pack-node.mjs +1674 -0
- package/dist/scriptable-pack-node.mjs.map +1 -0
- package/dist/scriptable-pack-worker.d.ts +2 -0
- package/dist/scriptable-pack-worker.d.ts.map +1 -0
- package/dist/scriptable-pack-worker.mjs +224 -0
- package/dist/scriptable-pack-worker.mjs.map +1 -0
- package/dist/scriptable-pack.d.ts +558 -0
- package/dist/scriptable-pack.d.ts.map +1 -0
- package/dist/scriptable-pack.mjs +672 -0
- package/dist/scriptable-pack.mjs.map +1 -0
- package/dist/topology.d.ts +10 -0
- package/dist/topology.d.ts.map +1 -0
- package/package.json +154 -0
- package/schema/meta.schema.json +173 -0
- package/schema/pack.schema.json +232 -0
- package/src/__tests__/ai-recovery.unit.test.ts +114 -0
- package/src/__tests__/artifact-path.test.ts +41 -0
- package/src/__tests__/asset-evidence-error-hints.test.ts +46 -0
- package/src/__tests__/asset-evidence-lifecycle.test.ts +104 -0
- package/src/__tests__/asset-evidence-schema.test.ts +86 -0
- package/src/__tests__/atlas-cli-region-mismatch.test.ts +134 -0
- package/src/__tests__/atlas-error-code-owner.test-d.ts +23 -0
- package/src/__tests__/cli-asset-evidence.integration.test.ts +172 -0
- package/src/__tests__/cli.unit.test.ts +448 -0
- package/src/__tests__/evidence-source-inventory.unit.test.ts +37 -0
- package/src/__tests__/guid-collision.unit.test.ts +112 -0
- package/src/__tests__/guid-validator-surface.unit.test.ts +20 -0
- package/src/__tests__/load-asset-config.test.ts +121 -0
- package/src/__tests__/material-cook-dependencies.unit.test.ts +112 -0
- package/src/__tests__/material-cook-receipt.unit.test.ts +148 -0
- package/src/__tests__/material-cook-schema.unit.test.ts +152 -0
- package/src/__tests__/mesh-bin-consumer-surface.unit.test.ts +22 -0
- package/src/__tests__/mesh-bin-contract.unit.test.ts +32 -0
- package/src/__tests__/meta-schema-optional.test.ts +116 -0
- package/src/__tests__/meta-source-overrides.unit.test.ts +71 -0
- package/src/__tests__/native-cooker-registry.test.ts +64 -0
- package/src/__tests__/offline-evidence-artifact-verification-owner.test-d.ts +64 -0
- package/src/__tests__/offline-evidence.integration.test.ts +106 -0
- package/src/__tests__/owner-chain.integration.test.ts +78 -0
- package/src/__tests__/pack-v2-fixture-migration.test.ts +87 -0
- package/src/__tests__/pack-v2-schema.test.ts +142 -0
- package/src/__tests__/pack.unit.test.ts +2218 -0
- package/src/__tests__/package-finalizer.contract.test.ts +105 -0
- package/src/__tests__/producer-contract-schema.unit.test.ts +92 -0
- package/src/__tests__/resolve-asset-source.test.ts +121 -0
- package/src/__tests__/runtime-projection.unit.test.ts +87 -0
- package/src/__tests__/runtime.browser.test.ts +15 -0
- package/src/__tests__/scanner-inventory.contract.test.ts +167 -0
- package/src/__tests__/scanner-inventory.test.ts +76 -0
- package/src/__tests__/scanner-mount-cycle.test.ts +232 -0
- package/src/__tests__/scriptable-pack-cli.integration.test.ts +47 -0
- package/src/__tests__/scriptable-pack-ddc-key.unit.test.ts +63 -0
- package/src/__tests__/scriptable-pack.test-d.ts +131 -0
- package/src/__tests__/scriptable-pack.unit.test.ts +789 -0
- package/src/__tests__/topology-actions.unit.test.ts +94 -0
- package/src/__tests__/topology.unit.test.ts +85 -0
- package/src/artifact-path.ts +69 -0
- package/src/atlas/run-atlas.ts +358 -0
- package/src/atlas/shelf-pack.ts +172 -0
- package/src/atlas/upng-ambient.d.ts +13 -0
- package/src/bridge.ts +2 -0
- package/src/build.ts +139 -0
- package/src/builtin.ts +117 -0
- package/src/catalog-builder.ts +313 -0
- package/src/catalog-delta.ts +189 -0
- package/src/catalog-projection.ts +344 -0
- package/src/cli-asset.ts +586 -0
- package/src/config.ts +36 -0
- package/src/deriveAssetName.ts +42 -0
- package/src/errors.ts +47 -0
- package/src/evidence/material-cook.ts +411 -0
- package/src/evidence/offline-evidence.ts +90 -0
- package/src/evidence/source-inventory.ts +193 -0
- package/src/guid.ts +99 -0
- package/src/index.ts +108 -0
- package/src/material/artifact-writer.ts +26 -0
- package/src/material-cook.ts +15 -0
- package/src/mesh-bin-contract.ts +150 -0
- package/src/native-cooker-registry.ts +235 -0
- package/src/native-cooker.ts +2 -0
- package/src/package-finalizer.ts +394 -0
- package/src/producer-contract.ts +277 -0
- package/src/resolve-asset-source.ts +88 -0
- package/src/runtime-projection.ts +100 -0
- package/src/runtime-publication.ts +190 -0
- package/src/runtime.ts +23 -0
- package/src/scanner.ts +715 -0
- package/src/schema/material-cook.schema.json +119 -0
- package/src/schema-compiled.ts +238 -0
- package/src/scriptable-pack-node.ts +1394 -0
- package/src/scriptable-pack-worker.ts +308 -0
- package/src/scriptable-pack.ts +1006 -0
- package/src/topology.ts +157 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { validateMeta } from '../schema-compiled.js';
|
|
3
|
+
import { SCRIPTABLE_PACK_ASSET_KINDS } from '../scriptable-pack.js';
|
|
4
|
+
|
|
5
|
+
describe('meta.schema.json source optional (w8 / AC-4)', () => {
|
|
6
|
+
const minimalMeta = {
|
|
7
|
+
schemaVersion: '1.0.0',
|
|
8
|
+
kind: 'external-asset-package',
|
|
9
|
+
importer: 'image',
|
|
10
|
+
importSettings: {},
|
|
11
|
+
subAssets: [{ guid: '00000000-0000-4000-8000-000000000001', sourceIndex: 0, kind: 'texture' }],
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
it('meta without source key passes ajv validation (AC-4)', () => {
|
|
15
|
+
const valid = validateMeta(minimalMeta);
|
|
16
|
+
expect(valid).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('meta with explicit source passes ajv validation (AC-1 back-compat)', () => {
|
|
20
|
+
const metaWithSource = { ...minimalMeta, source: 'foo.png' };
|
|
21
|
+
const valid = validateMeta(metaWithSource);
|
|
22
|
+
expect(valid).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('meta missing required field importer fails validation', () => {
|
|
26
|
+
const { importer: _, ...withoutImporter } = minimalMeta;
|
|
27
|
+
const valid = validateMeta(withoutImporter);
|
|
28
|
+
expect(valid).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('ajv reinstantiation idempotent — same validator returns same result', () => {
|
|
32
|
+
const valid1 = validateMeta(minimalMeta);
|
|
33
|
+
const valid2 = validateMeta(minimalMeta);
|
|
34
|
+
expect(valid1).toBe(valid2);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('meta.schema.json subAssets[].kind open-string (w6 / D-1 scheme A)', () => {
|
|
39
|
+
const metaWithSubKind = (kind: string) => ({
|
|
40
|
+
schemaVersion: '1.0.0',
|
|
41
|
+
kind: 'external-asset-package',
|
|
42
|
+
importer: 'image',
|
|
43
|
+
importSettings: {},
|
|
44
|
+
subAssets: [{ guid: '00000000-0000-4000-8000-000000000001', sourceIndex: 0, kind }],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// (a) Host custom kind passes validation — RED against current closed enum,
|
|
48
|
+
// GREEN after schema enum is dropped to open string with minLength: 1.
|
|
49
|
+
// When this test is red it proves the enum wall exists; when it turns
|
|
50
|
+
// green it proves D-1 scheme A is effective.
|
|
51
|
+
it('(a) host custom kind passes validation (red→green)', () => {
|
|
52
|
+
const valid = validateMeta(metaWithSubKind('reel-game-blob'));
|
|
53
|
+
expect(valid).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// (b) Zero-regression: all 12 known kind values from the prior closed enum
|
|
57
|
+
// still pass validation. 'image' is included here because the schema
|
|
58
|
+
// layer does NOT decommission it — the fold layer (M4) handles that.
|
|
59
|
+
const knownKinds = [
|
|
60
|
+
'mesh',
|
|
61
|
+
'material',
|
|
62
|
+
'scene',
|
|
63
|
+
'texture',
|
|
64
|
+
'image',
|
|
65
|
+
'cube-texture',
|
|
66
|
+
'material-shader',
|
|
67
|
+
'skeleton',
|
|
68
|
+
'skin',
|
|
69
|
+
'animation-clip',
|
|
70
|
+
'audio',
|
|
71
|
+
'font',
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (const kind of knownKinds) {
|
|
75
|
+
it(`(b) known kind '${kind}' passes validation (zero-regression)`, () => {
|
|
76
|
+
const valid = validateMeta(metaWithSubKind(kind));
|
|
77
|
+
expect(valid).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const kind of SCRIPTABLE_PACK_ASSET_KINDS) {
|
|
82
|
+
it(`published durable kind '${kind}' remains valid in open Meta schema`, () => {
|
|
83
|
+
expect(validateMeta(metaWithSubKind(kind))).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// (c) Empty string rejected by minLength: 1 (schema layer gate).
|
|
88
|
+
it('(c) empty-string kind fails validation (minLength: 1)', () => {
|
|
89
|
+
const valid = validateMeta(metaWithSubKind(''));
|
|
90
|
+
expect(valid).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// (d) subAssets[].kind field still exists in the schema and is typed as
|
|
94
|
+
// string (AC-06 + OOS-2: field not deleted, only enum removed).
|
|
95
|
+
it('(d) subAssets[].kind field is string-typed and required', () => {
|
|
96
|
+
// Verify kind field is required — missing it fails.
|
|
97
|
+
const withoutKind = {
|
|
98
|
+
schemaVersion: '1.0.0',
|
|
99
|
+
kind: 'external-asset-package',
|
|
100
|
+
importer: 'image',
|
|
101
|
+
importSettings: {},
|
|
102
|
+
subAssets: [{ guid: '00000000-0000-4000-8000-000000000001', sourceIndex: 0 }],
|
|
103
|
+
};
|
|
104
|
+
expect(validateMeta(withoutKind)).toBe(false);
|
|
105
|
+
|
|
106
|
+
// Verify kind with non-string value (number) fails type check.
|
|
107
|
+
const numberKind = {
|
|
108
|
+
schemaVersion: '1.0.0',
|
|
109
|
+
kind: 'external-asset-package',
|
|
110
|
+
importer: 'image',
|
|
111
|
+
importSettings: {},
|
|
112
|
+
subAssets: [{ guid: '00000000-0000-4000-8000-000000000001', sourceIndex: 0, kind: 42 }],
|
|
113
|
+
};
|
|
114
|
+
expect(validateMeta(numberKind)).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { validateProducerContract } from '../producer-contract.js';
|
|
3
|
+
import { validateMeta } from '../schema-compiled.js';
|
|
4
|
+
|
|
5
|
+
const baseMeta = () => ({
|
|
6
|
+
schemaVersion: '1.0.0',
|
|
7
|
+
kind: 'external-asset-package',
|
|
8
|
+
importer: 'fixture',
|
|
9
|
+
importSettings: {},
|
|
10
|
+
subAssets: [
|
|
11
|
+
{
|
|
12
|
+
guid: '11111111-1111-4111-8111-111111111111',
|
|
13
|
+
sourceIndex: 0,
|
|
14
|
+
sourceKey: 'mesh/main',
|
|
15
|
+
kind: 'mesh',
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('Meta sourceOverrides', () => {
|
|
21
|
+
it('accepts a legal optional override without narrowing its payload', () => {
|
|
22
|
+
const meta = {
|
|
23
|
+
...baseMeta(),
|
|
24
|
+
sourceOverrides: { 'mesh/main': { lod: 2, customProducerField: true } },
|
|
25
|
+
};
|
|
26
|
+
expect(validateMeta(meta)).toBe(true);
|
|
27
|
+
expect(validateProducerContract(meta)).toMatchObject({ ok: true });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('rejects unknown or duplicate source keys before publication', () => {
|
|
31
|
+
const unknown = {
|
|
32
|
+
...baseMeta(),
|
|
33
|
+
sourceOverrides: { 'mesh/other': { lod: 1 } },
|
|
34
|
+
};
|
|
35
|
+
const unknownResult = validateProducerContract(unknown);
|
|
36
|
+
expect(unknownResult).toMatchObject({
|
|
37
|
+
ok: false,
|
|
38
|
+
error: {
|
|
39
|
+
code: 'unknown-source-key',
|
|
40
|
+
authority: 'pack',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const duplicate = {
|
|
45
|
+
...baseMeta(),
|
|
46
|
+
subAssets: [
|
|
47
|
+
...baseMeta().subAssets,
|
|
48
|
+
{
|
|
49
|
+
guid: '22222222-2222-4222-8222-222222222222',
|
|
50
|
+
sourceIndex: 1,
|
|
51
|
+
sourceKey: 'mesh/main',
|
|
52
|
+
kind: 'mesh',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
sourceOverrides: { 'mesh/main': { lod: 1 } },
|
|
56
|
+
};
|
|
57
|
+
expect(validateProducerContract(duplicate)).toMatchObject({
|
|
58
|
+
ok: false,
|
|
59
|
+
error: { code: 'duplicate-source-key' },
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('keeps omitted and empty overrides equivalent to legacy Meta', () => {
|
|
64
|
+
const legacy = baseMeta();
|
|
65
|
+
const empty = { ...legacy, sourceOverrides: {} };
|
|
66
|
+
expect(validateMeta(legacy)).toBe(true);
|
|
67
|
+
expect(validateMeta(empty)).toBe(true);
|
|
68
|
+
expect(validateProducerContract(legacy)).toMatchObject({ ok: true });
|
|
69
|
+
expect(validateProducerContract(empty)).toMatchObject({ ok: true });
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { NativeCookerRegistry } from '../native-cooker-registry.js';
|
|
3
|
+
|
|
4
|
+
describe('NativeCookerRegistry artifact boundary', () => {
|
|
5
|
+
it('keeps native artifact bytes available before converting to CookProduct descriptors', async () => {
|
|
6
|
+
const bytes = new TextEncoder().encode('{"program":true}');
|
|
7
|
+
const registry = new NativeCookerRegistry();
|
|
8
|
+
registry.register({
|
|
9
|
+
key: 'test-effect',
|
|
10
|
+
cook: () => ({
|
|
11
|
+
guid: '019e9c00-0000-7000-8000-000000000000',
|
|
12
|
+
payload: { kind: 'test-effect' },
|
|
13
|
+
refs: ['019e9c00-0000-7000-8000-000000000001'],
|
|
14
|
+
artifacts: {
|
|
15
|
+
'test-effect/program.json': { mediaType: 'application/json', bytes },
|
|
16
|
+
},
|
|
17
|
+
inputFingerprint: 'sha256:test-input',
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const draft = await registry.runDraft('test-effect', {});
|
|
22
|
+
expect(draft.ok).toBe(true);
|
|
23
|
+
if (!draft.ok) throw new Error(draft.error.hint);
|
|
24
|
+
expect(draft.value.artifacts['test-effect/program.json']?.bytes).toEqual(bytes);
|
|
25
|
+
|
|
26
|
+
const product = await registry.run('test-effect', {});
|
|
27
|
+
expect(product.ok).toBe(true);
|
|
28
|
+
if (!product.ok) throw new Error(product.error.hint);
|
|
29
|
+
expect(product.value.artifacts['test-effect/program.json']).toMatchObject({
|
|
30
|
+
path: 'test-effect/program.json',
|
|
31
|
+
mediaType: 'application/json',
|
|
32
|
+
byteLength: bytes.byteLength,
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('discovers a typed producer input before cooking and preserves the typed output contract', async () => {
|
|
37
|
+
const registry = new NativeCookerRegistry();
|
|
38
|
+
registry.register({
|
|
39
|
+
key: 'typed-effect',
|
|
40
|
+
discover: (input) => {
|
|
41
|
+
if (typeof input !== 'object' || input === null || !('source' in input)) {
|
|
42
|
+
throw new Error('source is required');
|
|
43
|
+
}
|
|
44
|
+
return { source: String(input.source), revision: 3 };
|
|
45
|
+
},
|
|
46
|
+
cook: (input: { readonly source: string; readonly revision: number }) => ({
|
|
47
|
+
guid: '019e9c00-0000-7000-8000-000000000010',
|
|
48
|
+
payload: { source: input.source, revision: input.revision },
|
|
49
|
+
refs: [],
|
|
50
|
+
artifacts: {
|
|
51
|
+
'typed-effect/program.json': {
|
|
52
|
+
mediaType: 'application/json',
|
|
53
|
+
bytes: new TextEncoder().encode(JSON.stringify(input)),
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
inputFingerprint: `sha256:${input.source}`,
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const result = await registry.runDraft('typed-effect', { source: 'module.wgsl' });
|
|
61
|
+
expect(result.ok).toBe(true);
|
|
62
|
+
if (result.ok) expect(result.value.payload).toEqual({ source: 'module.wgsl', revision: 3 });
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -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,78 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { finalizePackageProduct } from '../package-finalizer.js';
|
|
6
|
+
import { scanInventory } from '../scanner.js';
|
|
7
|
+
|
|
8
|
+
const GUID = '019e3969-1d48-7c3b-ac24-6d68f457065f';
|
|
9
|
+
|
|
10
|
+
describe('Pack owner chain', () => {
|
|
11
|
+
const roots: string[] = [];
|
|
12
|
+
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('scans source identity and finalizes the terminal package product', async () => {
|
|
18
|
+
const root = await mkdtemp(join(tmpdir(), 'forgeax-owner-chain-'));
|
|
19
|
+
roots.push(root);
|
|
20
|
+
await writeFile(
|
|
21
|
+
join(root, 'hero.pack.json'),
|
|
22
|
+
JSON.stringify({
|
|
23
|
+
schemaVersion: '2.0.0',
|
|
24
|
+
kind: 'internal-text-package',
|
|
25
|
+
assets: [
|
|
26
|
+
{
|
|
27
|
+
guid: GUID,
|
|
28
|
+
kind: 'texture',
|
|
29
|
+
sourceKey: 'hero/albedo',
|
|
30
|
+
sourceIndex: 0,
|
|
31
|
+
payload: {},
|
|
32
|
+
refs: [],
|
|
33
|
+
artifacts: {},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
const inventory = await scanInventory([root]);
|
|
39
|
+
expect(inventory.ok).toBe(true);
|
|
40
|
+
if (!inventory.ok) return;
|
|
41
|
+
const writes: string[] = [];
|
|
42
|
+
const result = await finalizePackageProduct(
|
|
43
|
+
{
|
|
44
|
+
assets: [
|
|
45
|
+
{
|
|
46
|
+
guid: GUID,
|
|
47
|
+
kind: 'texture',
|
|
48
|
+
payload: { kind: 'texture' },
|
|
49
|
+
refs: [],
|
|
50
|
+
artifacts: {
|
|
51
|
+
body: { mediaType: 'application/octet-stream', bytes: new Uint8Array([1]) },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
receipts: [
|
|
56
|
+
{
|
|
57
|
+
guid: GUID,
|
|
58
|
+
origin: 'sourceMeta',
|
|
59
|
+
status: 'succeeded',
|
|
60
|
+
inputFingerprint: inventory.value.inventory[0]?.sourceRevision ?? 'missing',
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
diagnostics: [],
|
|
64
|
+
sourceRevision: inventory.value.inventory[0]?.sourceRevision ?? 'missing',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
base: '/assets',
|
|
68
|
+
packagePath: 'hero.pack.json',
|
|
69
|
+
artifactPath: (guid, key) => `${guid}/${key}.bin`,
|
|
70
|
+
sink: (path) => {
|
|
71
|
+
writes.push(path);
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
expect(result.ok).toBe(true);
|
|
76
|
+
if (result.ok) expect(writes).toEqual([`${GUID}/body.bin`, 'hero.pack.json']);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { parsePackV2, validateCookedMaterialRecord } 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
|
+
|
|
65
|
+
it('rejects retired material cook records instead of retaining a cross-generation reader', () => {
|
|
66
|
+
const retiredSchema = ['material-cook', '2'].join('/');
|
|
67
|
+
expect(
|
|
68
|
+
validateCookedMaterialRecord({
|
|
69
|
+
schemaVersion: retiredSchema,
|
|
70
|
+
guid: 'mat-v2',
|
|
71
|
+
resolved: { passes: [], parameters: [], values: {} },
|
|
72
|
+
refs: { parent: [], textures: [], samplers: [], modules: [] },
|
|
73
|
+
artifact: { mediaType: 'text/wgsl', path: 'shader.wgsl', digest: 'sha256:a', bytes: [] },
|
|
74
|
+
receipt: {
|
|
75
|
+
schemaVersion: retiredSchema,
|
|
76
|
+
sourceClosure: [],
|
|
77
|
+
profile: 'webgpu/v1',
|
|
78
|
+
compilerVersion: 'compiler/1',
|
|
79
|
+
inputDigest: 'sha256:i',
|
|
80
|
+
outputDigest: 'sha256:a',
|
|
81
|
+
layoutIdentity: 'sha256:l',
|
|
82
|
+
derivedInterface: { layoutIdentity: 'sha256:l' },
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
).toMatchObject({ ok: false, error: { code: 'material-cook-record-invalid' } });
|
|
86
|
+
});
|
|
87
|
+
});
|