@forgeax/engine-import 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 +15 -0
- package/dist/__tests__/scriptable-pack-fingerprint.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-fingerprint.unit.test.d.ts.map +1 -0
- package/dist/build-production.d.ts.map +1 -1
- package/dist/index.mjs +1250 -1144
- package/dist/index.mjs.map +1 -1
- package/dist/scriptable-pack-build.d.ts.map +1 -1
- package/dist/scriptable-pack-file-snapshot.d.ts.map +1 -1
- package/dist/scriptable-pack-fingerprint.d.ts +3 -0
- package/dist/scriptable-pack-fingerprint.d.ts.map +1 -0
- package/dist/scriptable-pack-output-producers.d.ts.map +1 -1
- package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -1
- package/dist/scriptable-pack.d.ts +9 -4
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/scriptable-pack-build.unit.test.ts +193 -1
- package/src/__tests__/scriptable-pack-fingerprint.unit.test.ts +65 -0
- package/src/__tests__/scriptable-pack-host.unit.test.ts +4 -4
- package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +65 -26
- package/src/build-production.ts +3 -7
- package/src/scriptable-pack-build.ts +129 -35
- package/src/scriptable-pack-file-snapshot.ts +2 -28
- package/src/scriptable-pack-fingerprint.ts +48 -0
- package/src/scriptable-pack-output-producers.ts +24 -2
- package/src/scriptable-pack-staged-snapshot.ts +2 -22
- package/src/scriptable-pack.ts +9 -3
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ export { IMPORT_ERROR_HINTS, ImportError } from '@forgeax/engine-types';
|
|
|
3
3
|
import { resolve, dirname, isAbsolute, relative } from 'path';
|
|
4
4
|
import { resolveAssetSource, finalizePackageTransportSource, packageTransportRevision, catalogSourcePathFor, buildCatalogProjection, projectPackageCatalog, createRuntimePackPublication, metaPathForGuid } from '@forgeax/engine-pack/build';
|
|
5
5
|
import { AssetGuid as AssetGuid$1, PackageId as PackageId$1 } from '@forgeax/engine-pack/guid';
|
|
6
|
-
import { resolvePackParameterValues, PackageId, isValidPackSourceKey, AssetGuid, parsePackSourceJson, projectDirectPackJson,
|
|
6
|
+
import { resolvePackParameterValues, PackageId, isValidPackSourceKey, isScriptablePackAssetKind, AssetGuid, parsePackSourceJson, projectDirectPackJson, projectScriptablePackSceneComponents, resolvePackParameterInheritance } from '@forgeax/engine-pack/source';
|
|
7
7
|
import { loadScriptablePack } from '@forgeax/engine-pack/source-node';
|
|
8
8
|
import { stat, readFile, mkdir, writeFile, rename, rm, readdir } from 'fs/promises';
|
|
9
9
|
import { createAcceptedPublication, ddcOutputDigest, DdcLifecycle, DdcGenerationSession } from '@forgeax/engine-ddc';
|
|
@@ -11,12 +11,12 @@ import { deriveVertexLayoutProjection, normalizeMeshPayload } from '@forgeax/eng
|
|
|
11
11
|
import { BUILTIN_MESH_ASSETS } from '@forgeax/engine-pack/builtin';
|
|
12
12
|
import { NativeCookerRegistry } from '@forgeax/engine-pack/native-cooker';
|
|
13
13
|
import { isEngineMaterial } from '@forgeax/engine-shader';
|
|
14
|
-
import { externalizeSceneAsset } from '@forgeax/engine-scene';
|
|
15
14
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
16
15
|
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
16
|
+
import { externalizeSceneAsset } from '@forgeax/engine-scene';
|
|
17
17
|
import { MESH_BIN_HEADER_V4_BYTES, writeMeshBinHeader } from '@forgeax/engine-pack/mesh-bin-contract';
|
|
18
|
-
import { createHash, randomUUID } from 'crypto';
|
|
19
18
|
import { validatePack, isScriptablePackAssetKind as isScriptablePackAssetKind$1 } from '@forgeax/engine-pack';
|
|
19
|
+
import { createHash, randomUUID } from 'crypto';
|
|
20
20
|
import { canonicalDdcJson } from '@forgeax/engine-ddc/key';
|
|
21
21
|
|
|
22
22
|
// src/index.ts
|
|
@@ -45,8 +45,8 @@ async function sha256Hex(bytes2) {
|
|
|
45
45
|
if (subtle === void 0) throw new Error("Web Crypto API is required for importer digests");
|
|
46
46
|
const owned = new Uint8Array(bytes2.byteLength);
|
|
47
47
|
owned.set(bytes2);
|
|
48
|
-
const
|
|
49
|
-
return Array.from(new Uint8Array(
|
|
48
|
+
const digest = await subtle.digest("SHA-256", owned.buffer);
|
|
49
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
50
50
|
}
|
|
51
51
|
async function artifactDigest(bytes2) {
|
|
52
52
|
return `sha256:${await sha256Hex(bytes2)}`;
|
|
@@ -109,19 +109,19 @@ function finalizeImportProducts(product, inputFingerprint) {
|
|
|
109
109
|
const products = [];
|
|
110
110
|
for (const asset of product.assets) {
|
|
111
111
|
const artifacts = await artifactDescriptors(asset.artifacts);
|
|
112
|
-
const
|
|
112
|
+
const digest = await productDigest(asset, artifacts);
|
|
113
113
|
products.push({
|
|
114
114
|
guid: asset.guid,
|
|
115
115
|
payload: asset.payload,
|
|
116
116
|
refs: asset.refs.map((ref2) => ref2.guid),
|
|
117
117
|
artifacts,
|
|
118
|
-
digest
|
|
118
|
+
digest,
|
|
119
119
|
receipt: {
|
|
120
120
|
guid: asset.guid,
|
|
121
121
|
origin: "sourceMeta",
|
|
122
122
|
status: "succeeded",
|
|
123
123
|
inputFingerprint,
|
|
124
|
-
outputDigest:
|
|
124
|
+
outputDigest: digest
|
|
125
125
|
}
|
|
126
126
|
});
|
|
127
127
|
}
|
|
@@ -779,1217 +779,1364 @@ function projectImportProductForBuild(product) {
|
|
|
779
779
|
}))
|
|
780
780
|
};
|
|
781
781
|
}
|
|
782
|
-
function
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
async function digest(value) {
|
|
804
|
-
const crypto = globalThis.crypto?.subtle;
|
|
805
|
-
if (crypto === void 0) throw new Error("Web Crypto API is required for Pack fingerprints");
|
|
806
|
-
const bytes2 = await crypto.digest("SHA-256", new TextEncoder().encode(stable(value)));
|
|
807
|
-
return `sha256:${Array.from(new Uint8Array(bytes2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
808
|
-
}
|
|
809
|
-
function observedReader(source) {
|
|
810
|
-
const reads = /* @__PURE__ */ new Map();
|
|
811
|
-
const reader = {
|
|
812
|
-
async readByGuid(guid) {
|
|
813
|
-
const key = AssetGuid.format(guid).toLowerCase();
|
|
814
|
-
const cached = reads.get(key);
|
|
815
|
-
if (cached !== void 0) return ok(clone(cached.asset));
|
|
816
|
-
if (source === void 0) {
|
|
817
|
-
return err(
|
|
818
|
-
new AssetError({
|
|
819
|
-
code: "asset-not-found",
|
|
820
|
-
expected: `a published Asset snapshot for content dependency ${key}`,
|
|
821
|
-
hint: "publish the dependency or remove the content read from the Pack build",
|
|
822
|
-
detail: { sourcePath: key }
|
|
823
|
-
})
|
|
824
|
-
);
|
|
782
|
+
function scriptablePackFingerprint(value) {
|
|
783
|
+
const hash = sha256.create();
|
|
784
|
+
const encoder = new TextEncoder();
|
|
785
|
+
const text = (part) => {
|
|
786
|
+
hash.update(encoder.encode(part));
|
|
787
|
+
};
|
|
788
|
+
const binary = (type, bytes2) => {
|
|
789
|
+
text(`\0${type}:${bytes2.byteLength}:`);
|
|
790
|
+
hash.update(bytes2);
|
|
791
|
+
text("\0");
|
|
792
|
+
};
|
|
793
|
+
const visit = (item) => {
|
|
794
|
+
if (ArrayBuffer.isView(item)) {
|
|
795
|
+
binary(item.constructor.name, new Uint8Array(item.buffer, item.byteOffset, item.byteLength));
|
|
796
|
+
} else if (item instanceof ArrayBuffer) {
|
|
797
|
+
binary("ArrayBuffer", new Uint8Array(item));
|
|
798
|
+
} else if (Array.isArray(item)) {
|
|
799
|
+
text("[");
|
|
800
|
+
for (let i = 0; i < item.length; i++) {
|
|
801
|
+
if (i) text(",");
|
|
802
|
+
visit(item[i]);
|
|
825
803
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
804
|
+
text("]");
|
|
805
|
+
} else if (item !== null && typeof item === "object") {
|
|
806
|
+
const object = item;
|
|
807
|
+
text("{");
|
|
808
|
+
let first = true;
|
|
809
|
+
for (const key of Object.keys(object).sort()) {
|
|
810
|
+
if (!first) text(",");
|
|
811
|
+
first = false;
|
|
812
|
+
text(`${JSON.stringify(key)}:`);
|
|
813
|
+
visit(object[key]);
|
|
814
|
+
}
|
|
815
|
+
text("}");
|
|
816
|
+
} else {
|
|
817
|
+
text(JSON.stringify(item) ?? "null");
|
|
836
818
|
}
|
|
837
819
|
};
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if (values === void 0) return { packageId, readByGuid: reader.readByGuid };
|
|
842
|
-
return { packageId, values, readByGuid: reader.readByGuid };
|
|
843
|
-
}
|
|
844
|
-
function sourceKeyFailure(sourcePath, sourceKey) {
|
|
845
|
-
return {
|
|
846
|
-
code: "pack-source-key-invalid",
|
|
847
|
-
expected: "a sourceKey matching the Pack source-key grammar",
|
|
848
|
-
hint: "return stable lower-case semantic keys instead of paths or output indexes",
|
|
849
|
-
detail: { sourcePath, sourceKey }
|
|
850
|
-
};
|
|
820
|
+
text("scriptable-pack-fingerprint/2:");
|
|
821
|
+
visit(value);
|
|
822
|
+
return `sha256:${bytesToHex(hash.digest())}`;
|
|
851
823
|
}
|
|
852
|
-
function
|
|
824
|
+
function failure(sourceKey, expected, actual) {
|
|
853
825
|
return {
|
|
854
|
-
code: "
|
|
826
|
+
code: "mesh-bin-payload-invalid",
|
|
827
|
+
subject: "mesh-bin",
|
|
828
|
+
sourceKey,
|
|
855
829
|
expected,
|
|
856
|
-
|
|
857
|
-
|
|
830
|
+
actual,
|
|
831
|
+
recovery: "re-cook the source with its Meta sidecar through the build-time importer"
|
|
858
832
|
};
|
|
859
833
|
}
|
|
860
|
-
function
|
|
861
|
-
return {
|
|
862
|
-
code,
|
|
863
|
-
expected: code === "pack-output-reference-missing" ? "every output reference to resolve to a local or published AssetGuid" : "removed output GUIDs to have no incoming references",
|
|
864
|
-
hint: code === "pack-output-reference-missing" ? "build or publish the referenced Pack before verifying this output" : "migrate incoming references before publishing the topology change",
|
|
865
|
-
detail: { sourcePath, guids: [...guids].sort() }
|
|
866
|
-
};
|
|
834
|
+
function asAttributeMap(value) {
|
|
835
|
+
return value ?? {};
|
|
867
836
|
}
|
|
868
|
-
function
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
product
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
if (!Array.isArray(product.refs)) {
|
|
878
|
-
return outputValueError(
|
|
879
|
-
sourcePath,
|
|
880
|
-
sourceKey,
|
|
881
|
-
`producer ${producer.kind} to return a refs array`,
|
|
882
|
-
product.refs
|
|
883
|
-
);
|
|
884
|
-
}
|
|
885
|
-
if (!record(product.artifacts)) {
|
|
886
|
-
return outputValueError(
|
|
887
|
-
sourcePath,
|
|
888
|
-
sourceKey,
|
|
889
|
-
`producer ${producer.kind} to return an artifacts object`,
|
|
890
|
-
product.artifacts
|
|
891
|
-
);
|
|
892
|
-
}
|
|
893
|
-
const payload = product.payload;
|
|
894
|
-
if (!record(payload) || payload.kind !== producer.kind) {
|
|
895
|
-
return outputValueError(
|
|
896
|
-
sourcePath,
|
|
897
|
-
sourceKey,
|
|
898
|
-
`producer ${producer.kind} to return a matching payload kind`,
|
|
899
|
-
payload
|
|
837
|
+
function jsonValue(value) {
|
|
838
|
+
if (value instanceof Float32Array || value instanceof Uint16Array) return Array.from(value);
|
|
839
|
+
if (Array.isArray(value)) return value.map(jsonValue);
|
|
840
|
+
if (value !== null && typeof value === "object") {
|
|
841
|
+
return Object.fromEntries(
|
|
842
|
+
Object.entries(value).map(([key, nested]) => [key, jsonValue(nested)])
|
|
900
843
|
);
|
|
901
844
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
)
|
|
845
|
+
return value;
|
|
846
|
+
}
|
|
847
|
+
function refsMeta(payload, refs) {
|
|
848
|
+
const materialSlots = (payload.materialSlots ?? [{ slotName: "Default" }]).map(
|
|
849
|
+
(slot, slotIndex) => {
|
|
850
|
+
const defaultMaterial = slot.defaultMaterial;
|
|
851
|
+
let defaultMaterialRef;
|
|
852
|
+
if (defaultMaterial !== void 0) {
|
|
853
|
+
const guid = AssetGuid$1.format(defaultMaterial);
|
|
854
|
+
defaultMaterialRef = refs.findIndex((candidate) => candidate.toLowerCase() === guid);
|
|
855
|
+
if (defaultMaterialRef < 0) {
|
|
856
|
+
throw new Error(
|
|
857
|
+
`material slot ${slotIndex} default material ${guid} is absent from refs`
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return {
|
|
862
|
+
slotName: slot.slotName,
|
|
863
|
+
...slot.sourceKey === void 0 ? {} : { sourceKey: slot.sourceKey },
|
|
864
|
+
...defaultMaterialRef === void 0 ? {} : { defaultMaterialRef }
|
|
865
|
+
};
|
|
910
866
|
}
|
|
867
|
+
);
|
|
868
|
+
if (payload.lods !== void 0 && payload.lods.length > 7) {
|
|
869
|
+
throw new Error("MeshAsset LOD chain supports at most seven lower-detail levels");
|
|
911
870
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
871
|
+
let previousCoverage = 1;
|
|
872
|
+
const seenLodGuids = /* @__PURE__ */ new Set();
|
|
873
|
+
const lods = payload.lods?.map((lod, lodIndex) => {
|
|
874
|
+
const guid = AssetGuid$1.format(lod.mesh).toLowerCase();
|
|
875
|
+
const meshRef = refs.findIndex((candidate) => candidate.toLowerCase() === guid);
|
|
876
|
+
if (meshRef < 0) {
|
|
877
|
+
throw new Error(`LOD ${lodIndex} mesh ${guid} is absent from refs`);
|
|
878
|
+
}
|
|
879
|
+
if (seenLodGuids.has(guid)) {
|
|
880
|
+
throw new Error(`LOD ${lodIndex} mesh ${guid} is duplicated`);
|
|
881
|
+
}
|
|
882
|
+
if (!Number.isFinite(lod.screenCoverage) || lod.screenCoverage <= 0 || lod.screenCoverage > 1 || lod.screenCoverage >= previousCoverage) {
|
|
883
|
+
throw new Error(
|
|
884
|
+
`LOD ${lodIndex} screenCoverage must be finite, in (0, 1], and strictly decreasing`
|
|
919
885
|
);
|
|
920
886
|
}
|
|
887
|
+
seenLodGuids.add(guid);
|
|
888
|
+
previousCoverage = lod.screenCoverage;
|
|
889
|
+
return { meshRef, screenCoverage: lod.screenCoverage };
|
|
890
|
+
});
|
|
891
|
+
if (payload.lodHysteresis !== void 0 && (!Number.isFinite(payload.lodHysteresis) || payload.lodHysteresis < 0 || payload.lodHysteresis >= 1)) {
|
|
892
|
+
throw new Error("lodHysteresis must be finite and in [0, 1)");
|
|
921
893
|
}
|
|
922
|
-
return
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
|
|
894
|
+
return {
|
|
895
|
+
submeshes: payload.submeshes === void 0 || payload.submeshes.length === 0 ? [{ indexOffset: 0, indexCount: payload.indices?.length ?? 0, materialSlot: 0 }] : payload.submeshes,
|
|
896
|
+
materialSlots,
|
|
897
|
+
...payload.aabb === void 0 ? {} : { aabb: jsonValue(payload.aabb) },
|
|
898
|
+
...payload.morphTargets === void 0 ? {} : { morphTargets: jsonValue(payload.morphTargets) },
|
|
899
|
+
...payload.morphWeights === void 0 ? {} : { morphWeights: jsonValue(payload.morphWeights) },
|
|
900
|
+
...lods === void 0 ? {} : { lods },
|
|
901
|
+
...payload.lodHysteresis === void 0 ? {} : { lodHysteresis: payload.lodHysteresis }
|
|
902
|
+
};
|
|
929
903
|
}
|
|
930
|
-
|
|
931
|
-
const subjectPackageId = options.subjectPackageId ?? options.definition.packageId;
|
|
932
|
-
const availableGuids = normalizedGuidSet(options.availableGuids);
|
|
933
|
-
const observed = observedReader(options.assetSource);
|
|
934
|
-
let effectiveValues;
|
|
935
|
-
if ("parameters" in options.definition) {
|
|
936
|
-
const resolved = resolvePackParameterValues(
|
|
937
|
-
options.definition,
|
|
938
|
-
options.values ?? {},
|
|
939
|
-
options.inheritedValues
|
|
940
|
-
);
|
|
941
|
-
if (!resolved.ok) return resolved;
|
|
942
|
-
effectiveValues = resolved.value;
|
|
943
|
-
} else if (options.values !== void 0 && Object.keys(options.values).length > 0) {
|
|
944
|
-
return err(
|
|
945
|
-
outputValueError(
|
|
946
|
-
options.sourcePath,
|
|
947
|
-
"$.values",
|
|
948
|
-
"zero-parameter Packs to omit values and instance capabilities",
|
|
949
|
-
options.values
|
|
950
|
-
)
|
|
951
|
-
);
|
|
952
|
-
} else if (options.subjectPackageId !== void 0 && PackageId.format(options.subjectPackageId).toLowerCase() !== PackageId.format(options.definition.packageId).toLowerCase()) {
|
|
953
|
-
return err({
|
|
954
|
-
code: "pack-parent-has-no-parameters",
|
|
955
|
-
expected: "a ScriptablePack source with parameters for an independent instance packageId",
|
|
956
|
-
hint: "use clone for a zero-parameter Pack instead of building it as an instance",
|
|
957
|
-
detail: {
|
|
958
|
-
sourcePath: options.sourcePath,
|
|
959
|
-
rootPackageId: PackageId.format(options.definition.packageId),
|
|
960
|
-
subjectPackageId: PackageId.format(options.subjectPackageId)
|
|
961
|
-
}
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
|
-
let built;
|
|
904
|
+
function packMeshBinV4(payload, sourceKey, refs = []) {
|
|
965
905
|
try {
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
} catch (cause) {
|
|
970
|
-
return err(
|
|
971
|
-
new ImportError({
|
|
972
|
-
code: "import-internal-error",
|
|
973
|
-
expected: "Pack build to return a structured Result without throwing",
|
|
974
|
-
hint: "repair the authoring function and return err(...) for expected failures",
|
|
975
|
-
detail: {
|
|
976
|
-
reason: `${options.sourcePath}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
977
|
-
}
|
|
978
|
-
})
|
|
979
|
-
);
|
|
980
|
-
}
|
|
981
|
-
if (!record(built) || typeof built.ok !== "boolean") {
|
|
982
|
-
return err(
|
|
983
|
-
new ImportError({
|
|
984
|
-
code: "import-internal-error",
|
|
985
|
-
expected: "Pack build to return a Result object",
|
|
986
|
-
hint: "return ok(sourceKeyToAsset) or err(structuredError) from the authoring function",
|
|
987
|
-
detail: { reason: `${options.sourcePath}: malformed build result` }
|
|
988
|
-
})
|
|
989
|
-
);
|
|
990
|
-
}
|
|
991
|
-
if (!built.ok) {
|
|
992
|
-
if (errorCode(built.error) !== void 0) return err(built.error);
|
|
993
|
-
return err(
|
|
994
|
-
new ImportError({
|
|
995
|
-
code: "import-internal-error",
|
|
996
|
-
expected: "a structured Pack build error",
|
|
997
|
-
hint: "return an error carrying code, expected, hint and detail",
|
|
998
|
-
detail: { reason: `${options.sourcePath}: ${String(built.error)}` }
|
|
999
|
-
})
|
|
1000
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
if (!record(built.value)) {
|
|
1003
|
-
return err(
|
|
1004
|
-
outputValueError(
|
|
1005
|
-
options.sourcePath,
|
|
1006
|
-
"$",
|
|
1007
|
-
"build to return a sourceKey-to-Asset object",
|
|
1008
|
-
built.value
|
|
1009
|
-
)
|
|
1010
|
-
);
|
|
1011
|
-
}
|
|
1012
|
-
const imported = [];
|
|
1013
|
-
const stagedOutputs = [];
|
|
1014
|
-
const localGuids = /* @__PURE__ */ new Set();
|
|
1015
|
-
const materialCookerRegistry = new NativeCookerRegistry();
|
|
1016
|
-
for (const cooker of options.cookers ?? []) materialCookerRegistry.register(cooker);
|
|
1017
|
-
const nativeFingerprints = /* @__PURE__ */ new Map();
|
|
1018
|
-
for (const sourceKey of Object.keys(built.value).sort()) {
|
|
1019
|
-
if (!isValidPackSourceKey(sourceKey))
|
|
1020
|
-
return err(sourceKeyFailure(options.sourcePath, sourceKey));
|
|
1021
|
-
const asset = built.value[sourceKey];
|
|
1022
|
-
if (!isAsset(asset)) {
|
|
906
|
+
const vertices = payload.vertices;
|
|
907
|
+
const indices = payload.indices;
|
|
908
|
+
if (!(vertices instanceof Float32Array)) {
|
|
1023
909
|
return err(
|
|
1024
|
-
|
|
1025
|
-
options.sourcePath,
|
|
1026
|
-
sourceKey,
|
|
1027
|
-
"a concrete Asset with a supported kind",
|
|
1028
|
-
asset
|
|
1029
|
-
)
|
|
910
|
+
failure(sourceKey, "Float32Array interleaved vertices", "vertices is not Float32Array")
|
|
1030
911
|
);
|
|
1031
912
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
code: "pack-guid-collision",
|
|
1037
|
-
expected: "derived output GUIDs to be unique in the global source index",
|
|
1038
|
-
hint: "change the colliding packageId or repair the source index before publishing",
|
|
1039
|
-
detail: { sourcePath: options.sourcePath, guid }
|
|
1040
|
-
});
|
|
913
|
+
if (indices !== void 0 && !(indices instanceof Uint16Array || indices instanceof Uint32Array)) {
|
|
914
|
+
return err(
|
|
915
|
+
failure(sourceKey, "Uint16Array or Uint32Array indices", "indices has an unsupported type")
|
|
916
|
+
);
|
|
1041
917
|
}
|
|
1042
|
-
|
|
1043
|
-
const
|
|
1044
|
-
if (
|
|
918
|
+
const attributes = asAttributeMap(payload.attributes);
|
|
919
|
+
const projection = deriveVertexLayoutProjection(attributes);
|
|
920
|
+
if (projection.attributes.length === 0 || projection.arrayStride === 0) {
|
|
1045
921
|
return err(
|
|
1046
|
-
|
|
1047
|
-
options.sourcePath,
|
|
922
|
+
failure(
|
|
1048
923
|
sourceKey,
|
|
1049
|
-
|
|
1050
|
-
|
|
924
|
+
"a non-empty canonical geometry projection",
|
|
925
|
+
"projection has no attributes"
|
|
1051
926
|
)
|
|
1052
927
|
);
|
|
1053
928
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
produced = await producer.produce({ guid, sourceKey, asset });
|
|
1057
|
-
} catch (cause) {
|
|
929
|
+
const vertexCount = payload.vertexCount ?? vertices.byteLength / projection.arrayStride;
|
|
930
|
+
if (!Number.isSafeInteger(vertexCount) || vertexCount < 0) {
|
|
1058
931
|
return err(
|
|
1059
|
-
|
|
1060
|
-
code: "import-internal-error",
|
|
1061
|
-
expected: `producer ${producer.kind} to return a structured Result without throwing`,
|
|
1062
|
-
hint: "repair the output producer and return err(...) for expected failures",
|
|
1063
|
-
detail: {
|
|
1064
|
-
reason: `${options.sourcePath}:${sourceKey}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
1065
|
-
}
|
|
1066
|
-
})
|
|
932
|
+
failure(sourceKey, "a non-negative safe vertex cardinality", `vertexCount=${vertexCount}`)
|
|
1067
933
|
);
|
|
1068
934
|
}
|
|
1069
|
-
if (
|
|
935
|
+
if (vertices.byteLength !== vertexCount * projection.arrayStride) {
|
|
1070
936
|
return err(
|
|
1071
|
-
|
|
1072
|
-
options.sourcePath,
|
|
937
|
+
failure(
|
|
1073
938
|
sourceKey,
|
|
1074
|
-
`
|
|
1075
|
-
|
|
939
|
+
`vertices.byteLength=${vertexCount * projection.arrayStride}`,
|
|
940
|
+
`vertices.byteLength=${vertices.byteLength}; stride=${projection.arrayStride}`
|
|
1076
941
|
)
|
|
1077
942
|
);
|
|
1078
943
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
hint: "return an error carrying code, expected, hint and detail",
|
|
1086
|
-
detail: { reason: `${options.sourcePath}:${sourceKey}: ${String(produced.error)}` }
|
|
1087
|
-
})
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
const invalidProduct2 = productError(options.sourcePath, sourceKey, producer, produced.value);
|
|
1091
|
-
if (invalidProduct2 !== void 0) return err(invalidProduct2);
|
|
1092
|
-
const product2 = produced.value;
|
|
1093
|
-
let payload = product2.payload;
|
|
1094
|
-
let artifacts2 = product2.artifacts;
|
|
1095
|
-
if (needsMaterialCook(asset) && materialCookerRegistry.get("material") !== void 0) {
|
|
1096
|
-
const cooked = await materialCookerRegistry.runDraft("material", {
|
|
1097
|
-
guid,
|
|
1098
|
-
source: asset,
|
|
1099
|
-
sourceKey,
|
|
1100
|
-
sourcePath: options.sourcePath,
|
|
1101
|
-
refs: product2.refs.map((reference) => reference.guid)
|
|
1102
|
-
});
|
|
1103
|
-
if (!cooked.ok) return err(cooked.error);
|
|
1104
|
-
if (cooked.value.guid.toLowerCase() !== normalizedGuid) {
|
|
1105
|
-
return err({
|
|
1106
|
-
code: "pack-source-output-invalid",
|
|
1107
|
-
expected: "the authored material cooker to preserve the derived AssetGuid",
|
|
1108
|
-
hint: "repair the native material cooker output GUID and rebuild the Pack",
|
|
1109
|
-
detail: {
|
|
1110
|
-
sourcePath: options.sourcePath,
|
|
944
|
+
for (const attribute of projection.attributes) {
|
|
945
|
+
const value = attributes[attribute.key];
|
|
946
|
+
const components = attribute.byteLength / (attribute.format === "uint16x4" ? 2 : 4);
|
|
947
|
+
if (value === void 0 || !(value instanceof Float32Array) && !(value instanceof Uint16Array) || value.length !== vertexCount * components) {
|
|
948
|
+
return err(
|
|
949
|
+
failure(
|
|
1111
950
|
sourceKey,
|
|
1112
|
-
|
|
1113
|
-
|
|
951
|
+
`${attribute.key} cardinality=${vertexCount * components}`,
|
|
952
|
+
`${attribute.key} cardinality=${value?.byteLength ?? "missing"}`
|
|
953
|
+
)
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
const interleaved = new Uint8Array(vertexCount * projection.arrayStride);
|
|
958
|
+
const interleavedView = new DataView(interleaved.buffer);
|
|
959
|
+
for (const attribute of projection.attributes) {
|
|
960
|
+
const value = attributes[attribute.key];
|
|
961
|
+
if (value === void 0) continue;
|
|
962
|
+
const components = attribute.byteLength / (attribute.format === "uint16x4" ? 2 : 4);
|
|
963
|
+
for (let vertex = 0; vertex < vertexCount; vertex++) {
|
|
964
|
+
for (let component = 0; component < components; component++) {
|
|
965
|
+
const sourceIndex = vertex * components + component;
|
|
966
|
+
const targetOffset = vertex * projection.arrayStride + attribute.offset + component * (attribute.format === "uint16x4" ? 2 : 4);
|
|
967
|
+
if (attribute.format === "uint16x4") {
|
|
968
|
+
interleavedView.setUint16(targetOffset, value[sourceIndex] ?? 0, true);
|
|
969
|
+
} else {
|
|
970
|
+
interleavedView.setFloat32(
|
|
971
|
+
targetOffset,
|
|
972
|
+
value[sourceIndex] ?? 0,
|
|
973
|
+
true
|
|
974
|
+
);
|
|
1114
975
|
}
|
|
1115
|
-
}
|
|
976
|
+
}
|
|
1116
977
|
}
|
|
1117
|
-
payload = cooked.value.payload;
|
|
1118
|
-
artifacts2 = cooked.value.artifacts;
|
|
1119
|
-
nativeFingerprints.set(sourceKey, cooked.value.inputFingerprint);
|
|
1120
978
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
digest:
|
|
1133
|
-
|
|
979
|
+
const indexCount = indices?.length ?? 0;
|
|
980
|
+
const indexWidth = indices === void 0 || indexCount === 0 ? 0 : indices.BYTES_PER_ELEMENT;
|
|
981
|
+
const indexBytes = indexCount * indexWidth;
|
|
982
|
+
if (!Number.isSafeInteger(indexBytes) || indexBytes > 4294967295) {
|
|
983
|
+
return err(failure(sourceKey, "safe index payload byte length", `indexBytes=${indexBytes}`));
|
|
984
|
+
}
|
|
985
|
+
const meta = new TextEncoder().encode(JSON.stringify(refsMeta(payload, refs)));
|
|
986
|
+
const header = {
|
|
987
|
+
version: 4,
|
|
988
|
+
projectionVersion: projection.schemaVersion,
|
|
989
|
+
mask: projection.mask,
|
|
990
|
+
digest: projection.digest,
|
|
991
|
+
stride: projection.arrayStride,
|
|
992
|
+
vertexCount,
|
|
993
|
+
vertexBytes: interleaved.byteLength,
|
|
994
|
+
indexCount,
|
|
995
|
+
indexWidth,
|
|
996
|
+
indexBytes,
|
|
997
|
+
jsonBytes: meta.byteLength
|
|
998
|
+
};
|
|
999
|
+
const total = MESH_BIN_HEADER_V4_BYTES + interleaved.byteLength + indexBytes + meta.byteLength;
|
|
1000
|
+
if (!Number.isSafeInteger(total) || total > 4294967295) {
|
|
1001
|
+
return err(failure(sourceKey, "safe mesh binary byte length", `total=${total}`));
|
|
1002
|
+
}
|
|
1003
|
+
const out = new Uint8Array(total);
|
|
1004
|
+
writeMeshBinHeader(header, out);
|
|
1005
|
+
let offset = MESH_BIN_HEADER_V4_BYTES;
|
|
1006
|
+
out.set(interleaved, offset);
|
|
1007
|
+
offset += interleaved.byteLength;
|
|
1008
|
+
if (indices !== void 0 && indexBytes > 0) {
|
|
1009
|
+
out.set(new Uint8Array(indices.buffer, indices.byteOffset, indices.byteLength), offset);
|
|
1010
|
+
offset += indexBytes;
|
|
1011
|
+
}
|
|
1012
|
+
out.set(meta, offset);
|
|
1013
|
+
return ok(out);
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
return err(
|
|
1016
|
+
failure(
|
|
1017
|
+
sourceKey,
|
|
1018
|
+
"valid canonical mesh payload",
|
|
1019
|
+
error instanceof Error ? error.message : String(error)
|
|
1020
|
+
)
|
|
1021
|
+
);
|
|
1134
1022
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// src/scriptable-pack.ts
|
|
1026
|
+
var AssetOutputProducerRegistry = class {
|
|
1027
|
+
producers = /* @__PURE__ */ new Map();
|
|
1028
|
+
register(producer) {
|
|
1029
|
+
if (producer.kind.trim().length === 0 || producer.version.trim().length === 0) {
|
|
1030
|
+
throw new TypeError("Pack output producer kind and version must be non-empty");
|
|
1031
|
+
}
|
|
1032
|
+
if (typeof producer.produce !== "function") {
|
|
1033
|
+
throw new TypeError(`Pack output producer ${producer.kind} must expose produce`);
|
|
1140
1034
|
}
|
|
1035
|
+
this.producers.set(producer.kind, producer);
|
|
1141
1036
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1037
|
+
get(kind) {
|
|
1038
|
+
return this.producers.get(kind);
|
|
1039
|
+
}
|
|
1040
|
+
versions() {
|
|
1041
|
+
return Object.fromEntries(
|
|
1042
|
+
[...this.producers.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([kind, producer]) => [kind, producer.version])
|
|
1145
1043
|
);
|
|
1146
|
-
if (missing.length > 0)
|
|
1147
|
-
return err(referenceError("pack-output-reference-missing", options.sourcePath, missing));
|
|
1148
1044
|
}
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
packageId: PackageId.format(subjectPackageId),
|
|
1159
|
-
sourcePath: options.sourcePath,
|
|
1160
|
-
sourceClosure: options.sourceClosure ?? [],
|
|
1161
|
-
values: effectiveValues,
|
|
1162
|
-
externalEvidence: externalEvidence.map(({ guid, usage, digest: evidenceDigest }) => ({
|
|
1163
|
-
guid,
|
|
1164
|
-
usage,
|
|
1165
|
-
digest: evidenceDigest
|
|
1166
|
-
})),
|
|
1167
|
-
authoringContractVersion: options.authoringContractVersion ?? "scriptable-pack/1",
|
|
1168
|
-
producerVersions: options.outputs.versions()
|
|
1169
|
-
});
|
|
1170
|
-
const inputFingerprint = nativeFingerprints.size === 0 ? authoredInputFingerprint : await digest({
|
|
1171
|
-
sourceRevision: authoredInputFingerprint,
|
|
1172
|
-
nativeCookers: [...nativeFingerprints.entries()].sort(
|
|
1173
|
-
([left], [right]) => left.localeCompare(right)
|
|
1174
|
-
)
|
|
1175
|
-
});
|
|
1176
|
-
const refs = imported.flatMap((asset) => asset.refs);
|
|
1177
|
-
const artifacts = Object.fromEntries(
|
|
1178
|
-
imported.flatMap(
|
|
1179
|
-
(asset) => Object.entries(asset.artifacts).map(([key, artifact]) => [`${asset.guid}/${key}`, artifact])
|
|
1180
|
-
)
|
|
1181
|
-
);
|
|
1182
|
-
const product = createImportProduct({
|
|
1183
|
-
assets: imported,
|
|
1184
|
-
sourceDependencies: (options.sourceClosure ?? []).map((entry) => entry.path),
|
|
1185
|
-
refs,
|
|
1186
|
-
artifacts,
|
|
1187
|
-
receipts: imported.map((asset) => ({
|
|
1188
|
-
guid: asset.guid,
|
|
1189
|
-
origin: "authoredPack",
|
|
1190
|
-
status: "succeeded",
|
|
1191
|
-
inputFingerprint
|
|
1192
|
-
})),
|
|
1193
|
-
diagnostics: [],
|
|
1194
|
-
sourceRevision: inputFingerprint,
|
|
1195
|
-
sourceKey: options.sourcePath
|
|
1196
|
-
});
|
|
1197
|
-
if (!product.ok) return err(product.error);
|
|
1198
|
-
return ok({
|
|
1199
|
-
product: product.value,
|
|
1200
|
-
stagedOutputs,
|
|
1201
|
-
externalEvidence,
|
|
1202
|
-
inputFingerprint,
|
|
1203
|
-
...options.publication === void 0 ? {} : { publication: options.publication }
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
// src/scriptable-pack-output-producers.ts
|
|
1048
|
+
function producerError(input, reason) {
|
|
1049
|
+
return new ImportError({
|
|
1050
|
+
code: "import-internal-error",
|
|
1051
|
+
expected: `ScriptablePack ${input.asset.kind} output ${input.sourceKey} to satisfy its domain producer contract`,
|
|
1052
|
+
hint: "fix the generated Asset payload and rebuild the ScriptablePack",
|
|
1053
|
+
detail: { reason: reason instanceof Error ? reason.message : String(reason) }
|
|
1204
1054
|
});
|
|
1205
1055
|
}
|
|
1206
|
-
function
|
|
1056
|
+
function formatGuid(value) {
|
|
1057
|
+
if (typeof value === "string") {
|
|
1058
|
+
const parsed = AssetGuid$1.parse(value);
|
|
1059
|
+
if (!parsed.ok) throw parsed.error;
|
|
1060
|
+
return AssetGuid$1.format(parsed.value);
|
|
1061
|
+
}
|
|
1062
|
+
return AssetGuid$1.format(value);
|
|
1063
|
+
}
|
|
1064
|
+
function canonical(value) {
|
|
1065
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
1066
|
+
if (value !== null && typeof value === "object") {
|
|
1067
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`).join(",")}}`;
|
|
1068
|
+
}
|
|
1069
|
+
return JSON.stringify(value) ?? "null";
|
|
1070
|
+
}
|
|
1071
|
+
function particleProgramArtifact(effect) {
|
|
1072
|
+
const artifactProgram = { format: effect.program.format, emitters: effect.program.emitters };
|
|
1073
|
+
const bytes2 = new TextEncoder().encode(canonical(artifactProgram));
|
|
1074
|
+
const fingerprint = `sha256:${bytesToHex(sha256(bytes2))}`;
|
|
1207
1075
|
return {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
if (local !== void 0) {
|
|
1212
|
-
return ok({
|
|
1213
|
-
asset: clone(local.asset),
|
|
1214
|
-
generation: 1,
|
|
1215
|
-
digest: local.digest ?? "sha256:staged"
|
|
1216
|
-
});
|
|
1217
|
-
}
|
|
1218
|
-
if (fallback === void 0) {
|
|
1219
|
-
return err({
|
|
1220
|
-
code: "asset-not-found",
|
|
1221
|
-
expected: "a staged or published content dependency",
|
|
1222
|
-
hint: "wait for the dependency subject to materialize",
|
|
1223
|
-
detail: { guid: key }
|
|
1224
|
-
});
|
|
1225
|
-
}
|
|
1226
|
-
return fallback.readByGuid(guid);
|
|
1227
|
-
}
|
|
1076
|
+
program: { ...effect.program, fingerprint },
|
|
1077
|
+
bytes: bytes2,
|
|
1078
|
+
fingerprint
|
|
1228
1079
|
};
|
|
1229
1080
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
const
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
...BUILTIN_MESH_ASSETS.map((asset) => asset.guid.toLowerCase())
|
|
1242
|
-
]);
|
|
1243
|
-
const maxPasses = options.maxPasses ?? Math.max(1, options.subjects.length + 1);
|
|
1244
|
-
let iterations = 0;
|
|
1245
|
-
for (; iterations < maxPasses && pending.size > 0; iterations += 1) {
|
|
1246
|
-
let progress = false;
|
|
1247
|
-
const waiting = /* @__PURE__ */ new Set();
|
|
1248
|
-
for (const [key, subject] of pending) {
|
|
1249
|
-
const result = await buildScriptablePack({
|
|
1250
|
-
definition: subject.definition,
|
|
1251
|
-
sourcePath: subject.sourcePath,
|
|
1252
|
-
...subject.subjectPackageId === void 0 ? {} : { subjectPackageId: subject.subjectPackageId },
|
|
1253
|
-
...subject.values === void 0 ? {} : { values: subject.values },
|
|
1254
|
-
...subject.inheritedValues === void 0 ? {} : { inheritedValues: subject.inheritedValues },
|
|
1255
|
-
...subject.sourceClosure === void 0 ? {} : { sourceClosure: subject.sourceClosure },
|
|
1256
|
-
outputs: options.outputs,
|
|
1257
|
-
...options.cookers === void 0 ? {} : { cookers: options.cookers },
|
|
1258
|
-
assetSource: stagedSource(staged, options.assetSource),
|
|
1259
|
-
availableGuids: /* @__PURE__ */ new Set([...availableGuids, ...staged.keys()]),
|
|
1260
|
-
deferReferenceValidation: true
|
|
1261
|
-
});
|
|
1262
|
-
if (result.ok) {
|
|
1263
|
-
results.set(key, result.value);
|
|
1264
|
-
for (const output of result.value.stagedOutputs)
|
|
1265
|
-
staged.set(AssetGuid.format(output.guid).toLowerCase(), output);
|
|
1266
|
-
pending.delete(key);
|
|
1267
|
-
progress = true;
|
|
1268
|
-
continue;
|
|
1269
|
-
}
|
|
1270
|
-
if (errorCode(result.error) === "asset-not-found") {
|
|
1271
|
-
const detail = record(result.error) && record(result.error.detail) ? result.error.detail : void 0;
|
|
1272
|
-
const guid = detail !== void 0 && typeof detail.guid === "string" ? detail.guid : void 0;
|
|
1273
|
-
if (guid !== void 0) waiting.add(guid.toLowerCase());
|
|
1274
|
-
continue;
|
|
1275
|
-
}
|
|
1276
|
-
return result;
|
|
1081
|
+
function materialProduct(input) {
|
|
1082
|
+
if (input.asset.kind !== "material") throw new TypeError("expected MaterialAsset");
|
|
1083
|
+
const material = input.asset;
|
|
1084
|
+
const refs = [];
|
|
1085
|
+
const addRef = (guid, sourceField) => {
|
|
1086
|
+
refs.push({ guid: formatGuid(guid), sourceField });
|
|
1087
|
+
return refs.length - 1;
|
|
1088
|
+
};
|
|
1089
|
+
const addMaterialRef = (value, sourceField) => {
|
|
1090
|
+
if (typeof value === "number") {
|
|
1091
|
+
throw new TypeError("material texture references must be GUIDs, not runtime handles");
|
|
1277
1092
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1093
|
+
return addRef(value, sourceField);
|
|
1094
|
+
};
|
|
1095
|
+
const textureFields = material.parameters === void 0 ? new Set(MATERIAL_TEXTURE_SLOTS) : new Set(
|
|
1096
|
+
material.parameters.filter(
|
|
1097
|
+
(parameter) => parameter.type === "texture" || parameter.type === "texture_cube"
|
|
1098
|
+
).map((parameter) => parameter.name)
|
|
1099
|
+
);
|
|
1100
|
+
const parent = material.parent === void 0 ? void 0 : addRef(material.parent, { fieldName: "parent" });
|
|
1101
|
+
const values = {};
|
|
1102
|
+
for (const fieldName of Object.keys(material.values ?? {}).sort()) {
|
|
1103
|
+
const value = material.values?.[fieldName];
|
|
1104
|
+
if (typeof value === "string" && textureFields.has(fieldName)) {
|
|
1105
|
+
values[fieldName] = {
|
|
1106
|
+
texture: addMaterialRef(value, { componentName: "<material>", fieldName })
|
|
1107
|
+
};
|
|
1108
|
+
} else if (value !== null && typeof value === "object" && !Array.isArray(value) && "texture" in value) {
|
|
1109
|
+
const texture = value;
|
|
1110
|
+
values[fieldName] = {
|
|
1111
|
+
...texture,
|
|
1112
|
+
texture: addMaterialRef(texture.texture, { componentName: "<material>", fieldName }),
|
|
1113
|
+
...texture.sampler === void 0 ? {} : {
|
|
1114
|
+
sampler: addMaterialRef(texture.sampler, {
|
|
1115
|
+
componentName: "<material>",
|
|
1116
|
+
fieldName: `${fieldName}.sampler`
|
|
1117
|
+
})
|
|
1288
1118
|
}
|
|
1289
|
-
}
|
|
1119
|
+
};
|
|
1120
|
+
} else {
|
|
1121
|
+
values[fieldName] = value;
|
|
1290
1122
|
}
|
|
1291
1123
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1124
|
+
const { parent: _parent, values: _values, ...materialShape } = material;
|
|
1125
|
+
return {
|
|
1126
|
+
payload: {
|
|
1127
|
+
...materialShape,
|
|
1128
|
+
...parent === void 0 ? {} : { parent },
|
|
1129
|
+
...material.values === void 0 ? {} : { values }
|
|
1130
|
+
},
|
|
1131
|
+
refs,
|
|
1132
|
+
artifacts: {}
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
function meshProduct(input) {
|
|
1136
|
+
if (input.asset.kind !== "mesh") throw new TypeError("expected MeshAsset");
|
|
1137
|
+
const mesh = input.asset;
|
|
1138
|
+
const refs = [];
|
|
1139
|
+
for (let slotIndex = 0; slotIndex < mesh.materialSlots.length; slotIndex++) {
|
|
1140
|
+
const defaultMaterial = mesh.materialSlots[slotIndex]?.defaultMaterial;
|
|
1141
|
+
if (defaultMaterial === void 0) continue;
|
|
1142
|
+
refs.push({
|
|
1143
|
+
guid: formatGuid(defaultMaterial),
|
|
1144
|
+
sourceField: { fieldName: "materialSlots", arrayIndex: slotIndex }
|
|
1302
1145
|
});
|
|
1303
1146
|
}
|
|
1304
|
-
const
|
|
1305
|
-
const
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
const guid = reference.guid.toLowerCase();
|
|
1311
|
-
if (!knownGuids.has(guid) && !contentReads.has(guid)) missingReferences.add(guid);
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
if (missingReferences.size > 0) {
|
|
1316
|
-
return err(
|
|
1317
|
-
referenceError("pack-output-reference-missing", "worklist", [...missingReferences].sort())
|
|
1318
|
-
);
|
|
1147
|
+
const seenRefs = new Set(refs.map((reference) => reference.guid.toLowerCase()));
|
|
1148
|
+
for (const [lodIndex, lod] of (mesh.lods ?? []).entries()) {
|
|
1149
|
+
const guid = formatGuid(lod.mesh);
|
|
1150
|
+
if (seenRefs.has(guid.toLowerCase())) continue;
|
|
1151
|
+
seenRefs.add(guid.toLowerCase());
|
|
1152
|
+
refs.push({ guid, sourceField: { fieldName: "lods", arrayIndex: lodIndex } });
|
|
1319
1153
|
}
|
|
1320
|
-
const removed = [...options.incomingRefs?.keys() ?? []].filter(
|
|
1321
|
-
(guid) => !staged.has(guid.toLowerCase())
|
|
1322
|
-
);
|
|
1323
|
-
const referencedRemoved = removed.filter(
|
|
1324
|
-
(guid) => (options.incomingRefs?.get(guid) ?? []).length > 0
|
|
1325
|
-
);
|
|
1326
|
-
if (referencedRemoved.length > 0)
|
|
1327
|
-
return err(referenceError("pack-output-reference-conflict", "worklist", referencedRemoved));
|
|
1328
|
-
return ok({
|
|
1329
|
-
products: [...results.values()].map((result) => result.product),
|
|
1330
|
-
buildProducts: [...results.values()],
|
|
1331
|
-
stagedOutputs: [...staged.values()],
|
|
1332
|
-
iterations: pending.size === 0 && options.subjects.length > 0 ? iterations + 1 : iterations
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
function failure(sourceKey, expected, actual) {
|
|
1336
1154
|
return {
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1155
|
+
payload: mesh,
|
|
1156
|
+
refs,
|
|
1157
|
+
artifacts: {
|
|
1158
|
+
body: {
|
|
1159
|
+
mediaType: "application/x-forgeax-mesh",
|
|
1160
|
+
assetCodec: { name: "mesh-binary", version: "4" },
|
|
1161
|
+
bytes: (() => {
|
|
1162
|
+
const packed = packMeshBinV4(
|
|
1163
|
+
mesh,
|
|
1164
|
+
input.sourceKey,
|
|
1165
|
+
refs.map((reference) => reference.guid)
|
|
1166
|
+
);
|
|
1167
|
+
if (!packed.ok) {
|
|
1168
|
+
throw packed.error;
|
|
1169
|
+
}
|
|
1170
|
+
return packed.value;
|
|
1171
|
+
})()
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1343
1174
|
};
|
|
1344
1175
|
}
|
|
1345
|
-
function
|
|
1346
|
-
|
|
1176
|
+
function sceneProduct(input, components) {
|
|
1177
|
+
if (input.asset.kind !== "scene") throw new TypeError("expected SceneAsset");
|
|
1178
|
+
const externalized = externalizeSceneAsset(input.asset, (componentName) => {
|
|
1179
|
+
const schema = components.get(componentName);
|
|
1180
|
+
if (schema === void 0) {
|
|
1181
|
+
throw new TypeError(
|
|
1182
|
+
`ScriptablePack scene component ${componentName} is missing from sceneComponents`
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
return schema;
|
|
1186
|
+
});
|
|
1187
|
+
if (!externalized.ok) throw new TypeError(`scene field ${externalized.error.field} is invalid`);
|
|
1188
|
+
return {
|
|
1189
|
+
payload: { kind: "scene", ...externalized.value.payload },
|
|
1190
|
+
refs: externalized.value.refs,
|
|
1191
|
+
artifacts: {}
|
|
1192
|
+
};
|
|
1347
1193
|
}
|
|
1348
|
-
function
|
|
1349
|
-
if (
|
|
1350
|
-
if (Array.isArray(value)) return value.
|
|
1194
|
+
function containsAssetGuid(value) {
|
|
1195
|
+
if (typeof value === "string") return AssetGuid$1.parse(value).ok;
|
|
1196
|
+
if (Array.isArray(value)) return value.some(containsAssetGuid);
|
|
1351
1197
|
if (value !== null && typeof value === "object") {
|
|
1352
|
-
return Object.
|
|
1353
|
-
Object.entries(value).map(([key, nested]) => [key, jsonValue(nested)])
|
|
1354
|
-
);
|
|
1198
|
+
return Object.values(value).some(containsAssetGuid);
|
|
1355
1199
|
}
|
|
1356
|
-
return
|
|
1200
|
+
return false;
|
|
1357
1201
|
}
|
|
1358
|
-
function
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
let defaultMaterialRef;
|
|
1363
|
-
if (defaultMaterial !== void 0) {
|
|
1364
|
-
const guid = AssetGuid$1.format(defaultMaterial);
|
|
1365
|
-
defaultMaterialRef = refs.findIndex((candidate) => candidate.toLowerCase() === guid);
|
|
1366
|
-
if (defaultMaterialRef < 0) {
|
|
1367
|
-
throw new Error(
|
|
1368
|
-
`material slot ${slotIndex} default material ${guid} is absent from refs`
|
|
1369
|
-
);
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
return {
|
|
1373
|
-
slotName: slot.slotName,
|
|
1374
|
-
...slot.sourceKey === void 0 ? {} : { sourceKey: slot.sourceKey },
|
|
1375
|
-
...defaultMaterialRef === void 0 ? {} : { defaultMaterialRef }
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
);
|
|
1379
|
-
if (payload.lods !== void 0 && payload.lods.length > 7) {
|
|
1380
|
-
throw new Error("MeshAsset LOD chain supports at most seven lower-detail levels");
|
|
1202
|
+
function preExternalizedSceneProduct(input) {
|
|
1203
|
+
if (input.asset.kind !== "scene") throw new TypeError("expected SceneAsset");
|
|
1204
|
+
if (containsAssetGuid(input.asset)) {
|
|
1205
|
+
throw new TypeError("direct scene payload must use explicit refs with runtime indices");
|
|
1381
1206
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1207
|
+
return {
|
|
1208
|
+
payload: { ...input.asset, kind: "scene" },
|
|
1209
|
+
refs: [],
|
|
1210
|
+
artifacts: {}
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
function createSafeProducer(kind, version, product) {
|
|
1214
|
+
return {
|
|
1215
|
+
kind,
|
|
1216
|
+
version,
|
|
1217
|
+
produce(input) {
|
|
1218
|
+
try {
|
|
1219
|
+
return ok(product(input));
|
|
1220
|
+
} catch (error) {
|
|
1221
|
+
return err(producerError(input, error));
|
|
1222
|
+
}
|
|
1397
1223
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
function jsonArtifact(value) {
|
|
1227
|
+
return {
|
|
1228
|
+
mediaType: "application/json",
|
|
1229
|
+
assetCodec: { name: "forgeax-json", version: "1" },
|
|
1230
|
+
bytes: new TextEncoder().encode(JSON.stringify(value))
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
function ref(guid, fieldName, arrayIndex) {
|
|
1234
|
+
return {
|
|
1235
|
+
guid: formatGuid(guid),
|
|
1236
|
+
sourceField: { fieldName, ...arrayIndex === void 0 ? {} : { arrayIndex } }
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
function bytes(value) {
|
|
1240
|
+
return Uint8Array.from(value);
|
|
1241
|
+
}
|
|
1242
|
+
function textureProduct(input) {
|
|
1243
|
+
if (input.asset.kind !== "texture") throw new TypeError("expected TextureAsset");
|
|
1244
|
+
const texture = input.asset;
|
|
1245
|
+
const layout = deriveTextureLayout({
|
|
1246
|
+
shape: texture.shape,
|
|
1247
|
+
format: texture.format,
|
|
1248
|
+
mips: texture.mips,
|
|
1249
|
+
actualByteLength: texture.data.byteLength,
|
|
1250
|
+
order: "mip-major,image-major,row-major"
|
|
1401
1251
|
});
|
|
1402
|
-
if (
|
|
1403
|
-
|
|
1252
|
+
if (!layout.ok) {
|
|
1253
|
+
const expectedBytes = layout.error.code === "texture-packing-invalid" ? layout.error.detail.expectedBytes : texture.data.byteLength;
|
|
1254
|
+
const actualBytes = layout.error.code === "texture-packing-invalid" ? layout.error.detail.actualBytes : texture.data.byteLength;
|
|
1255
|
+
throw new TypeError(
|
|
1256
|
+
`texture data is not canonical: expected ${expectedBytes} bytes, got ${actualBytes}`
|
|
1257
|
+
);
|
|
1404
1258
|
}
|
|
1405
1259
|
return {
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1260
|
+
payload: texture,
|
|
1261
|
+
refs: [],
|
|
1262
|
+
artifacts: {
|
|
1263
|
+
body: {
|
|
1264
|
+
mediaType: texture.format === "r8unorm" ? "application/x-forgeax-r8" : `application/x-forgeax-${texture.format}`,
|
|
1265
|
+
assetCodec: { name: texture.format, version: "1" },
|
|
1266
|
+
bytes: bytes(texture.data)
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1413
1269
|
};
|
|
1414
1270
|
}
|
|
1415
|
-
function
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1271
|
+
function ordinaryPodProduct(input) {
|
|
1272
|
+
const asset = input.asset;
|
|
1273
|
+
switch (asset.kind) {
|
|
1274
|
+
case "texture":
|
|
1275
|
+
return textureProduct(input);
|
|
1276
|
+
case "equirect": {
|
|
1277
|
+
const equirect = asset;
|
|
1278
|
+
return {
|
|
1279
|
+
payload: equirect,
|
|
1280
|
+
refs: [],
|
|
1281
|
+
artifacts: {
|
|
1282
|
+
body: {
|
|
1283
|
+
mediaType: "image/raw",
|
|
1284
|
+
assetCodec: { name: "raw-image", version: "1" },
|
|
1285
|
+
bytes: bytes(equirect.data)
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1423
1289
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
);
|
|
1290
|
+
case "sampler": {
|
|
1291
|
+
const sampler = asset;
|
|
1292
|
+
return { payload: sampler, refs: [], artifacts: { body: jsonArtifact(sampler) } };
|
|
1428
1293
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1294
|
+
case "font": {
|
|
1295
|
+
const font = asset;
|
|
1296
|
+
const atlas = ref(font.atlas, "atlas");
|
|
1297
|
+
const sampler = ref(font.sampler, "sampler");
|
|
1298
|
+
return {
|
|
1299
|
+
payload: {
|
|
1300
|
+
kind: font.kind,
|
|
1301
|
+
glyphs: font.glyphs,
|
|
1302
|
+
common: font.common,
|
|
1303
|
+
atlasGuid: atlas.guid,
|
|
1304
|
+
samplerGuid: sampler.guid
|
|
1305
|
+
},
|
|
1306
|
+
refs: [atlas, sampler],
|
|
1307
|
+
artifacts: { body: jsonArtifact(font) }
|
|
1308
|
+
};
|
|
1439
1309
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
return
|
|
1443
|
-
|
|
1444
|
-
|
|
1310
|
+
case "render-pipeline": {
|
|
1311
|
+
const pipeline = asset;
|
|
1312
|
+
return {
|
|
1313
|
+
payload: pipeline,
|
|
1314
|
+
refs: [],
|
|
1315
|
+
artifacts: { body: jsonArtifact(pipeline) }
|
|
1316
|
+
};
|
|
1445
1317
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1318
|
+
case "tileset": {
|
|
1319
|
+
const tileset = asset;
|
|
1320
|
+
const refs = tileset.atlases.map((atlas, index) => {
|
|
1321
|
+
const parsed = AssetGuid$1.parse(atlas);
|
|
1322
|
+
if (!parsed.ok) throw parsed.error;
|
|
1323
|
+
return ref(parsed.value, "atlases", index);
|
|
1324
|
+
});
|
|
1325
|
+
return {
|
|
1326
|
+
payload: { ...tileset, atlases: refs.map((_entry, index) => index) },
|
|
1327
|
+
refs,
|
|
1328
|
+
artifacts: { body: jsonArtifact(tileset) }
|
|
1329
|
+
};
|
|
1454
1330
|
}
|
|
1455
|
-
|
|
1456
|
-
const
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
)
|
|
1331
|
+
case "video": {
|
|
1332
|
+
const video = asset;
|
|
1333
|
+
try {
|
|
1334
|
+
const url = new URL(video.url);
|
|
1335
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
1336
|
+
throw new Error("unsupported URL scheme");
|
|
1337
|
+
} catch (error) {
|
|
1338
|
+
throw new TypeError(
|
|
1339
|
+
`video URL is invalid: ${error instanceof Error ? error.message : String(error)}`
|
|
1465
1340
|
);
|
|
1466
1341
|
}
|
|
1342
|
+
return { payload: video, refs: [], artifacts: {} };
|
|
1467
1343
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
for (let component = 0; component < components; component++) {
|
|
1476
|
-
const sourceIndex = vertex * components + component;
|
|
1477
|
-
const targetOffset = vertex * projection.arrayStride + attribute.offset + component * (attribute.format === "uint16x4" ? 2 : 4);
|
|
1478
|
-
if (attribute.format === "uint16x4") {
|
|
1479
|
-
interleavedView.setUint16(targetOffset, value[sourceIndex] ?? 0, true);
|
|
1480
|
-
} else {
|
|
1481
|
-
interleavedView.setFloat32(
|
|
1482
|
-
targetOffset,
|
|
1483
|
-
value[sourceIndex] ?? 0,
|
|
1484
|
-
true
|
|
1485
|
-
);
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1344
|
+
case "skeleton": {
|
|
1345
|
+
const skeleton = asset;
|
|
1346
|
+
return {
|
|
1347
|
+
payload: skeleton,
|
|
1348
|
+
refs: [],
|
|
1349
|
+
artifacts: { body: jsonArtifact(skeleton) }
|
|
1350
|
+
};
|
|
1489
1351
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
return
|
|
1352
|
+
case "skin": {
|
|
1353
|
+
const skin = asset;
|
|
1354
|
+
const skeletonGuid = AssetGuid$1.parse(skin.skeletonGuid);
|
|
1355
|
+
if (!skeletonGuid.ok) throw skeletonGuid.error;
|
|
1356
|
+
return {
|
|
1357
|
+
payload: skin,
|
|
1358
|
+
refs: [ref(skeletonGuid.value, "skeletonGuid")],
|
|
1359
|
+
artifacts: { body: jsonArtifact(skin) }
|
|
1360
|
+
};
|
|
1495
1361
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
return
|
|
1362
|
+
case "animation-clip":
|
|
1363
|
+
return {
|
|
1364
|
+
payload: asset,
|
|
1365
|
+
refs: [],
|
|
1366
|
+
artifacts: { body: jsonArtifact(asset) }
|
|
1367
|
+
};
|
|
1368
|
+
case "animation-graph": {
|
|
1369
|
+
const graph = asset;
|
|
1370
|
+
const refs = [];
|
|
1371
|
+
const nodes = graph.nodes.map((node, index) => {
|
|
1372
|
+
if (node.type !== "clip") return node;
|
|
1373
|
+
const parsed = AssetGuid$1.parse(node.clip);
|
|
1374
|
+
if (!parsed.ok) throw parsed.error;
|
|
1375
|
+
const referenceIndex = refs.push(ref(parsed.value, "nodes", index)) - 1;
|
|
1376
|
+
return { ...node, clip: referenceIndex };
|
|
1377
|
+
});
|
|
1378
|
+
return {
|
|
1379
|
+
payload: { kind: graph.kind, root: graph.root, nodes },
|
|
1380
|
+
refs,
|
|
1381
|
+
artifacts: { body: jsonArtifact(graph) }
|
|
1382
|
+
};
|
|
1513
1383
|
}
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1384
|
+
case "audio": {
|
|
1385
|
+
const audio = asset;
|
|
1386
|
+
return {
|
|
1387
|
+
payload: {
|
|
1388
|
+
kind: audio.kind,
|
|
1389
|
+
sourceKey: audio.sourceKey,
|
|
1390
|
+
mediaType: audio.mediaType,
|
|
1391
|
+
bytes: audio.bytes.slice()
|
|
1392
|
+
},
|
|
1393
|
+
refs: [],
|
|
1394
|
+
artifacts: {
|
|
1395
|
+
source: {
|
|
1396
|
+
mediaType: audio.mediaType,
|
|
1397
|
+
assetCodec: { name: "browser-audio", version: "1" },
|
|
1398
|
+
bytes: audio.bytes.slice()
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1522
1402
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1403
|
+
case "particle-effect": {
|
|
1404
|
+
const effect = asset;
|
|
1405
|
+
const cooked = particleProgramArtifact(effect);
|
|
1406
|
+
const refs = /* @__PURE__ */ new Set();
|
|
1407
|
+
for (const emitter of cooked.program.emitters) {
|
|
1408
|
+
for (const renderer of emitter.renderers) {
|
|
1409
|
+
if (!("material" in renderer) || typeof renderer.material !== "string") {
|
|
1410
|
+
throw new Error(`Particle emitter '${emitter.id}' requires a renderer Material GUID`);
|
|
1411
|
+
}
|
|
1412
|
+
refs.add(formatGuid(renderer.material));
|
|
1413
|
+
if ("kind" in renderer && renderer.kind === "mesh") {
|
|
1414
|
+
if (!("mesh" in renderer) || typeof renderer.mesh !== "string") {
|
|
1415
|
+
throw new Error(`Particle emitter '${emitter.id}' requires a renderer Mesh GUID`);
|
|
1416
|
+
}
|
|
1417
|
+
refs.add(formatGuid(renderer.mesh));
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
return {
|
|
1422
|
+
payload: { ...effect, programFingerprint: cooked.fingerprint, program: cooked.program },
|
|
1423
|
+
refs: [...refs].sort().map((guid) => ({ guid })),
|
|
1424
|
+
artifacts: {
|
|
1425
|
+
"particle-effect/program.json": {
|
|
1426
|
+
mediaType: "application/json",
|
|
1427
|
+
assetCodec: { name: "forgeax-vfx-program", version: effect.program.format },
|
|
1428
|
+
bytes: cooked.bytes
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1542
1432
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1433
|
+
case "ies-profile": {
|
|
1434
|
+
const profile = asset;
|
|
1435
|
+
return {
|
|
1436
|
+
payload: profile,
|
|
1437
|
+
refs: [],
|
|
1438
|
+
artifacts: {
|
|
1439
|
+
body: {
|
|
1440
|
+
mediaType: "application/octet-stream",
|
|
1441
|
+
assetCodec: { name: "forgeax-ies-profile", version: "1" },
|
|
1442
|
+
bytes: bytes(profile.data)
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1545
1446
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1447
|
+
case "material":
|
|
1448
|
+
case "mesh":
|
|
1449
|
+
case "scene":
|
|
1450
|
+
throw new TypeError(`ordinary producer received already-owned ${asset.kind} asset`);
|
|
1550
1451
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1452
|
+
}
|
|
1453
|
+
var materialAssetOutputProducer = createSafeProducer(
|
|
1454
|
+
"material",
|
|
1455
|
+
"material-pack/2",
|
|
1456
|
+
materialProduct
|
|
1457
|
+
);
|
|
1458
|
+
var meshAssetOutputProducer = createSafeProducer("mesh", "mesh-binary/4", meshProduct);
|
|
1459
|
+
var textureAssetOutputProducer = createSafeProducer(
|
|
1460
|
+
"texture",
|
|
1461
|
+
"texture-pack/1",
|
|
1462
|
+
textureProduct
|
|
1463
|
+
);
|
|
1464
|
+
function createSceneAssetOutputProducer(sceneComponents = []) {
|
|
1465
|
+
const schemas = new Map(
|
|
1466
|
+
sceneComponents.map((component) => [component.name, component.fields])
|
|
1467
|
+
);
|
|
1468
|
+
return createSafeProducer("scene", "scene-pack/3", (input) => sceneProduct(input, schemas));
|
|
1469
|
+
}
|
|
1470
|
+
function createPreExternalizedSceneAssetOutputProducer() {
|
|
1471
|
+
return createSafeProducer("scene", "scene-pack/3", preExternalizedSceneProduct);
|
|
1472
|
+
}
|
|
1473
|
+
function createStandardAssetOutputProducerRegistry(sceneComponents = []) {
|
|
1474
|
+
const registry = new AssetOutputProducerRegistry();
|
|
1475
|
+
registry.register(materialAssetOutputProducer);
|
|
1476
|
+
registry.register(meshAssetOutputProducer);
|
|
1477
|
+
registry.register(textureAssetOutputProducer);
|
|
1478
|
+
registry.register(createSceneAssetOutputProducer(sceneComponents));
|
|
1479
|
+
for (const kind of [
|
|
1480
|
+
"equirect",
|
|
1481
|
+
"sampler",
|
|
1482
|
+
"font",
|
|
1483
|
+
"render-pipeline",
|
|
1484
|
+
"tileset",
|
|
1485
|
+
"video",
|
|
1486
|
+
"skeleton",
|
|
1487
|
+
"skin",
|
|
1488
|
+
"animation-clip",
|
|
1489
|
+
"animation-graph",
|
|
1490
|
+
"audio",
|
|
1491
|
+
"particle-effect",
|
|
1492
|
+
"ies-profile"
|
|
1493
|
+
]) {
|
|
1494
|
+
registry.register(
|
|
1495
|
+
createSafeProducer(
|
|
1496
|
+
kind,
|
|
1497
|
+
kind === "particle-effect" ? "particle-effect/2" : "ordinary-pod/1",
|
|
1498
|
+
ordinaryPodProduct
|
|
1499
|
+
)
|
|
1554
1500
|
);
|
|
1555
1501
|
}
|
|
1556
|
-
|
|
1502
|
+
return registry;
|
|
1503
|
+
}
|
|
1557
1504
|
|
|
1558
|
-
// src/scriptable-pack-
|
|
1559
|
-
function
|
|
1560
|
-
return
|
|
1561
|
-
code: "import-internal-error",
|
|
1562
|
-
expected: `ScriptablePack ${input.asset.kind} output ${input.sourceKey} to satisfy its domain producer contract`,
|
|
1563
|
-
hint: "fix the generated Asset payload and rebuild the ScriptablePack",
|
|
1564
|
-
detail: { reason: reason instanceof Error ? reason.message : String(reason) }
|
|
1565
|
-
});
|
|
1505
|
+
// src/scriptable-pack-build.ts
|
|
1506
|
+
function record(value) {
|
|
1507
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1566
1508
|
}
|
|
1567
|
-
function
|
|
1568
|
-
|
|
1569
|
-
const parsed = AssetGuid$1.parse(value);
|
|
1570
|
-
if (!parsed.ok) throw parsed.error;
|
|
1571
|
-
return AssetGuid$1.format(parsed.value);
|
|
1572
|
-
}
|
|
1573
|
-
return AssetGuid$1.format(value);
|
|
1509
|
+
function isAsset(value) {
|
|
1510
|
+
return record(value) && typeof value.kind === "string" && isScriptablePackAssetKind(value.kind);
|
|
1574
1511
|
}
|
|
1575
|
-
function
|
|
1576
|
-
|
|
1577
|
-
if (value !== null && typeof value === "object") {
|
|
1578
|
-
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`).join(",")}}`;
|
|
1579
|
-
}
|
|
1580
|
-
return JSON.stringify(value) ?? "null";
|
|
1512
|
+
function needsMaterialCook(asset) {
|
|
1513
|
+
return asset.kind === "material" && Array.isArray(asset.passes) && !isEngineMaterial(asset);
|
|
1581
1514
|
}
|
|
1582
|
-
function
|
|
1583
|
-
|
|
1584
|
-
const bytes2 = new TextEncoder().encode(canonical(artifactProgram));
|
|
1585
|
-
const fingerprint = `sha256:${bytesToHex(sha256(bytes2))}`;
|
|
1586
|
-
return {
|
|
1587
|
-
program: { ...effect.program, fingerprint },
|
|
1588
|
-
bytes: bytes2,
|
|
1589
|
-
fingerprint
|
|
1590
|
-
};
|
|
1515
|
+
function clone(value) {
|
|
1516
|
+
return structuredClone(value);
|
|
1591
1517
|
}
|
|
1592
|
-
function
|
|
1593
|
-
|
|
1594
|
-
const
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
const textureFields = material.parameters === void 0 ? new Set(MATERIAL_TEXTURE_SLOTS) : new Set(
|
|
1607
|
-
material.parameters.filter(
|
|
1608
|
-
(parameter) => parameter.type === "texture" || parameter.type === "texture_cube"
|
|
1609
|
-
).map((parameter) => parameter.name)
|
|
1610
|
-
);
|
|
1611
|
-
const parent = material.parent === void 0 ? void 0 : addRef(material.parent, { fieldName: "parent" });
|
|
1612
|
-
const values = {};
|
|
1613
|
-
for (const fieldName of Object.keys(material.values ?? {}).sort()) {
|
|
1614
|
-
const value = material.values?.[fieldName];
|
|
1615
|
-
if (typeof value === "string" && textureFields.has(fieldName)) {
|
|
1616
|
-
values[fieldName] = {
|
|
1617
|
-
texture: addMaterialRef(value, { componentName: "<material>", fieldName })
|
|
1618
|
-
};
|
|
1619
|
-
} else if (value !== null && typeof value === "object" && !Array.isArray(value) && "texture" in value) {
|
|
1620
|
-
const texture = value;
|
|
1621
|
-
values[fieldName] = {
|
|
1622
|
-
...texture,
|
|
1623
|
-
texture: addMaterialRef(texture.texture, { componentName: "<material>", fieldName }),
|
|
1624
|
-
...texture.sampler === void 0 ? {} : {
|
|
1625
|
-
sampler: addMaterialRef(texture.sampler, {
|
|
1626
|
-
componentName: "<material>",
|
|
1627
|
-
fieldName: `${fieldName}.sampler`
|
|
1518
|
+
function observedReader(source) {
|
|
1519
|
+
const reads = /* @__PURE__ */ new Map();
|
|
1520
|
+
const reader = {
|
|
1521
|
+
async readByGuid(guid) {
|
|
1522
|
+
const key = AssetGuid.format(guid).toLowerCase();
|
|
1523
|
+
const cached = reads.get(key);
|
|
1524
|
+
if (cached !== void 0) return ok(clone(cached.asset));
|
|
1525
|
+
if (source === void 0) {
|
|
1526
|
+
return err(
|
|
1527
|
+
new AssetError({
|
|
1528
|
+
code: "asset-not-found",
|
|
1529
|
+
expected: `a published Asset snapshot for content dependency ${key}`,
|
|
1530
|
+
hint: "publish the dependency or remove the content read from the Pack build",
|
|
1531
|
+
detail: { sourcePath: key }
|
|
1628
1532
|
})
|
|
1629
|
-
|
|
1630
|
-
};
|
|
1631
|
-
} else {
|
|
1632
|
-
values[fieldName] = value;
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
const { parent: _parent, values: _values, ...materialShape } = material;
|
|
1636
|
-
return {
|
|
1637
|
-
payload: {
|
|
1638
|
-
...materialShape,
|
|
1639
|
-
...parent === void 0 ? {} : { parent },
|
|
1640
|
-
...material.values === void 0 ? {} : { values }
|
|
1641
|
-
},
|
|
1642
|
-
refs,
|
|
1643
|
-
artifacts: {}
|
|
1644
|
-
};
|
|
1645
|
-
}
|
|
1646
|
-
function meshProduct(input) {
|
|
1647
|
-
if (input.asset.kind !== "mesh") throw new TypeError("expected MeshAsset");
|
|
1648
|
-
const mesh = input.asset;
|
|
1649
|
-
const refs = [];
|
|
1650
|
-
for (let slotIndex = 0; slotIndex < mesh.materialSlots.length; slotIndex++) {
|
|
1651
|
-
const defaultMaterial = mesh.materialSlots[slotIndex]?.defaultMaterial;
|
|
1652
|
-
if (defaultMaterial === void 0) continue;
|
|
1653
|
-
refs.push({
|
|
1654
|
-
guid: formatGuid(defaultMaterial),
|
|
1655
|
-
sourceField: { fieldName: "materialSlots", arrayIndex: slotIndex }
|
|
1656
|
-
});
|
|
1657
|
-
}
|
|
1658
|
-
const seenRefs = new Set(refs.map((reference) => reference.guid.toLowerCase()));
|
|
1659
|
-
for (const [lodIndex, lod] of (mesh.lods ?? []).entries()) {
|
|
1660
|
-
const guid = formatGuid(lod.mesh);
|
|
1661
|
-
if (seenRefs.has(guid.toLowerCase())) continue;
|
|
1662
|
-
seenRefs.add(guid.toLowerCase());
|
|
1663
|
-
refs.push({ guid, sourceField: { fieldName: "lods", arrayIndex: lodIndex } });
|
|
1664
|
-
}
|
|
1665
|
-
return {
|
|
1666
|
-
payload: mesh,
|
|
1667
|
-
refs,
|
|
1668
|
-
artifacts: {
|
|
1669
|
-
body: {
|
|
1670
|
-
mediaType: "application/x-forgeax-mesh",
|
|
1671
|
-
assetCodec: { name: "mesh-binary", version: "4" },
|
|
1672
|
-
bytes: (() => {
|
|
1673
|
-
const packed = packMeshBinV4(
|
|
1674
|
-
mesh,
|
|
1675
|
-
input.sourceKey,
|
|
1676
|
-
refs.map((reference) => reference.guid)
|
|
1677
|
-
);
|
|
1678
|
-
if (!packed.ok) {
|
|
1679
|
-
throw packed.error;
|
|
1680
|
-
}
|
|
1681
|
-
return packed.value;
|
|
1682
|
-
})()
|
|
1533
|
+
);
|
|
1683
1534
|
}
|
|
1535
|
+
const result = await source.readByGuid(guid);
|
|
1536
|
+
if (!result.ok) return result;
|
|
1537
|
+
const asset = clone(result.value.asset);
|
|
1538
|
+
reads.set(key, {
|
|
1539
|
+
guid: key,
|
|
1540
|
+
asset,
|
|
1541
|
+
generation: result.value.generation,
|
|
1542
|
+
digest: result.value.digest
|
|
1543
|
+
});
|
|
1544
|
+
return ok(clone(asset));
|
|
1684
1545
|
}
|
|
1685
1546
|
};
|
|
1547
|
+
return { reader, reads };
|
|
1686
1548
|
}
|
|
1687
|
-
function
|
|
1688
|
-
if (
|
|
1689
|
-
|
|
1690
|
-
const schema = components.get(componentName);
|
|
1691
|
-
if (schema === void 0) {
|
|
1692
|
-
throw new TypeError(
|
|
1693
|
-
`ScriptablePack scene component ${componentName} is missing from sceneComponents`
|
|
1694
|
-
);
|
|
1695
|
-
}
|
|
1696
|
-
return schema;
|
|
1697
|
-
});
|
|
1698
|
-
if (!externalized.ok) throw new TypeError(`scene field ${externalized.error.field} is invalid`);
|
|
1699
|
-
return {
|
|
1700
|
-
payload: { kind: "scene", ...externalized.value.payload },
|
|
1701
|
-
refs: externalized.value.refs,
|
|
1702
|
-
artifacts: {}
|
|
1703
|
-
};
|
|
1704
|
-
}
|
|
1705
|
-
function containsAssetGuid(value) {
|
|
1706
|
-
if (typeof value === "string") return AssetGuid$1.parse(value).ok;
|
|
1707
|
-
if (Array.isArray(value)) return value.some(containsAssetGuid);
|
|
1708
|
-
if (value !== null && typeof value === "object") {
|
|
1709
|
-
return Object.values(value).some(containsAssetGuid);
|
|
1710
|
-
}
|
|
1711
|
-
return false;
|
|
1712
|
-
}
|
|
1713
|
-
function preExternalizedSceneProduct(input) {
|
|
1714
|
-
if (input.asset.kind !== "scene") throw new TypeError("expected SceneAsset");
|
|
1715
|
-
if (containsAssetGuid(input.asset)) {
|
|
1716
|
-
throw new TypeError("direct scene payload must use explicit refs with runtime indices");
|
|
1717
|
-
}
|
|
1718
|
-
return {
|
|
1719
|
-
payload: { ...input.asset, kind: "scene" },
|
|
1720
|
-
refs: [],
|
|
1721
|
-
artifacts: {}
|
|
1722
|
-
};
|
|
1549
|
+
function buildContext(packageId, values, reader) {
|
|
1550
|
+
if (values === void 0) return { packageId, readByGuid: reader.readByGuid };
|
|
1551
|
+
return { packageId, values, readByGuid: reader.readByGuid };
|
|
1723
1552
|
}
|
|
1724
|
-
function
|
|
1553
|
+
function sourceKeyFailure(sourcePath, sourceKey) {
|
|
1725
1554
|
return {
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
return ok(product(input));
|
|
1731
|
-
} catch (error) {
|
|
1732
|
-
return err(producerError(input, error));
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1555
|
+
code: "pack-source-key-invalid",
|
|
1556
|
+
expected: "a sourceKey matching the Pack source-key grammar",
|
|
1557
|
+
hint: "return stable lower-case semantic keys instead of paths or output indexes",
|
|
1558
|
+
detail: { sourcePath, sourceKey }
|
|
1735
1559
|
};
|
|
1736
1560
|
}
|
|
1737
|
-
function
|
|
1561
|
+
function outputValueError(sourcePath, sourceKey, expected, actual) {
|
|
1738
1562
|
return {
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1563
|
+
code: "pack-parameter-invalid",
|
|
1564
|
+
expected,
|
|
1565
|
+
hint: "repair the build output and rebuild the Pack from a fresh generation",
|
|
1566
|
+
detail: { sourcePath, sourceKey, actual: typeof actual === "string" ? actual : typeof actual }
|
|
1742
1567
|
};
|
|
1743
1568
|
}
|
|
1744
|
-
function
|
|
1569
|
+
function referenceError(code, sourcePath, guids) {
|
|
1745
1570
|
return {
|
|
1746
|
-
|
|
1747
|
-
|
|
1571
|
+
code,
|
|
1572
|
+
expected: code === "pack-output-reference-missing" ? "every output reference to resolve to a local or published AssetGuid" : "removed output GUIDs to have no incoming references",
|
|
1573
|
+
hint: code === "pack-output-reference-missing" ? "build or publish the referenced Pack before verifying this output" : "migrate incoming references before publishing the topology change",
|
|
1574
|
+
detail: { sourcePath, guids: [...guids].sort() }
|
|
1748
1575
|
};
|
|
1749
1576
|
}
|
|
1750
|
-
function
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
shape: texture.shape,
|
|
1758
|
-
format: texture.format,
|
|
1759
|
-
mips: texture.mips,
|
|
1760
|
-
actualByteLength: texture.data.byteLength,
|
|
1761
|
-
order: "mip-major,image-major,row-major"
|
|
1762
|
-
});
|
|
1763
|
-
if (!layout.ok) {
|
|
1764
|
-
const expectedBytes = layout.error.code === "texture-packing-invalid" ? layout.error.detail.expectedBytes : texture.data.byteLength;
|
|
1765
|
-
const actualBytes = layout.error.code === "texture-packing-invalid" ? layout.error.detail.actualBytes : texture.data.byteLength;
|
|
1766
|
-
throw new TypeError(
|
|
1767
|
-
`texture data is not canonical: expected ${expectedBytes} bytes, got ${actualBytes}`
|
|
1577
|
+
function productError(sourcePath, sourceKey, producer, product) {
|
|
1578
|
+
if (!record(product)) {
|
|
1579
|
+
return outputValueError(
|
|
1580
|
+
sourcePath,
|
|
1581
|
+
sourceKey,
|
|
1582
|
+
`producer ${producer.kind} to return an asset product object`,
|
|
1583
|
+
product
|
|
1768
1584
|
);
|
|
1769
1585
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1586
|
+
if (!Array.isArray(product.refs)) {
|
|
1587
|
+
return outputValueError(
|
|
1588
|
+
sourcePath,
|
|
1589
|
+
sourceKey,
|
|
1590
|
+
`producer ${producer.kind} to return a refs array`,
|
|
1591
|
+
product.refs
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
if (!record(product.artifacts)) {
|
|
1595
|
+
return outputValueError(
|
|
1596
|
+
sourcePath,
|
|
1597
|
+
sourceKey,
|
|
1598
|
+
`producer ${producer.kind} to return an artifacts object`,
|
|
1599
|
+
product.artifacts
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
const payload = product.payload;
|
|
1603
|
+
if (!record(payload) || payload.kind !== producer.kind) {
|
|
1604
|
+
return outputValueError(
|
|
1605
|
+
sourcePath,
|
|
1606
|
+
sourceKey,
|
|
1607
|
+
`producer ${producer.kind} to return a matching payload kind`,
|
|
1608
|
+
payload
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1611
|
+
for (const ref2 of product.refs) {
|
|
1612
|
+
if (!record(ref2) || typeof ref2.guid !== "string" || !AssetGuid.parse(ref2.guid).ok) {
|
|
1613
|
+
return outputValueError(
|
|
1614
|
+
sourcePath,
|
|
1615
|
+
sourceKey,
|
|
1616
|
+
"producer refs to contain valid AssetGuid values",
|
|
1617
|
+
ref2
|
|
1618
|
+
);
|
|
1800
1619
|
}
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1620
|
+
}
|
|
1621
|
+
for (const [key, artifact] of Object.entries(product.artifacts)) {
|
|
1622
|
+
if (key.length === 0 || key.startsWith("/") || key.includes("..") || key.includes("\\") || !record(artifact) || typeof artifact.mediaType !== "string" || !(artifact.bytes instanceof Uint8Array)) {
|
|
1623
|
+
return outputValueError(
|
|
1624
|
+
sourcePath,
|
|
1625
|
+
sourceKey,
|
|
1626
|
+
"asset-local artifacts with safe keys and bytes",
|
|
1627
|
+
key
|
|
1628
|
+
);
|
|
1804
1629
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1630
|
+
}
|
|
1631
|
+
return void 0;
|
|
1632
|
+
}
|
|
1633
|
+
function errorCode(value) {
|
|
1634
|
+
return record(value) && typeof value.code === "string" ? value.code : void 0;
|
|
1635
|
+
}
|
|
1636
|
+
function normalizedGuidSet(value) {
|
|
1637
|
+
return value === void 0 ? void 0 : new Set([...value].map((guid) => guid.toLowerCase()));
|
|
1638
|
+
}
|
|
1639
|
+
async function buildScriptablePack(options) {
|
|
1640
|
+
const subjectPackageId = options.subjectPackageId ?? options.definition.packageId;
|
|
1641
|
+
const availableGuids = normalizedGuidSet(options.availableGuids);
|
|
1642
|
+
const observed = observedReader(options.assetSource);
|
|
1643
|
+
let effectiveValues;
|
|
1644
|
+
if ("parameters" in options.definition) {
|
|
1645
|
+
const resolved = resolvePackParameterValues(
|
|
1646
|
+
options.definition,
|
|
1647
|
+
options.values ?? {},
|
|
1648
|
+
options.inheritedValues
|
|
1649
|
+
);
|
|
1650
|
+
if (!resolved.ok) return resolved;
|
|
1651
|
+
effectiveValues = resolved.value;
|
|
1652
|
+
} else if (options.values !== void 0 && Object.keys(options.values).length > 0) {
|
|
1653
|
+
return err(
|
|
1654
|
+
outputValueError(
|
|
1655
|
+
options.sourcePath,
|
|
1656
|
+
"$.values",
|
|
1657
|
+
"zero-parameter Packs to omit values and instance capabilities",
|
|
1658
|
+
options.values
|
|
1659
|
+
)
|
|
1660
|
+
);
|
|
1661
|
+
} else if (options.subjectPackageId !== void 0 && PackageId.format(options.subjectPackageId).toLowerCase() !== PackageId.format(options.definition.packageId).toLowerCase()) {
|
|
1662
|
+
return err({
|
|
1663
|
+
code: "pack-parent-has-no-parameters",
|
|
1664
|
+
expected: "a ScriptablePack source with parameters for an independent instance packageId",
|
|
1665
|
+
hint: "use clone for a zero-parameter Pack instead of building it as an instance",
|
|
1666
|
+
detail: {
|
|
1667
|
+
sourcePath: options.sourcePath,
|
|
1668
|
+
rootPackageId: PackageId.format(options.definition.packageId),
|
|
1669
|
+
subjectPackageId: PackageId.format(options.subjectPackageId)
|
|
1670
|
+
}
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
let built;
|
|
1674
|
+
try {
|
|
1675
|
+
const context = buildContext(subjectPackageId, effectiveValues, observed.reader);
|
|
1676
|
+
built = options.definition.build(context);
|
|
1677
|
+
built = await built;
|
|
1678
|
+
} catch (cause) {
|
|
1679
|
+
return err(
|
|
1680
|
+
new ImportError({
|
|
1681
|
+
code: "import-internal-error",
|
|
1682
|
+
expected: "Pack build to return a structured Result without throwing",
|
|
1683
|
+
hint: "repair the authoring function and return err(...) for expected failures",
|
|
1684
|
+
detail: {
|
|
1685
|
+
reason: `${options.sourcePath}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
1686
|
+
}
|
|
1687
|
+
})
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
if (!record(built) || typeof built.ok !== "boolean") {
|
|
1691
|
+
return err(
|
|
1692
|
+
new ImportError({
|
|
1693
|
+
code: "import-internal-error",
|
|
1694
|
+
expected: "Pack build to return a Result object",
|
|
1695
|
+
hint: "return ok(sourceKeyToAsset) or err(structuredError) from the authoring function",
|
|
1696
|
+
detail: { reason: `${options.sourcePath}: malformed build result` }
|
|
1697
|
+
})
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
if (!built.ok) {
|
|
1701
|
+
if (errorCode(built.error) !== void 0) return err(built.error);
|
|
1702
|
+
return err(
|
|
1703
|
+
new ImportError({
|
|
1704
|
+
code: "import-internal-error",
|
|
1705
|
+
expected: "a structured Pack build error",
|
|
1706
|
+
hint: "return an error carrying code, expected, hint and detail",
|
|
1707
|
+
detail: { reason: `${options.sourcePath}: ${String(built.error)}` }
|
|
1708
|
+
})
|
|
1709
|
+
);
|
|
1710
|
+
}
|
|
1711
|
+
if (!record(built.value)) {
|
|
1712
|
+
return err(
|
|
1713
|
+
outputValueError(
|
|
1714
|
+
options.sourcePath,
|
|
1715
|
+
"$",
|
|
1716
|
+
"build to return a sourceKey-to-Asset object",
|
|
1717
|
+
built.value
|
|
1718
|
+
)
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
const imported = [];
|
|
1722
|
+
const stagedOutputs = [];
|
|
1723
|
+
const localGuids = /* @__PURE__ */ new Set();
|
|
1724
|
+
const cookerRegistry = new NativeCookerRegistry();
|
|
1725
|
+
for (const cooker of options.cookers ?? []) cookerRegistry.register(cooker);
|
|
1726
|
+
const nativeFingerprints = /* @__PURE__ */ new Map();
|
|
1727
|
+
for (const sourceKey of Object.keys(built.value).sort()) {
|
|
1728
|
+
if (!isValidPackSourceKey(sourceKey))
|
|
1729
|
+
return err(sourceKeyFailure(options.sourcePath, sourceKey));
|
|
1730
|
+
const asset = built.value[sourceKey];
|
|
1731
|
+
const customSource = record(asset) && asset.execution === "cooked";
|
|
1732
|
+
if (customSource && (typeof asset.kind !== "string" || asset.kind.trim().length === 0 || isScriptablePackAssetKind(asset.kind) || !("source" in asset) || Object.keys(asset).some((key) => !["kind", "execution", "source"].includes(key)))) {
|
|
1733
|
+
return err(
|
|
1734
|
+
outputValueError(
|
|
1735
|
+
options.sourcePath,
|
|
1736
|
+
sourceKey,
|
|
1737
|
+
"a custom kind with only execution: cooked and source; ordinary Assets use their standard producers",
|
|
1738
|
+
asset
|
|
1739
|
+
)
|
|
1740
|
+
);
|
|
1820
1741
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1742
|
+
if (!customSource && !isAsset(asset)) {
|
|
1743
|
+
return err(
|
|
1744
|
+
outputValueError(
|
|
1745
|
+
options.sourcePath,
|
|
1746
|
+
sourceKey,
|
|
1747
|
+
"a concrete Asset with a supported kind",
|
|
1748
|
+
asset
|
|
1749
|
+
)
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
const guid = AssetGuid.format(AssetGuid.derive(subjectPackageId, sourceKey));
|
|
1753
|
+
const normalizedGuid = guid.toLowerCase();
|
|
1754
|
+
if (localGuids.has(normalizedGuid) || availableGuids?.has(normalizedGuid)) {
|
|
1755
|
+
return err({
|
|
1756
|
+
code: "pack-guid-collision",
|
|
1757
|
+
expected: "derived output GUIDs to be unique in the global source index",
|
|
1758
|
+
hint: "change the colliding packageId or repair the source index before publishing",
|
|
1759
|
+
detail: { sourcePath: options.sourcePath, guid }
|
|
1760
|
+
});
|
|
1761
|
+
}
|
|
1762
|
+
localGuids.add(normalizedGuid);
|
|
1763
|
+
if (customSource) {
|
|
1764
|
+
const cooked = await cookerRegistry.runDraft(asset.kind, {
|
|
1765
|
+
guid,
|
|
1766
|
+
sourceKey,
|
|
1767
|
+
sourcePath: options.sourcePath,
|
|
1768
|
+
source: asset.source,
|
|
1769
|
+
refs: []
|
|
1770
|
+
});
|
|
1771
|
+
if (!cooked.ok) return err(cooked.error);
|
|
1772
|
+
if (cooked.value.guid.toLowerCase() !== normalizedGuid)
|
|
1773
|
+
return err({
|
|
1774
|
+
code: "pack-source-output-invalid",
|
|
1775
|
+
expected: "the custom cooker to preserve the derived AssetGuid",
|
|
1776
|
+
hint: "remove cooker-owned identity generation and retain the host GUID",
|
|
1777
|
+
detail: {
|
|
1778
|
+
sourcePath: options.sourcePath,
|
|
1779
|
+
sourceKey,
|
|
1780
|
+
expectedGuid: guid,
|
|
1781
|
+
actualGuid: cooked.value.guid
|
|
1782
|
+
}
|
|
1783
|
+
});
|
|
1784
|
+
const product3 = {
|
|
1785
|
+
payload: cooked.value.payload,
|
|
1786
|
+
refs: cooked.value.refs.map((guid2) => ({ guid: guid2 })),
|
|
1787
|
+
artifacts: cooked.value.artifacts
|
|
1827
1788
|
};
|
|
1789
|
+
const invalid = productError(options.sourcePath, sourceKey, { kind: asset.kind }, product3);
|
|
1790
|
+
if (invalid !== void 0) return err(invalid);
|
|
1791
|
+
imported.push({ guid, kind: asset.kind, ...product3 });
|
|
1792
|
+
nativeFingerprints.set(sourceKey, cooked.value.inputFingerprint);
|
|
1793
|
+
stagedOutputs.push({
|
|
1794
|
+
guid: AssetGuid.derive(subjectPackageId, sourceKey),
|
|
1795
|
+
sourceKey,
|
|
1796
|
+
asset: clone(asset),
|
|
1797
|
+
digest: await scriptablePackFingerprint(asset)
|
|
1798
|
+
});
|
|
1799
|
+
continue;
|
|
1828
1800
|
}
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1801
|
+
if (!isAsset(asset))
|
|
1802
|
+
return err(outputValueError(options.sourcePath, sourceKey, "an ordinary Asset", asset));
|
|
1803
|
+
const producer = options.outputs.get(asset.kind);
|
|
1804
|
+
if (producer === void 0) {
|
|
1805
|
+
return err(
|
|
1806
|
+
outputValueError(
|
|
1807
|
+
options.sourcePath,
|
|
1808
|
+
sourceKey,
|
|
1809
|
+
`a registered output producer for ${asset.kind}`,
|
|
1810
|
+
asset.kind
|
|
1811
|
+
)
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
let produced;
|
|
1815
|
+
try {
|
|
1816
|
+
produced = await producer.produce({ guid, sourceKey, asset });
|
|
1817
|
+
} catch (cause) {
|
|
1818
|
+
return err(
|
|
1819
|
+
new ImportError({
|
|
1820
|
+
code: "import-internal-error",
|
|
1821
|
+
expected: `producer ${producer.kind} to return a structured Result without throwing`,
|
|
1822
|
+
hint: "repair the output producer and return err(...) for expected failures",
|
|
1823
|
+
detail: {
|
|
1824
|
+
reason: `${options.sourcePath}:${sourceKey}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
1825
|
+
}
|
|
1826
|
+
})
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
if (!record(produced) || typeof produced.ok !== "boolean") {
|
|
1830
|
+
return err(
|
|
1831
|
+
outputValueError(
|
|
1832
|
+
options.sourcePath,
|
|
1833
|
+
sourceKey,
|
|
1834
|
+
`producer ${producer.kind} to return a Result`,
|
|
1835
|
+
produced
|
|
1836
|
+
)
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
if (!produced.ok) {
|
|
1840
|
+
if (errorCode(produced.error) !== void 0) return err(produced.error);
|
|
1841
|
+
return err(
|
|
1842
|
+
new ImportError({
|
|
1843
|
+
code: "import-internal-error",
|
|
1844
|
+
expected: `producer ${producer.kind} to return a structured error`,
|
|
1845
|
+
hint: "return an error carrying code, expected, hint and detail",
|
|
1846
|
+
detail: { reason: `${options.sourcePath}:${sourceKey}: ${String(produced.error)}` }
|
|
1847
|
+
})
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
const invalidProduct2 = productError(options.sourcePath, sourceKey, producer, produced.value);
|
|
1851
|
+
if (invalidProduct2 !== void 0) return err(invalidProduct2);
|
|
1852
|
+
const product2 = produced.value;
|
|
1853
|
+
let payload = product2.payload;
|
|
1854
|
+
let artifacts2 = product2.artifacts;
|
|
1855
|
+
let refs2 = product2.refs;
|
|
1856
|
+
if (needsMaterialCook(asset) && cookerRegistry.get("material") !== void 0) {
|
|
1857
|
+
const cooked = await cookerRegistry.runDraft("material", {
|
|
1858
|
+
guid,
|
|
1859
|
+
source: asset,
|
|
1860
|
+
sourceKey,
|
|
1861
|
+
sourcePath: options.sourcePath,
|
|
1862
|
+
refs: product2.refs.map((reference) => reference.guid)
|
|
1835
1863
|
});
|
|
1836
|
-
return
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1864
|
+
if (!cooked.ok) return err(cooked.error);
|
|
1865
|
+
if (cooked.value.guid.toLowerCase() !== normalizedGuid) {
|
|
1866
|
+
return err({
|
|
1867
|
+
code: "pack-source-output-invalid",
|
|
1868
|
+
expected: "the authored material cooker to preserve the derived AssetGuid",
|
|
1869
|
+
hint: "repair the native material cooker output GUID and rebuild the Pack",
|
|
1870
|
+
detail: {
|
|
1871
|
+
sourcePath: options.sourcePath,
|
|
1872
|
+
sourceKey,
|
|
1873
|
+
expectedGuid: guid,
|
|
1874
|
+
actualGuid: cooked.value.guid
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
const cookedAsset = cooked.value.payload;
|
|
1879
|
+
if (!isAsset(cookedAsset) || cookedAsset.kind !== "material") {
|
|
1880
|
+
return err({
|
|
1881
|
+
code: "pack-source-output-invalid",
|
|
1882
|
+
expected: "the authored material cooker to return a material payload",
|
|
1883
|
+
hint: "repair the native material cooker payload and rebuild the Pack",
|
|
1884
|
+
detail: { sourcePath: options.sourcePath, sourceKey }
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
const projected = await materialAssetOutputProducer.produce({
|
|
1888
|
+
guid,
|
|
1889
|
+
sourceKey,
|
|
1890
|
+
asset: cookedAsset
|
|
1891
|
+
});
|
|
1892
|
+
if (!projected.ok) return err(projected.error);
|
|
1893
|
+
const projectedInvalid = productError(
|
|
1894
|
+
options.sourcePath,
|
|
1895
|
+
sourceKey,
|
|
1896
|
+
materialAssetOutputProducer,
|
|
1897
|
+
projected.value
|
|
1898
|
+
);
|
|
1899
|
+
if (projectedInvalid !== void 0) return err(projectedInvalid);
|
|
1900
|
+
payload = projected.value.payload;
|
|
1901
|
+
artifacts2 = {
|
|
1902
|
+
...product2.artifacts,
|
|
1903
|
+
...cooked.value.artifacts,
|
|
1904
|
+
...projected.value.artifacts
|
|
1840
1905
|
};
|
|
1906
|
+
const mergedRefs = [...projected.value.refs];
|
|
1907
|
+
const seenRefs = new Set(mergedRefs.map((reference) => reference.guid.toLowerCase()));
|
|
1908
|
+
for (const cookerRef of cooked.value.refs) {
|
|
1909
|
+
const parsed = AssetGuid.parse(cookerRef);
|
|
1910
|
+
if (!parsed.ok) {
|
|
1911
|
+
return err({
|
|
1912
|
+
code: "pack-source-output-invalid",
|
|
1913
|
+
expected: "the authored material cooker refs to contain valid AssetGuid values",
|
|
1914
|
+
hint: "repair the native material cooker refs and rebuild the Pack",
|
|
1915
|
+
detail: { sourcePath: options.sourcePath, sourceKey, ref: cookerRef }
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
const formatted = AssetGuid.format(parsed.value);
|
|
1919
|
+
const normalizedRef = formatted.toLowerCase();
|
|
1920
|
+
if (seenRefs.has(normalizedRef)) continue;
|
|
1921
|
+
seenRefs.add(normalizedRef);
|
|
1922
|
+
mergedRefs.push({ guid: formatted });
|
|
1923
|
+
}
|
|
1924
|
+
refs2 = mergedRefs;
|
|
1925
|
+
nativeFingerprints.set(sourceKey, cooked.value.inputFingerprint);
|
|
1841
1926
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1927
|
+
imported.push({
|
|
1928
|
+
guid,
|
|
1929
|
+
kind: asset.kind,
|
|
1930
|
+
payload,
|
|
1931
|
+
refs: refs2,
|
|
1932
|
+
artifacts: artifacts2
|
|
1933
|
+
});
|
|
1934
|
+
stagedOutputs.push({
|
|
1935
|
+
guid: AssetGuid.derive(subjectPackageId, sourceKey),
|
|
1936
|
+
sourceKey,
|
|
1937
|
+
asset: clone(asset),
|
|
1938
|
+
digest: await scriptablePackFingerprint(asset)
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
1942
|
+
for (const asset of imported) {
|
|
1943
|
+
for (const ref2 of asset.refs) {
|
|
1944
|
+
const guid = ref2.guid.toLowerCase();
|
|
1945
|
+
if (!localGuids.has(guid)) referenced.add(guid);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
if (options.deferReferenceValidation !== true) {
|
|
1949
|
+
const missing = [...referenced].filter(
|
|
1950
|
+
(guid) => availableGuids !== void 0 && !availableGuids.has(guid)
|
|
1951
|
+
);
|
|
1952
|
+
if (missing.length > 0)
|
|
1953
|
+
return err(referenceError("pack-output-reference-missing", options.sourcePath, missing));
|
|
1954
|
+
}
|
|
1955
|
+
const externalEvidence = [...observed.reads.values()].sort((left, right) => left.guid.localeCompare(right.guid)).map(
|
|
1956
|
+
(read) => ({
|
|
1957
|
+
guid: read.guid,
|
|
1958
|
+
usage: referenced.has(read.guid) ? "both" : "content",
|
|
1959
|
+
generation: read.generation,
|
|
1960
|
+
digest: read.digest
|
|
1961
|
+
})
|
|
1962
|
+
);
|
|
1963
|
+
const authoredInputFingerprint = await scriptablePackFingerprint({
|
|
1964
|
+
packageId: PackageId.format(subjectPackageId),
|
|
1965
|
+
sourcePath: options.sourcePath,
|
|
1966
|
+
sourceClosure: options.sourceClosure ?? [],
|
|
1967
|
+
values: effectiveValues,
|
|
1968
|
+
externalEvidence: externalEvidence.map(({ guid, usage, digest: evidenceDigest }) => ({
|
|
1969
|
+
guid,
|
|
1970
|
+
usage,
|
|
1971
|
+
digest: evidenceDigest
|
|
1972
|
+
})),
|
|
1973
|
+
authoringContractVersion: options.authoringContractVersion ?? "scriptable-pack/1",
|
|
1974
|
+
producerVersions: options.outputs.versions()
|
|
1975
|
+
});
|
|
1976
|
+
const inputFingerprint = nativeFingerprints.size === 0 ? authoredInputFingerprint : await scriptablePackFingerprint({
|
|
1977
|
+
sourceRevision: authoredInputFingerprint,
|
|
1978
|
+
nativeCookers: [...nativeFingerprints.entries()].sort(
|
|
1979
|
+
([left], [right]) => left.localeCompare(right)
|
|
1980
|
+
)
|
|
1981
|
+
});
|
|
1982
|
+
const refs = imported.flatMap((asset) => asset.refs);
|
|
1983
|
+
const artifacts = Object.fromEntries(
|
|
1984
|
+
imported.flatMap(
|
|
1985
|
+
(asset) => Object.entries(asset.artifacts).map(([key, artifact]) => [`${asset.guid}/${key}`, artifact])
|
|
1986
|
+
)
|
|
1987
|
+
);
|
|
1988
|
+
const product = createImportProduct({
|
|
1989
|
+
assets: imported,
|
|
1990
|
+
sourceDependencies: (options.sourceClosure ?? []).map((entry) => entry.path),
|
|
1991
|
+
refs,
|
|
1992
|
+
artifacts,
|
|
1993
|
+
receipts: imported.map((asset) => ({
|
|
1994
|
+
guid: asset.guid,
|
|
1995
|
+
origin: "authoredPack",
|
|
1996
|
+
status: "succeeded",
|
|
1997
|
+
inputFingerprint
|
|
1998
|
+
})),
|
|
1999
|
+
diagnostics: [],
|
|
2000
|
+
sourceRevision: inputFingerprint,
|
|
2001
|
+
sourceKey: options.sourcePath
|
|
2002
|
+
});
|
|
2003
|
+
if (!product.ok) return err(product.error);
|
|
2004
|
+
return ok({
|
|
2005
|
+
product: product.value,
|
|
2006
|
+
stagedOutputs,
|
|
2007
|
+
externalEvidence,
|
|
2008
|
+
inputFingerprint,
|
|
2009
|
+
...options.publication === void 0 ? {} : { publication: options.publication }
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
function stagedSource(staged, fallback) {
|
|
2013
|
+
return {
|
|
2014
|
+
async readByGuid(guid) {
|
|
2015
|
+
const key = AssetGuid.format(guid).toLowerCase();
|
|
2016
|
+
const local = staged.get(key);
|
|
2017
|
+
if (local !== void 0) {
|
|
2018
|
+
return ok({
|
|
2019
|
+
asset: clone(local.asset),
|
|
2020
|
+
generation: 1,
|
|
2021
|
+
digest: local.digest ?? "sha256:staged"
|
|
2022
|
+
});
|
|
1852
2023
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
}
|
|
1863
|
-
case "skin": {
|
|
1864
|
-
const skin = asset;
|
|
1865
|
-
const skeletonGuid = AssetGuid$1.parse(skin.skeletonGuid);
|
|
1866
|
-
if (!skeletonGuid.ok) throw skeletonGuid.error;
|
|
1867
|
-
return {
|
|
1868
|
-
payload: skin,
|
|
1869
|
-
refs: [ref(skeletonGuid.value, "skeletonGuid")],
|
|
1870
|
-
artifacts: { body: jsonArtifact(skin) }
|
|
1871
|
-
};
|
|
2024
|
+
if (fallback === void 0) {
|
|
2025
|
+
return err({
|
|
2026
|
+
code: "asset-not-found",
|
|
2027
|
+
expected: "a staged or published content dependency",
|
|
2028
|
+
hint: "wait for the dependency subject to materialize",
|
|
2029
|
+
detail: { guid: key }
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
return fallback.readByGuid(guid);
|
|
1872
2033
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
async function buildScriptablePackWorklist(options) {
|
|
2037
|
+
const orderedSubjects = [...options.subjects].sort(
|
|
2038
|
+
(left, right) => left.sourcePath.localeCompare(right.sourcePath)
|
|
2039
|
+
);
|
|
2040
|
+
const pending = new Map(
|
|
2041
|
+
orderedSubjects.map((subject, index) => [`${index}:${subject.sourcePath}`, subject])
|
|
2042
|
+
);
|
|
2043
|
+
const staged = /* @__PURE__ */ new Map();
|
|
2044
|
+
const results = /* @__PURE__ */ new Map();
|
|
2045
|
+
const availableGuids = /* @__PURE__ */ new Set([
|
|
2046
|
+
...normalizedGuidSet(options.availableGuids) ?? [],
|
|
2047
|
+
...BUILTIN_MESH_ASSETS.map((asset) => asset.guid.toLowerCase())
|
|
2048
|
+
]);
|
|
2049
|
+
const maxPasses = options.maxPasses ?? Math.max(1, options.subjects.length + 1);
|
|
2050
|
+
let iterations = 0;
|
|
2051
|
+
for (; iterations < maxPasses && pending.size > 0; iterations += 1) {
|
|
2052
|
+
let progress = false;
|
|
2053
|
+
const waiting = /* @__PURE__ */ new Set();
|
|
2054
|
+
for (const [key, subject] of pending) {
|
|
2055
|
+
const result = await buildScriptablePack({
|
|
2056
|
+
definition: subject.definition,
|
|
2057
|
+
sourcePath: subject.sourcePath,
|
|
2058
|
+
...subject.subjectPackageId === void 0 ? {} : { subjectPackageId: subject.subjectPackageId },
|
|
2059
|
+
...subject.values === void 0 ? {} : { values: subject.values },
|
|
2060
|
+
...subject.inheritedValues === void 0 ? {} : { inheritedValues: subject.inheritedValues },
|
|
2061
|
+
...subject.sourceClosure === void 0 ? {} : { sourceClosure: subject.sourceClosure },
|
|
2062
|
+
outputs: options.outputs,
|
|
2063
|
+
...options.cookers === void 0 ? {} : { cookers: options.cookers },
|
|
2064
|
+
assetSource: stagedSource(staged, options.assetSource),
|
|
2065
|
+
availableGuids: /* @__PURE__ */ new Set([...availableGuids, ...staged.keys()]),
|
|
2066
|
+
deferReferenceValidation: true
|
|
1888
2067
|
});
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
refs: [],
|
|
1905
|
-
artifacts: {
|
|
1906
|
-
source: {
|
|
1907
|
-
mediaType: audio.mediaType,
|
|
1908
|
-
assetCodec: { name: "browser-audio", version: "1" },
|
|
1909
|
-
bytes: audio.bytes.slice()
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
};
|
|
2068
|
+
if (result.ok) {
|
|
2069
|
+
results.set(key, result.value);
|
|
2070
|
+
for (const output of result.value.stagedOutputs)
|
|
2071
|
+
staged.set(AssetGuid.format(output.guid).toLowerCase(), output);
|
|
2072
|
+
pending.delete(key);
|
|
2073
|
+
progress = true;
|
|
2074
|
+
continue;
|
|
2075
|
+
}
|
|
2076
|
+
if (errorCode(result.error) === "asset-not-found") {
|
|
2077
|
+
const detail = record(result.error) && record(result.error.detail) ? result.error.detail : void 0;
|
|
2078
|
+
const guid = detail !== void 0 && typeof detail.guid === "string" ? detail.guid : void 0;
|
|
2079
|
+
if (guid !== void 0) waiting.add(guid.toLowerCase());
|
|
2080
|
+
continue;
|
|
2081
|
+
}
|
|
2082
|
+
return result;
|
|
1913
2083
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
bytes: cooked.bytes
|
|
1925
|
-
}
|
|
2084
|
+
if (pending.size === 0) break;
|
|
2085
|
+
if (!progress) {
|
|
2086
|
+
return err({
|
|
2087
|
+
code: "pack-content-dependency-stalled",
|
|
2088
|
+
expected: "the content dependency worklist to make progress",
|
|
2089
|
+
hint: "inspect waitingGuids and repair the missing output or content-read cycle, then rebuild",
|
|
2090
|
+
detail: {
|
|
2091
|
+
waitingGuids: [...waiting].sort(),
|
|
2092
|
+
pendingSubjects: [...pending.values()].map((subject) => subject.sourcePath).sort(),
|
|
2093
|
+
iterations: iterations + 1
|
|
1926
2094
|
}
|
|
1927
|
-
};
|
|
2095
|
+
});
|
|
1928
2096
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
2097
|
+
}
|
|
2098
|
+
if (pending.size > 0) {
|
|
2099
|
+
return err({
|
|
2100
|
+
code: "pack-content-dependency-stalled",
|
|
2101
|
+
expected: "the content dependency worklist to finish within its bounded retry budget",
|
|
2102
|
+
hint: "inspect pendingSubjects and waitingGuids, then repair the dependency graph",
|
|
2103
|
+
detail: {
|
|
2104
|
+
pendingSubjects: [...pending.values()].map((subject) => subject.sourcePath).sort(),
|
|
2105
|
+
waitingGuids: [],
|
|
2106
|
+
iterations
|
|
2107
|
+
}
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
const knownGuids = /* @__PURE__ */ new Set([...availableGuids, ...staged.keys()]);
|
|
2111
|
+
const missingReferences = /* @__PURE__ */ new Set();
|
|
2112
|
+
for (const product of results.values()) {
|
|
2113
|
+
const contentReads = new Set(product.externalEvidence.map((read) => read.guid.toLowerCase()));
|
|
2114
|
+
for (const asset of product.product.assets) {
|
|
2115
|
+
for (const reference of asset.refs) {
|
|
2116
|
+
const guid = reference.guid.toLowerCase();
|
|
2117
|
+
if (!knownGuids.has(guid) && !contentReads.has(guid)) missingReferences.add(guid);
|
|
2118
|
+
}
|
|
1942
2119
|
}
|
|
1943
|
-
case "material":
|
|
1944
|
-
case "mesh":
|
|
1945
|
-
case "scene":
|
|
1946
|
-
throw new TypeError(`ordinary producer received already-owned ${asset.kind} asset`);
|
|
1947
2120
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
materialProduct
|
|
1953
|
-
);
|
|
1954
|
-
var meshAssetOutputProducer = createSafeProducer("mesh", "mesh-binary/4", meshProduct);
|
|
1955
|
-
var textureAssetOutputProducer = createSafeProducer(
|
|
1956
|
-
"texture",
|
|
1957
|
-
"texture-pack/1",
|
|
1958
|
-
textureProduct
|
|
1959
|
-
);
|
|
1960
|
-
function createSceneAssetOutputProducer(sceneComponents = []) {
|
|
1961
|
-
const schemas = new Map(
|
|
1962
|
-
sceneComponents.map((component) => [component.name, component.fields])
|
|
1963
|
-
);
|
|
1964
|
-
return createSafeProducer("scene", "scene-pack/3", (input) => sceneProduct(input, schemas));
|
|
1965
|
-
}
|
|
1966
|
-
function createPreExternalizedSceneAssetOutputProducer() {
|
|
1967
|
-
return createSafeProducer("scene", "scene-pack/3", preExternalizedSceneProduct);
|
|
1968
|
-
}
|
|
1969
|
-
function createStandardAssetOutputProducerRegistry(sceneComponents = []) {
|
|
1970
|
-
const registry = new AssetOutputProducerRegistry();
|
|
1971
|
-
registry.register(materialAssetOutputProducer);
|
|
1972
|
-
registry.register(meshAssetOutputProducer);
|
|
1973
|
-
registry.register(textureAssetOutputProducer);
|
|
1974
|
-
registry.register(createSceneAssetOutputProducer(sceneComponents));
|
|
1975
|
-
for (const kind of [
|
|
1976
|
-
"equirect",
|
|
1977
|
-
"sampler",
|
|
1978
|
-
"font",
|
|
1979
|
-
"render-pipeline",
|
|
1980
|
-
"tileset",
|
|
1981
|
-
"video",
|
|
1982
|
-
"skeleton",
|
|
1983
|
-
"skin",
|
|
1984
|
-
"animation-clip",
|
|
1985
|
-
"animation-graph",
|
|
1986
|
-
"audio",
|
|
1987
|
-
"particle-effect",
|
|
1988
|
-
"ies-profile"
|
|
1989
|
-
]) {
|
|
1990
|
-
registry.register(createSafeProducer(kind, "ordinary-pod/1", ordinaryPodProduct));
|
|
2121
|
+
if (missingReferences.size > 0) {
|
|
2122
|
+
return err(
|
|
2123
|
+
referenceError("pack-output-reference-missing", "worklist", [...missingReferences].sort())
|
|
2124
|
+
);
|
|
1991
2125
|
}
|
|
1992
|
-
|
|
2126
|
+
const removed = [...options.incomingRefs?.keys() ?? []].filter(
|
|
2127
|
+
(guid) => !staged.has(guid.toLowerCase())
|
|
2128
|
+
);
|
|
2129
|
+
const referencedRemoved = removed.filter(
|
|
2130
|
+
(guid) => (options.incomingRefs?.get(guid) ?? []).length > 0
|
|
2131
|
+
);
|
|
2132
|
+
if (referencedRemoved.length > 0)
|
|
2133
|
+
return err(referenceError("pack-output-reference-conflict", "worklist", referencedRemoved));
|
|
2134
|
+
return ok({
|
|
2135
|
+
products: [...results.values()].map((result) => result.product),
|
|
2136
|
+
buildProducts: [...results.values()],
|
|
2137
|
+
stagedOutputs: [...staged.values()],
|
|
2138
|
+
iterations: pending.size === 0 && options.subjects.length > 0 ? iterations + 1 : iterations
|
|
2139
|
+
});
|
|
1993
2140
|
}
|
|
1994
2141
|
function parseProducerReadiness(value) {
|
|
1995
2142
|
if (value === void 0 || value === "before-consume" || value === "on-demand") {
|
|
@@ -2779,7 +2926,7 @@ async function produceScriptablePackProducts(options) {
|
|
|
2779
2926
|
}
|
|
2780
2927
|
return ok(prepared);
|
|
2781
2928
|
}
|
|
2782
|
-
async function scriptablePackResourceRevision(displaySourcePath,
|
|
2929
|
+
async function scriptablePackResourceRevision(displaySourcePath, digest, sourceClosure) {
|
|
2783
2930
|
const mtimes = await Promise.all(
|
|
2784
2931
|
sourceClosure.map(async (entry) => {
|
|
2785
2932
|
try {
|
|
@@ -2793,7 +2940,7 @@ async function scriptablePackResourceRevision(displaySourcePath, digest3, source
|
|
|
2793
2940
|
})
|
|
2794
2941
|
);
|
|
2795
2942
|
const observedAt = mtimes.length === 0 ? 0 : Math.trunc(Math.max(...mtimes));
|
|
2796
|
-
return { digest
|
|
2943
|
+
return { digest, observedAt, rootId: displaySourcePath };
|
|
2797
2944
|
}
|
|
2798
2945
|
function normalizeCatalogPath(path) {
|
|
2799
2946
|
return path.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
@@ -2826,16 +2973,11 @@ function runtimePublicationFor(context, input) {
|
|
|
2826
2973
|
...input.digest === void 0 ? {} : { digest: input.digest },
|
|
2827
2974
|
...input.inputFingerprint === void 0 ? {} : { inputFingerprint: input.inputFingerprint },
|
|
2828
2975
|
...input.outputs === void 0 ? {} : { outputs: input.outputs },
|
|
2976
|
+
...input.sourceKeys === void 0 ? {} : { sourceKeys: input.sourceKeys },
|
|
2829
2977
|
...input.externalEvidence === void 0 ? {} : { externalEvidence: input.externalEvidence },
|
|
2830
2978
|
generation: context.runtimeBinding?.generation ?? context.generation
|
|
2831
2979
|
};
|
|
2832
|
-
|
|
2833
|
-
const derived = createRuntimePackPublication(publicationInput);
|
|
2834
|
-
const outputs = derived.publication.outputs.map((output) => ({
|
|
2835
|
-
...output,
|
|
2836
|
-
sourceKey: input.sourceKeys?.get(output.guid.toLowerCase()) ?? output.sourceKey
|
|
2837
|
-
}));
|
|
2838
|
-
return createRuntimePackPublication({ ...publicationInput, outputs });
|
|
2980
|
+
return createRuntimePackPublication(publicationInput);
|
|
2839
2981
|
}
|
|
2840
2982
|
function sourceKeysFor(declarations) {
|
|
2841
2983
|
return new Map(
|
|
@@ -3234,6 +3376,7 @@ async function emitAuthoredPack(work, guidSeen, entry, availableGuids) {
|
|
|
3234
3376
|
};
|
|
3235
3377
|
const runtimePublication = runtimePublicationFor(work.context, {
|
|
3236
3378
|
pack: authoredPack,
|
|
3379
|
+
sourceKeys: sourceKeysFor(legacy.assets),
|
|
3237
3380
|
sourcePath: entry.sourcePath,
|
|
3238
3381
|
sourceRevision: declaration.sourceRevision,
|
|
3239
3382
|
packageUrl: prepared.finalized?.packageUrl ?? `${work.context.basePrefix === "/" ? "" : work.context.basePrefix}/assets/${outputGuid}.pack.json`,
|
|
@@ -3816,25 +3959,6 @@ var ImporterRegistry = class {
|
|
|
3816
3959
|
function isRecord2(value) {
|
|
3817
3960
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3818
3961
|
}
|
|
3819
|
-
function stable2(value) {
|
|
3820
|
-
if (value instanceof ArrayBuffer) {
|
|
3821
|
-
return `ArrayBuffer:${JSON.stringify(Array.from(new Uint8Array(value)))}`;
|
|
3822
|
-
}
|
|
3823
|
-
if (ArrayBuffer.isView(value)) {
|
|
3824
|
-
return `${value.constructor.name}:${JSON.stringify(
|
|
3825
|
-
Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength))
|
|
3826
|
-
)}`;
|
|
3827
|
-
}
|
|
3828
|
-
if (Array.isArray(value)) return `[${value.map(stable2).join(",")}]`;
|
|
3829
|
-
if (value !== null && typeof value === "object") {
|
|
3830
|
-
const record2 = value;
|
|
3831
|
-
return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stable2(record2[key])}`).join(",")}}`;
|
|
3832
|
-
}
|
|
3833
|
-
return JSON.stringify(value) ?? "null";
|
|
3834
|
-
}
|
|
3835
|
-
function digest2(value) {
|
|
3836
|
-
return `sha256:${createHash("sha256").update(stable2(value)).digest("hex")}`;
|
|
3837
|
-
}
|
|
3838
3962
|
function generationFromDigest(value) {
|
|
3839
3963
|
const parsed = Number.parseInt(value.slice("sha256:".length, "sha256:".length + 8), 16);
|
|
3840
3964
|
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : 1;
|
|
@@ -3937,7 +4061,7 @@ function assetFromRow(path, row) {
|
|
|
3937
4061
|
});
|
|
3938
4062
|
}
|
|
3939
4063
|
const asset = { ...row.payload, kind: row.kind };
|
|
3940
|
-
return ok({ asset, digest:
|
|
4064
|
+
return ok({ asset, digest: scriptablePackFingerprint(asset) });
|
|
3941
4065
|
}
|
|
3942
4066
|
async function indexPackFiles(assetRoots) {
|
|
3943
4067
|
const files = await collectPackFiles(assetRoots);
|
|
@@ -3998,9 +4122,7 @@ async function indexPackFiles(assetRoots) {
|
|
|
3998
4122
|
byGuid.set(key, asset.value);
|
|
3999
4123
|
}
|
|
4000
4124
|
}
|
|
4001
|
-
const sourceDigest =
|
|
4002
|
-
[...documents].sort((left, right) => stable2(left).localeCompare(stable2(right)))
|
|
4003
|
-
);
|
|
4125
|
+
const sourceDigest = scriptablePackFingerprint(documents.map(scriptablePackFingerprint).sort());
|
|
4004
4126
|
return ok({
|
|
4005
4127
|
generation: generationFromDigest(sourceDigest),
|
|
4006
4128
|
assets: byGuid
|
|
@@ -4030,22 +4152,6 @@ function createScriptablePackFileAssetSnapshotSource(options) {
|
|
|
4030
4152
|
}
|
|
4031
4153
|
};
|
|
4032
4154
|
}
|
|
4033
|
-
function stable3(value) {
|
|
4034
|
-
if (ArrayBuffer.isView(value)) {
|
|
4035
|
-
return `${value.constructor.name}:${JSON.stringify(Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)))}`;
|
|
4036
|
-
}
|
|
4037
|
-
if (Array.isArray(value)) return `[${value.map(stable3).join(",")}]`;
|
|
4038
|
-
if (value !== null && typeof value === "object") {
|
|
4039
|
-
const record2 = value;
|
|
4040
|
-
return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stable3(record2[key])}`).join(",")}}`;
|
|
4041
|
-
}
|
|
4042
|
-
return JSON.stringify(value) ?? "null";
|
|
4043
|
-
}
|
|
4044
|
-
async function assetDigest(asset) {
|
|
4045
|
-
const bytes2 = new TextEncoder().encode(stable3(asset));
|
|
4046
|
-
const digest3 = await globalThis.crypto.subtle.digest("SHA-256", bytes2);
|
|
4047
|
-
return `sha256:${Array.from(new Uint8Array(digest3), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
4048
|
-
}
|
|
4049
4155
|
function cycleError(stack, owner, guid) {
|
|
4050
4156
|
const cycleStart = stack.indexOf(owner);
|
|
4051
4157
|
const cycle = [...stack.slice(cycleStart), owner];
|
|
@@ -4123,7 +4229,7 @@ function createScriptablePackStagedAssetSnapshotSource(options) {
|
|
|
4123
4229
|
next.set(outputGuid, {
|
|
4124
4230
|
asset: structuredClone(output.asset),
|
|
4125
4231
|
generation: options.generation,
|
|
4126
|
-
digest: output.digest ?? await
|
|
4232
|
+
digest: output.digest ?? await scriptablePackFingerprint(output.asset)
|
|
4127
4233
|
});
|
|
4128
4234
|
}
|
|
4129
4235
|
for (const declared of owner.guids) {
|
|
@@ -4537,8 +4643,8 @@ function validatePublicationArtifactClosure(input) {
|
|
|
4537
4643
|
return ok(publicationArtifacts(input.transport));
|
|
4538
4644
|
}
|
|
4539
4645
|
function projectImportPublication(input, head, observedAt) {
|
|
4540
|
-
const
|
|
4541
|
-
const revision = { digest
|
|
4646
|
+
const digest = head.currentKey ?? input.desiredKey;
|
|
4647
|
+
const revision = { digest, observedAt, rootId: input.root };
|
|
4542
4648
|
const published = new Set(input.publishedGuids.map((guid) => guid.toLowerCase()));
|
|
4543
4649
|
const catalog = input.nextCatalog.map((row) => {
|
|
4544
4650
|
if (!published.has(row.guid.toLowerCase())) return row;
|