@forgeax/engine-pack 0.1.28 → 0.1.30
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 +33 -5
- package/dist/build.mjs +99 -60
- package/dist/build.mjs.map +1 -1
- package/dist/catalog-projection.d.ts +3 -3
- package/dist/cli-asset.mjs +22 -13
- 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/inventory/declaration.d.ts +1 -1
- package/dist/inventory/declaration.d.ts.map +1 -1
- package/dist/inventory/sync.d.ts.map +1 -1
- package/dist/material-cook.mjs +61 -5
- package/dist/material-cook.mjs.map +1 -1
- package/dist/native-cooker-registry.d.ts +1 -0
- package/dist/native-cooker-registry.d.ts.map +1 -1
- package/dist/native-cooker.mjs.map +1 -1
- package/dist/pack-authoring-node.mjs +22 -13
- 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/runtime.mjs.map +1 -1
- package/dist/scanner.mjs +22 -13
- package/dist/scanner.mjs.map +1 -1
- package/dist/schema-compiled.d.ts.map +1 -1
- package/dist/schema.mjs +40 -62
- package/dist/schema.mjs.map +1 -1
- package/dist/scriptable-pack-node.d.ts.map +1 -1
- package/dist/scriptable-pack-node.mjs +12 -4
- package/dist/scriptable-pack-node.mjs.map +1 -1
- package/dist/scriptable-pack-worker.mjs +12 -5
- 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__/inventory-schema.test.ts +10 -10
- package/src/__tests__/material-cook-schema.unit.test.ts +92 -1
- package/src/__tests__/pack-authoring.unit.test.ts +2 -4
- package/src/__tests__/pack.unit.test.ts +86 -124
- package/src/__tests__/runtime-publication.unit.test.ts +36 -0
- package/src/__tests__/scanner-instance-cycle.test.ts +92 -0
- package/src/__tests__/scanner-inventory.contract.test.ts +2 -2
- package/src/__tests__/scriptable-pack-cli.integration.test.ts +35 -1
- package/src/__tests__/scriptable-pack-diagnostic.integration.test.ts +45 -0
- package/src/evidence/material-cook.ts +79 -5
- package/src/inventory/binding.ts +3 -3
- package/src/inventory/declaration.ts +10 -48
- package/src/inventory/sync.ts +3 -2
- package/src/native-cooker-registry.ts +1 -0
- package/src/pack-authoring.ts +11 -2
- package/src/runtime-publication.ts +7 -3
- package/src/scanner.ts +28 -28
- package/src/schema/material-cook.schema.json +107 -0
- package/src/schema-compiled.ts +40 -79
- package/src/scriptable-pack-node.ts +16 -4
- package/src/scriptable-pack-worker.ts +23 -5
- package/src/scriptable-pack.ts +2 -1
- package/src/__tests__/scanner-mount-cycle.test.ts +0 -232
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MaterialAsset, MaterialParameter, MaterialPass, MaterialValue, Result } from '@forgeax/engine-types';
|
|
1
|
+
import type { MaterialAsset, MaterialParameter, MaterialPass, MaterialProgramAbi, MaterialProgramAddress, MaterialValue, Result } from '@forgeax/engine-types';
|
|
2
2
|
export interface MaterialCookRefs {
|
|
3
3
|
readonly parent: readonly string[];
|
|
4
4
|
readonly textures: readonly string[];
|
|
@@ -29,6 +29,12 @@ export interface MaterialCookProgram {
|
|
|
29
29
|
readonly selections: readonly {
|
|
30
30
|
readonly pass: string;
|
|
31
31
|
readonly context: MaterialCookProgramContext;
|
|
32
|
+
/** Renderer submission address selected by this publication. */
|
|
33
|
+
readonly address?: MaterialProgramAddress;
|
|
34
|
+
/** Actual entry point paired with the selected address. */
|
|
35
|
+
readonly entry?: string;
|
|
36
|
+
/** Producer-reflected ABI facts for the selected artifact/entry. */
|
|
37
|
+
readonly abi?: MaterialProgramAbi;
|
|
32
38
|
}[];
|
|
33
39
|
}
|
|
34
40
|
export interface MaterialCookWasmProvenance {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"material-cook.d.ts","sourceRoot":"","sources":["../../src/evidence/material-cook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,YAAY,
|
|
1
|
+
{"version":3,"file":"material-cook.d.ts","sourceRoot":"","sources":["../../src/evidence/material-cook.ts"],"names":[],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deriveStandardLayerPlan, MATERIAL_TEXTURE_SLOTS, ok, projectCookProductEvidence, projectAssetEvidence, err, validateSourceOverrideMap } from '@forgeax/engine-types';
|
|
1
|
+
import { deriveStandardLayerPlan, MATERIAL_TEXTURE_SLOTS, ok, isMaterialProgramAbi, projectCookProductEvidence, projectAssetEvidence, err, validateSourceOverrideMap } from '@forgeax/engine-types';
|
|
2
2
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
3
3
|
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
4
4
|
import { sha1 } from '@noble/hashes/legacy.js';
|
|
@@ -7078,6 +7078,9 @@ function validateCookedMaterialRecord(value) {
|
|
|
7078
7078
|
const programs = [];
|
|
7079
7079
|
const programKeys = /* @__PURE__ */ new Set();
|
|
7080
7080
|
const selections = /* @__PURE__ */ new Set();
|
|
7081
|
+
const submissionSelections = /* @__PURE__ */ new Map();
|
|
7082
|
+
let modernPublication = false;
|
|
7083
|
+
let legacySelectionField;
|
|
7081
7084
|
const selectedPasses = /* @__PURE__ */ new Set();
|
|
7082
7085
|
for (const [index, entry] of candidate.programs.entries()) {
|
|
7083
7086
|
const field = `programs[${index}]`;
|
|
@@ -7095,9 +7098,11 @@ function validateCookedMaterialRecord(value) {
|
|
|
7095
7098
|
if (!Array.isArray(program.selections) || program.selections.length === 0)
|
|
7096
7099
|
return invalid(`${field}.selections`);
|
|
7097
7100
|
const programSelections = [];
|
|
7098
|
-
for (const [selectionIndex,
|
|
7101
|
+
for (const [selectionIndex, rawSelection] of program.selections.entries()) {
|
|
7099
7102
|
const selectionField = `${field}.selections[${selectionIndex}]`;
|
|
7100
|
-
if (
|
|
7103
|
+
if (rawSelection === null || typeof rawSelection !== "object") return invalid(selectionField);
|
|
7104
|
+
const selection = rawSelection;
|
|
7105
|
+
if (typeof selection.pass !== "string" || !passNames.has(selection.pass))
|
|
7101
7106
|
return invalid(selectionField);
|
|
7102
7107
|
const context = validateMaterialCookProgramContext(selection.context);
|
|
7103
7108
|
if (!context.ok)
|
|
@@ -7105,11 +7110,51 @@ function validateCookedMaterialRecord(value) {
|
|
|
7105
7110
|
`${selectionField}.${context.error.detail.field}`,
|
|
7106
7111
|
context.error.detail.actual
|
|
7107
7112
|
);
|
|
7108
|
-
const
|
|
7113
|
+
const address = selection.address === void 0 ? "direct" : selection.address;
|
|
7114
|
+
if (address !== "direct" && address !== "scene-index")
|
|
7115
|
+
return invalid(`${selectionField}.address`, selection.address);
|
|
7116
|
+
const hasAddressFacts = selection.address !== void 0 || selection.entry !== void 0 || selection.abi !== void 0;
|
|
7117
|
+
modernPublication ||= hasAddressFacts;
|
|
7118
|
+
if (!hasAddressFacts) legacySelectionField ??= selectionField;
|
|
7119
|
+
const rawEntry = selection.entry;
|
|
7120
|
+
const entry2 = typeof rawEntry === "string" ? rawEntry : void 0;
|
|
7121
|
+
if (hasAddressFacts && selection.address === void 0)
|
|
7122
|
+
return invalid(
|
|
7123
|
+
`${selectionField}.address`,
|
|
7124
|
+
"modern ABI selections require an explicit address"
|
|
7125
|
+
);
|
|
7126
|
+
if (hasAddressFacts && (entry2 === void 0 || entry2.length === 0))
|
|
7127
|
+
return invalid(`${selectionField}.entry`, rawEntry);
|
|
7128
|
+
if (hasAddressFacts && !isMaterialProgramAbi(selection.abi))
|
|
7129
|
+
return invalid(`${selectionField}.abi`, selection.abi);
|
|
7130
|
+
if (hasAddressFacts) {
|
|
7131
|
+
const abi = selection.abi;
|
|
7132
|
+
const expectedEntry = address === "direct" ? abi.directEntry : abi.sceneIndexEntry;
|
|
7133
|
+
if (entry2 !== expectedEntry)
|
|
7134
|
+
return invalid(`${selectionField}.entry`, "entry does not match published ABI");
|
|
7135
|
+
const submissionKey = JSON.stringify([
|
|
7136
|
+
selection.pass,
|
|
7137
|
+
materialProgramContextKey(context.value)
|
|
7138
|
+
]);
|
|
7139
|
+
const addresses = submissionSelections.get(submissionKey) ?? /* @__PURE__ */ new Set();
|
|
7140
|
+
addresses.add(address);
|
|
7141
|
+
submissionSelections.set(submissionKey, addresses);
|
|
7142
|
+
}
|
|
7143
|
+
const key = JSON.stringify([
|
|
7144
|
+
selection.pass,
|
|
7145
|
+
materialProgramContextKey(context.value),
|
|
7146
|
+
address
|
|
7147
|
+
]);
|
|
7109
7148
|
if (selections.has(key)) return invalid(selectionField, "ambiguous Pass/context selection");
|
|
7110
7149
|
selections.add(key);
|
|
7111
7150
|
selectedPasses.add(selection.pass);
|
|
7112
|
-
programSelections.push({
|
|
7151
|
+
programSelections.push({
|
|
7152
|
+
pass: selection.pass,
|
|
7153
|
+
context: context.value,
|
|
7154
|
+
...selection.address === void 0 ? {} : { address },
|
|
7155
|
+
...entry2 === void 0 ? {} : { entry: entry2 },
|
|
7156
|
+
...selection.abi === void 0 ? {} : { abi: selection.abi }
|
|
7157
|
+
});
|
|
7113
7158
|
}
|
|
7114
7159
|
programs.push({
|
|
7115
7160
|
specializationKey: program.specializationKey,
|
|
@@ -7117,6 +7162,17 @@ function validateCookedMaterialRecord(value) {
|
|
|
7117
7162
|
selections: programSelections
|
|
7118
7163
|
});
|
|
7119
7164
|
}
|
|
7165
|
+
if (modernPublication && legacySelectionField !== void 0) {
|
|
7166
|
+
return invalid(
|
|
7167
|
+
legacySelectionField,
|
|
7168
|
+
"modern material publications require address, entry, and ABI facts on every selection"
|
|
7169
|
+
);
|
|
7170
|
+
}
|
|
7171
|
+
for (const [selectionKey, addresses] of submissionSelections) {
|
|
7172
|
+
if (addresses.size !== 2) {
|
|
7173
|
+
return invalid("programs.selections", `incomplete submission address pair: ${selectionKey}`);
|
|
7174
|
+
}
|
|
7175
|
+
}
|
|
7120
7176
|
if ([...passNames].some((pass) => !selectedPasses.has(pass)))
|
|
7121
7177
|
return invalid("programs.selections", "unpublished Pass");
|
|
7122
7178
|
const manifestDigest = createMaterialProgramSetDigest(programs, passes);
|