@forgeax/engine-import 0.1.20 → 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 +91 -3
- package/dist/__tests__/ies-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/ies-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/ies-producer.unit.test.d.ts +2 -0
- package/dist/__tests__/ies-producer.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-lod-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-lod-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-lod-reimport.integration.test.d.ts +2 -0
- package/dist/__tests__/mesh-lod-reimport.integration.test.d.ts.map +1 -0
- package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts +2 -0
- package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-file-snapshot.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-file-snapshot.unit.test.d.ts.map +1 -0
- package/dist/__tests__/source-package-publication-lod.integration.test.d.ts +2 -0
- package/dist/__tests__/source-package-publication-lod.integration.test.d.ts.map +1 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.mjs +230 -3
- package/dist/browser.mjs.map +1 -1
- package/dist/build-production.d.ts +1 -8
- package/dist/build-production.d.ts.map +1 -1
- package/dist/ies/ies-importer.d.ts +4 -0
- package/dist/ies/ies-importer.d.ts.map +1 -0
- package/dist/ies/parse-lm63.d.ts +13 -0
- package/dist/ies/parse-lm63.d.ts.map +1 -0
- package/dist/ies/resample-type-c.d.ts +4 -0
- package/dist/ies/resample-type-c.d.ts.map +1 -0
- package/dist/import-runner.d.ts.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +2550 -1029
- package/dist/index.mjs.map +1 -1
- package/dist/mesh-bin.d.ts.map +1 -1
- package/dist/mesh-bin.mjs +29 -1
- package/dist/mesh-bin.mjs.map +1 -1
- package/dist/mesh-lod.d.ts +60 -0
- package/dist/mesh-lod.d.ts.map +1 -0
- package/dist/parameterized-scriptable-pack.d.ts +68 -0
- package/dist/parameterized-scriptable-pack.d.ts.map +1 -0
- package/dist/scriptable-pack-file-snapshot.d.ts +71 -0
- package/dist/scriptable-pack-file-snapshot.d.ts.map +1 -0
- package/dist/scriptable-pack-host.d.ts +70 -39
- package/dist/scriptable-pack-host.d.ts.map +1 -1
- package/dist/scriptable-pack-output-producers.d.ts +1 -0
- package/dist/scriptable-pack-output-producers.d.ts.map +1 -1
- package/dist/scriptable-pack-staged-snapshot.d.ts +2 -2
- package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -1
- package/dist/scriptable-pack.d.ts +13 -26
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/source-package-errors.d.ts.map +1 -1
- package/dist/source-package-publication.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/__tests__/ies-contract.unit.test.ts +31 -0
- package/src/__tests__/ies-producer.unit.test.ts +104 -0
- package/src/__tests__/mesh-bin.test.ts +69 -0
- package/src/__tests__/mesh-lod-contract.unit.test.ts +92 -0
- package/src/__tests__/mesh-lod-reimport.integration.test.ts +15 -0
- package/src/__tests__/parameterized-scriptable-pack.unit.test.ts +270 -0
- package/src/__tests__/scriptable-pack-file-snapshot.unit.test.ts +177 -0
- package/src/__tests__/scriptable-pack-host.unit.test.ts +196 -4
- package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +7 -1
- package/src/__tests__/scriptable-pack-staged-snapshot.unit.test.ts +43 -12
- package/src/__tests__/source-package-publication-lod.integration.test.ts +29 -0
- package/src/__tests__/source-package-publication.integration.test.ts +26 -1
- package/src/browser.ts +5 -0
- package/src/build-production.ts +498 -141
- package/src/ies/ies-importer.ts +126 -0
- package/src/ies/parse-lm63.ts +98 -0
- package/src/ies/resample-type-c.ts +108 -0
- package/src/import-runner.ts +58 -0
- package/src/index.ts +49 -19
- package/src/mesh-bin.ts +38 -0
- package/src/mesh-lod.ts +265 -0
- package/src/parameterized-scriptable-pack.ts +725 -0
- package/src/scriptable-pack-file-snapshot.ts +346 -0
- package/src/scriptable-pack-host.ts +790 -343
- package/src/scriptable-pack-output-producers.ts +58 -1
- package/src/scriptable-pack-staged-snapshot.ts +12 -11
- package/src/scriptable-pack.ts +16 -485
- package/src/source-package-errors.ts +6 -0
- package/src/source-package-publication.ts +32 -2
- package/dist/.tsbuildinfo +0 -1
- package/dist/__tests__/scriptable-pack-product.contract.test.d.ts +0 -2
- package/dist/__tests__/scriptable-pack-product.contract.test.d.ts.map +0 -1
- package/dist/__tests__/scriptable-pack.integration.test.d.ts +0 -2
- package/dist/__tests__/scriptable-pack.integration.test.d.ts.map +0 -1
- package/dist/scriptable-source-package.d.ts +0 -14
- package/dist/scriptable-source-package.d.ts.map +0 -1
- package/src/__tests__/scriptable-pack-product.contract.test.ts +0 -46
- package/src/__tests__/scriptable-pack.integration.test.ts +0 -435
- package/src/scriptable-source-package.ts +0 -88
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
2
|
-
import type {
|
|
3
|
-
ScriptablePackAssetDeclarations,
|
|
4
|
-
ScriptablePackDefinition,
|
|
5
|
-
} from '@forgeax/engine-pack/source';
|
|
6
|
-
import type { Asset, AssetGuid as AssetGuidType, MeshAsset } from '@forgeax/engine-types';
|
|
7
|
-
import { AssetError, err, ok } from '@forgeax/engine-types';
|
|
8
|
-
import { describe, expect, it } from 'vitest';
|
|
9
|
-
import {
|
|
10
|
-
type AssetOutputPayload,
|
|
11
|
-
AssetOutputProducerRegistry,
|
|
12
|
-
buildScriptablePack,
|
|
13
|
-
type ScriptablePackAssetSnapshotSource,
|
|
14
|
-
} from '../scriptable-pack.js';
|
|
15
|
-
|
|
16
|
-
function fixturePayload(asset: Asset): AssetOutputPayload {
|
|
17
|
-
switch (asset.kind) {
|
|
18
|
-
case 'material':
|
|
19
|
-
return { kind: 'material', values: {} };
|
|
20
|
-
case 'scene':
|
|
21
|
-
return { kind: 'scene', entities: [] };
|
|
22
|
-
case 'font':
|
|
23
|
-
return {
|
|
24
|
-
kind: 'font',
|
|
25
|
-
glyphs: asset.glyphs,
|
|
26
|
-
common: asset.common,
|
|
27
|
-
atlasGuid: AssetGuid.format(asset.atlas),
|
|
28
|
-
samplerGuid: AssetGuid.format(asset.sampler),
|
|
29
|
-
};
|
|
30
|
-
case 'tileset':
|
|
31
|
-
return { ...asset, atlases: asset.atlases.map((_atlas, index) => index) };
|
|
32
|
-
case 'animation-graph':
|
|
33
|
-
return {
|
|
34
|
-
kind: 'animation-graph',
|
|
35
|
-
root: asset.root,
|
|
36
|
-
nodes: asset.nodes.map((node) => (node.type === 'clip' ? { ...node, clip: 0 } : node)),
|
|
37
|
-
};
|
|
38
|
-
default:
|
|
39
|
-
return asset;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function guid(value: string): AssetGuidType {
|
|
44
|
-
const parsed = AssetGuid.parse(value);
|
|
45
|
-
if (!parsed.ok) throw parsed.error;
|
|
46
|
-
return parsed.value;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const REFERENCE = guid('019ffa97-0000-7000-8000-000000000001');
|
|
50
|
-
const CONTENT = guid('019ffa97-0000-7000-8000-000000000002');
|
|
51
|
-
const BOTH = guid('019ffa97-0000-7000-8000-000000000003');
|
|
52
|
-
const UNUSED = guid('019ffa97-0000-7000-8000-000000000004');
|
|
53
|
-
const ASSETS = {
|
|
54
|
-
mesh: { guid: guid('019ffa97-1000-7000-8000-000000000001'), kind: 'mesh' },
|
|
55
|
-
material: { guid: guid('019ffa97-1000-7000-8000-000000000002'), kind: 'material' },
|
|
56
|
-
scene: { guid: guid('019ffa97-1000-7000-8000-000000000003'), kind: 'scene' },
|
|
57
|
-
} as const satisfies ScriptablePackAssetDeclarations;
|
|
58
|
-
|
|
59
|
-
function source(
|
|
60
|
-
generation = 4,
|
|
61
|
-
contentDigest = 'sha256:content',
|
|
62
|
-
bothDigest = 'sha256:both',
|
|
63
|
-
): ScriptablePackAssetSnapshotSource & { readonly original: MeshAsset } {
|
|
64
|
-
const original = {
|
|
65
|
-
kind: 'mesh',
|
|
66
|
-
vertices: new Float32Array([1, 2, 3]),
|
|
67
|
-
attributes: {},
|
|
68
|
-
submeshes: [
|
|
69
|
-
{
|
|
70
|
-
topology: 'triangle-list',
|
|
71
|
-
indexOffset: 0,
|
|
72
|
-
indexCount: 0,
|
|
73
|
-
vertexCount: 3,
|
|
74
|
-
materialSlot: 0,
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
materialSlots: [{ slotName: 'Default' }],
|
|
78
|
-
} satisfies MeshAsset;
|
|
79
|
-
const entries = new Map([
|
|
80
|
-
[AssetGuid.format(CONTENT), { asset: original as Asset, generation, digest: contentDigest }],
|
|
81
|
-
[
|
|
82
|
-
AssetGuid.format(BOTH),
|
|
83
|
-
{ asset: original as Asset, generation: generation + 3, digest: bothDigest },
|
|
84
|
-
],
|
|
85
|
-
]);
|
|
86
|
-
return {
|
|
87
|
-
original,
|
|
88
|
-
async readByGuid(requested) {
|
|
89
|
-
const entry = entries.get(AssetGuid.format(requested));
|
|
90
|
-
if (entry === undefined) throw new Error('unexpected test GUID');
|
|
91
|
-
return ok(entry);
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function fixture(): ScriptablePackDefinition<typeof ASSETS> {
|
|
97
|
-
return {
|
|
98
|
-
schemaVersion: '1.0.0',
|
|
99
|
-
packageId: guid('019ffa97-1000-7000-8000-000000000000'),
|
|
100
|
-
assets: ASSETS,
|
|
101
|
-
externalAssets: { reference: REFERENCE, content: CONTENT, both: BOTH },
|
|
102
|
-
async build(reader) {
|
|
103
|
-
const content = await reader.readByGuid<MeshAsset>(CONTENT);
|
|
104
|
-
if (!content.ok) return content;
|
|
105
|
-
content.value.vertices[0] = 99;
|
|
106
|
-
const both = await reader.readByGuid<MeshAsset>(BOTH);
|
|
107
|
-
if (!both.ok) return both;
|
|
108
|
-
return ok({
|
|
109
|
-
mesh: {
|
|
110
|
-
kind: 'mesh',
|
|
111
|
-
vertices: new Float32Array([0, 0, 0]),
|
|
112
|
-
attributes: {},
|
|
113
|
-
submeshes: [
|
|
114
|
-
{
|
|
115
|
-
topology: 'triangle-list',
|
|
116
|
-
indexOffset: 0,
|
|
117
|
-
indexCount: 0,
|
|
118
|
-
vertexCount: 3,
|
|
119
|
-
materialSlot: 0,
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
materialSlots: [{ slotName: 'Default' }],
|
|
123
|
-
},
|
|
124
|
-
material: { kind: 'material', values: {} },
|
|
125
|
-
scene: { kind: 'scene', entities: [] },
|
|
126
|
-
});
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function outputs(version = '1'): AssetOutputProducerRegistry {
|
|
132
|
-
const registry = new AssetOutputProducerRegistry();
|
|
133
|
-
for (const kind of ['mesh', 'material', 'scene']) {
|
|
134
|
-
registry.register({
|
|
135
|
-
kind,
|
|
136
|
-
version: `fixture/${version}`,
|
|
137
|
-
produce(input) {
|
|
138
|
-
const refs =
|
|
139
|
-
input.sourceKey === 'mesh'
|
|
140
|
-
? [{ guid: AssetGuid.format(REFERENCE) }]
|
|
141
|
-
: input.sourceKey === 'scene'
|
|
142
|
-
? [{ guid: AssetGuid.format(BOTH), sourceField: { fieldName: 'template' } }]
|
|
143
|
-
: [];
|
|
144
|
-
return ok({ payload: fixturePayload(input.asset), refs, artifacts: {} });
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
return registry;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
describe('ScriptablePack build bridge', () => {
|
|
152
|
-
it('does not require an Asset snapshot source when build performs no content reads', async () => {
|
|
153
|
-
const definition: ScriptablePackDefinition = {
|
|
154
|
-
schemaVersion: '1.0.0',
|
|
155
|
-
packageId: guid('019ffa97-1000-7000-8000-000000000010'),
|
|
156
|
-
assets: {
|
|
157
|
-
material: {
|
|
158
|
-
guid: guid('019ffa97-1000-7000-8000-000000000011'),
|
|
159
|
-
kind: 'material',
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
externalAssets: {},
|
|
163
|
-
build: () => ok({ material: { kind: 'material', values: {} } }),
|
|
164
|
-
};
|
|
165
|
-
const registry = new AssetOutputProducerRegistry();
|
|
166
|
-
registry.register({
|
|
167
|
-
kind: 'material',
|
|
168
|
-
version: 'fixture/1',
|
|
169
|
-
produce: (input) => ok({ payload: fixturePayload(input.asset), refs: [], artifacts: {} }),
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
const result = await buildScriptablePack({
|
|
173
|
-
definition,
|
|
174
|
-
sourcePath: 'material.pack.ts',
|
|
175
|
-
outputs: registry,
|
|
176
|
-
sourceClosure: [],
|
|
177
|
-
authoringContractVersion: 'fixture/1',
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
expect(result.ok).toBe(true);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('derives reference/content/both, protects input snapshots, and is deterministic', async () => {
|
|
184
|
-
const assetSource = source();
|
|
185
|
-
const options = {
|
|
186
|
-
definition: fixture(),
|
|
187
|
-
sourcePath: 'house.pack.ts',
|
|
188
|
-
assetSource,
|
|
189
|
-
outputs: outputs(),
|
|
190
|
-
sourceClosure: [{ path: 'house.pack.ts', digest: 'sha256:source' }],
|
|
191
|
-
authoringContractVersion: 'geometry/1',
|
|
192
|
-
} as const;
|
|
193
|
-
const first = await buildScriptablePack(options);
|
|
194
|
-
const second = await buildScriptablePack(options);
|
|
195
|
-
expect(first.ok).toBe(true);
|
|
196
|
-
expect(second.ok).toBe(true);
|
|
197
|
-
if (!first.ok || !second.ok) return;
|
|
198
|
-
expect(first.value.externalEvidence).toEqual([
|
|
199
|
-
{ guid: AssetGuid.format(REFERENCE), usage: 'reference' },
|
|
200
|
-
{
|
|
201
|
-
guid: AssetGuid.format(CONTENT),
|
|
202
|
-
usage: 'content',
|
|
203
|
-
generation: 4,
|
|
204
|
-
digest: 'sha256:content',
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
guid: AssetGuid.format(BOTH),
|
|
208
|
-
usage: 'both',
|
|
209
|
-
generation: 7,
|
|
210
|
-
digest: 'sha256:both',
|
|
211
|
-
},
|
|
212
|
-
]);
|
|
213
|
-
expect(assetSource.original.vertices[0]).toBe(1);
|
|
214
|
-
expect(first.value.inputFingerprint).toBe(second.value.inputFingerprint);
|
|
215
|
-
expect(first.value.product.assets.find((asset) => asset.kind === 'scene')?.refs).toEqual([
|
|
216
|
-
{ guid: AssetGuid.format(BOTH), sourceField: { fieldName: 'template' } },
|
|
217
|
-
]);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it('keeps the source fingerprint stable when dependency generations advance', async () => {
|
|
221
|
-
const base = {
|
|
222
|
-
definition: fixture(),
|
|
223
|
-
outputs: outputs(),
|
|
224
|
-
sourcePath: 'assets/house.pack.ts',
|
|
225
|
-
sourceClosure: [{ path: 'house.pack.ts', digest: 'sha256:source' }],
|
|
226
|
-
authoringContractVersion: 'geometry/1',
|
|
227
|
-
} as const;
|
|
228
|
-
const first = await buildScriptablePack({ ...base, assetSource: source(4) });
|
|
229
|
-
const second = await buildScriptablePack({ ...base, assetSource: source(104) });
|
|
230
|
-
expect(first.ok).toBe(true);
|
|
231
|
-
expect(second.ok).toBe(true);
|
|
232
|
-
if (!first.ok || !second.ok) return;
|
|
233
|
-
expect(
|
|
234
|
-
first.value.externalEvidence.find((entry) => entry.guid === AssetGuid.format(CONTENT))
|
|
235
|
-
?.generation,
|
|
236
|
-
).toBe(4);
|
|
237
|
-
expect(
|
|
238
|
-
second.value.externalEvidence.find((entry) => entry.guid === AssetGuid.format(CONTENT))
|
|
239
|
-
?.generation,
|
|
240
|
-
).toBe(104);
|
|
241
|
-
expect(first.value.inputFingerprint).toBe(second.value.inputFingerprint);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('keeps the build fingerprint stable across host-specific source roots', async () => {
|
|
245
|
-
const base = {
|
|
246
|
-
definition: fixture(),
|
|
247
|
-
assetSource: source(),
|
|
248
|
-
outputs: outputs(),
|
|
249
|
-
authoringContractVersion: 'geometry/1',
|
|
250
|
-
} as const;
|
|
251
|
-
const editor = await buildScriptablePack({
|
|
252
|
-
...base,
|
|
253
|
-
sourcePath: 'assets/house.pack.ts',
|
|
254
|
-
sourceClosure: [
|
|
255
|
-
{ path: '/tmp/e2e/sample/assets/house.pack.ts', digest: 'sha256:source' },
|
|
256
|
-
{ path: '/tmp/e2e/sample/assets/house.pack-lib.ts', digest: 'sha256:lib' },
|
|
257
|
-
],
|
|
258
|
-
});
|
|
259
|
-
const carrier = await buildScriptablePack({
|
|
260
|
-
...base,
|
|
261
|
-
sourcePath: 'assets/house.pack.ts',
|
|
262
|
-
sourceClosure: [
|
|
263
|
-
{
|
|
264
|
-
path: '/workspace/play-runtime/host-games/sample/assets/house.pack.ts',
|
|
265
|
-
digest: 'sha256:source',
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
path: '/workspace/play-runtime/host-games/sample/assets/house.pack-lib.ts',
|
|
269
|
-
digest: 'sha256:lib',
|
|
270
|
-
},
|
|
271
|
-
],
|
|
272
|
-
});
|
|
273
|
-
expect(editor.ok).toBe(true);
|
|
274
|
-
expect(carrier.ok).toBe(true);
|
|
275
|
-
if (!editor.ok || !carrier.ok) return;
|
|
276
|
-
expect(editor.value.inputFingerprint).toBe(carrier.value.inputFingerprint);
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it('invalidates the input fingerprint when closure, evidence, or producer versions change', async () => {
|
|
280
|
-
const definition = fixture();
|
|
281
|
-
const base = {
|
|
282
|
-
definition,
|
|
283
|
-
sourcePath: 'house.pack.ts',
|
|
284
|
-
assetSource: source(4, 'sha256:content-a', 'sha256:both-a'),
|
|
285
|
-
outputs: outputs('1'),
|
|
286
|
-
sourceClosure: [{ path: 'house.pack.ts', digest: 'sha256:source-a' }],
|
|
287
|
-
authoringContractVersion: 'geometry/1',
|
|
288
|
-
} as const;
|
|
289
|
-
const stable = await buildScriptablePack(base);
|
|
290
|
-
const closureChanged = await buildScriptablePack({
|
|
291
|
-
...base,
|
|
292
|
-
sourceClosure: [{ path: 'house.pack.ts', digest: 'sha256:source-b' }],
|
|
293
|
-
});
|
|
294
|
-
const evidenceChanged = await buildScriptablePack({
|
|
295
|
-
...base,
|
|
296
|
-
assetSource: source(5, 'sha256:content-b', 'sha256:both-b'),
|
|
297
|
-
});
|
|
298
|
-
const producerChanged = await buildScriptablePack({ ...base, outputs: outputs('2') });
|
|
299
|
-
|
|
300
|
-
expect(stable.ok && closureChanged.ok).toBe(true);
|
|
301
|
-
expect(stable.ok && evidenceChanged.ok).toBe(true);
|
|
302
|
-
expect(stable.ok && producerChanged.ok).toBe(true);
|
|
303
|
-
if (!stable.ok || !closureChanged.ok || !evidenceChanged.ok || !producerChanged.ok) return;
|
|
304
|
-
expect(closureChanged.value.inputFingerprint).not.toBe(stable.value.inputFingerprint);
|
|
305
|
-
expect(evidenceChanged.value.inputFingerprint).not.toBe(stable.value.inputFingerprint);
|
|
306
|
-
expect(producerChanged.value.inputFingerprint).not.toBe(stable.value.inputFingerprint);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('fails when an external declaration is unused', async () => {
|
|
310
|
-
const definition = fixture();
|
|
311
|
-
const result = await buildScriptablePack({
|
|
312
|
-
definition: {
|
|
313
|
-
...definition,
|
|
314
|
-
externalAssets: { ...definition.externalAssets, unused: UNUSED },
|
|
315
|
-
},
|
|
316
|
-
sourcePath: 'unused.pack.ts',
|
|
317
|
-
assetSource: source(),
|
|
318
|
-
outputs: outputs(),
|
|
319
|
-
sourceClosure: [],
|
|
320
|
-
authoringContractVersion: 'geometry/1',
|
|
321
|
-
});
|
|
322
|
-
expect(result).toMatchObject({
|
|
323
|
-
ok: false,
|
|
324
|
-
error: {
|
|
325
|
-
code: 'pack-source-external-closure-mismatch',
|
|
326
|
-
detail: { unusedDeclaredGuids: [AssetGuid.format(UNUSED)] },
|
|
327
|
-
},
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it('fails when an output reference is not declared in the external closure', async () => {
|
|
332
|
-
const definition = fixture();
|
|
333
|
-
const result = await buildScriptablePack({
|
|
334
|
-
definition: {
|
|
335
|
-
...definition,
|
|
336
|
-
externalAssets: { content: CONTENT, both: BOTH },
|
|
337
|
-
},
|
|
338
|
-
sourcePath: 'undeclared-reference.pack.ts',
|
|
339
|
-
assetSource: source(),
|
|
340
|
-
outputs: outputs(),
|
|
341
|
-
sourceClosure: [],
|
|
342
|
-
authoringContractVersion: 'geometry/1',
|
|
343
|
-
});
|
|
344
|
-
expect(result).toMatchObject({
|
|
345
|
-
ok: false,
|
|
346
|
-
error: {
|
|
347
|
-
code: 'pack-source-external-closure-mismatch',
|
|
348
|
-
detail: { undeclaredReferencedGuids: [AssetGuid.format(REFERENCE)] },
|
|
349
|
-
},
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
it('rejects malformed producer refs before publication', async () => {
|
|
354
|
-
const invalidOutputs = outputs();
|
|
355
|
-
invalidOutputs.register({
|
|
356
|
-
kind: 'mesh',
|
|
357
|
-
version: 'fixture/invalid-ref',
|
|
358
|
-
produce: (input) =>
|
|
359
|
-
ok({
|
|
360
|
-
payload: fixturePayload(input.asset),
|
|
361
|
-
refs: [{ guid: 'not-a-guid' }],
|
|
362
|
-
artifacts: {},
|
|
363
|
-
}),
|
|
364
|
-
});
|
|
365
|
-
const result = await buildScriptablePack({
|
|
366
|
-
definition: fixture(),
|
|
367
|
-
sourcePath: 'invalid-ref.pack.ts',
|
|
368
|
-
assetSource: source(),
|
|
369
|
-
outputs: invalidOutputs,
|
|
370
|
-
sourceClosure: [],
|
|
371
|
-
authoringContractVersion: 'geometry/1',
|
|
372
|
-
});
|
|
373
|
-
expect(result).toMatchObject({
|
|
374
|
-
ok: false,
|
|
375
|
-
error: {
|
|
376
|
-
code: 'pack-source-output-invalid',
|
|
377
|
-
detail: { kindMismatches: [{ actual: 'not-a-guid' }] },
|
|
378
|
-
},
|
|
379
|
-
});
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
it('returns a structured missing-dependency error before producing outputs', async () => {
|
|
383
|
-
const missing = guid('019ffa97-0000-7000-8000-000000000005');
|
|
384
|
-
const definition: ScriptablePackDefinition = {
|
|
385
|
-
schemaVersion: '1.0.0',
|
|
386
|
-
packageId: guid('019ffa97-1000-7000-8000-000000000010'),
|
|
387
|
-
assets: {
|
|
388
|
-
mesh: {
|
|
389
|
-
guid: guid('019ffa97-1000-7000-8000-000000000011'),
|
|
390
|
-
kind: 'mesh',
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
externalAssets: { missing },
|
|
394
|
-
async build(reader) {
|
|
395
|
-
const result = await reader.readByGuid<MeshAsset>(missing);
|
|
396
|
-
if (!result.ok) return result;
|
|
397
|
-
return ok({ mesh: result.value });
|
|
398
|
-
},
|
|
399
|
-
};
|
|
400
|
-
const registry = new AssetOutputProducerRegistry();
|
|
401
|
-
registry.register({
|
|
402
|
-
kind: 'mesh',
|
|
403
|
-
version: 'fixture/1',
|
|
404
|
-
produce: (input) => ok({ payload: fixturePayload(input.asset), refs: [], artifacts: {} }),
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
const result = await buildScriptablePack({
|
|
408
|
-
definition,
|
|
409
|
-
sourcePath: 'missing.pack.ts',
|
|
410
|
-
assetSource: {
|
|
411
|
-
async readByGuid() {
|
|
412
|
-
return err(
|
|
413
|
-
new AssetError({
|
|
414
|
-
code: 'asset-not-imported',
|
|
415
|
-
expected: 'the declared external asset to be available',
|
|
416
|
-
hint: 'publish the dependency before rebuilding the source package',
|
|
417
|
-
}),
|
|
418
|
-
);
|
|
419
|
-
},
|
|
420
|
-
},
|
|
421
|
-
outputs: registry,
|
|
422
|
-
sourceClosure: [],
|
|
423
|
-
authoringContractVersion: 'fixture/1',
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
expect(result).toMatchObject({
|
|
427
|
-
ok: false,
|
|
428
|
-
error: {
|
|
429
|
-
code: 'asset-not-imported',
|
|
430
|
-
expected: 'the declared external asset to be available',
|
|
431
|
-
hint: 'publish the dependency before rebuilding the source package',
|
|
432
|
-
},
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
2
|
-
import { projectScriptablePackMeta, type ScriptablePackError } from '@forgeax/engine-pack/source';
|
|
3
|
-
import type {
|
|
4
|
-
AssetError,
|
|
5
|
-
ImportError,
|
|
6
|
-
ImportedAsset,
|
|
7
|
-
ImportProduct,
|
|
8
|
-
Result,
|
|
9
|
-
} from '@forgeax/engine-types';
|
|
10
|
-
import { err, ok } from '@forgeax/engine-types';
|
|
11
|
-
import {
|
|
12
|
-
type BuildScriptablePackOptions,
|
|
13
|
-
buildScriptablePack,
|
|
14
|
-
type ScriptablePackDomainError,
|
|
15
|
-
type ScriptablePackExternalEvidence,
|
|
16
|
-
type ScriptablePackStagedOutput,
|
|
17
|
-
} from './scriptable-pack.js';
|
|
18
|
-
|
|
19
|
-
const META_ARTIFACT_KEY = 'scriptable-pack.meta.json';
|
|
20
|
-
|
|
21
|
-
export interface ScriptableSourcePackageProduct {
|
|
22
|
-
readonly anchorGuid: string;
|
|
23
|
-
readonly declaredGuids: readonly string[];
|
|
24
|
-
readonly product: ImportProduct<unknown>;
|
|
25
|
-
readonly inputFingerprint: string;
|
|
26
|
-
readonly externalEvidence: readonly ScriptablePackExternalEvidence[];
|
|
27
|
-
readonly stagedOutputs: readonly ScriptablePackStagedOutput[];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export type ScriptableSourcePackageResult = Result<
|
|
31
|
-
ScriptableSourcePackageProduct,
|
|
32
|
-
ScriptablePackError | AssetError | ImportError | ScriptablePackDomainError
|
|
33
|
-
>;
|
|
34
|
-
|
|
35
|
-
function withPrebuiltMeta<P>(
|
|
36
|
-
product: ImportProduct<P>,
|
|
37
|
-
anchorGuid: string,
|
|
38
|
-
meta: unknown,
|
|
39
|
-
): ImportProduct<P> {
|
|
40
|
-
const bytes = new TextEncoder().encode(`${JSON.stringify(meta)}\n`);
|
|
41
|
-
return {
|
|
42
|
-
...product,
|
|
43
|
-
assets: product.assets.map(
|
|
44
|
-
(asset): ImportedAsset<P> =>
|
|
45
|
-
asset.guid.toLowerCase() === anchorGuid
|
|
46
|
-
? {
|
|
47
|
-
...asset,
|
|
48
|
-
artifacts: {
|
|
49
|
-
...asset.artifacts,
|
|
50
|
-
[META_ARTIFACT_KEY]: { mediaType: 'application/json', bytes },
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
: asset,
|
|
54
|
-
),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function produceScriptableSourcePackage(
|
|
59
|
-
options: BuildScriptablePackOptions,
|
|
60
|
-
): Promise<ScriptableSourcePackageResult> {
|
|
61
|
-
const built = await buildScriptablePack(options);
|
|
62
|
-
if (!built.ok) return err(built.error);
|
|
63
|
-
const declaredGuids = Object.values(options.definition.assets)
|
|
64
|
-
.map((descriptor) => AssetGuid.format(descriptor.guid).toLowerCase())
|
|
65
|
-
.sort();
|
|
66
|
-
const anchorGuid = declaredGuids[0];
|
|
67
|
-
if (anchorGuid === undefined) {
|
|
68
|
-
return err({
|
|
69
|
-
code: 'pack-source-output-invalid',
|
|
70
|
-
expected: 'at least one declared ScriptablePack output',
|
|
71
|
-
hint: 'add an output descriptor before building the package',
|
|
72
|
-
detail: { missingGuids: [], unexpectedSourceKeys: [], kindMismatches: [] },
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
const product = withPrebuiltMeta(
|
|
76
|
-
built.value.product,
|
|
77
|
-
anchorGuid,
|
|
78
|
-
projectScriptablePackMeta(options.definition, options.sourcePath),
|
|
79
|
-
);
|
|
80
|
-
return ok({
|
|
81
|
-
anchorGuid,
|
|
82
|
-
declaredGuids,
|
|
83
|
-
product,
|
|
84
|
-
stagedOutputs: built.value.stagedOutputs,
|
|
85
|
-
inputFingerprint: built.value.inputFingerprint,
|
|
86
|
-
externalEvidence: built.value.externalEvidence,
|
|
87
|
-
});
|
|
88
|
-
}
|