@forgeax/engine-import 0.1.3 → 0.1.4
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 +6 -5
- package/dist/.tsbuildinfo +1 -1
- 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__/scriptable-pack-product.contract.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-product.contract.test.d.ts.map +1 -0
- package/dist/browser.d.ts +5 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.mjs +782 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/build-production.d.ts +48 -0
- package/dist/build-production.d.ts.map +1 -0
- package/dist/catalog-importer-policy.d.ts +13 -0
- package/dist/catalog-importer-policy.d.ts.map +1 -0
- package/dist/catalog-inventory.d.ts +4 -0
- package/dist/catalog-inventory.d.ts.map +1 -0
- package/dist/import-product.d.ts +16 -25
- package/dist/import-product.d.ts.map +1 -1
- package/dist/import-runner.d.ts +6 -3
- package/dist/import-runner.d.ts.map +1 -1
- package/dist/importer-registry.d.ts +9 -1
- package/dist/importer-registry.d.ts.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1315 -155
- package/dist/index.mjs.map +1 -1
- package/dist/mesh-bin.d.ts +1 -1
- package/dist/mesh-bin.d.ts.map +1 -1
- package/dist/mesh-bin.mjs +182 -0
- package/dist/mesh-bin.mjs.map +1 -0
- package/dist/pack-projection.d.ts +5 -0
- package/dist/pack-projection.d.ts.map +1 -0
- package/dist/scriptable-pack-host.d.ts +64 -0
- package/dist/scriptable-pack-host.d.ts.map +1 -0
- package/dist/scriptable-pack-output-producers.d.ts +3 -2
- package/dist/scriptable-pack-output-producers.d.ts.map +1 -1
- package/dist/scriptable-pack-staged-snapshot.d.ts +1 -2
- package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -1
- package/dist/scriptable-pack.d.ts +6 -18
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/scriptable-source-package.d.ts +14 -0
- package/dist/scriptable-source-package.d.ts.map +1 -0
- package/dist/source-package-errors.d.ts +52 -0
- package/dist/source-package-errors.d.ts.map +1 -0
- package/dist/source-package-publication.d.ts +63 -0
- package/dist/source-package-publication.d.ts.map +1 -0
- package/dist/source-package.d.ts +60 -0
- package/dist/source-package.d.ts.map +1 -0
- package/package.json +13 -7
- package/src/__tests__/import-contract-migration.test.ts +33 -0
- package/src/__tests__/import-local-artifacts.test.ts +3 -1
- package/src/__tests__/owner-chain.integration.test.ts +37 -0
- package/src/__tests__/scriptable-pack-product.contract.test.ts +47 -0
- package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +78 -19
- package/src/__tests__/scriptable-pack-staged-snapshot.unit.test.ts +52 -20
- package/src/browser.ts +14 -0
- package/src/build-production.ts +487 -0
- package/src/catalog-importer-policy.ts +58 -0
- package/src/catalog-inventory.ts +24 -0
- package/src/import-product.ts +37 -69
- package/src/import-runner.ts +36 -7
- package/src/importer-registry.ts +24 -1
- package/src/index.ts +79 -8
- package/src/mesh-bin.ts +2 -2
- package/src/pack-projection.ts +21 -0
- package/src/scriptable-pack-host.ts +461 -0
- package/src/scriptable-pack-output-producers.ts +48 -58
- package/src/scriptable-pack-staged-snapshot.ts +17 -6
- package/src/scriptable-pack.ts +37 -26
- package/src/scriptable-source-package.ts +88 -0
- package/src/source-package-errors.ts +201 -0
- package/src/source-package-publication.ts +295 -0
- package/src/source-package.ts +187 -0
- package/dist/__tests__/material-import-product.unit.test.d.ts +0 -2
- package/dist/__tests__/material-import-product.unit.test.d.ts.map +0 -1
- package/src/__tests__/material-import-product.unit.test.ts +0 -56
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ImportedAsset, ImportProduct, ImportProductFinalizeOptions, ImportProductFinalizeResult, Result } from '@forgeax/engine-types';
|
|
2
|
+
import type { ImportRunnerFs, RunImportMeta } from './import-runner.js';
|
|
3
|
+
import type { ImporterRegistry } from './importer-registry.js';
|
|
4
|
+
export type ProducerReadiness = 'before-consume' | 'on-demand';
|
|
5
|
+
export interface ProducerReadinessError {
|
|
6
|
+
readonly code: 'producer-readiness-invalid';
|
|
7
|
+
readonly expected: "'before-consume' or 'on-demand'";
|
|
8
|
+
readonly hint: 'set producerReadiness to before-consume or on-demand';
|
|
9
|
+
readonly detail: {
|
|
10
|
+
readonly value: unknown;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export type ProducerReadinessResult = {
|
|
14
|
+
readonly ok: true;
|
|
15
|
+
readonly value: ProducerReadiness;
|
|
16
|
+
} | {
|
|
17
|
+
readonly ok: false;
|
|
18
|
+
readonly error: ProducerReadinessError;
|
|
19
|
+
};
|
|
20
|
+
export declare function parseProducerReadiness(value: unknown): ProducerReadinessResult;
|
|
21
|
+
export interface SourcePackageProducerInput {
|
|
22
|
+
readonly meta: RunImportMeta;
|
|
23
|
+
readonly registry: ImporterRegistry;
|
|
24
|
+
readonly fs: ImportRunnerFs;
|
|
25
|
+
}
|
|
26
|
+
export interface SourcePackageClosureDetail {
|
|
27
|
+
readonly stage: 'closure';
|
|
28
|
+
readonly declaredGuids: readonly string[];
|
|
29
|
+
readonly producedGuids: readonly string[];
|
|
30
|
+
readonly missingGuids: readonly string[];
|
|
31
|
+
readonly unexpectedGuids: readonly string[];
|
|
32
|
+
readonly duplicateGuids: readonly string[];
|
|
33
|
+
}
|
|
34
|
+
export interface SourcePackageClosureError {
|
|
35
|
+
readonly code: 'source-package-guid-closure-mismatch';
|
|
36
|
+
readonly expected: string;
|
|
37
|
+
readonly hint: string;
|
|
38
|
+
readonly detail: SourcePackageClosureDetail;
|
|
39
|
+
}
|
|
40
|
+
export interface SourcePackageProduct {
|
|
41
|
+
readonly anchorGuid: string;
|
|
42
|
+
readonly declaredGuids: readonly string[];
|
|
43
|
+
readonly product: ImportProduct<unknown>;
|
|
44
|
+
}
|
|
45
|
+
type SourcePackageFinalizeError = Extract<ImportProductFinalizeResult, {
|
|
46
|
+
readonly ok: false;
|
|
47
|
+
}>['error'];
|
|
48
|
+
/** Apply an importer-owned transport finalizer without teaching the adapter its kind. */
|
|
49
|
+
export declare function finalizeSourcePackage(product: ImportProduct<unknown>, finalizer: ((product: ImportProduct<unknown>, options: ImportProductFinalizeOptions) => ImportProductFinalizeResult) | undefined, artifactUrl: ImportProductFinalizeOptions['artifactUrl']): Result<ImportProduct<unknown>, SourcePackageFinalizeError>;
|
|
50
|
+
export type SourcePackageProducerResult = {
|
|
51
|
+
readonly ok: true;
|
|
52
|
+
readonly value: SourcePackageProduct;
|
|
53
|
+
} | {
|
|
54
|
+
readonly ok: false;
|
|
55
|
+
readonly error: SourcePackageClosureError;
|
|
56
|
+
};
|
|
57
|
+
export declare function produceSourcePackage(input: SourcePackageProducerInput): Promise<SourcePackageProducerResult>;
|
|
58
|
+
export declare function sourcePackageAssetsByGuid(source: SourcePackageProduct): ReadonlyMap<string, ImportedAsset<unknown>>;
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=source-package.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-package.d.ts","sourceRoot":"","sources":["../src/source-package.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgeax/engine-import",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -9,9 +9,14 @@
|
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
|
+
"browser": "./dist/browser.mjs",
|
|
12
13
|
"import": "./dist/index.mjs"
|
|
13
14
|
},
|
|
14
|
-
"./package.json": "./package.json"
|
|
15
|
+
"./package.json": "./package.json",
|
|
16
|
+
"./mesh-bin": {
|
|
17
|
+
"types": "./dist/mesh-bin.d.ts",
|
|
18
|
+
"import": "./dist/mesh-bin.mjs"
|
|
19
|
+
}
|
|
15
20
|
},
|
|
16
21
|
"main": "./dist/index.mjs",
|
|
17
22
|
"types": "./dist/index.d.ts",
|
|
@@ -22,11 +27,12 @@
|
|
|
22
27
|
"LICENSE"
|
|
23
28
|
],
|
|
24
29
|
"dependencies": {
|
|
25
|
-
"@forgeax/engine-
|
|
26
|
-
"@forgeax/engine-
|
|
27
|
-
"@forgeax/engine-
|
|
28
|
-
"@forgeax/engine-
|
|
29
|
-
"@forgeax/engine-
|
|
30
|
+
"@forgeax/engine-ddc": "0.1.4",
|
|
31
|
+
"@forgeax/engine-ecs": "0.1.4",
|
|
32
|
+
"@forgeax/engine-geometry": "0.1.4",
|
|
33
|
+
"@forgeax/engine-pack": "0.1.4",
|
|
34
|
+
"@forgeax/engine-scene": "0.1.4",
|
|
35
|
+
"@forgeax/engine-types": "0.1.4",
|
|
30
36
|
"@noble/hashes": "^2.2.0"
|
|
31
37
|
},
|
|
32
38
|
"devDependencies": {
|
|
@@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import type { Asset, AssetRelation, ImportedAsset, ImportResult } from '@forgeax/engine-types';
|
|
4
4
|
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { createImportProduct } from '../import-product.js';
|
|
5
6
|
import { type RunImportMeta, runImport } from '../import-runner.js';
|
|
6
7
|
import { ImporterRegistry } from '../importer-registry.js';
|
|
7
8
|
|
|
@@ -188,3 +189,35 @@ describe('import runner producer fact propagation', () => {
|
|
|
188
189
|
expect(source).not.toContain("a.kind === 'mesh'");
|
|
189
190
|
});
|
|
190
191
|
});
|
|
192
|
+
|
|
193
|
+
describe('engine-import terminal product contract', () => {
|
|
194
|
+
it('projects refs, artifacts, receipts, diagnostics, and source identity together', () => {
|
|
195
|
+
const result = createImportProduct({
|
|
196
|
+
assets: [
|
|
197
|
+
{
|
|
198
|
+
guid: '019e3969-1d48-7c3b-ac24-6d68f457065f',
|
|
199
|
+
kind: 'texture',
|
|
200
|
+
payload: { kind: 'texture', width: 1, height: 1 },
|
|
201
|
+
refs: [],
|
|
202
|
+
artifacts: {},
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
sourceDependencies: ['hero.png'],
|
|
206
|
+
refs: [],
|
|
207
|
+
artifacts: {},
|
|
208
|
+
receipts: [],
|
|
209
|
+
diagnostics: [],
|
|
210
|
+
sourceRevision: 'sha256:hero',
|
|
211
|
+
sourceKey: 'hero/albedo',
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(result.ok).toBe(true);
|
|
215
|
+
if (!result.ok) return;
|
|
216
|
+
expect(result.value.sourceRevision).toBe('sha256:hero');
|
|
217
|
+
expect(result.value.sourceKey).toBe('hero/albedo');
|
|
218
|
+
expect(result.value).toHaveProperty('refs');
|
|
219
|
+
expect(result.value).toHaveProperty('artifacts');
|
|
220
|
+
expect(result.value).toHaveProperty('receipts');
|
|
221
|
+
expect(result.value).toHaveProperty('diagnostics');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
@@ -66,7 +66,9 @@ describe('asset-local ImportProduct contract', () => {
|
|
|
66
66
|
expect(result.ok).toBe(true);
|
|
67
67
|
if (result.ok && 'product' in result.value) {
|
|
68
68
|
expect(result.value).not.toHaveProperty('bins');
|
|
69
|
-
expect(result.value.product).
|
|
69
|
+
expect(result.value.product.artifacts).toHaveProperty(`${MESH_GUID}/body`);
|
|
70
|
+
expect(result.value.product.receipts).toHaveLength(2);
|
|
71
|
+
expect(result.value.product.sourceRevision).toBe('source:fixture.source');
|
|
70
72
|
expect(result.value.pack.assets[0]?.artifacts).toHaveProperty('body');
|
|
71
73
|
expect(result.value.pack.assets[1]?.artifacts).toHaveProperty('body');
|
|
72
74
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createImportProduct } from '../import-product.js';
|
|
3
|
+
|
|
4
|
+
describe('Import owner chain', () => {
|
|
5
|
+
it('hands one complete terminal product to the next owner', () => {
|
|
6
|
+
const result = createImportProduct({
|
|
7
|
+
assets: [
|
|
8
|
+
{
|
|
9
|
+
guid: '019e3969-1d48-7c3b-ac24-6d68f457065f',
|
|
10
|
+
kind: 'texture',
|
|
11
|
+
payload: { kind: 'texture' },
|
|
12
|
+
refs: [],
|
|
13
|
+
artifacts: {},
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
sourceDependencies: ['hero.png'],
|
|
17
|
+
refs: [],
|
|
18
|
+
artifacts: {},
|
|
19
|
+
receipts: [],
|
|
20
|
+
diagnostics: [],
|
|
21
|
+
sourceRevision: 'sha256:hero',
|
|
22
|
+
sourceKey: 'hero/albedo',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(result.ok).toBe(true);
|
|
26
|
+
if (result.ok) {
|
|
27
|
+
expect(result.value).toMatchObject({
|
|
28
|
+
sourceRevision: 'sha256:hero',
|
|
29
|
+
sourceKey: 'hero/albedo',
|
|
30
|
+
refs: [],
|
|
31
|
+
artifacts: {},
|
|
32
|
+
receipts: [],
|
|
33
|
+
diagnostics: [],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
2
|
+
import { ok } from '@forgeax/engine-types';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { buildScriptablePack } from '../scriptable-pack.js';
|
|
5
|
+
import { createStandardAssetOutputProducerRegistry } from '../scriptable-pack-output-producers.js';
|
|
6
|
+
|
|
7
|
+
const PACKAGE_ID = AssetGuid.parse('019e3969-1d48-7c3b-ac24-6d68f457065e');
|
|
8
|
+
const HERO_GUID = AssetGuid.parse('019e3969-1d48-7c3b-ac24-6d68f457065f');
|
|
9
|
+
|
|
10
|
+
describe('ScriptablePack terminal product contract', () => {
|
|
11
|
+
it('keeps one complete product shape for direct and custom producer outputs', async () => {
|
|
12
|
+
const result = await buildScriptablePack({
|
|
13
|
+
definition: {
|
|
14
|
+
schemaVersion: '1.0.0',
|
|
15
|
+
packageId: PACKAGE_ID.ok ? PACKAGE_ID.value : ({} as never),
|
|
16
|
+
assets: {
|
|
17
|
+
hero: { guid: HERO_GUID.ok ? HERO_GUID.value : ({} as never), kind: 'texture' },
|
|
18
|
+
},
|
|
19
|
+
externalAssets: {},
|
|
20
|
+
build: async () =>
|
|
21
|
+
ok({
|
|
22
|
+
hero: {
|
|
23
|
+
kind: 'texture',
|
|
24
|
+
width: 1,
|
|
25
|
+
height: 1,
|
|
26
|
+
format: 'rgba8unorm',
|
|
27
|
+
data: new Uint8Array(),
|
|
28
|
+
colorSpace: 'srgb',
|
|
29
|
+
mipmap: false,
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
sourcePath: 'hero.pack.ts',
|
|
34
|
+
outputs: createStandardAssetOutputProducerRegistry(),
|
|
35
|
+
sourceClosure: [{ path: 'hero.pack.ts', digest: 'sha256:hero' }],
|
|
36
|
+
authoringContractVersion: 'scriptable-pack-production/1',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(result.ok).toBe(true);
|
|
40
|
+
if (!result.ok) return;
|
|
41
|
+
expect(result.value.product).toHaveProperty('refs');
|
|
42
|
+
expect(result.value.product).toHaveProperty('artifacts');
|
|
43
|
+
expect(result.value.product).toHaveProperty('receipts');
|
|
44
|
+
expect(result.value.product).toHaveProperty('diagnostics');
|
|
45
|
+
expect(result.value.product).toHaveProperty('sourceRevision');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { defineComponent } from '@forgeax/engine-ecs';
|
|
2
1
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
2
|
+
import type { ScriptablePackSceneComponent } from '@forgeax/engine-pack/source';
|
|
3
3
|
import type {
|
|
4
4
|
AnimationClip,
|
|
5
5
|
AnimationGraph,
|
|
@@ -31,19 +31,17 @@ function guid(value: string): AssetGuidType {
|
|
|
31
31
|
|
|
32
32
|
const MATERIAL_GUID = guid('019ffa97-3000-7000-8000-000000000001');
|
|
33
33
|
const MESH_GUID = guid('019ffa97-3000-7000-8000-000000000002');
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
[ScriptableMeshRenderer.name, ScriptableMeshRenderer],
|
|
46
|
-
]);
|
|
34
|
+
const EQUIRECT_GUID = guid('019ffa97-3000-7000-8000-000000000005');
|
|
35
|
+
const sceneComponents = [
|
|
36
|
+
{
|
|
37
|
+
name: 'ScriptablePackTestMeshFilter',
|
|
38
|
+
fields: { assetHandle: 'shared<MeshAsset>' },
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'ScriptablePackTestMeshRenderer',
|
|
42
|
+
fields: { materials: 'array<shared<MaterialAsset>>' },
|
|
43
|
+
},
|
|
44
|
+
] satisfies readonly ScriptablePackSceneComponent[];
|
|
47
45
|
|
|
48
46
|
const OUTPUT_GUID = guid('019ffa97-3000-7000-8000-000000000010');
|
|
49
47
|
const ATLAS_GUID = guid('019ffa97-3000-7000-8000-000000000011');
|
|
@@ -165,8 +163,6 @@ const matrixAssets = [
|
|
|
165
163
|
] as const satisfies readonly Asset[];
|
|
166
164
|
|
|
167
165
|
describe('standard ScriptablePack output producers', () => {
|
|
168
|
-
void ScriptableMeshFilter;
|
|
169
|
-
void ScriptableMeshRenderer;
|
|
170
166
|
it('registers the production producer versions for all ordinary Asset kinds', () => {
|
|
171
167
|
const registry = createStandardAssetOutputProducerRegistry();
|
|
172
168
|
expect(registry.versions()).toEqual({
|
|
@@ -180,7 +176,7 @@ describe('standard ScriptablePack output producers', () => {
|
|
|
180
176
|
'particle-effect': 'ordinary-pod/1',
|
|
181
177
|
'render-pipeline': 'ordinary-pod/1',
|
|
182
178
|
sampler: 'ordinary-pod/1',
|
|
183
|
-
scene: 'scene-pack/
|
|
179
|
+
scene: 'scene-pack/3',
|
|
184
180
|
skeleton: 'ordinary-pod/1',
|
|
185
181
|
skin: 'ordinary-pod/1',
|
|
186
182
|
texture: 'ordinary-pod/1',
|
|
@@ -262,7 +258,7 @@ describe('standard ScriptablePack output producers', () => {
|
|
|
262
258
|
});
|
|
263
259
|
|
|
264
260
|
it('serializes a scene through component schemas and emits recursive GUID refs', async () => {
|
|
265
|
-
const registry = createStandardAssetOutputProducerRegistry();
|
|
261
|
+
const registry = createStandardAssetOutputProducerRegistry(sceneComponents);
|
|
266
262
|
const producer = registry.get('scene');
|
|
267
263
|
expect(producer).toBeDefined();
|
|
268
264
|
if (producer === undefined) return;
|
|
@@ -285,7 +281,6 @@ describe('standard ScriptablePack output producers', () => {
|
|
|
285
281
|
guid: '019ffa97-3000-7000-8000-000000000003',
|
|
286
282
|
sourceKey: 'scene/showcase',
|
|
287
283
|
asset: scene,
|
|
288
|
-
components: componentCatalog,
|
|
289
284
|
});
|
|
290
285
|
|
|
291
286
|
expect(result.ok).toBe(true);
|
|
@@ -322,6 +317,70 @@ describe('standard ScriptablePack output producers', () => {
|
|
|
322
317
|
]);
|
|
323
318
|
});
|
|
324
319
|
|
|
320
|
+
it('fails closed when a scene component schema is not declared by the Pack', async () => {
|
|
321
|
+
const registry = createStandardAssetOutputProducerRegistry();
|
|
322
|
+
const producer = registry.get('scene');
|
|
323
|
+
expect(producer).toBeDefined();
|
|
324
|
+
if (producer === undefined) return;
|
|
325
|
+
|
|
326
|
+
const result = await producer.produce({
|
|
327
|
+
guid: '019ffa97-3000-7000-8000-000000000004',
|
|
328
|
+
sourceKey: 'scene/unknown-component',
|
|
329
|
+
asset: {
|
|
330
|
+
kind: 'scene',
|
|
331
|
+
entities: [{ localId: 0 as never, components: { Mystery: { asset: 'not-a-ref' } } }],
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
expect(result).toMatchObject({
|
|
336
|
+
ok: false,
|
|
337
|
+
error: {
|
|
338
|
+
code: 'import-internal-error',
|
|
339
|
+
detail: { reason: expect.stringContaining('missing from sceneComponents') },
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('deduplicates one shared scene GUID across multiple component fields', async () => {
|
|
345
|
+
const registry = createStandardAssetOutputProducerRegistry([
|
|
346
|
+
...sceneComponents,
|
|
347
|
+
{ name: 'Skylight', fields: { equirect: 'shared<EquirectAsset>' } },
|
|
348
|
+
{ name: 'SkyboxBackground', fields: { equirect: 'shared<EquirectAsset>' } },
|
|
349
|
+
]);
|
|
350
|
+
const producer = registry.get('scene');
|
|
351
|
+
expect(producer).toBeDefined();
|
|
352
|
+
if (producer === undefined) return;
|
|
353
|
+
|
|
354
|
+
const result = await producer.produce({
|
|
355
|
+
guid: '019ffa97-3000-7000-8000-000000000006',
|
|
356
|
+
sourceKey: 'scene/environment',
|
|
357
|
+
asset: {
|
|
358
|
+
kind: 'scene',
|
|
359
|
+
entities: [
|
|
360
|
+
{
|
|
361
|
+
localId: 0 as never,
|
|
362
|
+
components: { Skylight: { equirect: AssetGuid.format(EQUIRECT_GUID) } },
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
localId: 1 as never,
|
|
366
|
+
components: { SkyboxBackground: { equirect: AssetGuid.format(EQUIRECT_GUID) } },
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
expect(result.ok).toBe(true);
|
|
373
|
+
if (!result.ok) return;
|
|
374
|
+
expect(result.value.refs).toHaveLength(1);
|
|
375
|
+
expect(result.value.refs[0]?.guid).toBe(AssetGuid.format(EQUIRECT_GUID));
|
|
376
|
+
expect(result.value.payload).toMatchObject({
|
|
377
|
+
entities: [
|
|
378
|
+
{ components: { Skylight: { equirect: 0 } } },
|
|
379
|
+
{ components: { SkyboxBackground: { equirect: 0 } } },
|
|
380
|
+
],
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
325
384
|
it('packs mesh bytes and projects material parent references', async () => {
|
|
326
385
|
const registry = createStandardAssetOutputProducerRegistry();
|
|
327
386
|
const mesh = registry.get('mesh');
|
|
@@ -74,8 +74,7 @@ describe('ScriptablePack staged asset snapshot source', () => {
|
|
|
74
74
|
expect(second.ok && second.value.digest).toMatch(/^sha256:/);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
it('fails a content dependency cycle structurally
|
|
78
|
-
let fallbackReads = 0;
|
|
77
|
+
it('fails a content dependency cycle structurally', async () => {
|
|
79
78
|
const owners: ScriptablePackStagedOwner[] = [
|
|
80
79
|
{
|
|
81
80
|
id: 'a.pack.ts',
|
|
@@ -97,12 +96,6 @@ describe('ScriptablePack staged asset snapshot source', () => {
|
|
|
97
96
|
const source = createScriptablePackStagedAssetSnapshotSource({
|
|
98
97
|
generation: 1,
|
|
99
98
|
owners,
|
|
100
|
-
fallback: {
|
|
101
|
-
async readByGuid() {
|
|
102
|
-
fallbackReads += 1;
|
|
103
|
-
return ok({ asset: mesh(8), generation: 0, digest: 'lkg' });
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
99
|
});
|
|
107
100
|
const result = await source.readByGuid(ROOT);
|
|
108
101
|
expect(result).toMatchObject({
|
|
@@ -112,14 +105,63 @@ describe('ScriptablePack staged asset snapshot source', () => {
|
|
|
112
105
|
actual: 'a.pack.ts -> b.pack.ts -> a.pack.ts',
|
|
113
106
|
},
|
|
114
107
|
});
|
|
115
|
-
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('fails a content dependency without a local owner', async () => {
|
|
111
|
+
const source = createScriptablePackStagedAssetSnapshotSource({
|
|
112
|
+
generation: 1,
|
|
113
|
+
owners: [
|
|
114
|
+
{
|
|
115
|
+
id: 'root.pack.ts',
|
|
116
|
+
guids: [ROOT],
|
|
117
|
+
async build(ownerSource) {
|
|
118
|
+
const result = await ownerSource.readByGuid(DEPENDENCY);
|
|
119
|
+
return result.ok ? ok([{ guid: ROOT, asset: result.value.asset }]) : result;
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await expect(source.readByGuid(ROOT)).resolves.toMatchObject({
|
|
126
|
+
ok: false,
|
|
127
|
+
error: {
|
|
128
|
+
code: 'asset-not-imported',
|
|
129
|
+
expected: 'a staged local owner for the requested GUID',
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('resolves declared direct Pack outputs in the same generation', async () => {
|
|
135
|
+
const source = createScriptablePackStagedAssetSnapshotSource({
|
|
136
|
+
generation: 3,
|
|
137
|
+
declaredExternalOutputs: [{ guid: DEPENDENCY, asset: mesh(7) }],
|
|
138
|
+
owners: [
|
|
139
|
+
{
|
|
140
|
+
id: 'root.pack.ts',
|
|
141
|
+
guids: [ROOT],
|
|
142
|
+
async build(ownerSource) {
|
|
143
|
+
const dependency = await ownerSource.readByGuid(DEPENDENCY);
|
|
144
|
+
return dependency.ok
|
|
145
|
+
? ok([
|
|
146
|
+
{
|
|
147
|
+
guid: ROOT,
|
|
148
|
+
asset: mesh((dependency.value.asset as MeshAsset).vertices[0] ?? 0),
|
|
149
|
+
},
|
|
150
|
+
])
|
|
151
|
+
: dependency;
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
});
|
|
156
|
+
const result = await source.readByGuid(ROOT);
|
|
157
|
+
expect(result).toMatchObject({ ok: true, value: { generation: 3 } });
|
|
158
|
+
expect(result.ok && (result.value.asset as MeshAsset).vertices[0]).toBe(7);
|
|
116
159
|
});
|
|
117
160
|
|
|
118
161
|
it('retries a refused local owner on the same source and coalesces repaired reads', async () => {
|
|
119
162
|
let rootBuilds = 0;
|
|
120
163
|
let dependencyBuilds = 0;
|
|
121
164
|
let healthyBuilds = 0;
|
|
122
|
-
let fallbackReads = 0;
|
|
123
165
|
let repaired = false;
|
|
124
166
|
let releaseRepair!: () => void;
|
|
125
167
|
const repairBuildGate = new Promise<void>((resolve) => {
|
|
@@ -166,12 +208,6 @@ describe('ScriptablePack staged asset snapshot source', () => {
|
|
|
166
208
|
const source = createScriptablePackStagedAssetSnapshotSource({
|
|
167
209
|
generation: 27,
|
|
168
210
|
owners,
|
|
169
|
-
fallback: {
|
|
170
|
-
async readByGuid() {
|
|
171
|
-
fallbackReads += 1;
|
|
172
|
-
return ok({ asset: mesh(99), generation: 0, digest: 'sha256:lkg' });
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
211
|
});
|
|
176
212
|
|
|
177
213
|
const refused = await source.readByGuid(ROOT);
|
|
@@ -185,7 +221,6 @@ describe('ScriptablePack staged asset snapshot source', () => {
|
|
|
185
221
|
});
|
|
186
222
|
expect(rootBuilds).toBe(1);
|
|
187
223
|
expect(dependencyBuilds).toBe(1);
|
|
188
|
-
expect(fallbackReads).toBe(0);
|
|
189
224
|
|
|
190
225
|
const healthy = await source.readByGuid(HEALTHY);
|
|
191
226
|
expect(healthy).toMatchObject({
|
|
@@ -193,7 +228,6 @@ describe('ScriptablePack staged asset snapshot source', () => {
|
|
|
193
228
|
value: { generation: 27, digest: 'sha256:healthy' },
|
|
194
229
|
});
|
|
195
230
|
expect(healthyBuilds).toBe(1);
|
|
196
|
-
expect(fallbackReads).toBe(0);
|
|
197
231
|
|
|
198
232
|
repaired = true;
|
|
199
233
|
const repairedReads = [
|
|
@@ -220,7 +254,6 @@ describe('ScriptablePack staged asset snapshot source', () => {
|
|
|
220
254
|
expect(rootBuilds).toBe(2);
|
|
221
255
|
expect(dependencyBuilds).toBe(1);
|
|
222
256
|
expect(healthyBuilds).toBe(1);
|
|
223
|
-
expect(fallbackReads).toBe(0);
|
|
224
257
|
|
|
225
258
|
const dependency = await source.readByGuid(DEPENDENCY);
|
|
226
259
|
expect(dependency).toMatchObject({
|
|
@@ -232,7 +265,6 @@ describe('ScriptablePack staged asset snapshot source', () => {
|
|
|
232
265
|
expect(memoized).toMatchObject({ ok: true, value: { generation: 27, digest: 'sha256:root' } });
|
|
233
266
|
expect(memoized.ok && (memoized.value.asset as MeshAsset).vertices[0]).toBe(3);
|
|
234
267
|
expect(rootBuilds).toBe(2);
|
|
235
|
-
expect(fallbackReads).toBe(0);
|
|
236
268
|
});
|
|
237
269
|
|
|
238
270
|
it('carries the staged generation into every locally built snapshot', async () => {
|
package/src/browser.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { IMPORT_ERROR_HINTS, ImportError } from '@forgeax/engine-types';
|
|
2
|
+
export {
|
|
3
|
+
type DdcPack,
|
|
4
|
+
type ImportRunnerFs,
|
|
5
|
+
normaliseForPack,
|
|
6
|
+
type RunImportMeta,
|
|
7
|
+
type RunImportOk,
|
|
8
|
+
type RunImportProductResult,
|
|
9
|
+
type RunImportResult,
|
|
10
|
+
runImport,
|
|
11
|
+
SHADER_RESERVED_IMPORTER_KEY,
|
|
12
|
+
} from './import-runner.js';
|
|
13
|
+
export { ImporterRegistry } from './importer-registry.js';
|
|
14
|
+
export { packMeshBinV4 } from './mesh-bin.js';
|