@forgeax/engine-pack 0.1.29 → 0.1.31
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 +16 -0
- package/dist/build.mjs +65 -9
- package/dist/build.mjs.map +1 -1
- package/dist/catalog-projection.d.ts +3 -3
- package/dist/cli-asset.mjs +1 -1
- package/dist/cli-asset.mjs.map +1 -1
- package/dist/evidence/material-cook.d.ts +7 -1
- package/dist/evidence/material-cook.d.ts.map +1 -1
- package/dist/index.mjs +61 -5
- package/dist/index.mjs.map +1 -1
- package/dist/material-cook.mjs +61 -5
- package/dist/material-cook.mjs.map +1 -1
- package/dist/pack-authoring-node.mjs +1 -1
- package/dist/pack-authoring-node.mjs.map +1 -1
- package/dist/pack-authoring.d.ts +8 -2
- package/dist/pack-authoring.d.ts.map +1 -1
- package/dist/pack-authoring.mjs.map +1 -1
- package/dist/runtime-publication.d.ts +2 -0
- package/dist/runtime-publication.d.ts.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/dist/scriptable-pack-worker.mjs +7 -4
- package/dist/scriptable-pack-worker.mjs.map +1 -1
- package/dist/scriptable-pack.d.ts +2 -1
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/scriptable-pack.mjs.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/material-cook-schema.unit.test.ts +92 -1
- package/src/__tests__/runtime-publication.unit.test.ts +36 -0
- package/src/__tests__/scriptable-pack-cli.integration.test.ts +35 -1
- package/src/evidence/material-cook.ts +79 -5
- package/src/pack-authoring.ts +11 -2
- package/src/runtime-publication.ts +7 -3
- package/src/schema/material-cook.schema.json +107 -0
- package/src/scriptable-pack-node.ts +5 -1
- package/src/scriptable-pack-worker.ts +15 -4
- package/src/scriptable-pack.ts +2 -1
package/README.md
CHANGED
|
@@ -278,6 +278,22 @@ vectors, colors, and asset-GUID constraints. The isolated Pack worker passes
|
|
|
278
278
|
only serializable authoring data to the build and never exposes filesystem or
|
|
279
279
|
runtime state.
|
|
280
280
|
|
|
281
|
+
Custom build-only content uses `PackCookSource` in the same output map:
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
{ 'volume/main': { kind: 'game-volume', execution: 'cooked', source: { size: values.size } } }
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Register the matching `NativeCooker` in the build host. The host supplies the
|
|
288
|
+
derived GUID, source key/path and source data; the cooker returns matching-kind
|
|
289
|
+
metadata, references and artifact bytes. Missing cookers, rewritten GUIDs and
|
|
290
|
+
invalid products fail before publication. Custom sources can coexist with
|
|
291
|
+
ordinary Assets and inherit normal Pack parameters and instance identities.
|
|
292
|
+
They cannot override the closed ordinary Asset kinds. Authoring `readByGuid`
|
|
293
|
+
can explicitly request a `PackCookSource` from the current build generation;
|
|
294
|
+
that is source data, not a decoded runtime asset. Runtime custom kinds still
|
|
295
|
+
need their normal Registry loader/decoder. No cooker enters the player.
|
|
296
|
+
|
|
281
297
|
`forgeax asset inspect --subject <source.pack.ts> --root <project> --json` executes module initialization, validates the default export, and projects canonical Meta without calling `build`. `@forgeax/engine-pack/source-node` accepts a host executor with `load` and optional `dispose`; `timeoutMs` bounds module initialization and `buildTimeoutMs` bounds one `build(context)` call. A build timeout returns one structured `pack-source-load-failed` Result with `detail.phase: 'build'`, the configured `timeoutMs`, and deterministic cleanup of the isolated worker and compile root.
|
|
282
298
|
|
|
283
299
|
The default worker executes the complete relative TypeScript module closure on the supported Node floor, including Node 22 hosts that do not load `.ts` files directly. It transpiles that closure into a disposable ESM directory, resolves bare imports through the source project's nearest `node_modules`, and removes the directory when the worker is disposed. Bulk producers use the internal `createScriptablePackModuleExecutorPool()` with two recyclable workers; a pooled lease is released after metadata projection or one build, so a generation never retains one live Worker-backed definition per source.
|
package/dist/build.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ok, err, catalogOperationsFor, authoringCapabilityForAssetKind, MATERIAL_TEXTURE_SLOTS, projectCookProductEvidence, projectAssetEvidence, validateSourceOverrideMap, PACK_ERROR_HINTS, catalogEntryDigest, deriveStandardLayerPlan, AssetError, ImportError } from '@forgeax/engine-types';
|
|
1
|
+
import { ok, err, catalogOperationsFor, authoringCapabilityForAssetKind, MATERIAL_TEXTURE_SLOTS, isMaterialProgramAbi, projectCookProductEvidence, projectAssetEvidence, validateSourceOverrideMap, PACK_ERROR_HINTS, catalogEntryDigest, deriveStandardLayerPlan, AssetError, ImportError } from '@forgeax/engine-types';
|
|
2
2
|
import { dirname, resolve, isAbsolute, relative, join, basename, sep, extname } from 'path';
|
|
3
3
|
import { sha1 } from '@noble/hashes/legacy.js';
|
|
4
4
|
import { uuidv7obj } from 'uuidv7';
|
|
@@ -9149,7 +9149,7 @@ function scriptablePackLoadFailure(sourcePath, reason, phase, diagnostic, timeou
|
|
|
9149
9149
|
}
|
|
9150
9150
|
async function loadScriptablePack(sourcePath, options = {}) {
|
|
9151
9151
|
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
9152
|
-
const buildTimeoutMs = options.buildTimeoutMs ??
|
|
9152
|
+
const buildTimeoutMs = options.buildTimeoutMs ?? 15e3;
|
|
9153
9153
|
const executor = options.executor ?? new WorkerScriptablePackExecutor();
|
|
9154
9154
|
let disposeReason;
|
|
9155
9155
|
let timeout;
|
|
@@ -10547,6 +10547,9 @@ function validateCookedMaterialRecord(value) {
|
|
|
10547
10547
|
const programs = [];
|
|
10548
10548
|
const programKeys = /* @__PURE__ */ new Set();
|
|
10549
10549
|
const selections = /* @__PURE__ */ new Set();
|
|
10550
|
+
const submissionSelections = /* @__PURE__ */ new Map();
|
|
10551
|
+
let modernPublication = false;
|
|
10552
|
+
let legacySelectionField;
|
|
10550
10553
|
const selectedPasses = /* @__PURE__ */ new Set();
|
|
10551
10554
|
for (const [index, entry] of candidate.programs.entries()) {
|
|
10552
10555
|
const field = `programs[${index}]`;
|
|
@@ -10564,9 +10567,11 @@ function validateCookedMaterialRecord(value) {
|
|
|
10564
10567
|
if (!Array.isArray(program.selections) || program.selections.length === 0)
|
|
10565
10568
|
return invalid(`${field}.selections`);
|
|
10566
10569
|
const programSelections = [];
|
|
10567
|
-
for (const [selectionIndex,
|
|
10570
|
+
for (const [selectionIndex, rawSelection] of program.selections.entries()) {
|
|
10568
10571
|
const selectionField = `${field}.selections[${selectionIndex}]`;
|
|
10569
|
-
if (
|
|
10572
|
+
if (rawSelection === null || typeof rawSelection !== "object") return invalid(selectionField);
|
|
10573
|
+
const selection = rawSelection;
|
|
10574
|
+
if (typeof selection.pass !== "string" || !passNames.has(selection.pass))
|
|
10570
10575
|
return invalid(selectionField);
|
|
10571
10576
|
const context = validateMaterialCookProgramContext(selection.context);
|
|
10572
10577
|
if (!context.ok)
|
|
@@ -10574,11 +10579,51 @@ function validateCookedMaterialRecord(value) {
|
|
|
10574
10579
|
`${selectionField}.${context.error.detail.field}`,
|
|
10575
10580
|
context.error.detail.actual
|
|
10576
10581
|
);
|
|
10577
|
-
const
|
|
10582
|
+
const address = selection.address === void 0 ? "direct" : selection.address;
|
|
10583
|
+
if (address !== "direct" && address !== "scene-index")
|
|
10584
|
+
return invalid(`${selectionField}.address`, selection.address);
|
|
10585
|
+
const hasAddressFacts = selection.address !== void 0 || selection.entry !== void 0 || selection.abi !== void 0;
|
|
10586
|
+
modernPublication ||= hasAddressFacts;
|
|
10587
|
+
if (!hasAddressFacts) legacySelectionField ??= selectionField;
|
|
10588
|
+
const rawEntry = selection.entry;
|
|
10589
|
+
const entry2 = typeof rawEntry === "string" ? rawEntry : void 0;
|
|
10590
|
+
if (hasAddressFacts && selection.address === void 0)
|
|
10591
|
+
return invalid(
|
|
10592
|
+
`${selectionField}.address`,
|
|
10593
|
+
"modern ABI selections require an explicit address"
|
|
10594
|
+
);
|
|
10595
|
+
if (hasAddressFacts && (entry2 === void 0 || entry2.length === 0))
|
|
10596
|
+
return invalid(`${selectionField}.entry`, rawEntry);
|
|
10597
|
+
if (hasAddressFacts && !isMaterialProgramAbi(selection.abi))
|
|
10598
|
+
return invalid(`${selectionField}.abi`, selection.abi);
|
|
10599
|
+
if (hasAddressFacts) {
|
|
10600
|
+
const abi = selection.abi;
|
|
10601
|
+
const expectedEntry = address === "direct" ? abi.directEntry : abi.sceneIndexEntry;
|
|
10602
|
+
if (entry2 !== expectedEntry)
|
|
10603
|
+
return invalid(`${selectionField}.entry`, "entry does not match published ABI");
|
|
10604
|
+
const submissionKey = JSON.stringify([
|
|
10605
|
+
selection.pass,
|
|
10606
|
+
materialProgramContextKey(context.value)
|
|
10607
|
+
]);
|
|
10608
|
+
const addresses = submissionSelections.get(submissionKey) ?? /* @__PURE__ */ new Set();
|
|
10609
|
+
addresses.add(address);
|
|
10610
|
+
submissionSelections.set(submissionKey, addresses);
|
|
10611
|
+
}
|
|
10612
|
+
const key = JSON.stringify([
|
|
10613
|
+
selection.pass,
|
|
10614
|
+
materialProgramContextKey(context.value),
|
|
10615
|
+
address
|
|
10616
|
+
]);
|
|
10578
10617
|
if (selections.has(key)) return invalid(selectionField, "ambiguous Pass/context selection");
|
|
10579
10618
|
selections.add(key);
|
|
10580
10619
|
selectedPasses.add(selection.pass);
|
|
10581
|
-
programSelections.push({
|
|
10620
|
+
programSelections.push({
|
|
10621
|
+
pass: selection.pass,
|
|
10622
|
+
context: context.value,
|
|
10623
|
+
...selection.address === void 0 ? {} : { address },
|
|
10624
|
+
...entry2 === void 0 ? {} : { entry: entry2 },
|
|
10625
|
+
...selection.abi === void 0 ? {} : { abi: selection.abi }
|
|
10626
|
+
});
|
|
10582
10627
|
}
|
|
10583
10628
|
programs.push({
|
|
10584
10629
|
specializationKey: program.specializationKey,
|
|
@@ -10586,6 +10631,17 @@ function validateCookedMaterialRecord(value) {
|
|
|
10586
10631
|
selections: programSelections
|
|
10587
10632
|
});
|
|
10588
10633
|
}
|
|
10634
|
+
if (modernPublication && legacySelectionField !== void 0) {
|
|
10635
|
+
return invalid(
|
|
10636
|
+
legacySelectionField,
|
|
10637
|
+
"modern material publications require address, entry, and ABI facts on every selection"
|
|
10638
|
+
);
|
|
10639
|
+
}
|
|
10640
|
+
for (const [selectionKey, addresses] of submissionSelections) {
|
|
10641
|
+
if (addresses.size !== 2) {
|
|
10642
|
+
return invalid("programs.selections", `incomplete submission address pair: ${selectionKey}`);
|
|
10643
|
+
}
|
|
10644
|
+
}
|
|
10589
10645
|
if ([...passNames].some((pass) => !selectedPasses.has(pass)))
|
|
10590
10646
|
return invalid("programs.selections", "unpublished Pass");
|
|
10591
10647
|
const manifestDigest = createMaterialProgramSetDigest(programs, passes);
|
|
@@ -12767,10 +12823,10 @@ function normalizedAssets(pack) {
|
|
|
12767
12823
|
};
|
|
12768
12824
|
});
|
|
12769
12825
|
}
|
|
12770
|
-
function outputFor(asset) {
|
|
12826
|
+
function outputFor(asset, sourceKey2) {
|
|
12771
12827
|
return {
|
|
12772
12828
|
guid: asset.guid.toLowerCase(),
|
|
12773
|
-
sourceKey: asset.guid.toLowerCase(),
|
|
12829
|
+
sourceKey: sourceKey2 ?? asset.guid.toLowerCase(),
|
|
12774
12830
|
kind: asset.kind,
|
|
12775
12831
|
digest: digest2({
|
|
12776
12832
|
guid: asset.guid.toLowerCase(),
|
|
@@ -12809,7 +12865,7 @@ function createRuntimePackPublication(input) {
|
|
|
12809
12865
|
)
|
|
12810
12866
|
};
|
|
12811
12867
|
const valueDigest = input.digest ?? digest2(semantic);
|
|
12812
|
-
const outputs = input.outputs ?? assets.map(outputFor);
|
|
12868
|
+
const outputs = input.outputs ?? assets.map((asset) => outputFor(asset, input.sourceKeys?.get(asset.guid.toLowerCase())));
|
|
12813
12869
|
const outputDigest = outputSetDigest(outputs);
|
|
12814
12870
|
const generation = input.generation ?? publicationGeneration(input.sourceRevision, valueDigest, outputDigest);
|
|
12815
12871
|
const externalEvidence = input.externalEvidence ?? [];
|