@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,232 @@
|
|
|
1
|
+
// scanner-mount-cycle.test - scanner step-6 mount-asset cycle coverage
|
|
2
|
+
// (feat-20260608-scene-nesting-ecs-fication M1 / w13).
|
|
3
|
+
//
|
|
4
|
+
// Coverage (AC-12; plan-strategy D-1):
|
|
5
|
+
// (a) acyclic mount chain A -> B -> C: scanner returns Ok (no cycle).
|
|
6
|
+
// (b) two-asset cycle A -> B -> A: scanner returns
|
|
7
|
+
// Err(PackError{ code: 'pack-cyclic-reference', detail.kind: 'mount-asset',
|
|
8
|
+
// detail.cycle: GUID[] }) with the GUIDs in cycle order (first === last).
|
|
9
|
+
// (c) three-asset cycle A -> B -> C -> A: same, longer cycle.
|
|
10
|
+
// (d) self-mount A -> A: same, single-element cycle.
|
|
11
|
+
//
|
|
12
|
+
// The fixtures are hand-authored scene .pack.json files in a tmpdir;
|
|
13
|
+
// each scene asset uses `payload.mounts[].source` as an integer index into
|
|
14
|
+
// the parent scene's `refs[]` (D-1 + research Finding 11). The mount window
|
|
15
|
+
// (memberFirst / memberCount) is set conservatively so the scanner step-6
|
|
16
|
+
// cycle pass triggers before any mount-window collision check.
|
|
17
|
+
//
|
|
18
|
+
// TDD red signal: until w14 wires `payload.mounts[].source` -> `refsByGuid`
|
|
19
|
+
// edge into scanner step-6, the scanner does NOT see the mount edge in the
|
|
20
|
+
// DFS and the cycle assertions miss. With w14 the cycle producer surfaces
|
|
21
|
+
// `pack-cyclic-reference + detail.kind: 'mount-asset'` (w8 landed the
|
|
22
|
+
// detail.kind tagging at the producer).
|
|
23
|
+
|
|
24
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
25
|
+
import { tmpdir } from 'node:os';
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
28
|
+
import { scan } from '../scanner.js';
|
|
29
|
+
|
|
30
|
+
const GUID_A = 'aa000000-0000-4000-8000-000000000aaa';
|
|
31
|
+
const GUID_B = 'bb000000-0000-4000-8000-000000000bbb';
|
|
32
|
+
const GUID_C = 'cc000000-0000-4000-8000-000000000ccc';
|
|
33
|
+
|
|
34
|
+
interface MountSpec {
|
|
35
|
+
readonly localId: number;
|
|
36
|
+
readonly source: number;
|
|
37
|
+
readonly memberFirst: number;
|
|
38
|
+
readonly memberCount: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface SceneAssetSpec {
|
|
42
|
+
readonly guid: string;
|
|
43
|
+
/** GUIDs the scene asset's `payload.mounts[].source` integers index into. */
|
|
44
|
+
readonly refs: readonly string[];
|
|
45
|
+
readonly mounts: readonly MountSpec[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function sceneAssetPack(spec: SceneAssetSpec): unknown {
|
|
49
|
+
return {
|
|
50
|
+
schemaVersion: '1.0.0',
|
|
51
|
+
kind: 'internal-text-package',
|
|
52
|
+
assets: [
|
|
53
|
+
{
|
|
54
|
+
guid: spec.guid,
|
|
55
|
+
kind: 'scene',
|
|
56
|
+
refs: [...spec.refs],
|
|
57
|
+
payload: {
|
|
58
|
+
kind: 'scene',
|
|
59
|
+
entities: [{ localId: 0, components: {} }],
|
|
60
|
+
mounts: spec.mounts.map((m) => ({
|
|
61
|
+
localId: m.localId,
|
|
62
|
+
source: m.source,
|
|
63
|
+
memberFirst: m.memberFirst,
|
|
64
|
+
memberCount: m.memberCount,
|
|
65
|
+
})),
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface CycleErrorShape {
|
|
73
|
+
readonly code: string;
|
|
74
|
+
readonly detail: {
|
|
75
|
+
readonly code?: string;
|
|
76
|
+
readonly kind?: string;
|
|
77
|
+
readonly cycle?: readonly string[];
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
describe('scanner step-6 mount-asset cycle detection (AC-12; D-1)', () => {
|
|
82
|
+
let dir: string;
|
|
83
|
+
|
|
84
|
+
beforeEach(async () => {
|
|
85
|
+
dir = await mkdtemp(join(tmpdir(), 'pack-mount-cycle-'));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
afterEach(async () => {
|
|
89
|
+
await rm(dir, { recursive: true, force: true });
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('(a) accepts an acyclic mount chain A -> B -> C', async () => {
|
|
93
|
+
// A mounts B (source=0 in A.refs) -> B mounts C (source=0 in B.refs) ->
|
|
94
|
+
// C mounts nothing.
|
|
95
|
+
await writeFile(
|
|
96
|
+
join(dir, 'a.pack.json'),
|
|
97
|
+
JSON.stringify(
|
|
98
|
+
sceneAssetPack({
|
|
99
|
+
guid: GUID_A,
|
|
100
|
+
refs: [GUID_B],
|
|
101
|
+
mounts: [{ localId: 1, source: 0, memberFirst: 2, memberCount: 1 }],
|
|
102
|
+
}),
|
|
103
|
+
),
|
|
104
|
+
'utf-8',
|
|
105
|
+
);
|
|
106
|
+
await writeFile(
|
|
107
|
+
join(dir, 'b.pack.json'),
|
|
108
|
+
JSON.stringify(
|
|
109
|
+
sceneAssetPack({
|
|
110
|
+
guid: GUID_B,
|
|
111
|
+
refs: [GUID_C],
|
|
112
|
+
mounts: [{ localId: 1, source: 0, memberFirst: 2, memberCount: 1 }],
|
|
113
|
+
}),
|
|
114
|
+
),
|
|
115
|
+
'utf-8',
|
|
116
|
+
);
|
|
117
|
+
await writeFile(
|
|
118
|
+
join(dir, 'c.pack.json'),
|
|
119
|
+
JSON.stringify(sceneAssetPack({ guid: GUID_C, refs: [], mounts: [] })),
|
|
120
|
+
'utf-8',
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const result = await scan([dir]);
|
|
124
|
+
expect(result.ok).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('(b) rejects a two-asset cycle A -> B -> A with kind: mount-asset', async () => {
|
|
128
|
+
await writeFile(
|
|
129
|
+
join(dir, 'a.pack.json'),
|
|
130
|
+
JSON.stringify(
|
|
131
|
+
sceneAssetPack({
|
|
132
|
+
guid: GUID_A,
|
|
133
|
+
refs: [GUID_B],
|
|
134
|
+
mounts: [{ localId: 1, source: 0, memberFirst: 2, memberCount: 1 }],
|
|
135
|
+
}),
|
|
136
|
+
),
|
|
137
|
+
'utf-8',
|
|
138
|
+
);
|
|
139
|
+
await writeFile(
|
|
140
|
+
join(dir, 'b.pack.json'),
|
|
141
|
+
JSON.stringify(
|
|
142
|
+
sceneAssetPack({
|
|
143
|
+
guid: GUID_B,
|
|
144
|
+
refs: [GUID_A],
|
|
145
|
+
mounts: [{ localId: 1, source: 0, memberFirst: 2, memberCount: 1 }],
|
|
146
|
+
}),
|
|
147
|
+
),
|
|
148
|
+
'utf-8',
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const result = await scan([dir]);
|
|
152
|
+
expect(result.ok).toBe(false);
|
|
153
|
+
if (result.ok) return;
|
|
154
|
+
const err = result.error as unknown as CycleErrorShape;
|
|
155
|
+
expect(err.code).toBe('pack-cyclic-reference');
|
|
156
|
+
expect(err.detail.kind).toBe('mount-asset');
|
|
157
|
+
const cycle = err.detail.cycle ?? [];
|
|
158
|
+
expect(cycle.length).toBeGreaterThanOrEqual(2);
|
|
159
|
+
expect(cycle[0]).toBe(cycle[cycle.length - 1]);
|
|
160
|
+
// Both GUIDs participate in the cycle (case-insensitive compare; scanner
|
|
161
|
+
// lowercases before storing).
|
|
162
|
+
const lower = cycle.map((g) => g.toLowerCase());
|
|
163
|
+
expect(lower).toContain(GUID_A.toLowerCase());
|
|
164
|
+
expect(lower).toContain(GUID_B.toLowerCase());
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('(c) rejects a three-asset cycle A -> B -> C -> A with kind: mount-asset', async () => {
|
|
168
|
+
await writeFile(
|
|
169
|
+
join(dir, 'a.pack.json'),
|
|
170
|
+
JSON.stringify(
|
|
171
|
+
sceneAssetPack({
|
|
172
|
+
guid: GUID_A,
|
|
173
|
+
refs: [GUID_B],
|
|
174
|
+
mounts: [{ localId: 1, source: 0, memberFirst: 2, memberCount: 1 }],
|
|
175
|
+
}),
|
|
176
|
+
),
|
|
177
|
+
'utf-8',
|
|
178
|
+
);
|
|
179
|
+
await writeFile(
|
|
180
|
+
join(dir, 'b.pack.json'),
|
|
181
|
+
JSON.stringify(
|
|
182
|
+
sceneAssetPack({
|
|
183
|
+
guid: GUID_B,
|
|
184
|
+
refs: [GUID_C],
|
|
185
|
+
mounts: [{ localId: 1, source: 0, memberFirst: 2, memberCount: 1 }],
|
|
186
|
+
}),
|
|
187
|
+
),
|
|
188
|
+
'utf-8',
|
|
189
|
+
);
|
|
190
|
+
await writeFile(
|
|
191
|
+
join(dir, 'c.pack.json'),
|
|
192
|
+
JSON.stringify(
|
|
193
|
+
sceneAssetPack({
|
|
194
|
+
guid: GUID_C,
|
|
195
|
+
refs: [GUID_A],
|
|
196
|
+
mounts: [{ localId: 1, source: 0, memberFirst: 2, memberCount: 1 }],
|
|
197
|
+
}),
|
|
198
|
+
),
|
|
199
|
+
'utf-8',
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const result = await scan([dir]);
|
|
203
|
+
expect(result.ok).toBe(false);
|
|
204
|
+
if (result.ok) return;
|
|
205
|
+
const err = result.error as unknown as CycleErrorShape;
|
|
206
|
+
expect(err.code).toBe('pack-cyclic-reference');
|
|
207
|
+
expect(err.detail.kind).toBe('mount-asset');
|
|
208
|
+
const cycle = err.detail.cycle ?? [];
|
|
209
|
+
expect(cycle.length).toBeGreaterThanOrEqual(3);
|
|
210
|
+
expect(cycle[0]).toBe(cycle[cycle.length - 1]);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('(d) rejects a self-mount A -> A', async () => {
|
|
214
|
+
await writeFile(
|
|
215
|
+
join(dir, 'a.pack.json'),
|
|
216
|
+
JSON.stringify(
|
|
217
|
+
sceneAssetPack({
|
|
218
|
+
guid: GUID_A,
|
|
219
|
+
refs: [GUID_A],
|
|
220
|
+
mounts: [{ localId: 1, source: 0, memberFirst: 2, memberCount: 1 }],
|
|
221
|
+
}),
|
|
222
|
+
),
|
|
223
|
+
'utf-8',
|
|
224
|
+
);
|
|
225
|
+
const result = await scan([dir]);
|
|
226
|
+
expect(result.ok).toBe(false);
|
|
227
|
+
if (result.ok) return;
|
|
228
|
+
const err = result.error as unknown as CycleErrorShape;
|
|
229
|
+
expect(err.code).toBe('pack-cyclic-reference');
|
|
230
|
+
expect(err.detail.kind).toBe('mount-asset');
|
|
231
|
+
});
|
|
232
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// @perf-budget-skip: intentional ScriptablePack CLI integration gate.
|
|
2
|
+
|
|
3
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { describe, expect, it } from 'vitest';
|
|
7
|
+
import { runCliAsset } from '../cli-asset.js';
|
|
8
|
+
|
|
9
|
+
describe('ScriptablePack CLI Meta inspection', () => {
|
|
10
|
+
it('loads the definition without invoking build', async () => {
|
|
11
|
+
const root = await mkdtemp(join(tmpdir(), 'forgeax-scriptable-pack-'));
|
|
12
|
+
try {
|
|
13
|
+
const source = join(root, 'house.pack.mjs');
|
|
14
|
+
await writeFile(
|
|
15
|
+
source,
|
|
16
|
+
`
|
|
17
|
+
const guid = (hex) => new Uint8Array(hex.match(/../g).map((byte) => Number.parseInt(byte, 16)));
|
|
18
|
+
export default {
|
|
19
|
+
schemaVersion: '1.0.0',
|
|
20
|
+
packageId: guid('019ffa978b397ad284cc273268591ab4'),
|
|
21
|
+
assets: {
|
|
22
|
+
wall: { guid: guid('019ffa97a5ee7645b613043323952808'), kind: 'mesh' }
|
|
23
|
+
},
|
|
24
|
+
externalAssets: {},
|
|
25
|
+
build() { throw new Error('META_MUST_NOT_BUILD'); }
|
|
26
|
+
};
|
|
27
|
+
`,
|
|
28
|
+
);
|
|
29
|
+
const stdout: string[] = [];
|
|
30
|
+
const stderr: string[] = [];
|
|
31
|
+
const code = await runCliAsset(['meta', source, '--json'], {
|
|
32
|
+
stdoutWrite: (line) => stdout.push(line),
|
|
33
|
+
stderrWrite: (line) => stderr.push(line),
|
|
34
|
+
cwd: root,
|
|
35
|
+
});
|
|
36
|
+
expect(code).toBe(0);
|
|
37
|
+
expect(stderr).toEqual([]);
|
|
38
|
+
expect(JSON.parse(stdout[0] ?? '{}')).toMatchObject({
|
|
39
|
+
importer: 'pack-ts',
|
|
40
|
+
source,
|
|
41
|
+
subAssets: [{ sourceKey: 'wall', kind: 'mesh' }],
|
|
42
|
+
});
|
|
43
|
+
} finally {
|
|
44
|
+
await rm(root, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
type ProducerSemanticIdentityInput,
|
|
4
|
+
producerRelativeDdcIdentity,
|
|
5
|
+
producerRelativeDdcKey,
|
|
6
|
+
producerRelativeLogicalPath,
|
|
7
|
+
} from '../evidence/source-inventory.js';
|
|
8
|
+
import { scriptablePackDdcKey } from '../scriptable-pack-node.js';
|
|
9
|
+
|
|
10
|
+
const base = (
|
|
11
|
+
overrides: Partial<ProducerSemanticIdentityInput> = {},
|
|
12
|
+
): ProducerSemanticIdentityInput => ({
|
|
13
|
+
producerRoot: '/checkout/game-a',
|
|
14
|
+
sourcePath: '/checkout/game-a/assets/vehicle.pack.ts',
|
|
15
|
+
sourceDigest: 'sha256:source',
|
|
16
|
+
schemaVersion: '1.0.0',
|
|
17
|
+
importer: 'scriptable-pack-importer@2',
|
|
18
|
+
codec: 'pack-v2',
|
|
19
|
+
settings: { compression: 'basis', quality: 8 },
|
|
20
|
+
producer: 'scriptable-pack@4',
|
|
21
|
+
profile: 'webgpu/v1',
|
|
22
|
+
declaredGuids: ['guid-vehicle'],
|
|
23
|
+
...overrides,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('producer-relative ScriptablePack DDC identity', () => {
|
|
27
|
+
it('uses producer-root-relative logical path rather than absolute checkout path', () => {
|
|
28
|
+
expect(
|
|
29
|
+
producerRelativeLogicalPath('/checkout/game-a', '/checkout/game-a/assets/vehicle.pack.ts'),
|
|
30
|
+
).toBe('assets/vehicle.pack.ts');
|
|
31
|
+
expect(
|
|
32
|
+
producerRelativeDdcKey(
|
|
33
|
+
base({
|
|
34
|
+
producerRoot: '/checkout/game-b',
|
|
35
|
+
sourcePath: '/checkout/game-b/assets/vehicle.pack.ts',
|
|
36
|
+
}),
|
|
37
|
+
),
|
|
38
|
+
).toBe(producerRelativeDdcKey(base()));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it.each([
|
|
42
|
+
['source', { sourceDigest: 'sha256:changed' }],
|
|
43
|
+
['importer', { importer: 'other-importer@1' }],
|
|
44
|
+
['codec', { codec: 'other-codec' }],
|
|
45
|
+
['settings', { settings: { compression: 'raw', quality: 8 } }],
|
|
46
|
+
['producer', { producer: 'other-producer@1' }],
|
|
47
|
+
['profile', { profile: 'webgl2/fallback' }],
|
|
48
|
+
['schema', { schemaVersion: '2.0.0' }],
|
|
49
|
+
])('misses when semantic input %s changes', (_name, change) => {
|
|
50
|
+
expect(producerRelativeDdcKey(base(change))).not.toBe(producerRelativeDdcKey(base()));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns an inspectable identity and preserves producer-relative path in the key input', () => {
|
|
54
|
+
const identity = producerRelativeDdcIdentity(base());
|
|
55
|
+
|
|
56
|
+
expect(identity).toMatchObject({
|
|
57
|
+
logicalPath: 'assets/vehicle.pack.ts',
|
|
58
|
+
sourceDigest: 'sha256:source',
|
|
59
|
+
key: expect.stringMatching(/^[a-f0-9]{64}$/),
|
|
60
|
+
});
|
|
61
|
+
expect(scriptablePackDdcKey(base())).toBe(identity.key);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnimationClip,
|
|
3
|
+
AnimationGraph,
|
|
4
|
+
AssetGuid,
|
|
5
|
+
AudioClipAsset,
|
|
6
|
+
EquirectAsset,
|
|
7
|
+
FontAsset,
|
|
8
|
+
MaterialAsset,
|
|
9
|
+
MeshAsset,
|
|
10
|
+
ParticleEffectAsset,
|
|
11
|
+
RenderPipelineAsset,
|
|
12
|
+
SamplerAsset,
|
|
13
|
+
SceneAsset,
|
|
14
|
+
SkeletonAsset,
|
|
15
|
+
SkinAsset,
|
|
16
|
+
TextureAsset,
|
|
17
|
+
TilesetAsset,
|
|
18
|
+
VideoAsset,
|
|
19
|
+
} from '@forgeax/engine-types';
|
|
20
|
+
import { describe, expectTypeOf, it } from 'vitest';
|
|
21
|
+
import type {
|
|
22
|
+
ScriptablePackAssetDeclarations,
|
|
23
|
+
ScriptablePackAssetFor,
|
|
24
|
+
ScriptablePackAssetKind,
|
|
25
|
+
ScriptablePackGatewayOperation,
|
|
26
|
+
ScriptablePackOutputs,
|
|
27
|
+
} from '../scriptable-pack.js';
|
|
28
|
+
import { SCRIPTABLE_PACK_ASSET_KINDS } from '../scriptable-pack.js';
|
|
29
|
+
|
|
30
|
+
function typeContract(): void {
|
|
31
|
+
const meshGuid = null as unknown as AssetGuid;
|
|
32
|
+
const sceneGuid = null as unknown as AssetGuid;
|
|
33
|
+
const mesh = null as unknown as MeshAsset;
|
|
34
|
+
const scene = null as unknown as SceneAsset;
|
|
35
|
+
|
|
36
|
+
const assets = {
|
|
37
|
+
mesh: { guid: meshGuid, kind: 'mesh' },
|
|
38
|
+
scene: { guid: sceneGuid, kind: 'scene' },
|
|
39
|
+
} as const satisfies ScriptablePackAssetDeclarations;
|
|
40
|
+
|
|
41
|
+
const valid = { mesh, scene } satisfies ScriptablePackOutputs<typeof assets>;
|
|
42
|
+
void valid;
|
|
43
|
+
|
|
44
|
+
// @ts-expect-error mesh descriptor requires MeshAsset
|
|
45
|
+
const wrongKind = { mesh: scene, scene } satisfies ScriptablePackOutputs<typeof assets>;
|
|
46
|
+
void wrongKind;
|
|
47
|
+
|
|
48
|
+
const allKinds = SCRIPTABLE_PACK_ASSET_KINDS;
|
|
49
|
+
type ExpectedKinds =
|
|
50
|
+
| MeshAsset
|
|
51
|
+
| MaterialAsset
|
|
52
|
+
| SceneAsset
|
|
53
|
+
| TextureAsset
|
|
54
|
+
| EquirectAsset
|
|
55
|
+
| SamplerAsset
|
|
56
|
+
| FontAsset
|
|
57
|
+
| RenderPipelineAsset
|
|
58
|
+
| TilesetAsset
|
|
59
|
+
| VideoAsset
|
|
60
|
+
| SkeletonAsset
|
|
61
|
+
| SkinAsset
|
|
62
|
+
| AnimationClip
|
|
63
|
+
| AnimationGraph
|
|
64
|
+
| AudioClipAsset
|
|
65
|
+
| ParticleEffectAsset;
|
|
66
|
+
type DiscoveredKinds = ScriptablePackAssetFor<{
|
|
67
|
+
readonly guid: AssetGuid;
|
|
68
|
+
readonly kind: (typeof allKinds)[number];
|
|
69
|
+
}>;
|
|
70
|
+
expectTypeOf<DiscoveredKinds>().toEqualTypeOf<ExpectedKinds>();
|
|
71
|
+
|
|
72
|
+
// @ts-expect-error unknown kinds cannot enter the ordinary producer matrix
|
|
73
|
+
const unknownKind: ScriptablePackAssetKind = 'unknown-kind';
|
|
74
|
+
void unknownKind;
|
|
75
|
+
|
|
76
|
+
// @ts-expect-error scene output is required
|
|
77
|
+
const missing = { mesh } satisfies ScriptablePackOutputs<typeof assets>;
|
|
78
|
+
void missing;
|
|
79
|
+
|
|
80
|
+
// @ts-expect-error plain strings are not AssetGuid values
|
|
81
|
+
const plainStringGuid: AssetGuid = '019ffa97-a5ee-7645-b613-043323952808';
|
|
82
|
+
void plainStringGuid;
|
|
83
|
+
|
|
84
|
+
const gatewayOperations = [
|
|
85
|
+
{
|
|
86
|
+
requestId: 'create',
|
|
87
|
+
kind: 'create-scriptable-pack',
|
|
88
|
+
sourcePath: 'new.pack.ts',
|
|
89
|
+
initialOutput: { sourceKey: 'scene', kind: 'scene' },
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
requestId: 'add',
|
|
93
|
+
kind: 'add-output',
|
|
94
|
+
sourcePath: 'new.pack.ts',
|
|
95
|
+
sourceKey: 'mesh',
|
|
96
|
+
assetKind: 'mesh',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
requestId: 'external',
|
|
100
|
+
kind: 'add-external-asset',
|
|
101
|
+
sourcePath: 'new.pack.ts',
|
|
102
|
+
alias: 'material',
|
|
103
|
+
guid: meshGuid,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
requestId: 'rename',
|
|
107
|
+
kind: 'rename-display',
|
|
108
|
+
sourcePath: 'new.pack.ts',
|
|
109
|
+
target: { kind: 'output', sourceKey: 'mesh' },
|
|
110
|
+
name: 'Wall',
|
|
111
|
+
},
|
|
112
|
+
{ requestId: 'remove', kind: 'remove-output', sourcePath: 'new.pack.ts', sourceKey: 'mesh' },
|
|
113
|
+
{
|
|
114
|
+
requestId: 'clone',
|
|
115
|
+
kind: 'clone-scriptable-pack',
|
|
116
|
+
sourcePath: 'new.pack.ts',
|
|
117
|
+
targetPath: 'clone.pack.ts',
|
|
118
|
+
},
|
|
119
|
+
{ requestId: 'preflight', kind: 'preflight', sourcePath: 'new.pack.ts' },
|
|
120
|
+
{ requestId: 'inspect', kind: 'inspect-meta', sourcePath: 'new.pack.ts' },
|
|
121
|
+
{ requestId: 'rebuild', kind: 'rebuild', sourcePath: 'new.pack.ts' },
|
|
122
|
+
{ requestId: 'cold-cook', kind: 'cold-cook', sourcePath: 'new.pack.ts' },
|
|
123
|
+
] as const satisfies readonly ScriptablePackGatewayOperation[];
|
|
124
|
+
void gatewayOperations;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
describe('ScriptablePack public type contract', () => {
|
|
128
|
+
it('constrains output payloads, GUID brands, and gateway operations', () => {
|
|
129
|
+
typeContract();
|
|
130
|
+
});
|
|
131
|
+
});
|