@forgeax/engine-pack 0.1.21 → 0.1.23
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/README.md +159 -46
- package/dist/__tests__/inventory-schema.test.d.ts +2 -0
- package/dist/__tests__/inventory-schema.test.d.ts.map +1 -0
- package/dist/__tests__/parameterized-pack-authoring-gateway.unit.test.d.ts +2 -0
- package/dist/__tests__/parameterized-pack-authoring-gateway.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parameterized-pack-scanner.unit.test.d.ts +2 -0
- package/dist/__tests__/parameterized-pack-scanner.unit.test.d.ts.map +1 -0
- package/dist/__tests__/parameterized-pack.unit.test.d.ts +2 -0
- package/dist/__tests__/parameterized-pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-blacklist.test.d.ts +2 -0
- package/dist/__tests__/scanner-blacklist.test.d.ts.map +1 -0
- package/dist/build.d.ts +9 -4
- package/dist/build.d.ts.map +1 -1
- package/dist/build.mjs +3314 -803
- package/dist/build.mjs.map +1 -1
- package/dist/builtin.mjs +37 -4
- package/dist/builtin.mjs.map +1 -1
- package/dist/catalog-builder.d.ts.map +1 -1
- package/dist/cli-asset.d.ts.map +1 -1
- package/dist/cli-asset.mjs +1368 -850
- package/dist/cli-asset.mjs.map +1 -1
- package/dist/deriveAssetName.d.ts +11 -7
- package/dist/deriveAssetName.d.ts.map +1 -1
- package/dist/evidence/source-inventory.d.ts.map +1 -1
- package/dist/guid.d.ts +15 -0
- package/dist/guid.d.ts.map +1 -1
- package/dist/guid.mjs +53 -5
- package/dist/guid.mjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1252 -560
- package/dist/index.mjs.map +1 -1
- package/dist/inventory/binding.d.ts +6 -0
- package/dist/inventory/binding.d.ts.map +1 -0
- package/dist/inventory/declaration.d.ts +24 -0
- package/dist/inventory/declaration.d.ts.map +1 -0
- package/dist/inventory/index.d.ts +3 -0
- package/dist/inventory/index.d.ts.map +1 -0
- package/dist/inventory/sync.d.ts +5 -0
- package/dist/inventory/sync.d.ts.map +1 -0
- package/dist/name.mjs +4 -7
- package/dist/name.mjs.map +1 -1
- package/dist/package-finalizer.d.ts +2 -20
- package/dist/package-finalizer.d.ts.map +1 -1
- package/dist/parameterized-pack-node.d.ts +26 -0
- package/dist/parameterized-pack-node.d.ts.map +1 -0
- package/dist/parameterized-pack-node.mjs +11093 -0
- package/dist/parameterized-pack-node.mjs.map +1 -0
- package/dist/parameterized-pack.d.ts +233 -0
- package/dist/parameterized-pack.d.ts.map +1 -0
- package/dist/resolve-asset-source.d.ts +1 -3
- package/dist/resolve-asset-source.d.ts.map +1 -1
- package/dist/resolve-asset-source.mjs +6 -81
- package/dist/resolve-asset-source.mjs.map +1 -1
- package/dist/runtime.mjs +77 -7
- package/dist/runtime.mjs.map +1 -1
- package/dist/scanner.d.ts +19 -8
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.mjs +1169 -690
- package/dist/scanner.mjs.map +1 -1
- package/dist/schema-compiled.d.ts.map +1 -1
- package/dist/schema.mjs +79 -7
- package/dist/schema.mjs.map +1 -1
- package/dist/scriptable-pack-node.d.ts +8 -32
- package/dist/scriptable-pack-node.d.ts.map +1 -1
- package/dist/scriptable-pack-node.mjs +576 -1021
- package/dist/scriptable-pack-node.mjs.map +1 -1
- package/dist/scriptable-pack-worker.mjs +29 -11
- package/dist/scriptable-pack-worker.mjs.map +1 -1
- package/dist/scriptable-pack.d.ts +19 -532
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/scriptable-pack.mjs +1042 -527
- package/dist/scriptable-pack.mjs.map +1 -1
- package/package.json +7 -7
- package/schema/meta.schema.json +1 -1
- package/schema/pack.schema.json +76 -6
- package/src/__tests__/guid-collision.unit.test.ts +18 -9
- package/src/__tests__/inventory-schema.test.ts +109 -0
- package/src/__tests__/pack.unit.test.ts +1 -238
- package/src/__tests__/package-finalizer.contract.test.ts +55 -0
- package/src/__tests__/parameterized-pack-authoring-gateway.unit.test.ts +249 -0
- package/src/__tests__/parameterized-pack-scanner.unit.test.ts +123 -0
- package/src/__tests__/parameterized-pack.unit.test.ts +266 -0
- package/src/__tests__/resolve-asset-source.test.ts +15 -98
- package/src/__tests__/scanner-blacklist.test.ts +55 -0
- package/src/__tests__/scanner-inventory.contract.test.ts +14 -15
- package/src/__tests__/scanner-inventory.test.ts +24 -12
- package/src/__tests__/scriptable-pack-cli.integration.test.ts +4 -9
- package/src/__tests__/topology.unit.test.ts +23 -0
- package/src/build.ts +28 -18
- package/src/catalog-builder.ts +61 -34
- package/src/cli-asset.ts +70 -85
- package/src/deriveAssetName.ts +14 -13
- package/src/evidence/source-inventory.ts +34 -21
- package/src/guid.ts +65 -4
- package/src/index.ts +12 -11
- package/src/inventory/binding.ts +25 -0
- package/src/inventory/declaration.ts +168 -0
- package/src/inventory/index.ts +18 -0
- package/src/inventory/sync.ts +22 -0
- package/src/package-finalizer.ts +2 -42
- package/src/parameterized-pack-node.ts +1968 -0
- package/src/parameterized-pack.ts +1493 -0
- package/src/resolve-asset-source.ts +4 -76
- package/src/scanner.ts +241 -59
- package/src/schema-compiled.ts +2 -0
- package/src/scriptable-pack-node.ts +111 -722
- package/src/scriptable-pack-worker.ts +48 -16
- package/src/scriptable-pack.ts +27 -939
- package/dist/.tsbuildinfo +0 -1
- package/dist/__tests__/load-asset-config.test.d.ts +0 -2
- package/dist/__tests__/load-asset-config.test.d.ts.map +0 -1
- package/dist/__tests__/scriptable-pack.test-d.d.ts +0 -2
- package/dist/__tests__/scriptable-pack.test-d.d.ts.map +0 -1
- package/dist/__tests__/scriptable-pack.unit.test.d.ts +0 -2
- package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +0 -1
- package/dist/config.d.ts +0 -6
- package/dist/config.d.ts.map +0 -1
- package/dist/config.mjs +0 -29
- package/dist/config.mjs.map +0 -1
- package/src/__tests__/load-asset-config.test.ts +0 -121
- package/src/__tests__/scriptable-pack.test-d.ts +0 -131
- package/src/__tests__/scriptable-pack.unit.test.ts +0 -789
- package/src/config.ts +0 -36
|
@@ -2,6 +2,7 @@ import { mkdtemp, realpath, rm, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
5
|
+
import { inventoryDigest } from '../inventory/sync.js';
|
|
5
6
|
import { scanInventory } from '../scanner.js';
|
|
6
7
|
|
|
7
8
|
describe('AssetInventory', () => {
|
|
@@ -18,11 +19,8 @@ describe('AssetInventory', () => {
|
|
|
18
19
|
[
|
|
19
20
|
'const packageId = new Uint8Array(16);',
|
|
20
21
|
'packageId[15] = 1;',
|
|
21
|
-
'const meshGuid = new Uint8Array(16);',
|
|
22
|
-
'meshGuid[15] = 2;',
|
|
23
22
|
'export default {',
|
|
24
|
-
"schemaVersion: '
|
|
25
|
-
"assets: { mesh: { guid: meshGuid, kind: 'mesh' } }, externalAssets: {},",
|
|
23
|
+
"schemaVersion: '2.0.0', packageId,",
|
|
26
24
|
"build: () => ({ ok: true, value: { mesh: { kind: 'mesh' } } }),",
|
|
27
25
|
'};',
|
|
28
26
|
].join('\n'),
|
|
@@ -46,11 +44,13 @@ describe('AssetInventory', () => {
|
|
|
46
44
|
const { inventory, sourcePath } = getFixture();
|
|
47
45
|
const declaration = inventory.declarations.get(sourcePath);
|
|
48
46
|
expect(declaration).toMatchObject({
|
|
49
|
-
format: 'pack.ts',
|
|
47
|
+
format: 'pack.ts-parameterized',
|
|
50
48
|
sourcePath,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
value: {
|
|
50
|
+
schemaVersion: '2.0.0',
|
|
51
|
+
kind: 'parameterized-pack-source',
|
|
52
|
+
packageId: '00000000-0000-0000-0000-000000000001',
|
|
53
|
+
source: sourcePath,
|
|
54
54
|
},
|
|
55
55
|
});
|
|
56
56
|
});
|
|
@@ -59,7 +59,7 @@ describe('AssetInventory', () => {
|
|
|
59
59
|
const { inventory, sourcePath } = getFixture();
|
|
60
60
|
const declaration = inventory.declarations.get(sourcePath);
|
|
61
61
|
expect(
|
|
62
|
-
declaration?.format === 'pack.ts'
|
|
62
|
+
declaration?.format === 'pack.ts-parameterized'
|
|
63
63
|
? declaration.sourceClosure.map((entry) => entry.path)
|
|
64
64
|
: undefined,
|
|
65
65
|
).toEqual([await realpath(sourcePath)]);
|
|
@@ -69,8 +69,20 @@ describe('AssetInventory', () => {
|
|
|
69
69
|
const { inventory, sourcePath } = getFixture();
|
|
70
70
|
const declaration = inventory.declarations.get(sourcePath);
|
|
71
71
|
expect(inventory.inventory).toEqual([]);
|
|
72
|
-
expect(
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
expect(
|
|
73
|
+
declaration?.format === 'pack.ts-parameterized' ? declaration.definition : undefined,
|
|
74
|
+
).toMatchObject({ packageId: expect.any(Uint8Array), schemaVersion: '2.0.0' });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('keeps semantic identity stable when the source path is renamed', () => {
|
|
78
|
+
const before = inventoryDigest({
|
|
79
|
+
declarations: [{ guid, sourceKey: 'hero/body', kind: 'mesh', payload: {}, refs: [] }],
|
|
80
|
+
});
|
|
81
|
+
const after = inventoryDigest({
|
|
82
|
+
declarations: [{ guid, sourceKey: 'hero/body', kind: 'mesh', payload: {}, refs: [] }],
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(after).toBe(before);
|
|
86
|
+
expect(after).not.toContain('generated.pack.ts');
|
|
75
87
|
});
|
|
76
88
|
});
|
|
@@ -14,14 +14,9 @@ describe('ScriptablePack CLI Meta inspection', () => {
|
|
|
14
14
|
await writeFile(
|
|
15
15
|
source,
|
|
16
16
|
`
|
|
17
|
-
const guid = (hex) => new Uint8Array(hex.match(/../g).map((byte) => Number.parseInt(byte, 16)));
|
|
18
17
|
export default {
|
|
19
|
-
schemaVersion: '
|
|
20
|
-
packageId:
|
|
21
|
-
assets: {
|
|
22
|
-
wall: { guid: guid('019ffa97a5ee7645b613043323952808'), kind: 'mesh' }
|
|
23
|
-
},
|
|
24
|
-
externalAssets: {},
|
|
18
|
+
schemaVersion: '2.0.0',
|
|
19
|
+
packageId: new Uint8Array([1, 159, 250, 151, 139, 57, 122, 210, 132, 204, 39, 50, 104, 89, 26, 180]),
|
|
25
20
|
build() { throw new Error('META_MUST_NOT_BUILD'); }
|
|
26
21
|
};
|
|
27
22
|
`,
|
|
@@ -36,9 +31,9 @@ describe('ScriptablePack CLI Meta inspection', () => {
|
|
|
36
31
|
expect(code).toBe(0);
|
|
37
32
|
expect(stderr).toEqual([]);
|
|
38
33
|
expect(JSON.parse(stdout[0] ?? '{}')).toMatchObject({
|
|
39
|
-
|
|
34
|
+
kind: 'parameterized-pack-source',
|
|
40
35
|
source,
|
|
41
|
-
|
|
36
|
+
packageId: '019ffa97-8b39-7ad2-84cc-273268591ab4',
|
|
42
37
|
});
|
|
43
38
|
} finally {
|
|
44
39
|
await rm(root, { recursive: true, force: true });
|
|
@@ -82,4 +82,27 @@ describe('diffTopology', () => {
|
|
|
82
82
|
expect(result.preserved).toEqual([]);
|
|
83
83
|
expect(result.ambiguous).toMatchObject([{ reason: 'duplicate-source-key' }]);
|
|
84
84
|
});
|
|
85
|
+
|
|
86
|
+
it('keeps LOD source keys prefix-stable while allowing a suffix level', () => {
|
|
87
|
+
const result = diffTopology(
|
|
88
|
+
[
|
|
89
|
+
{ guid: 'root', sourceKey: 'mesh/root', sourceIndex: 0, kind: 'mesh' },
|
|
90
|
+
{ guid: 'lod1', sourceKey: 'mesh/lod1', sourceIndex: 1, kind: 'mesh' },
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
{ guid: 'root-next', sourceKey: 'mesh/root', sourceIndex: 0, kind: 'mesh' },
|
|
94
|
+
{ guid: 'lod1-next', sourceKey: 'mesh/lod1', sourceIndex: 1, kind: 'mesh' },
|
|
95
|
+
{ guid: 'lod2-next', sourceKey: 'mesh/lod2', sourceIndex: 2, kind: 'mesh' },
|
|
96
|
+
],
|
|
97
|
+
);
|
|
98
|
+
expect(result.preserved).toEqual([
|
|
99
|
+
{ guid: 'root', oldKey: 'mesh/root', newKey: 'mesh/root' },
|
|
100
|
+
{ guid: 'lod1', oldKey: 'mesh/lod1', newKey: 'mesh/lod1' },
|
|
101
|
+
]);
|
|
102
|
+
expect(result.added).toEqual([
|
|
103
|
+
{ guid: 'lod2-next', sourceKey: 'mesh/lod2', sourceIndex: 2, kind: 'mesh' },
|
|
104
|
+
]);
|
|
105
|
+
expect(result.removed).toEqual([]);
|
|
106
|
+
expect(result.ambiguous).toEqual([]);
|
|
107
|
+
});
|
|
85
108
|
});
|
package/src/build.ts
CHANGED
|
@@ -51,7 +51,6 @@ export {
|
|
|
51
51
|
projectRuntimeCatalogRow,
|
|
52
52
|
type RuntimeCatalogRowInput,
|
|
53
53
|
} from './catalog-projection.js';
|
|
54
|
-
export { type LoadAssetConfigResult, loadAssetConfig } from './config.js';
|
|
55
54
|
export {
|
|
56
55
|
type CookedMaterialRecord,
|
|
57
56
|
collectMaterialCookRefs,
|
|
@@ -68,6 +67,22 @@ export {
|
|
|
68
67
|
validateMaterialCookReceipt,
|
|
69
68
|
} from './evidence/material-cook.js';
|
|
70
69
|
export { buildOfflineAssetEvidence, packageVerification } from './evidence/offline-evidence.js';
|
|
70
|
+
export {
|
|
71
|
+
AssetGuid,
|
|
72
|
+
isValidAssetGuidString,
|
|
73
|
+
isValidPackSourceKey,
|
|
74
|
+
PACK_SOURCE_KEY_RE,
|
|
75
|
+
PackageId,
|
|
76
|
+
} from './guid.js';
|
|
77
|
+
export { projectAssetRefs, projectSceneEntityRefs } from './inventory/binding.js';
|
|
78
|
+
export {
|
|
79
|
+
type AuthorInventory,
|
|
80
|
+
type AuthorInventoryRow,
|
|
81
|
+
type InventoryError,
|
|
82
|
+
type InventoryErrorCode,
|
|
83
|
+
validateAuthorInventory,
|
|
84
|
+
} from './inventory/declaration.js';
|
|
85
|
+
export { inventoryDigest, syncAuthorInventory } from './inventory/sync.js';
|
|
71
86
|
export {
|
|
72
87
|
decodeMeshBinHeader,
|
|
73
88
|
MESH_BIN_DIGEST_BYTES,
|
|
@@ -80,8 +95,6 @@ export {
|
|
|
80
95
|
writeMeshBinHeader,
|
|
81
96
|
} from './mesh-bin-contract.js';
|
|
82
97
|
export {
|
|
83
|
-
type AuthoredPackAssetInput,
|
|
84
|
-
type AuthoredPackInput,
|
|
85
98
|
type FinalizedPackageProduct,
|
|
86
99
|
finalizePackageProduct,
|
|
87
100
|
finalizePackageTransportSource,
|
|
@@ -92,8 +105,14 @@ export {
|
|
|
92
105
|
type PackageProduct,
|
|
93
106
|
type PackageProductAsset,
|
|
94
107
|
packageTransportRevision,
|
|
95
|
-
upgradeLegacyAuthoredPack,
|
|
96
108
|
} from './package-finalizer.js';
|
|
109
|
+
export * from './parameterized-pack.js';
|
|
110
|
+
export {
|
|
111
|
+
createFileSystemParameterizedPackAuthoringGateway,
|
|
112
|
+
createFileSystemParameterizedPackAuthoringPort,
|
|
113
|
+
type FileSystemParameterizedPackAuthoringOptions,
|
|
114
|
+
type ParameterizedPackMaterializedAsset,
|
|
115
|
+
} from './parameterized-pack-node.js';
|
|
97
116
|
export { validateProducerContract, validateProducerOutputs } from './producer-contract.js';
|
|
98
117
|
export { resolveAssetSource } from './resolve-asset-source.js';
|
|
99
118
|
export { parsePackV2, validateMeta, validatePack, validatePackV2 } from './runtime.js';
|
|
@@ -107,8 +126,13 @@ export {
|
|
|
107
126
|
} from './runtime-publication.js';
|
|
108
127
|
export {
|
|
109
128
|
type InventoryDeclaration,
|
|
129
|
+
type LegacyPackInventoryDocument,
|
|
130
|
+
type PackInventoryAsset,
|
|
131
|
+
type PackInventoryDocument,
|
|
132
|
+
type ParameterizedPackInventoryDocument,
|
|
110
133
|
type ScanInventory,
|
|
111
134
|
type ScanOptions,
|
|
135
|
+
type ScanSourceDeclaration,
|
|
112
136
|
type ScriptablePackInventoryDeclaration,
|
|
113
137
|
type ScriptablePackScanOptions,
|
|
114
138
|
STANDARD_SCRIPTABLE_PACK_SCAN_OPTIONS,
|
|
@@ -117,24 +141,10 @@ export {
|
|
|
117
141
|
export {
|
|
118
142
|
type AssetReader,
|
|
119
143
|
isScriptablePackAssetKind,
|
|
120
|
-
projectScriptablePackMeta,
|
|
121
144
|
projectScriptablePackSceneComponents,
|
|
122
145
|
SCRIPTABLE_PACK_ASSET_KINDS,
|
|
123
|
-
SCRIPTABLE_PACK_CAPABILITY_MANIFEST,
|
|
124
|
-
type ScriptablePackAssetDeclaration,
|
|
125
|
-
type ScriptablePackAssetDeclarations,
|
|
126
|
-
type ScriptablePackAssetFor,
|
|
127
146
|
type ScriptablePackAssetKind,
|
|
128
|
-
type ScriptablePackDefinition,
|
|
129
|
-
type ScriptablePackError,
|
|
130
|
-
type ScriptablePackExternalAssets,
|
|
131
|
-
type ScriptablePackMetaJson,
|
|
132
|
-
type ScriptablePackOutputs,
|
|
133
|
-
type ScriptablePackPublicationEnvelope,
|
|
134
147
|
type ScriptablePackReadError,
|
|
135
|
-
type ScriptablePackSceneComponent,
|
|
136
|
-
type ScriptablePackSceneComponentInput,
|
|
137
148
|
type ScriptablePackSourceClosureEntry,
|
|
138
|
-
validateScriptablePackDefinition,
|
|
139
149
|
} from './scriptable-pack.js';
|
|
140
150
|
export { calculateTopologyDiff, diffTopology } from './topology.js';
|
package/src/catalog-builder.ts
CHANGED
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
projectExternalCatalogEntries,
|
|
6
6
|
projectPackageCatalog,
|
|
7
7
|
} from './catalog-projection.js';
|
|
8
|
-
import { loadAssetConfig } from './config.js';
|
|
9
8
|
import { deriveAssetName } from './deriveAssetName.js';
|
|
9
|
+
import { parseParameterizedPackJson, projectDirectPackJson } from './parameterized-pack.js';
|
|
10
10
|
import { resolveAssetSource } from './resolve-asset-source.js';
|
|
11
11
|
import {
|
|
12
12
|
type MetaInventoryDocument,
|
|
@@ -138,7 +138,6 @@ function projectMeta(
|
|
|
138
138
|
rawPath: string,
|
|
139
139
|
cwd: string,
|
|
140
140
|
base: string,
|
|
141
|
-
assetPaths: Record<string, string>,
|
|
142
141
|
importerPolicy: (importer: string) => CatalogImporterPolicy,
|
|
143
142
|
sourceDeclaration: Extract<ScanSourceDeclaration, { readonly format: 'meta.json' }>,
|
|
144
143
|
sourceIdentityFor: CatalogBuildProjectionOptions['sourceIdentityFor'],
|
|
@@ -152,22 +151,12 @@ function projectMeta(
|
|
|
152
151
|
if (policy.disposition !== 'publish') {
|
|
153
152
|
return { entries: [], error: unsupportedPolicyError(rawPath, meta.importer, policy) };
|
|
154
153
|
}
|
|
155
|
-
const resolved = resolveAssetSource(rawPath, meta.source
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
entries: [],
|
|
159
|
-
error: {
|
|
160
|
-
code: 'catalog-meta-schema-invalid',
|
|
161
|
-
path: rawPath,
|
|
162
|
-
message: `source resolution failed: ${resolved.error.code} - ${resolved.error.hint}`,
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
const sourcePath = catalogSourcePathFor(cwd, resolved.value, sourceIdentityFor);
|
|
154
|
+
const resolved = resolveAssetSource(rawPath, meta.source);
|
|
155
|
+
const sourcePath = catalogSourcePathFor(cwd, resolved, sourceIdentityFor);
|
|
167
156
|
const packageUrl = `${base}/__forgeax-ddc/${meta.subAssets[0]?.guid?.toLowerCase() ?? 'pack'}.pack.json`;
|
|
168
157
|
const declaration: CatalogImportMeta = {
|
|
169
158
|
importer: meta.importer,
|
|
170
|
-
source: resolved
|
|
159
|
+
source: resolved,
|
|
171
160
|
sourceRevision: sourceDeclaration.sourceRevision,
|
|
172
161
|
...(meta.packageId === undefined ? {} : { packageId: meta.packageId }),
|
|
173
162
|
...(meta.provenance === undefined ? {} : { provenance: meta.provenance }),
|
|
@@ -197,7 +186,7 @@ function projectMeta(
|
|
|
197
186
|
return {
|
|
198
187
|
declaration,
|
|
199
188
|
entries: projectExternalCatalogEntries(meta, sourcePath, packageUrl, meta.subAssets, (output) =>
|
|
200
|
-
deriveAssetName(resolved
|
|
189
|
+
deriveAssetName(resolved, meta.subAssets.length, output.name),
|
|
201
190
|
),
|
|
202
191
|
};
|
|
203
192
|
}
|
|
@@ -206,27 +195,17 @@ function projectSource(
|
|
|
206
195
|
path: string,
|
|
207
196
|
cwd: string,
|
|
208
197
|
base: string,
|
|
209
|
-
assetPaths: Record<string, string>,
|
|
210
198
|
importerPolicy: (importer: string) => CatalogImporterPolicy,
|
|
211
199
|
visibility: CatalogBuildProjectionOptions['visibility'],
|
|
212
200
|
sourceDeclaration: ScanSourceDeclaration,
|
|
213
201
|
declarations: Map<string, CatalogImportMeta>,
|
|
214
202
|
sourceIdentityFor: CatalogBuildProjectionOptions['sourceIdentityFor'],
|
|
215
203
|
): { readonly entries: PackIndexEntry[]; readonly error?: CatalogBuildError } {
|
|
216
|
-
if (sourceDeclaration.format === 'pack.ts') {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
: {
|
|
222
|
-
entries: projectExternalCatalogEntries(
|
|
223
|
-
meta,
|
|
224
|
-
catalogSourcePathFor(cwd, path, sourceIdentityFor),
|
|
225
|
-
`${base}/__forgeax-ddc/${anchorGuid}.pack.json`,
|
|
226
|
-
meta.subAssets,
|
|
227
|
-
(output) => deriveAssetName(path, meta.subAssets.length, output.name),
|
|
228
|
-
),
|
|
229
|
-
};
|
|
204
|
+
if (sourceDeclaration.format === 'pack.ts-parameterized') {
|
|
205
|
+
// A source declaration is an authoring identity and parameter contract,
|
|
206
|
+
// not a materialized output set. Dynamic build owns publication after its
|
|
207
|
+
// fixed-point worklist converges; Catalog scanning must not invent rows.
|
|
208
|
+
return { entries: [] };
|
|
230
209
|
}
|
|
231
210
|
if (sourceDeclaration.format === 'meta.json') {
|
|
232
211
|
const policy = importerPolicy(sourceDeclaration.value.importer);
|
|
@@ -244,7 +223,6 @@ function projectSource(
|
|
|
244
223
|
path,
|
|
245
224
|
cwd,
|
|
246
225
|
base,
|
|
247
|
-
assetPaths,
|
|
248
226
|
importerPolicy,
|
|
249
227
|
sourceDeclaration,
|
|
250
228
|
sourceIdentityFor,
|
|
@@ -254,6 +232,57 @@ function projectSource(
|
|
|
254
232
|
}
|
|
255
233
|
const parsed = sourceDeclaration.value;
|
|
256
234
|
const sourcePath = catalogSourcePathFor(cwd, path, sourceIdentityFor);
|
|
235
|
+
if (parsed.schemaVersion === '3.0.0') {
|
|
236
|
+
const authoring = parseParameterizedPackJson(parsed);
|
|
237
|
+
if (!authoring.ok) {
|
|
238
|
+
return {
|
|
239
|
+
entries: [],
|
|
240
|
+
error: {
|
|
241
|
+
code: 'catalog-meta-schema-invalid',
|
|
242
|
+
path,
|
|
243
|
+
message: `v3 Pack parse failed: ${authoring.error.code}`,
|
|
244
|
+
expected: 'a valid direct or instance v3 Pack document',
|
|
245
|
+
actual: authoring.error.code,
|
|
246
|
+
hint: authoring.error.hint,
|
|
247
|
+
subjects: [path],
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
if (authoring.value.format !== 'direct') return { entries: [] };
|
|
252
|
+
const projected = projectDirectPackJson(authoring.value);
|
|
253
|
+
if (!projected.ok) {
|
|
254
|
+
return {
|
|
255
|
+
entries: [],
|
|
256
|
+
error: {
|
|
257
|
+
code: 'catalog-meta-schema-invalid',
|
|
258
|
+
path,
|
|
259
|
+
message: `v3 Pack projection failed: ${projected.error.code}`,
|
|
260
|
+
expected: 'direct v3 assets to derive stable AssetGuids',
|
|
261
|
+
actual: projected.error.code,
|
|
262
|
+
hint: projected.error.hint,
|
|
263
|
+
subjects: [path],
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
const provenance = { provider: 'pack', version: '3.0.0' } satisfies ProviderProvenance;
|
|
268
|
+
return {
|
|
269
|
+
entries: projectPackageCatalog(
|
|
270
|
+
projected.value.assets.map((asset, sourceIndex) => ({
|
|
271
|
+
guid: asset.guid,
|
|
272
|
+
kind: asset.kind,
|
|
273
|
+
sourcePath,
|
|
274
|
+
sourceIndex,
|
|
275
|
+
sourceKey: asset.sourceKey,
|
|
276
|
+
refs: asset.refs,
|
|
277
|
+
execution: 'direct' as const,
|
|
278
|
+
packageId: projected.value.packageId,
|
|
279
|
+
provenance,
|
|
280
|
+
name: deriveAssetName(path, projected.value.assets.length, asset.name),
|
|
281
|
+
})),
|
|
282
|
+
`${base}/${sourcePath}`,
|
|
283
|
+
),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
257
286
|
const provenance =
|
|
258
287
|
parsed.provenance ??
|
|
259
288
|
({ provider: 'pack', version: parsed.schemaVersion } satisfies ProviderProvenance);
|
|
@@ -317,7 +346,6 @@ export async function buildCatalogProjection(
|
|
|
317
346
|
return empty('degraded', [error]);
|
|
318
347
|
}
|
|
319
348
|
const { paths, declarations: sourceDeclarations } = scanned.value;
|
|
320
|
-
const { paths: assetPaths } = loadAssetConfig(cwd);
|
|
321
349
|
const entries: PackIndexEntry[] = [];
|
|
322
350
|
const errors: CatalogBuildError[] = [];
|
|
323
351
|
const declarations = new Map<string, CatalogImportMeta>();
|
|
@@ -328,7 +356,6 @@ export async function buildCatalogProjection(
|
|
|
328
356
|
path,
|
|
329
357
|
cwd,
|
|
330
358
|
base,
|
|
331
|
-
assetPaths,
|
|
332
359
|
options.importerPolicy,
|
|
333
360
|
options.visibility,
|
|
334
361
|
sourceDeclaration,
|
package/src/cli-asset.ts
CHANGED
|
@@ -26,9 +26,9 @@ import {
|
|
|
26
26
|
import { readSourceInventory } from './evidence/source-inventory.js';
|
|
27
27
|
import { isValidAssetGuidString } from './guid.js';
|
|
28
28
|
import { parsePackV2 } from './index.js';
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
29
|
+
import { projectParameterizedPackMeta } from './parameterized-pack.js';
|
|
30
|
+
import { type ScanSourceDeclaration, scanInventory } from './scanner.js';
|
|
31
|
+
import { loadParameterizedScriptablePack } from './scriptable-pack-node.js';
|
|
32
32
|
|
|
33
33
|
export interface PackEntry {
|
|
34
34
|
readonly guid: string;
|
|
@@ -51,6 +51,10 @@ interface ScanErrShape {
|
|
|
51
51
|
detail?: unknown;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
55
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
56
|
+
}
|
|
57
|
+
|
|
54
58
|
function emitError(ctx: AssetCtx, err: ScanErrShape): number {
|
|
55
59
|
const payload: Record<string, unknown> = {
|
|
56
60
|
code: err.code,
|
|
@@ -66,7 +70,7 @@ export async function scanEntries(
|
|
|
66
70
|
roots: readonly string[],
|
|
67
71
|
ctx: AssetCtx,
|
|
68
72
|
): Promise<{ ok: true; value: PackEntry[] } | { ok: false }> {
|
|
69
|
-
const result = await
|
|
73
|
+
const result = await scanInventory(roots);
|
|
70
74
|
if (!result.ok) {
|
|
71
75
|
emitError(ctx, {
|
|
72
76
|
code: result.error.code,
|
|
@@ -77,94 +81,73 @@ export async function scanEntries(
|
|
|
77
81
|
return { ok: false };
|
|
78
82
|
}
|
|
79
83
|
const entries: PackEntry[] = [];
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const packPaths = result.value.filter((p) => p.endsWith('.pack.json'));
|
|
90
|
-
const metaPaths = result.value.filter((p) => p.endsWith('.meta.json'));
|
|
91
|
-
for (const packPath of packPaths) {
|
|
92
|
-
let parsed: unknown;
|
|
93
|
-
try {
|
|
94
|
-
const raw = await readFile(packPath, 'utf-8');
|
|
95
|
-
parsed = JSON.parse(raw);
|
|
96
|
-
} catch {
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
const packObj = parsed as { assets?: { guid?: unknown; kind?: unknown; name?: unknown }[] };
|
|
100
|
-
if (!Array.isArray(packObj.assets)) continue;
|
|
101
|
-
for (const asset of packObj.assets) {
|
|
102
|
-
if (typeof asset.guid === 'string' && typeof asset.kind === 'string') {
|
|
103
|
-
entries.push({
|
|
104
|
-
guid: asset.guid,
|
|
105
|
-
kind: asset.kind,
|
|
106
|
-
sourcePath: packPath,
|
|
107
|
-
...(typeof asset.name === 'string' ? { name: asset.name } : {}),
|
|
108
|
-
});
|
|
84
|
+
// The scanner is the single identity projection for Pack JSON. Parameterized
|
|
85
|
+
// source modules deliberately contribute no rows until a build generation.
|
|
86
|
+
for (const declaration of result.value.declarations.values()) {
|
|
87
|
+
if (declaration.format !== 'meta.json') continue;
|
|
88
|
+
const sourcePath = declaration.sourcePath;
|
|
89
|
+
const assets = declaration.value.subAssets;
|
|
90
|
+
for (const asset of assets) {
|
|
91
|
+
if (!isRecord(asset) || typeof asset.guid !== 'string' || typeof asset.kind !== 'string') {
|
|
92
|
+
continue;
|
|
109
93
|
}
|
|
94
|
+
const name =
|
|
95
|
+
typeof asset.name === 'string'
|
|
96
|
+
? asset.name
|
|
97
|
+
: typeof asset.sourceKey === 'string'
|
|
98
|
+
? asset.sourceKey
|
|
99
|
+
: undefined;
|
|
100
|
+
entries.push({
|
|
101
|
+
guid: asset.guid,
|
|
102
|
+
kind: asset.kind,
|
|
103
|
+
sourcePath,
|
|
104
|
+
...(name === undefined ? {} : { name }),
|
|
105
|
+
});
|
|
110
106
|
}
|
|
111
107
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
const names = new Map<string, string>();
|
|
109
|
+
for (const declaration of result.value.declarations.values()) {
|
|
110
|
+
if (declaration.format !== 'pack.json') continue;
|
|
111
|
+
if (declaration.value.schemaVersion === '3.0.0') {
|
|
112
|
+
for (const [sourceKey, asset] of Object.entries(declaration.value.assets ?? {})) {
|
|
113
|
+
if (isRecord(asset) && typeof asset.name === 'string') {
|
|
114
|
+
names.set(`${declaration.sourcePath}:${sourceKey}`, asset.name);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
118
117
|
continue;
|
|
119
118
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (!Array.isArray(metaObj.subAssets)) continue;
|
|
124
|
-
for (const sub of metaObj.subAssets) {
|
|
125
|
-
if (typeof sub.guid === 'string' && typeof sub.kind === 'string') {
|
|
126
|
-
const name =
|
|
127
|
-
typeof sub.name === 'string'
|
|
128
|
-
? sub.name
|
|
129
|
-
: typeof sub.sourceKey === 'string'
|
|
130
|
-
? sub.sourceKey
|
|
131
|
-
: undefined;
|
|
132
|
-
entries.push({
|
|
133
|
-
guid: sub.guid,
|
|
134
|
-
kind: sub.kind,
|
|
135
|
-
sourcePath: metaPath,
|
|
136
|
-
...(name === undefined ? {} : { name }),
|
|
137
|
-
});
|
|
119
|
+
for (const asset of declaration.value.assets) {
|
|
120
|
+
if (typeof asset.name === 'string') {
|
|
121
|
+
names.set(`${declaration.sourcePath}:${asset.guid.toLowerCase()}`, asset.name);
|
|
138
122
|
}
|
|
139
123
|
}
|
|
140
124
|
}
|
|
141
|
-
for (const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
});
|
|
164
|
-
return { ok: false };
|
|
165
|
-
}
|
|
166
|
-
firstByGuid.set(guid, entry);
|
|
125
|
+
for (const inventoryEntry of result.value.inventory) {
|
|
126
|
+
const declaration = result.value.declarations.get(inventoryEntry.sourcePath) as
|
|
127
|
+
| ScanSourceDeclaration
|
|
128
|
+
| undefined;
|
|
129
|
+
const namedByKey =
|
|
130
|
+
inventoryEntry.sourceKey === undefined
|
|
131
|
+
? names.get(`${inventoryEntry.sourcePath}:${inventoryEntry.guid.toLowerCase()}`)
|
|
132
|
+
: names.get(`${inventoryEntry.sourcePath}:${inventoryEntry.sourceKey}`);
|
|
133
|
+
const name =
|
|
134
|
+
namedByKey ?? names.get(`${inventoryEntry.sourcePath}:${inventoryEntry.guid.toLowerCase()}`);
|
|
135
|
+
entries.push({
|
|
136
|
+
guid: inventoryEntry.guid,
|
|
137
|
+
kind: inventoryEntry.kind,
|
|
138
|
+
sourcePath: inventoryEntry.sourcePath,
|
|
139
|
+
...(name === undefined ? {} : { name }),
|
|
140
|
+
...(declaration?.format === 'pack.json' &&
|
|
141
|
+
declaration.value.schemaVersion === '3.0.0' &&
|
|
142
|
+
inventoryEntry.sourceKey !== undefined &&
|
|
143
|
+
name === undefined
|
|
144
|
+
? { name: inventoryEntry.sourceKey }
|
|
145
|
+
: {}),
|
|
146
|
+
});
|
|
167
147
|
}
|
|
148
|
+
entries.sort((left, right) =>
|
|
149
|
+
`${left.sourcePath}:${left.guid}`.localeCompare(`${right.sourcePath}:${right.guid}`),
|
|
150
|
+
);
|
|
168
151
|
return { ok: true, value: entries };
|
|
169
152
|
}
|
|
170
153
|
|
|
@@ -250,9 +233,11 @@ async function runMeta(rest: string[], ctx: AssetCtx): Promise<number> {
|
|
|
250
233
|
});
|
|
251
234
|
}
|
|
252
235
|
const cwd = ctx.cwd ?? process.cwd();
|
|
253
|
-
const loaded = await
|
|
236
|
+
const loaded = await loadParameterizedScriptablePack(absolutePath(source, cwd), {
|
|
237
|
+
metadataOnly: true,
|
|
238
|
+
});
|
|
254
239
|
if (!loaded.ok) return emitError(ctx, loaded.error);
|
|
255
|
-
ctx.stdoutWrite(JSON.stringify(
|
|
240
|
+
ctx.stdoutWrite(JSON.stringify(projectParameterizedPackMeta(loaded.value, source)));
|
|
256
241
|
return 0;
|
|
257
242
|
}
|
|
258
243
|
|
package/src/deriveAssetName.ts
CHANGED
|
@@ -16,27 +16,28 @@ function baseName(p: string): string {
|
|
|
16
16
|
*
|
|
17
17
|
* Pure function -- zero IO, zero side effects, browser-safe. Build-time
|
|
18
18
|
* (build-catalog) and runtime (resolveName) share this single source of truth
|
|
19
|
-
* for
|
|
19
|
+
* for deterministic asset display-name resolution.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* Resolution contract:
|
|
22
|
+
* - an explicit storedName is authoritative;
|
|
23
|
+
* - a package path is the fallback when no storedName is present;
|
|
24
|
+
* - an asset without a package and without a storedName resolves to ''.
|
|
25
|
+
*
|
|
26
|
+
* `assetCount` remains in the signature because callers derive it from their
|
|
27
|
+
* package model, but it must not erase an explicitly authored name. In
|
|
28
|
+
* particular, a single authored entry in `Materials.pack.json` resolves to its
|
|
29
|
+
* entry name rather than temporarily appearing as the package basename.
|
|
26
30
|
*/
|
|
27
31
|
export function deriveAssetName(
|
|
28
32
|
packagePath: string | null,
|
|
29
|
-
|
|
33
|
+
_assetCount: number,
|
|
30
34
|
storedName?: string,
|
|
31
35
|
): string {
|
|
32
|
-
if (packagePath === null) {
|
|
33
|
-
return storedName ?? '';
|
|
34
|
-
}
|
|
35
|
-
if (assetCount === 1) {
|
|
36
|
-
return baseName(packagePath);
|
|
37
|
-
}
|
|
38
36
|
if (storedName !== undefined) {
|
|
39
37
|
return storedName;
|
|
40
38
|
}
|
|
39
|
+
if (packagePath === null) {
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
41
42
|
return baseName(packagePath);
|
|
42
43
|
}
|