@forgeax/engine-pack 0.1.7 → 0.1.19
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/dist/.tsbuildinfo +1 -1
- package/dist/build.d.ts +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.mjs +24 -10
- package/dist/build.mjs.map +1 -1
- package/dist/catalog-builder.d.ts +12 -0
- package/dist/catalog-builder.d.ts.map +1 -1
- package/dist/cli-asset.mjs +1 -1
- package/dist/cli-asset.mjs.map +1 -1
- package/dist/scanner.mjs +1 -1
- package/dist/scanner.mjs.map +1 -1
- package/dist/scriptable-pack-node.d.ts.map +1 -1
- package/dist/scriptable-pack-node.mjs +1 -1
- package/dist/scriptable-pack-node.mjs.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/material-cook-dependencies.unit.test.ts +44 -0
- package/src/build.ts +1 -0
- package/src/catalog-builder.ts +42 -5
- package/src/scriptable-pack-node.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgeax/engine-pack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"ajv": "^8.20.0",
|
|
109
109
|
"ajv-formats": "^3.0.1",
|
|
110
110
|
"tsup": "^8.3.5",
|
|
111
|
-
"vitest": "4.
|
|
111
|
+
"vitest": "4.1.11"
|
|
112
112
|
},
|
|
113
113
|
"forgeax": {
|
|
114
114
|
"metrics": {
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
}
|
|
137
137
|
},
|
|
138
138
|
"dependencies": {
|
|
139
|
-
"@forgeax/engine-types": "0.1.
|
|
139
|
+
"@forgeax/engine-types": "0.1.19",
|
|
140
140
|
"@noble/hashes": "^2.2.0",
|
|
141
141
|
"fast-glob": "^3.3.3",
|
|
142
142
|
"typescript": "^5.8.3",
|
|
@@ -25,6 +25,50 @@ describe('material cook dependency closure', () => {
|
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
it('collects transmission and thickness texture dependencies', () => {
|
|
29
|
+
expect(
|
|
30
|
+
collectMaterialCookRefs({
|
|
31
|
+
parameters: [
|
|
32
|
+
{ name: 'transmissionTexture', type: 'texture' },
|
|
33
|
+
{ name: 'thicknessTexture', type: 'texture' },
|
|
34
|
+
],
|
|
35
|
+
values: {
|
|
36
|
+
transmissionTexture: { texture: 'texture/transmission', sampler: 'sampler/linear' },
|
|
37
|
+
thicknessTexture: { texture: 'texture/thickness', sampler: 'sampler/linear' },
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
).toEqual({
|
|
41
|
+
parent: [],
|
|
42
|
+
textures: ['texture/thickness', 'texture/transmission'],
|
|
43
|
+
samplers: ['sampler/linear'],
|
|
44
|
+
modules: [],
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('keeps authored Standard values with both texture dependency edges', () => {
|
|
49
|
+
const refs = collectMaterialCookRefs({
|
|
50
|
+
passes: [{ name: 'Forward', program: { module: 'forgeax::default-standard-pbr' } }],
|
|
51
|
+
parameters: [
|
|
52
|
+
{ name: 'transmissionTexture', type: 'texture' },
|
|
53
|
+
{ name: 'thicknessTexture', type: 'texture' },
|
|
54
|
+
],
|
|
55
|
+
values: {
|
|
56
|
+
transmission: 0.8,
|
|
57
|
+
ior: 1.45,
|
|
58
|
+
thickness: 0.25,
|
|
59
|
+
transmissionTexture: { texture: 'texture/transmission', sampler: 'sampler/linear' },
|
|
60
|
+
thicknessTexture: { texture: 'texture/thickness', sampler: 'sampler/linear' },
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(refs).toEqual({
|
|
65
|
+
parent: [],
|
|
66
|
+
textures: ['texture/thickness', 'texture/transmission'],
|
|
67
|
+
samplers: ['sampler/linear'],
|
|
68
|
+
modules: ['forgeax::default-standard-pbr'],
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
28
72
|
it('collects string shorthands only from declared texture parameters', () => {
|
|
29
73
|
expect(
|
|
30
74
|
collectMaterialCookRefs({
|
package/src/build.ts
CHANGED
package/src/catalog-builder.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { relative } from 'node:path';
|
|
1
|
+
import { isAbsolute, relative } from 'node:path';
|
|
2
2
|
import type { PackIndexEntry, ProviderProvenance } from '@forgeax/engine-types';
|
|
3
3
|
import {
|
|
4
4
|
findReservedAssetKindConflict,
|
|
@@ -75,12 +75,38 @@ export interface CatalogBuildProjectionOptions {
|
|
|
75
75
|
readonly scanOptions?: ScanOptions;
|
|
76
76
|
readonly importerPolicy: (importer: string) => CatalogImporterPolicy;
|
|
77
77
|
readonly visibility?: CatalogProducerVisibility;
|
|
78
|
+
/**
|
|
79
|
+
* Project physical source paths into a stable host-owned logical identity.
|
|
80
|
+
* Scanner/declaration maps retain physical paths for I/O; only Catalog rows
|
|
81
|
+
* use this projection so symlink farms cannot change publication identity.
|
|
82
|
+
*/
|
|
83
|
+
readonly sourceIdentityFor?: (sourcePath: string) => string;
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
function sourcePathFor(cwd: string, path: string): string {
|
|
81
87
|
return relative(cwd, path).replace(/\\/g, '/');
|
|
82
88
|
}
|
|
83
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Project one physical source path into the catalog's browser-safe logical
|
|
92
|
+
* locator. Physical paths remain the scanner/declaration keys; only catalog
|
|
93
|
+
* rows use this projection.
|
|
94
|
+
*/
|
|
95
|
+
export function catalogSourcePathFor(
|
|
96
|
+
cwd: string,
|
|
97
|
+
path: string,
|
|
98
|
+
sourceIdentityFor: CatalogBuildProjectionOptions['sourceIdentityFor'],
|
|
99
|
+
): string {
|
|
100
|
+
const projected = sourceIdentityFor?.(path);
|
|
101
|
+
// A host may intentionally leave paths outside its logical roots untouched.
|
|
102
|
+
// Keep the catalog's existing cwd-relative URL contract for that fallback;
|
|
103
|
+
// an absolute physical path is a valid dependency identity, but not a
|
|
104
|
+
// browser-served catalog locator.
|
|
105
|
+
const catalogPath =
|
|
106
|
+
projected === undefined || isAbsolute(projected) ? sourcePathFor(cwd, path) : projected;
|
|
107
|
+
return catalogPath.replaceAll('\\', '/').replace(/^\.\//, '');
|
|
108
|
+
}
|
|
109
|
+
|
|
84
110
|
export function metaPathForGuid(
|
|
85
111
|
declarations: ReadonlyMap<string, CatalogImportMeta>,
|
|
86
112
|
guid: string,
|
|
@@ -115,6 +141,7 @@ function projectMeta(
|
|
|
115
141
|
assetPaths: Record<string, string>,
|
|
116
142
|
importerPolicy: (importer: string) => CatalogImporterPolicy,
|
|
117
143
|
sourceDeclaration: Extract<ScanSourceDeclaration, { readonly format: 'meta.json' }>,
|
|
144
|
+
sourceIdentityFor: CatalogBuildProjectionOptions['sourceIdentityFor'],
|
|
118
145
|
): {
|
|
119
146
|
readonly entries: PackIndexEntry[];
|
|
120
147
|
readonly declaration?: CatalogImportMeta;
|
|
@@ -136,7 +163,7 @@ function projectMeta(
|
|
|
136
163
|
},
|
|
137
164
|
};
|
|
138
165
|
}
|
|
139
|
-
const sourcePath =
|
|
166
|
+
const sourcePath = catalogSourcePathFor(cwd, resolved.value, sourceIdentityFor);
|
|
140
167
|
const packageUrl = `${base}/__forgeax-ddc/${meta.subAssets[0]?.guid?.toLowerCase() ?? 'pack'}.pack.json`;
|
|
141
168
|
const declaration: CatalogImportMeta = {
|
|
142
169
|
importer: meta.importer,
|
|
@@ -184,6 +211,7 @@ function projectSource(
|
|
|
184
211
|
visibility: CatalogBuildProjectionOptions['visibility'],
|
|
185
212
|
sourceDeclaration: ScanSourceDeclaration,
|
|
186
213
|
declarations: Map<string, CatalogImportMeta>,
|
|
214
|
+
sourceIdentityFor: CatalogBuildProjectionOptions['sourceIdentityFor'],
|
|
187
215
|
): { readonly entries: PackIndexEntry[]; readonly error?: CatalogBuildError } {
|
|
188
216
|
if (sourceDeclaration.format === 'pack.ts') {
|
|
189
217
|
const meta = { ...sourceDeclaration.meta, source: sourcePathFor(cwd, path) };
|
|
@@ -193,7 +221,7 @@ function projectSource(
|
|
|
193
221
|
: {
|
|
194
222
|
entries: projectExternalCatalogEntries(
|
|
195
223
|
meta,
|
|
196
|
-
|
|
224
|
+
catalogSourcePathFor(cwd, path, sourceIdentityFor),
|
|
197
225
|
`${base}/__forgeax-ddc/${anchorGuid}.pack.json`,
|
|
198
226
|
meta.subAssets,
|
|
199
227
|
(output) => deriveAssetName(path, meta.subAssets.length, output.name),
|
|
@@ -212,12 +240,20 @@ function projectSource(
|
|
|
212
240
|
) {
|
|
213
241
|
return { entries: [] };
|
|
214
242
|
}
|
|
215
|
-
const projected = projectMeta(
|
|
243
|
+
const projected = projectMeta(
|
|
244
|
+
path,
|
|
245
|
+
cwd,
|
|
246
|
+
base,
|
|
247
|
+
assetPaths,
|
|
248
|
+
importerPolicy,
|
|
249
|
+
sourceDeclaration,
|
|
250
|
+
sourceIdentityFor,
|
|
251
|
+
);
|
|
216
252
|
if (projected.declaration !== undefined) declarations.set(path, projected.declaration);
|
|
217
253
|
return projected;
|
|
218
254
|
}
|
|
219
255
|
const parsed = sourceDeclaration.value;
|
|
220
|
-
const sourcePath =
|
|
256
|
+
const sourcePath = catalogSourcePathFor(cwd, path, sourceIdentityFor);
|
|
221
257
|
const provenance =
|
|
222
258
|
parsed.provenance ??
|
|
223
259
|
({ provider: 'pack', version: parsed.schemaVersion } satisfies ProviderProvenance);
|
|
@@ -297,6 +333,7 @@ export async function buildCatalogProjection(
|
|
|
297
333
|
options.visibility,
|
|
298
334
|
sourceDeclaration,
|
|
299
335
|
declarations,
|
|
336
|
+
options.sourceIdentityFor,
|
|
300
337
|
);
|
|
301
338
|
entries.push(...projected.entries);
|
|
302
339
|
if (projected.error !== undefined) errors.push(projected.error);
|
|
@@ -670,7 +670,10 @@ export async function loadScriptablePack(
|
|
|
670
670
|
sourcePath: string,
|
|
671
671
|
options: LoadScriptablePackOptions = {},
|
|
672
672
|
): Promise<Result<Readonly<ScriptablePackDefinition>, ScriptablePackError>> {
|
|
673
|
-
|
|
673
|
+
// Module initialization includes transpiling the trusted TypeScript source
|
|
674
|
+
// closure in a fresh Worker. A cold SDK workspace can legitimately exceed
|
|
675
|
+
// the build execution budget before any authored build() code has run.
|
|
676
|
+
const timeoutMs = options.timeoutMs ?? 15_000;
|
|
674
677
|
const buildTimeoutMs = options.buildTimeoutMs ?? 5_000;
|
|
675
678
|
const executor = options.executor ?? new WorkerScriptablePackExecutor();
|
|
676
679
|
let disposeReason: 'complete' | 'timeout' | 'failure' | undefined;
|