@forgeax/engine-import 0.1.20 → 0.1.23
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 +91 -3
- package/dist/__tests__/ies-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/ies-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/ies-producer.unit.test.d.ts +2 -0
- package/dist/__tests__/ies-producer.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-lod-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-lod-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-lod-reimport.integration.test.d.ts +2 -0
- package/dist/__tests__/mesh-lod-reimport.integration.test.d.ts.map +1 -0
- package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts +2 -0
- package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-file-snapshot.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-file-snapshot.unit.test.d.ts.map +1 -0
- package/dist/__tests__/source-package-publication-lod.integration.test.d.ts +2 -0
- package/dist/__tests__/source-package-publication-lod.integration.test.d.ts.map +1 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.mjs +230 -3
- package/dist/browser.mjs.map +1 -1
- package/dist/build-production.d.ts +1 -8
- package/dist/build-production.d.ts.map +1 -1
- package/dist/ies/ies-importer.d.ts +4 -0
- package/dist/ies/ies-importer.d.ts.map +1 -0
- package/dist/ies/parse-lm63.d.ts +13 -0
- package/dist/ies/parse-lm63.d.ts.map +1 -0
- package/dist/ies/resample-type-c.d.ts +4 -0
- package/dist/ies/resample-type-c.d.ts.map +1 -0
- package/dist/import-runner.d.ts.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +2550 -1029
- package/dist/index.mjs.map +1 -1
- package/dist/mesh-bin.d.ts.map +1 -1
- package/dist/mesh-bin.mjs +29 -1
- package/dist/mesh-bin.mjs.map +1 -1
- package/dist/mesh-lod.d.ts +60 -0
- package/dist/mesh-lod.d.ts.map +1 -0
- package/dist/parameterized-scriptable-pack.d.ts +68 -0
- package/dist/parameterized-scriptable-pack.d.ts.map +1 -0
- package/dist/scriptable-pack-file-snapshot.d.ts +71 -0
- package/dist/scriptable-pack-file-snapshot.d.ts.map +1 -0
- package/dist/scriptable-pack-host.d.ts +70 -39
- package/dist/scriptable-pack-host.d.ts.map +1 -1
- package/dist/scriptable-pack-output-producers.d.ts +1 -0
- package/dist/scriptable-pack-output-producers.d.ts.map +1 -1
- package/dist/scriptable-pack-staged-snapshot.d.ts +2 -2
- package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -1
- package/dist/scriptable-pack.d.ts +13 -26
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/source-package-errors.d.ts.map +1 -1
- package/dist/source-package-publication.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/__tests__/ies-contract.unit.test.ts +31 -0
- package/src/__tests__/ies-producer.unit.test.ts +104 -0
- package/src/__tests__/mesh-bin.test.ts +69 -0
- package/src/__tests__/mesh-lod-contract.unit.test.ts +92 -0
- package/src/__tests__/mesh-lod-reimport.integration.test.ts +15 -0
- package/src/__tests__/parameterized-scriptable-pack.unit.test.ts +270 -0
- package/src/__tests__/scriptable-pack-file-snapshot.unit.test.ts +177 -0
- package/src/__tests__/scriptable-pack-host.unit.test.ts +196 -4
- package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +7 -1
- package/src/__tests__/scriptable-pack-staged-snapshot.unit.test.ts +43 -12
- package/src/__tests__/source-package-publication-lod.integration.test.ts +29 -0
- package/src/__tests__/source-package-publication.integration.test.ts +26 -1
- package/src/browser.ts +5 -0
- package/src/build-production.ts +498 -141
- package/src/ies/ies-importer.ts +126 -0
- package/src/ies/parse-lm63.ts +98 -0
- package/src/ies/resample-type-c.ts +108 -0
- package/src/import-runner.ts +58 -0
- package/src/index.ts +49 -19
- package/src/mesh-bin.ts +38 -0
- package/src/mesh-lod.ts +265 -0
- package/src/parameterized-scriptable-pack.ts +725 -0
- package/src/scriptable-pack-file-snapshot.ts +346 -0
- package/src/scriptable-pack-host.ts +790 -343
- package/src/scriptable-pack-output-producers.ts +58 -1
- package/src/scriptable-pack-staged-snapshot.ts +12 -11
- package/src/scriptable-pack.ts +16 -485
- package/src/source-package-errors.ts +6 -0
- package/src/source-package-publication.ts +32 -2
- package/dist/.tsbuildinfo +0 -1
- package/dist/__tests__/scriptable-pack-product.contract.test.d.ts +0 -2
- package/dist/__tests__/scriptable-pack-product.contract.test.d.ts.map +0 -1
- package/dist/__tests__/scriptable-pack.integration.test.d.ts +0 -2
- package/dist/__tests__/scriptable-pack.integration.test.d.ts.map +0 -1
- package/dist/scriptable-source-package.d.ts +0 -14
- package/dist/scriptable-source-package.d.ts.map +0 -1
- package/src/__tests__/scriptable-pack-product.contract.test.ts +0 -46
- package/src/__tests__/scriptable-pack.integration.test.ts +0 -435
- package/src/scriptable-source-package.ts +0 -88
package/dist/index.mjs
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import { ok, validateSourceOverrideMap, ImportError, IMPORT_ERROR_HINTS, canonicalizeSourceOverrides,
|
|
1
|
+
import { ok, err, validateSourceOverrideMap, ImportError, IMPORT_ERROR_HINTS, canonicalizeSourceOverrides, AssetError, IES_PROFILE_HEIGHT, IES_PROFILE_WIDTH, IES_PROFILE_BYTE_LENGTH, MATERIAL_TEXTURE_SLOTS, deriveTextureLayout } from '@forgeax/engine-types';
|
|
2
2
|
export { IMPORT_ERROR_HINTS, ImportError } from '@forgeax/engine-types';
|
|
3
|
-
import { resolve, isAbsolute, relative
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { resolve, dirname, isAbsolute, relative } from 'path';
|
|
4
|
+
import { resolveAssetSource, finalizePackageTransportSource, packageTransportRevision, catalogSourcePathFor, buildCatalogProjection, projectPackageCatalog, createRuntimePackPublication, metaPathForGuid } from '@forgeax/engine-pack/build';
|
|
5
|
+
import { AssetGuid as AssetGuid$1, PackageId as PackageId$1 } from '@forgeax/engine-pack/guid';
|
|
6
|
+
import { resolvePackParameterValues, PackageId, isValidPackSourceKey, AssetGuid, parseParameterizedPackJson, projectDirectPackJson, isScriptablePackAssetKind, projectScriptablePackSceneComponents, resolvePackParameterInheritance } from '@forgeax/engine-pack/source';
|
|
7
|
+
import { loadParameterizedScriptablePack } from '@forgeax/engine-pack/source-node';
|
|
8
|
+
import { stat, readFile, mkdir, writeFile, rename, rm, readdir } from 'fs/promises';
|
|
7
9
|
import { createAcceptedPublication, ddcOutputDigest, DdcGenerationSession, DdcLifecycle } from '@forgeax/engine-ddc';
|
|
8
|
-
import {
|
|
10
|
+
import { deriveVertexLayoutProjection, normalizeMeshPayload } from '@forgeax/engine-geometry';
|
|
11
|
+
import { BUILTIN_MESH_ASSETS } from '@forgeax/engine-pack/builtin';
|
|
9
12
|
import { NativeCookerRegistry } from '@forgeax/engine-pack/native-cooker';
|
|
10
|
-
import {
|
|
11
|
-
import { inventoryScriptablePackSource } from '@forgeax/engine-pack/source-node';
|
|
13
|
+
import { isEngineMaterial } from '@forgeax/engine-shader';
|
|
12
14
|
import { externalizeSceneAsset } from '@forgeax/engine-scene';
|
|
13
15
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
14
16
|
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
15
|
-
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
16
17
|
import { MESH_BIN_HEADER_V4_BYTES, writeMeshBinHeader } from '@forgeax/engine-pack/mesh-bin-contract';
|
|
17
|
-
import {
|
|
18
|
+
import { randomUUID, createHash } from 'crypto';
|
|
19
|
+
import { validatePack, isScriptablePackAssetKind as isScriptablePackAssetKind$1 } from '@forgeax/engine-pack';
|
|
18
20
|
|
|
19
21
|
// src/index.ts
|
|
20
22
|
function invalidProduct(field) {
|
|
@@ -42,8 +44,8 @@ async function sha256Hex(bytes2) {
|
|
|
42
44
|
if (subtle === void 0) throw new Error("Web Crypto API is required for importer digests");
|
|
43
45
|
const owned = new Uint8Array(bytes2.byteLength);
|
|
44
46
|
owned.set(bytes2);
|
|
45
|
-
const
|
|
46
|
-
return Array.from(new Uint8Array(
|
|
47
|
+
const digest3 = await subtle.digest("SHA-256", owned.buffer);
|
|
48
|
+
return Array.from(new Uint8Array(digest3), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
47
49
|
}
|
|
48
50
|
async function artifactDigest(bytes2) {
|
|
49
51
|
return `sha256:${await sha256Hex(bytes2)}`;
|
|
@@ -106,25 +108,192 @@ function finalizeImportProducts(product, inputFingerprint) {
|
|
|
106
108
|
const products = [];
|
|
107
109
|
for (const asset of product.assets) {
|
|
108
110
|
const artifacts = await artifactDescriptors(asset.artifacts);
|
|
109
|
-
const
|
|
111
|
+
const digest3 = await productDigest(asset, artifacts);
|
|
110
112
|
products.push({
|
|
111
113
|
guid: asset.guid,
|
|
112
114
|
payload: asset.payload,
|
|
113
115
|
refs: asset.refs.map((ref2) => ref2.guid),
|
|
114
116
|
artifacts,
|
|
115
|
-
digest,
|
|
117
|
+
digest: digest3,
|
|
116
118
|
receipt: {
|
|
117
119
|
guid: asset.guid,
|
|
118
120
|
origin: "sourceMeta",
|
|
119
121
|
status: "succeeded",
|
|
120
122
|
inputFingerprint,
|
|
121
|
-
outputDigest:
|
|
123
|
+
outputDigest: digest3
|
|
122
124
|
}
|
|
123
125
|
});
|
|
124
126
|
}
|
|
125
127
|
return products;
|
|
126
128
|
})();
|
|
127
129
|
}
|
|
130
|
+
function deriveDefaultLodScreenCoverages(levelCount) {
|
|
131
|
+
if (!Number.isInteger(levelCount) || levelCount < 1 || levelCount > 8) {
|
|
132
|
+
throw new RangeError("levelCount must be an integer in [1, 8]");
|
|
133
|
+
}
|
|
134
|
+
return Array.from(
|
|
135
|
+
{ length: levelCount - 1 },
|
|
136
|
+
(_, index) => Math.round(0.5 * 0.4 ** index * 1e6) / 1e6
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
function validateMeshLodContract(input) {
|
|
140
|
+
if (input.lods.length > 7) {
|
|
141
|
+
return err({
|
|
142
|
+
code: "mesh-lod-contract-invalid",
|
|
143
|
+
reason: "at most seven lower-detail levels are supported"
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (input.lodHysteresis !== void 0 && (!Number.isFinite(input.lodHysteresis) || input.lodHysteresis < 0 || input.lodHysteresis >= 1)) {
|
|
147
|
+
return err({
|
|
148
|
+
code: "mesh-lod-contract-invalid",
|
|
149
|
+
reason: "lodHysteresis must be finite and in [0, 1)"
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (input.generation !== void 0 && (!Number.isSafeInteger(input.generation) || input.generation < 0)) {
|
|
153
|
+
return err({
|
|
154
|
+
code: "mesh-lod-contract-invalid",
|
|
155
|
+
reason: "generation must be a non-negative safe integer"
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const guids = /* @__PURE__ */ new Set();
|
|
159
|
+
let previous = 1;
|
|
160
|
+
for (const level of input.lods) {
|
|
161
|
+
if (level.meshGuid.trim() === "" || guids.has(level.meshGuid)) {
|
|
162
|
+
return err({
|
|
163
|
+
code: "mesh-lod-contract-invalid",
|
|
164
|
+
reason: "LOD mesh GUIDs must be unique and non-empty"
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (!Number.isFinite(level.screenCoverage) || level.screenCoverage <= 0 || level.screenCoverage > 1) {
|
|
168
|
+
return err({
|
|
169
|
+
code: "mesh-lod-contract-invalid",
|
|
170
|
+
reason: "screenCoverage must be finite and in (0, 1]"
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (level.screenCoverage >= previous) {
|
|
174
|
+
return err({
|
|
175
|
+
code: "mesh-lod-contract-invalid",
|
|
176
|
+
reason: "screenCoverage must strictly decrease by level"
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
guids.add(level.meshGuid);
|
|
180
|
+
previous = level.screenCoverage;
|
|
181
|
+
}
|
|
182
|
+
if (input.refs !== void 0) {
|
|
183
|
+
const refs = new Set(input.refs);
|
|
184
|
+
for (const level of input.lods) {
|
|
185
|
+
if (!refs.has(level.meshGuid)) {
|
|
186
|
+
return err({
|
|
187
|
+
code: "mesh-lod-contract-invalid",
|
|
188
|
+
reason: "every lower-detail mesh GUID must be enclosed by root refs"
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (input.rootMeshGuid !== void 0 && input.refs !== void 0 && !input.refs.includes(input.rootMeshGuid)) {
|
|
194
|
+
return err({
|
|
195
|
+
code: "mesh-lod-contract-invalid",
|
|
196
|
+
reason: "root mesh GUID must be included in refs"
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (input.rootBounds !== void 0) {
|
|
200
|
+
if (!validBounds(input.rootBounds)) {
|
|
201
|
+
return err({
|
|
202
|
+
code: "mesh-lod-contract-invalid",
|
|
203
|
+
reason: "root bounds must be finite and ordered"
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
for (const bounds of input.lodBounds ?? []) {
|
|
207
|
+
if (!validBounds(bounds) || !encloses(input.rootBounds, bounds)) {
|
|
208
|
+
return err({
|
|
209
|
+
code: "mesh-lod-contract-invalid",
|
|
210
|
+
reason: "root bounds must enclose every lower-detail bounds"
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (input.lodBounds !== void 0 && input.lodBounds.length !== input.lods.length) {
|
|
216
|
+
return err({
|
|
217
|
+
code: "mesh-lod-contract-invalid",
|
|
218
|
+
reason: "lod bounds must cover every lower level"
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
if (input.rootMaterialSlots !== void 0 || input.lodMaterialSlots !== void 0) {
|
|
222
|
+
const rootSlots = input.rootMaterialSlots ?? [];
|
|
223
|
+
const lowerSlots = input.lodMaterialSlots ?? [];
|
|
224
|
+
if (lowerSlots.length !== input.lods.length || lowerSlots.some((slots) => !sameSlots(rootSlots, slots))) {
|
|
225
|
+
return err({
|
|
226
|
+
code: "mesh-lod-contract-invalid",
|
|
227
|
+
reason: "lower-detail material slots must preserve root sourceKey order"
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (input.relations !== void 0 && hasCycle(input.relations)) {
|
|
232
|
+
return err({ code: "mesh-lod-contract-invalid", reason: "LOD relations must be acyclic" });
|
|
233
|
+
}
|
|
234
|
+
return ok({ lods: input.lods });
|
|
235
|
+
}
|
|
236
|
+
function validBounds(bounds) {
|
|
237
|
+
const [minX, minY, minZ] = bounds.min;
|
|
238
|
+
const [maxX, maxY, maxZ] = bounds.max;
|
|
239
|
+
return [minX, minY, minZ, maxX, maxY, maxZ].every(Number.isFinite) && minX <= maxX && minY <= maxY && minZ <= maxZ;
|
|
240
|
+
}
|
|
241
|
+
function encloses(root, child) {
|
|
242
|
+
return child.min[0] >= root.min[0] && child.min[1] >= root.min[1] && child.min[2] >= root.min[2] && child.max[0] <= root.max[0] && child.max[1] <= root.max[1] && child.max[2] <= root.max[2];
|
|
243
|
+
}
|
|
244
|
+
function sameSlots(root, child) {
|
|
245
|
+
return root.length === child.length && root.every((slot, index) => slot.sourceKey === child[index]?.sourceKey);
|
|
246
|
+
}
|
|
247
|
+
function hasCycle(relations) {
|
|
248
|
+
const edges = /* @__PURE__ */ new Map();
|
|
249
|
+
for (const relation of relations)
|
|
250
|
+
edges.set(relation.from, [...edges.get(relation.from) ?? [], relation.to]);
|
|
251
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
252
|
+
const visited = /* @__PURE__ */ new Set();
|
|
253
|
+
const visit = (node) => {
|
|
254
|
+
if (visiting.has(node)) return true;
|
|
255
|
+
if (visited.has(node)) return false;
|
|
256
|
+
visiting.add(node);
|
|
257
|
+
for (const next of edges.get(node) ?? []) if (visit(next)) return true;
|
|
258
|
+
visiting.delete(node);
|
|
259
|
+
visited.add(node);
|
|
260
|
+
return false;
|
|
261
|
+
};
|
|
262
|
+
return [...edges.keys()].some(visit);
|
|
263
|
+
}
|
|
264
|
+
function reconcileMeshLodMeta(previous, next) {
|
|
265
|
+
const overlap = Math.min(previous.length, next.length);
|
|
266
|
+
for (let index = 0; index < overlap; index++) {
|
|
267
|
+
const oldEntry = previous[index];
|
|
268
|
+
const nextEntry = next[index];
|
|
269
|
+
if (oldEntry?.sourceKey !== nextEntry?.sourceKey) {
|
|
270
|
+
return err({
|
|
271
|
+
code: "mesh-lod-topology-change",
|
|
272
|
+
reason: "existing LOD source keys must remain prefix-stable",
|
|
273
|
+
previousIndices: [index],
|
|
274
|
+
nextIndices: [index]
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (oldEntry?.meshGuid !== nextEntry?.meshGuid) {
|
|
278
|
+
return err({
|
|
279
|
+
code: "mesh-lod-authority-conflict",
|
|
280
|
+
reason: `sourceKey ${nextEntry?.sourceKey ?? "<missing>"} changed mesh GUID`
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const defaults = deriveDefaultLodScreenCoverages(next.length + 1);
|
|
285
|
+
const lods = next.map((entry, index) => ({
|
|
286
|
+
...entry,
|
|
287
|
+
screenCoverage: previous[index]?.screenCoverage ?? entry.screenCoverage ?? defaults[index] ?? 0
|
|
288
|
+
}));
|
|
289
|
+
const valid = validateMeshLodContract({
|
|
290
|
+
lods: lods.map(({ meshGuid, screenCoverage }) => ({ meshGuid, screenCoverage }))
|
|
291
|
+
});
|
|
292
|
+
if (!valid.ok) return valid;
|
|
293
|
+
return ok({ lods });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/import-runner.ts
|
|
128
297
|
var SHADER_RESERVED_IMPORTER_KEY = "shader";
|
|
129
298
|
function isModuleLoadFailure(e) {
|
|
130
299
|
if (!(e instanceof Error)) return false;
|
|
@@ -135,6 +304,9 @@ function isModuleLoadFailure(e) {
|
|
|
135
304
|
const msg = e.message;
|
|
136
305
|
return msg.includes("Cannot find module") || msg.includes("native addon") || msg.includes(".node");
|
|
137
306
|
}
|
|
307
|
+
function declarationsSourceKey(declarations, guid) {
|
|
308
|
+
return declarations.find((declaration) => declaration.guid === guid)?.sourceKey;
|
|
309
|
+
}
|
|
138
310
|
function normaliseForPack(value) {
|
|
139
311
|
if (value === null || value === void 0) return value;
|
|
140
312
|
if (value instanceof Float32Array || value instanceof Float64Array || value instanceof Uint8Array || value instanceof Uint16Array || value instanceof Uint32Array || value instanceof Int8Array || value instanceof Int16Array || value instanceof Int32Array) {
|
|
@@ -444,6 +616,37 @@ async function runImport(meta, registry, fs) {
|
|
|
444
616
|
);
|
|
445
617
|
}
|
|
446
618
|
const produced = product.assets;
|
|
619
|
+
for (const asset of produced) {
|
|
620
|
+
if (asset.kind !== "mesh" || asset.payload === null || typeof asset.payload !== "object")
|
|
621
|
+
continue;
|
|
622
|
+
const payload = asset.payload;
|
|
623
|
+
if (payload.lods === void 0) continue;
|
|
624
|
+
const lods = payload.lods.map((level) => ({
|
|
625
|
+
meshGuid: typeof level.meshGuid === "string" ? level.meshGuid : typeof level.mesh === "string" ? level.mesh : JSON.stringify(level.mesh),
|
|
626
|
+
screenCoverage: level.screenCoverage
|
|
627
|
+
}));
|
|
628
|
+
const validated = validateMeshLodContract({
|
|
629
|
+
lods,
|
|
630
|
+
...payload.lodHysteresis === void 0 ? {} : { lodHysteresis: payload.lodHysteresis }
|
|
631
|
+
});
|
|
632
|
+
if (!validated.ok) {
|
|
633
|
+
return errResult(
|
|
634
|
+
new ImportError({
|
|
635
|
+
code: validated.error.code,
|
|
636
|
+
expected: "MeshAsset LOD facts to satisfy the shared contract",
|
|
637
|
+
hint: IMPORT_ERROR_HINTS[validated.error.code],
|
|
638
|
+
detail: {
|
|
639
|
+
meshLodSourceKey: declarationsSourceKey(meta.subAssets, asset.guid),
|
|
640
|
+
reason: validated.error.reason,
|
|
641
|
+
...validated.error.code === "mesh-lod-topology-change" ? {
|
|
642
|
+
previousIndices: validated.error.previousIndices,
|
|
643
|
+
nextIndices: validated.error.nextIndices
|
|
644
|
+
} : {}
|
|
645
|
+
}
|
|
646
|
+
})
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
447
650
|
const declared = new Set(meta.subAssets.map((s) => s.guid));
|
|
448
651
|
const producedGuids = new Set(produced.map((a) => a.guid));
|
|
449
652
|
const unexpectedGuids = [...producedGuids].filter((g) => !declared.has(g));
|
|
@@ -575,105 +778,645 @@ function projectImportProductForBuild(product) {
|
|
|
575
778
|
}))
|
|
576
779
|
};
|
|
577
780
|
}
|
|
578
|
-
function
|
|
579
|
-
return
|
|
580
|
-
code: "mesh-bin-payload-invalid",
|
|
581
|
-
subject: "mesh-bin",
|
|
582
|
-
sourceKey,
|
|
583
|
-
expected,
|
|
584
|
-
actual,
|
|
585
|
-
recovery: "re-cook the source with its Meta sidecar through the build-time importer"
|
|
586
|
-
};
|
|
781
|
+
function record(value) {
|
|
782
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
587
783
|
}
|
|
588
|
-
function
|
|
589
|
-
return value
|
|
784
|
+
function isAsset(value) {
|
|
785
|
+
return record(value) && typeof value.kind === "string" && isScriptablePackAssetKind(value.kind);
|
|
590
786
|
}
|
|
591
|
-
function
|
|
592
|
-
|
|
593
|
-
|
|
787
|
+
function clone(value) {
|
|
788
|
+
return structuredClone(value);
|
|
789
|
+
}
|
|
790
|
+
function stable(value) {
|
|
791
|
+
if (value instanceof Uint8Array) return JSON.stringify(Array.from(value));
|
|
792
|
+
if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
|
|
594
793
|
if (value !== null && typeof value === "object") {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
);
|
|
794
|
+
const object = value;
|
|
795
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stable(object[key])}`).join(",")}}`;
|
|
598
796
|
}
|
|
599
|
-
return value;
|
|
797
|
+
return JSON.stringify(value) ?? "null";
|
|
600
798
|
}
|
|
601
|
-
function
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
799
|
+
async function digest(value) {
|
|
800
|
+
const crypto = globalThis.crypto?.subtle;
|
|
801
|
+
if (crypto === void 0) throw new Error("Web Crypto API is required for Pack fingerprints");
|
|
802
|
+
const bytes2 = await crypto.digest("SHA-256", new TextEncoder().encode(stable(value)));
|
|
803
|
+
return `sha256:${Array.from(new Uint8Array(bytes2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
804
|
+
}
|
|
805
|
+
function observedReader(source) {
|
|
806
|
+
const reads = /* @__PURE__ */ new Map();
|
|
807
|
+
const reader = {
|
|
808
|
+
async readByGuid(guid) {
|
|
809
|
+
const key = AssetGuid.format(guid).toLowerCase();
|
|
810
|
+
const cached = reads.get(key);
|
|
811
|
+
if (cached !== void 0) return ok(clone(cached.asset));
|
|
812
|
+
if (source === void 0) {
|
|
813
|
+
return err(
|
|
814
|
+
new AssetError({
|
|
815
|
+
code: "asset-not-found",
|
|
816
|
+
expected: `a published Asset snapshot for content dependency ${key}`,
|
|
817
|
+
hint: "publish the dependency or remove the content read from the Pack build",
|
|
818
|
+
detail: { sourcePath: key }
|
|
819
|
+
})
|
|
820
|
+
);
|
|
614
821
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
822
|
+
const result = await source.readByGuid(guid);
|
|
823
|
+
if (!result.ok) return result;
|
|
824
|
+
const asset = clone(result.value.asset);
|
|
825
|
+
reads.set(key, {
|
|
826
|
+
guid: key,
|
|
827
|
+
asset,
|
|
828
|
+
generation: result.value.generation,
|
|
829
|
+
digest: result.value.digest
|
|
830
|
+
});
|
|
831
|
+
return ok(clone(asset));
|
|
620
832
|
}
|
|
621
|
-
|
|
833
|
+
};
|
|
834
|
+
return { reader, reads };
|
|
835
|
+
}
|
|
836
|
+
function buildContext(packageId, values, reader) {
|
|
837
|
+
if (values === void 0) return { packageId, readByGuid: reader.readByGuid };
|
|
838
|
+
return { packageId, values, readByGuid: reader.readByGuid };
|
|
839
|
+
}
|
|
840
|
+
function sourceKeyFailure(sourcePath, sourceKey) {
|
|
622
841
|
return {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
...payload.morphWeights === void 0 ? {} : { morphWeights: jsonValue(payload.morphWeights) }
|
|
842
|
+
code: "pack-source-key-invalid",
|
|
843
|
+
expected: "a sourceKey matching the Pack source-key grammar",
|
|
844
|
+
hint: "return stable lower-case semantic keys instead of paths or output indexes",
|
|
845
|
+
detail: { sourcePath, sourceKey }
|
|
628
846
|
};
|
|
629
847
|
}
|
|
630
|
-
function
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
848
|
+
function outputValueError(sourcePath, sourceKey, expected, actual) {
|
|
849
|
+
return {
|
|
850
|
+
code: "pack-parameter-invalid",
|
|
851
|
+
expected,
|
|
852
|
+
hint: "repair the build output and rebuild the Pack from a fresh generation",
|
|
853
|
+
detail: { sourcePath, sourceKey, actual: typeof actual === "string" ? actual : typeof actual }
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
function referenceError(code, sourcePath, guids) {
|
|
857
|
+
return {
|
|
858
|
+
code,
|
|
859
|
+
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",
|
|
860
|
+
hint: code === "pack-output-reference-missing" ? "build or publish the referenced Pack before verifying this output" : "migrate incoming references before publishing the topology change",
|
|
861
|
+
detail: { sourcePath, guids: [...guids].sort() }
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function productError(sourcePath, sourceKey, producer, product) {
|
|
865
|
+
if (!record(product)) {
|
|
866
|
+
return outputValueError(
|
|
867
|
+
sourcePath,
|
|
868
|
+
sourceKey,
|
|
869
|
+
`producer ${producer.kind} to return an asset product object`,
|
|
870
|
+
product
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
if (!Array.isArray(product.refs)) {
|
|
874
|
+
return outputValueError(
|
|
875
|
+
sourcePath,
|
|
876
|
+
sourceKey,
|
|
877
|
+
`producer ${producer.kind} to return a refs array`,
|
|
878
|
+
product.refs
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
if (!record(product.artifacts)) {
|
|
882
|
+
return outputValueError(
|
|
883
|
+
sourcePath,
|
|
884
|
+
sourceKey,
|
|
885
|
+
`producer ${producer.kind} to return an artifacts object`,
|
|
886
|
+
product.artifacts
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
const payload = product.payload;
|
|
890
|
+
if (!record(payload) || payload.kind !== producer.kind) {
|
|
891
|
+
return outputValueError(
|
|
892
|
+
sourcePath,
|
|
893
|
+
sourceKey,
|
|
894
|
+
`producer ${producer.kind} to return a matching payload kind`,
|
|
895
|
+
payload
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
for (const ref2 of product.refs) {
|
|
899
|
+
if (!record(ref2) || typeof ref2.guid !== "string" || !AssetGuid.parse(ref2.guid).ok) {
|
|
900
|
+
return outputValueError(
|
|
901
|
+
sourcePath,
|
|
902
|
+
sourceKey,
|
|
903
|
+
"producer refs to contain valid AssetGuid values",
|
|
904
|
+
ref2
|
|
637
905
|
);
|
|
638
906
|
}
|
|
639
|
-
|
|
907
|
+
}
|
|
908
|
+
for (const [key, artifact] of Object.entries(product.artifacts)) {
|
|
909
|
+
if (key.length === 0 || key.startsWith("/") || key.includes("..") || key.includes("\\") || !record(artifact) || typeof artifact.mediaType !== "string" || !(artifact.bytes instanceof Uint8Array)) {
|
|
910
|
+
return outputValueError(
|
|
911
|
+
sourcePath,
|
|
912
|
+
sourceKey,
|
|
913
|
+
"asset-local artifacts with safe keys and bytes",
|
|
914
|
+
key
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
return void 0;
|
|
919
|
+
}
|
|
920
|
+
function errorCode(value) {
|
|
921
|
+
return record(value) && typeof value.code === "string" ? value.code : void 0;
|
|
922
|
+
}
|
|
923
|
+
function normalizedGuidSet(value) {
|
|
924
|
+
return value === void 0 ? void 0 : new Set([...value].map((guid) => guid.toLowerCase()));
|
|
925
|
+
}
|
|
926
|
+
async function buildParameterizedScriptablePack(options) {
|
|
927
|
+
const subjectPackageId = options.subjectPackageId ?? options.definition.packageId;
|
|
928
|
+
const availableGuids = normalizedGuidSet(options.availableGuids);
|
|
929
|
+
const observed = observedReader(options.assetSource);
|
|
930
|
+
let effectiveValues;
|
|
931
|
+
if ("parameters" in options.definition) {
|
|
932
|
+
const resolved = resolvePackParameterValues(
|
|
933
|
+
options.definition,
|
|
934
|
+
options.values ?? {},
|
|
935
|
+
options.inheritedValues
|
|
936
|
+
);
|
|
937
|
+
if (!resolved.ok) return resolved;
|
|
938
|
+
effectiveValues = resolved.value;
|
|
939
|
+
} else if (options.values !== void 0 && Object.keys(options.values).length > 0) {
|
|
940
|
+
return err(
|
|
941
|
+
outputValueError(
|
|
942
|
+
options.sourcePath,
|
|
943
|
+
"$.values",
|
|
944
|
+
"zero-parameter Packs to omit values and instance capabilities",
|
|
945
|
+
options.values
|
|
946
|
+
)
|
|
947
|
+
);
|
|
948
|
+
} else if (options.subjectPackageId !== void 0 && PackageId.format(options.subjectPackageId).toLowerCase() !== PackageId.format(options.definition.packageId).toLowerCase()) {
|
|
949
|
+
return err({
|
|
950
|
+
code: "pack-parent-not-parameterized",
|
|
951
|
+
expected: "a parameterized Pack source for an independent instance packageId",
|
|
952
|
+
hint: "use clone for a zero-parameter Pack instead of building it as an instance",
|
|
953
|
+
detail: {
|
|
954
|
+
sourcePath: options.sourcePath,
|
|
955
|
+
rootPackageId: PackageId.format(options.definition.packageId),
|
|
956
|
+
subjectPackageId: PackageId.format(options.subjectPackageId)
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
let built;
|
|
961
|
+
try {
|
|
962
|
+
const context = buildContext(subjectPackageId, effectiveValues, observed.reader);
|
|
963
|
+
built = options.definition.build(context);
|
|
964
|
+
built = await built;
|
|
965
|
+
} catch (cause) {
|
|
966
|
+
return err(
|
|
967
|
+
new ImportError({
|
|
968
|
+
code: "import-internal-error",
|
|
969
|
+
expected: "Pack build to return a structured Result without throwing",
|
|
970
|
+
hint: "repair the authoring function and return err(...) for expected failures",
|
|
971
|
+
detail: {
|
|
972
|
+
reason: `${options.sourcePath}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
973
|
+
}
|
|
974
|
+
})
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
if (!record(built) || typeof built.ok !== "boolean") {
|
|
978
|
+
return err(
|
|
979
|
+
new ImportError({
|
|
980
|
+
code: "import-internal-error",
|
|
981
|
+
expected: "Pack build to return a Result object",
|
|
982
|
+
hint: "return ok(sourceKeyToAsset) or err(structuredError) from the authoring function",
|
|
983
|
+
detail: { reason: `${options.sourcePath}: malformed build result` }
|
|
984
|
+
})
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
if (!built.ok) {
|
|
988
|
+
if (errorCode(built.error) !== void 0) return err(built.error);
|
|
989
|
+
return err(
|
|
990
|
+
new ImportError({
|
|
991
|
+
code: "import-internal-error",
|
|
992
|
+
expected: "a structured Pack build error",
|
|
993
|
+
hint: "return an error carrying code, expected, hint and detail",
|
|
994
|
+
detail: { reason: `${options.sourcePath}: ${String(built.error)}` }
|
|
995
|
+
})
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
if (!record(built.value)) {
|
|
999
|
+
return err(
|
|
1000
|
+
outputValueError(
|
|
1001
|
+
options.sourcePath,
|
|
1002
|
+
"$",
|
|
1003
|
+
"build to return a sourceKey-to-Asset object",
|
|
1004
|
+
built.value
|
|
1005
|
+
)
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
const imported = [];
|
|
1009
|
+
const stagedOutputs = [];
|
|
1010
|
+
const localGuids = /* @__PURE__ */ new Set();
|
|
1011
|
+
for (const sourceKey of Object.keys(built.value).sort()) {
|
|
1012
|
+
if (!isValidPackSourceKey(sourceKey))
|
|
1013
|
+
return err(sourceKeyFailure(options.sourcePath, sourceKey));
|
|
1014
|
+
const asset = built.value[sourceKey];
|
|
1015
|
+
if (!isAsset(asset)) {
|
|
640
1016
|
return err(
|
|
641
|
-
|
|
1017
|
+
outputValueError(
|
|
1018
|
+
options.sourcePath,
|
|
1019
|
+
sourceKey,
|
|
1020
|
+
"a concrete Asset with a supported kind",
|
|
1021
|
+
asset
|
|
1022
|
+
)
|
|
642
1023
|
);
|
|
643
1024
|
}
|
|
644
|
-
const
|
|
645
|
-
const
|
|
646
|
-
if (
|
|
1025
|
+
const guid = AssetGuid.format(AssetGuid.derive(subjectPackageId, sourceKey));
|
|
1026
|
+
const normalizedGuid = guid.toLowerCase();
|
|
1027
|
+
if (localGuids.has(normalizedGuid) || availableGuids?.has(normalizedGuid)) {
|
|
1028
|
+
return err({
|
|
1029
|
+
code: "pack-guid-collision",
|
|
1030
|
+
expected: "derived output GUIDs to be unique in the global source index",
|
|
1031
|
+
hint: "change the colliding packageId or repair the source index before publishing",
|
|
1032
|
+
detail: { sourcePath: options.sourcePath, guid }
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
localGuids.add(normalizedGuid);
|
|
1036
|
+
const producer = options.outputs.get(asset.kind);
|
|
1037
|
+
if (producer === void 0) {
|
|
647
1038
|
return err(
|
|
648
|
-
|
|
1039
|
+
outputValueError(
|
|
1040
|
+
options.sourcePath,
|
|
649
1041
|
sourceKey,
|
|
650
|
-
|
|
651
|
-
|
|
1042
|
+
`a registered output producer for ${asset.kind}`,
|
|
1043
|
+
asset.kind
|
|
652
1044
|
)
|
|
653
1045
|
);
|
|
654
1046
|
}
|
|
655
|
-
|
|
656
|
-
|
|
1047
|
+
let produced;
|
|
1048
|
+
try {
|
|
1049
|
+
produced = await producer.produce({ guid, sourceKey, asset });
|
|
1050
|
+
} catch (cause) {
|
|
657
1051
|
return err(
|
|
658
|
-
|
|
1052
|
+
new ImportError({
|
|
1053
|
+
code: "import-internal-error",
|
|
1054
|
+
expected: `producer ${producer.kind} to return a structured Result without throwing`,
|
|
1055
|
+
hint: "repair the output producer and return err(...) for expected failures",
|
|
1056
|
+
detail: {
|
|
1057
|
+
reason: `${options.sourcePath}:${sourceKey}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
1058
|
+
}
|
|
1059
|
+
})
|
|
659
1060
|
);
|
|
660
1061
|
}
|
|
661
|
-
if (
|
|
1062
|
+
if (!record(produced) || typeof produced.ok !== "boolean") {
|
|
662
1063
|
return err(
|
|
663
|
-
|
|
1064
|
+
outputValueError(
|
|
1065
|
+
options.sourcePath,
|
|
664
1066
|
sourceKey,
|
|
665
|
-
`
|
|
666
|
-
|
|
1067
|
+
`producer ${producer.kind} to return a Result`,
|
|
1068
|
+
produced
|
|
667
1069
|
)
|
|
668
1070
|
);
|
|
669
1071
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
1072
|
+
if (!produced.ok) {
|
|
1073
|
+
if (errorCode(produced.error) !== void 0) return err(produced.error);
|
|
1074
|
+
return err(
|
|
1075
|
+
new ImportError({
|
|
1076
|
+
code: "import-internal-error",
|
|
1077
|
+
expected: `producer ${producer.kind} to return a structured error`,
|
|
1078
|
+
hint: "return an error carrying code, expected, hint and detail",
|
|
1079
|
+
detail: { reason: `${options.sourcePath}:${sourceKey}: ${String(produced.error)}` }
|
|
1080
|
+
})
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
const invalidProduct2 = productError(options.sourcePath, sourceKey, producer, produced.value);
|
|
1084
|
+
if (invalidProduct2 !== void 0) return err(invalidProduct2);
|
|
1085
|
+
const product2 = produced.value;
|
|
1086
|
+
imported.push({
|
|
1087
|
+
guid,
|
|
1088
|
+
kind: asset.kind,
|
|
1089
|
+
payload: product2.payload,
|
|
1090
|
+
refs: product2.refs,
|
|
1091
|
+
artifacts: product2.artifacts
|
|
1092
|
+
});
|
|
1093
|
+
stagedOutputs.push({
|
|
1094
|
+
guid: AssetGuid.derive(subjectPackageId, sourceKey),
|
|
1095
|
+
sourceKey,
|
|
1096
|
+
asset: clone(asset),
|
|
1097
|
+
digest: await digest(asset)
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
1101
|
+
for (const asset of imported) {
|
|
1102
|
+
for (const ref2 of asset.refs) {
|
|
1103
|
+
const guid = ref2.guid.toLowerCase();
|
|
1104
|
+
if (!localGuids.has(guid)) referenced.add(guid);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
if (options.deferReferenceValidation !== true) {
|
|
1108
|
+
const missing = [...referenced].filter(
|
|
1109
|
+
(guid) => availableGuids !== void 0 && !availableGuids.has(guid)
|
|
1110
|
+
);
|
|
1111
|
+
if (missing.length > 0)
|
|
1112
|
+
return err(referenceError("pack-output-reference-missing", options.sourcePath, missing));
|
|
1113
|
+
}
|
|
1114
|
+
const externalEvidence = [...observed.reads.values()].sort((left, right) => left.guid.localeCompare(right.guid)).map(
|
|
1115
|
+
(read) => ({
|
|
1116
|
+
guid: read.guid,
|
|
1117
|
+
usage: referenced.has(read.guid) ? "both" : "content",
|
|
1118
|
+
generation: read.generation,
|
|
1119
|
+
digest: read.digest
|
|
1120
|
+
})
|
|
1121
|
+
);
|
|
1122
|
+
const inputFingerprint = await digest({
|
|
1123
|
+
packageId: PackageId.format(subjectPackageId),
|
|
1124
|
+
sourcePath: options.sourcePath,
|
|
1125
|
+
sourceClosure: options.sourceClosure ?? [],
|
|
1126
|
+
values: effectiveValues,
|
|
1127
|
+
externalEvidence: externalEvidence.map(({ guid, usage, digest: evidenceDigest }) => ({
|
|
1128
|
+
guid,
|
|
1129
|
+
usage,
|
|
1130
|
+
digest: evidenceDigest
|
|
1131
|
+
})),
|
|
1132
|
+
authoringContractVersion: options.authoringContractVersion ?? "parameterized-pack/1",
|
|
1133
|
+
producerVersions: options.outputs.versions()
|
|
1134
|
+
});
|
|
1135
|
+
const refs = imported.flatMap((asset) => asset.refs);
|
|
1136
|
+
const artifacts = Object.fromEntries(
|
|
1137
|
+
imported.flatMap(
|
|
1138
|
+
(asset) => Object.entries(asset.artifacts).map(([key, artifact]) => [`${asset.guid}/${key}`, artifact])
|
|
1139
|
+
)
|
|
1140
|
+
);
|
|
1141
|
+
const product = createImportProduct({
|
|
1142
|
+
assets: imported,
|
|
1143
|
+
sourceDependencies: (options.sourceClosure ?? []).map((entry) => entry.path),
|
|
1144
|
+
refs,
|
|
1145
|
+
artifacts,
|
|
1146
|
+
receipts: imported.map((asset) => ({
|
|
1147
|
+
guid: asset.guid,
|
|
1148
|
+
origin: "authoredPack",
|
|
1149
|
+
status: "succeeded",
|
|
1150
|
+
inputFingerprint
|
|
1151
|
+
})),
|
|
1152
|
+
diagnostics: [],
|
|
1153
|
+
sourceRevision: inputFingerprint,
|
|
1154
|
+
sourceKey: options.sourcePath
|
|
1155
|
+
});
|
|
1156
|
+
if (!product.ok) return err(product.error);
|
|
1157
|
+
return ok({
|
|
1158
|
+
product: product.value,
|
|
1159
|
+
stagedOutputs,
|
|
1160
|
+
externalEvidence,
|
|
1161
|
+
inputFingerprint,
|
|
1162
|
+
...options.publication === void 0 ? {} : { publication: options.publication }
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
function stagedSource(staged, fallback) {
|
|
1166
|
+
return {
|
|
1167
|
+
async readByGuid(guid) {
|
|
1168
|
+
const key = AssetGuid.format(guid).toLowerCase();
|
|
1169
|
+
const local = staged.get(key);
|
|
1170
|
+
if (local !== void 0) {
|
|
1171
|
+
return ok({
|
|
1172
|
+
asset: clone(local.asset),
|
|
1173
|
+
generation: 1,
|
|
1174
|
+
digest: local.digest ?? "sha256:staged"
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
if (fallback === void 0) {
|
|
1178
|
+
return err({
|
|
1179
|
+
code: "asset-not-found",
|
|
1180
|
+
expected: "a staged or published content dependency",
|
|
1181
|
+
hint: "wait for the dependency subject to materialize",
|
|
1182
|
+
detail: { guid: key }
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
return fallback.readByGuid(guid);
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
async function buildParameterizedScriptablePackWorklist(options) {
|
|
1190
|
+
const orderedSubjects = [...options.subjects].sort(
|
|
1191
|
+
(left, right) => left.sourcePath.localeCompare(right.sourcePath)
|
|
1192
|
+
);
|
|
1193
|
+
const pending = new Map(
|
|
1194
|
+
orderedSubjects.map((subject, index) => [`${index}:${subject.sourcePath}`, subject])
|
|
1195
|
+
);
|
|
1196
|
+
const staged = /* @__PURE__ */ new Map();
|
|
1197
|
+
const results = /* @__PURE__ */ new Map();
|
|
1198
|
+
const availableGuids = /* @__PURE__ */ new Set([
|
|
1199
|
+
...normalizedGuidSet(options.availableGuids) ?? [],
|
|
1200
|
+
...BUILTIN_MESH_ASSETS.map((asset) => asset.guid.toLowerCase())
|
|
1201
|
+
]);
|
|
1202
|
+
const maxPasses = options.maxPasses ?? Math.max(1, options.subjects.length + 1);
|
|
1203
|
+
let iterations = 0;
|
|
1204
|
+
for (; iterations < maxPasses && pending.size > 0; iterations += 1) {
|
|
1205
|
+
let progress = false;
|
|
1206
|
+
const waiting = /* @__PURE__ */ new Set();
|
|
1207
|
+
for (const [key, subject] of pending) {
|
|
1208
|
+
const result = await buildParameterizedScriptablePack({
|
|
1209
|
+
definition: subject.definition,
|
|
1210
|
+
sourcePath: subject.sourcePath,
|
|
1211
|
+
...subject.subjectPackageId === void 0 ? {} : { subjectPackageId: subject.subjectPackageId },
|
|
1212
|
+
...subject.values === void 0 ? {} : { values: subject.values },
|
|
1213
|
+
...subject.inheritedValues === void 0 ? {} : { inheritedValues: subject.inheritedValues },
|
|
1214
|
+
...subject.sourceClosure === void 0 ? {} : { sourceClosure: subject.sourceClosure },
|
|
1215
|
+
outputs: options.outputs,
|
|
1216
|
+
assetSource: stagedSource(staged, options.assetSource),
|
|
1217
|
+
availableGuids: /* @__PURE__ */ new Set([...availableGuids, ...staged.keys()]),
|
|
1218
|
+
deferReferenceValidation: true
|
|
1219
|
+
});
|
|
1220
|
+
if (result.ok) {
|
|
1221
|
+
results.set(key, result.value);
|
|
1222
|
+
for (const output of result.value.stagedOutputs)
|
|
1223
|
+
staged.set(AssetGuid.format(output.guid).toLowerCase(), output);
|
|
1224
|
+
pending.delete(key);
|
|
1225
|
+
progress = true;
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
if (errorCode(result.error) === "asset-not-found") {
|
|
1229
|
+
const detail = record(result.error) && record(result.error.detail) ? result.error.detail : void 0;
|
|
1230
|
+
const guid = detail !== void 0 && typeof detail.guid === "string" ? detail.guid : void 0;
|
|
1231
|
+
if (guid !== void 0) waiting.add(guid.toLowerCase());
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
return result;
|
|
1235
|
+
}
|
|
1236
|
+
if (pending.size === 0) break;
|
|
1237
|
+
if (!progress) {
|
|
1238
|
+
return err({
|
|
1239
|
+
code: "pack-content-dependency-stalled",
|
|
1240
|
+
expected: "the content dependency worklist to make progress",
|
|
1241
|
+
hint: "inspect waitingGuids and repair the missing output or content-read cycle, then rebuild",
|
|
1242
|
+
detail: {
|
|
1243
|
+
waitingGuids: [...waiting].sort(),
|
|
1244
|
+
pendingSubjects: [...pending.values()].map((subject) => subject.sourcePath).sort(),
|
|
1245
|
+
iterations: iterations + 1
|
|
1246
|
+
}
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
if (pending.size > 0) {
|
|
1251
|
+
return err({
|
|
1252
|
+
code: "pack-content-dependency-stalled",
|
|
1253
|
+
expected: "the content dependency worklist to finish within its bounded retry budget",
|
|
1254
|
+
hint: "inspect pendingSubjects and waitingGuids, then repair the dependency graph",
|
|
1255
|
+
detail: {
|
|
1256
|
+
pendingSubjects: [...pending.values()].map((subject) => subject.sourcePath).sort(),
|
|
1257
|
+
waitingGuids: [],
|
|
1258
|
+
iterations
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
const knownGuids = /* @__PURE__ */ new Set([...availableGuids, ...staged.keys()]);
|
|
1263
|
+
const missingReferences = /* @__PURE__ */ new Set();
|
|
1264
|
+
for (const product of results.values()) {
|
|
1265
|
+
const contentReads = new Set(product.externalEvidence.map((read) => read.guid.toLowerCase()));
|
|
1266
|
+
for (const asset of product.product.assets) {
|
|
1267
|
+
for (const reference of asset.refs) {
|
|
1268
|
+
const guid = reference.guid.toLowerCase();
|
|
1269
|
+
if (!knownGuids.has(guid) && !contentReads.has(guid)) missingReferences.add(guid);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
if (missingReferences.size > 0) {
|
|
1274
|
+
return err(
|
|
1275
|
+
referenceError("pack-output-reference-missing", "worklist", [...missingReferences].sort())
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
const removed = [...options.incomingRefs?.keys() ?? []].filter(
|
|
1279
|
+
(guid) => !staged.has(guid.toLowerCase())
|
|
1280
|
+
);
|
|
1281
|
+
const referencedRemoved = removed.filter(
|
|
1282
|
+
(guid) => (options.incomingRefs?.get(guid) ?? []).length > 0
|
|
1283
|
+
);
|
|
1284
|
+
if (referencedRemoved.length > 0)
|
|
1285
|
+
return err(referenceError("pack-output-reference-conflict", "worklist", referencedRemoved));
|
|
1286
|
+
return ok({
|
|
1287
|
+
products: [...results.values()].map((result) => result.product),
|
|
1288
|
+
buildProducts: [...results.values()],
|
|
1289
|
+
stagedOutputs: [...staged.values()],
|
|
1290
|
+
iterations: pending.size === 0 && options.subjects.length > 0 ? iterations + 1 : iterations
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
function failure(sourceKey, expected, actual) {
|
|
1294
|
+
return {
|
|
1295
|
+
code: "mesh-bin-payload-invalid",
|
|
1296
|
+
subject: "mesh-bin",
|
|
1297
|
+
sourceKey,
|
|
1298
|
+
expected,
|
|
1299
|
+
actual,
|
|
1300
|
+
recovery: "re-cook the source with its Meta sidecar through the build-time importer"
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
function asAttributeMap(value) {
|
|
1304
|
+
return value ?? {};
|
|
1305
|
+
}
|
|
1306
|
+
function jsonValue(value) {
|
|
1307
|
+
if (value instanceof Float32Array || value instanceof Uint16Array) return Array.from(value);
|
|
1308
|
+
if (Array.isArray(value)) return value.map(jsonValue);
|
|
1309
|
+
if (value !== null && typeof value === "object") {
|
|
1310
|
+
return Object.fromEntries(
|
|
1311
|
+
Object.entries(value).map(([key, nested]) => [key, jsonValue(nested)])
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
return value;
|
|
1315
|
+
}
|
|
1316
|
+
function refsMeta(payload, refs) {
|
|
1317
|
+
const materialSlots = (payload.materialSlots ?? [{ slotName: "Default" }]).map(
|
|
1318
|
+
(slot, slotIndex) => {
|
|
1319
|
+
const defaultMaterial = slot.defaultMaterial;
|
|
1320
|
+
let defaultMaterialRef;
|
|
1321
|
+
if (defaultMaterial !== void 0) {
|
|
1322
|
+
const guid = AssetGuid$1.format(defaultMaterial);
|
|
1323
|
+
defaultMaterialRef = refs.findIndex((candidate) => candidate.toLowerCase() === guid);
|
|
1324
|
+
if (defaultMaterialRef < 0) {
|
|
1325
|
+
throw new Error(
|
|
1326
|
+
`material slot ${slotIndex} default material ${guid} is absent from refs`
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
return {
|
|
1331
|
+
slotName: slot.slotName,
|
|
1332
|
+
...slot.sourceKey === void 0 ? {} : { sourceKey: slot.sourceKey },
|
|
1333
|
+
...defaultMaterialRef === void 0 ? {} : { defaultMaterialRef }
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
);
|
|
1337
|
+
if (payload.lods !== void 0 && payload.lods.length > 7) {
|
|
1338
|
+
throw new Error("MeshAsset LOD chain supports at most seven lower-detail levels");
|
|
1339
|
+
}
|
|
1340
|
+
let previousCoverage = 1;
|
|
1341
|
+
const seenLodGuids = /* @__PURE__ */ new Set();
|
|
1342
|
+
const lods = payload.lods?.map((lod, lodIndex) => {
|
|
1343
|
+
const guid = AssetGuid$1.format(lod.mesh).toLowerCase();
|
|
1344
|
+
const meshRef = refs.findIndex((candidate) => candidate.toLowerCase() === guid);
|
|
1345
|
+
if (meshRef < 0) {
|
|
1346
|
+
throw new Error(`LOD ${lodIndex} mesh ${guid} is absent from refs`);
|
|
1347
|
+
}
|
|
1348
|
+
if (seenLodGuids.has(guid)) {
|
|
1349
|
+
throw new Error(`LOD ${lodIndex} mesh ${guid} is duplicated`);
|
|
1350
|
+
}
|
|
1351
|
+
if (!Number.isFinite(lod.screenCoverage) || lod.screenCoverage <= 0 || lod.screenCoverage > 1 || lod.screenCoverage >= previousCoverage) {
|
|
1352
|
+
throw new Error(
|
|
1353
|
+
`LOD ${lodIndex} screenCoverage must be finite, in (0, 1], and strictly decreasing`
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
seenLodGuids.add(guid);
|
|
1357
|
+
previousCoverage = lod.screenCoverage;
|
|
1358
|
+
return { meshRef, screenCoverage: lod.screenCoverage };
|
|
1359
|
+
});
|
|
1360
|
+
if (payload.lodHysteresis !== void 0 && (!Number.isFinite(payload.lodHysteresis) || payload.lodHysteresis < 0 || payload.lodHysteresis >= 1)) {
|
|
1361
|
+
throw new Error("lodHysteresis must be finite and in [0, 1)");
|
|
1362
|
+
}
|
|
1363
|
+
return {
|
|
1364
|
+
submeshes: payload.submeshes === void 0 || payload.submeshes.length === 0 ? [{ indexOffset: 0, indexCount: payload.indices?.length ?? 0, materialSlot: 0 }] : payload.submeshes,
|
|
1365
|
+
materialSlots,
|
|
1366
|
+
...payload.aabb === void 0 ? {} : { aabb: jsonValue(payload.aabb) },
|
|
1367
|
+
...payload.morphTargets === void 0 ? {} : { morphTargets: jsonValue(payload.morphTargets) },
|
|
1368
|
+
...payload.morphWeights === void 0 ? {} : { morphWeights: jsonValue(payload.morphWeights) },
|
|
1369
|
+
...lods === void 0 ? {} : { lods },
|
|
1370
|
+
...payload.lodHysteresis === void 0 ? {} : { lodHysteresis: payload.lodHysteresis }
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
function packMeshBinV4(payload, sourceKey, refs = []) {
|
|
1374
|
+
try {
|
|
1375
|
+
const vertices = payload.vertices;
|
|
1376
|
+
const indices = payload.indices;
|
|
1377
|
+
if (!(vertices instanceof Float32Array)) {
|
|
1378
|
+
return err(
|
|
1379
|
+
failure(sourceKey, "Float32Array interleaved vertices", "vertices is not Float32Array")
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
if (indices !== void 0 && !(indices instanceof Uint16Array || indices instanceof Uint32Array)) {
|
|
1383
|
+
return err(
|
|
1384
|
+
failure(sourceKey, "Uint16Array or Uint32Array indices", "indices has an unsupported type")
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
const attributes = asAttributeMap(payload.attributes);
|
|
1388
|
+
const projection = deriveVertexLayoutProjection(attributes);
|
|
1389
|
+
if (projection.attributes.length === 0 || projection.arrayStride === 0) {
|
|
1390
|
+
return err(
|
|
1391
|
+
failure(
|
|
1392
|
+
sourceKey,
|
|
1393
|
+
"a non-empty canonical geometry projection",
|
|
1394
|
+
"projection has no attributes"
|
|
1395
|
+
)
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
const vertexCount = payload.vertexCount ?? vertices.byteLength / projection.arrayStride;
|
|
1399
|
+
if (!Number.isSafeInteger(vertexCount) || vertexCount < 0) {
|
|
1400
|
+
return err(
|
|
1401
|
+
failure(sourceKey, "a non-negative safe vertex cardinality", `vertexCount=${vertexCount}`)
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
if (vertices.byteLength !== vertexCount * projection.arrayStride) {
|
|
1405
|
+
return err(
|
|
1406
|
+
failure(
|
|
1407
|
+
sourceKey,
|
|
1408
|
+
`vertices.byteLength=${vertexCount * projection.arrayStride}`,
|
|
1409
|
+
`vertices.byteLength=${vertices.byteLength}; stride=${projection.arrayStride}`
|
|
1410
|
+
)
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
for (const attribute of projection.attributes) {
|
|
1414
|
+
const value = attributes[attribute.key];
|
|
1415
|
+
const components = attribute.byteLength / (attribute.format === "uint16x4" ? 2 : 4);
|
|
1416
|
+
if (value === void 0 || !(value instanceof Float32Array) && !(value instanceof Uint16Array) || value.length !== vertexCount * components) {
|
|
1417
|
+
return err(
|
|
1418
|
+
failure(
|
|
1419
|
+
sourceKey,
|
|
677
1420
|
`${attribute.key} cardinality=${vertexCount * components}`,
|
|
678
1421
|
`${attribute.key} cardinality=${value?.byteLength ?? "missing"}`
|
|
679
1422
|
)
|
|
@@ -747,41 +1490,16 @@ function packMeshBinV4(payload, sourceKey, refs = []) {
|
|
|
747
1490
|
);
|
|
748
1491
|
}
|
|
749
1492
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
if (first === void 0) return "";
|
|
753
|
-
const firstNormalized = first.replaceAll("\\", "/");
|
|
754
|
-
const firstDirectory = firstNormalized.slice(0, firstNormalized.lastIndexOf("/"));
|
|
755
|
-
const parts = firstDirectory.split("/");
|
|
756
|
-
let length = parts.length;
|
|
757
|
-
for (const path of paths.slice(1)) {
|
|
758
|
-
const normalized = path.replaceAll("\\", "/");
|
|
759
|
-
const candidate = normalized.slice(0, normalized.lastIndexOf("/")).split("/");
|
|
760
|
-
length = Math.min(length, candidate.length);
|
|
761
|
-
for (let index = 0; index < length; index += 1) {
|
|
762
|
-
if (parts[index] !== candidate[index]) {
|
|
763
|
-
length = index;
|
|
764
|
-
break;
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
return parts.slice(0, length).join("/");
|
|
769
|
-
}
|
|
770
|
-
function stableSourceClosure(closure) {
|
|
771
|
-
const root = commonPathPrefix(closure.map((entry) => entry.path));
|
|
772
|
-
return closure.map((entry) => ({
|
|
773
|
-
path: entry.path.replaceAll("\\", "/").slice(root.length).replace(/^\/+/, ""),
|
|
774
|
-
digest: entry.digest
|
|
775
|
-
})).sort((left, right) => left.path.localeCompare(right.path));
|
|
776
|
-
}
|
|
1493
|
+
|
|
1494
|
+
// src/scriptable-pack.ts
|
|
777
1495
|
var AssetOutputProducerRegistry = class {
|
|
778
1496
|
producers = /* @__PURE__ */ new Map();
|
|
779
1497
|
register(producer) {
|
|
780
1498
|
if (producer.kind.trim().length === 0 || producer.version.trim().length === 0) {
|
|
781
|
-
throw new TypeError("
|
|
1499
|
+
throw new TypeError("Pack output producer kind and version must be non-empty");
|
|
782
1500
|
}
|
|
783
1501
|
if (typeof producer.produce !== "function") {
|
|
784
|
-
throw new TypeError(`
|
|
1502
|
+
throw new TypeError(`Pack output producer ${producer.kind} must expose produce`);
|
|
785
1503
|
}
|
|
786
1504
|
this.producers.set(producer.kind, producer);
|
|
787
1505
|
}
|
|
@@ -794,327 +1512,6 @@ var AssetOutputProducerRegistry = class {
|
|
|
794
1512
|
);
|
|
795
1513
|
}
|
|
796
1514
|
};
|
|
797
|
-
function privateClone(value) {
|
|
798
|
-
return structuredClone(value);
|
|
799
|
-
}
|
|
800
|
-
function record(value) {
|
|
801
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
802
|
-
}
|
|
803
|
-
function isAsset(value) {
|
|
804
|
-
return record(value) && typeof value.kind === "string" && isScriptablePackAssetKind(value.kind);
|
|
805
|
-
}
|
|
806
|
-
function isStructuredDomainError(value) {
|
|
807
|
-
return value !== null && typeof value === "object" && typeof value.code === "string" && typeof value.expected === "string" && typeof value.hint === "string";
|
|
808
|
-
}
|
|
809
|
-
function remapBuildFailureSource(value, sourcePath) {
|
|
810
|
-
if (value === null || typeof value !== "object") return void 0;
|
|
811
|
-
const error = value;
|
|
812
|
-
if (error.code !== "pack-source-load-failed") return void 0;
|
|
813
|
-
if (error.detail === null || typeof error.detail !== "object") return void 0;
|
|
814
|
-
const detail = error.detail;
|
|
815
|
-
if (detail.phase !== "build") return void 0;
|
|
816
|
-
return {
|
|
817
|
-
...value,
|
|
818
|
-
detail: { ...detail, sourcePath }
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
function observedAssetReader(source) {
|
|
822
|
-
const reads = /* @__PURE__ */ new Map();
|
|
823
|
-
const reader = {
|
|
824
|
-
async readByGuid(guid) {
|
|
825
|
-
const key = AssetGuid.format(guid);
|
|
826
|
-
const cached = reads.get(key);
|
|
827
|
-
if (cached !== void 0) return ok(privateClone(cached.asset));
|
|
828
|
-
if (source === void 0) {
|
|
829
|
-
return err(
|
|
830
|
-
new AssetError({
|
|
831
|
-
code: "asset-not-found",
|
|
832
|
-
expected: `a host Asset snapshot source for ScriptablePack content read ${key}`,
|
|
833
|
-
hint: "configure the standard ScriptablePack asset source or remove the content read"
|
|
834
|
-
})
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
|
-
const result = await source.readByGuid(guid);
|
|
838
|
-
if (!result.ok) return result;
|
|
839
|
-
const fixed = privateClone(result.value.asset);
|
|
840
|
-
reads.set(key, {
|
|
841
|
-
guid: key,
|
|
842
|
-
asset: fixed,
|
|
843
|
-
generation: result.value.generation,
|
|
844
|
-
digest: result.value.digest
|
|
845
|
-
});
|
|
846
|
-
return ok(privateClone(fixed));
|
|
847
|
-
}
|
|
848
|
-
};
|
|
849
|
-
return { reader, reads };
|
|
850
|
-
}
|
|
851
|
-
function stable(value) {
|
|
852
|
-
if (value instanceof Uint8Array) return JSON.stringify(Array.from(value));
|
|
853
|
-
if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
|
|
854
|
-
if (value !== null && typeof value === "object") {
|
|
855
|
-
const record2 = value;
|
|
856
|
-
return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stable(record2[key])}`).join(",")}}`;
|
|
857
|
-
}
|
|
858
|
-
return JSON.stringify(value) ?? "null";
|
|
859
|
-
}
|
|
860
|
-
async function fingerprint(value) {
|
|
861
|
-
const subtle = globalThis.crypto?.subtle;
|
|
862
|
-
if (subtle === void 0)
|
|
863
|
-
throw new Error("Web Crypto API is required for ScriptablePack fingerprints");
|
|
864
|
-
const digest = await subtle.digest("SHA-256", new TextEncoder().encode(stable(value)));
|
|
865
|
-
const hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
866
|
-
""
|
|
867
|
-
);
|
|
868
|
-
return `sha256:${hex}`;
|
|
869
|
-
}
|
|
870
|
-
function outputError(definition, output) {
|
|
871
|
-
const declaredKeys = Object.keys(definition.assets);
|
|
872
|
-
const outputKeys = Object.keys(output);
|
|
873
|
-
const outputKeySet = new Set(outputKeys);
|
|
874
|
-
const declaredKeySet = new Set(declaredKeys);
|
|
875
|
-
const missingKeys = declaredKeys.filter((key) => !outputKeySet.has(key));
|
|
876
|
-
const unexpectedSourceKeys = outputKeys.filter((key) => !declaredKeySet.has(key));
|
|
877
|
-
const kindMismatches = declaredKeys.flatMap((sourceKey) => {
|
|
878
|
-
const descriptor = definition.assets[sourceKey];
|
|
879
|
-
const value = output[sourceKey];
|
|
880
|
-
if (descriptor === void 0 || value === void 0) return [];
|
|
881
|
-
const actualKind = value !== null && typeof value === "object" && "kind" in value ? String(value.kind) : typeof value;
|
|
882
|
-
return actualKind === descriptor.kind ? [] : [{ sourceKey, expected: descriptor.kind, actual: actualKind }];
|
|
883
|
-
});
|
|
884
|
-
if (missingKeys.length === 0 && unexpectedSourceKeys.length === 0 && kindMismatches.length === 0) {
|
|
885
|
-
return void 0;
|
|
886
|
-
}
|
|
887
|
-
return {
|
|
888
|
-
code: "pack-source-output-invalid",
|
|
889
|
-
expected: "build output keys and kinds to exactly match definition.assets",
|
|
890
|
-
hint: "inspect sourceKey and GUID topology, then rebuild or cold-cook the ScriptablePack",
|
|
891
|
-
detail: {
|
|
892
|
-
missingGuids: missingKeys.map(
|
|
893
|
-
(key) => AssetGuid.format(definition.assets[key]?.guid)
|
|
894
|
-
),
|
|
895
|
-
unexpectedSourceKeys,
|
|
896
|
-
kindMismatches
|
|
897
|
-
}
|
|
898
|
-
};
|
|
899
|
-
}
|
|
900
|
-
function externalClosureError(declared, referenced, read) {
|
|
901
|
-
const used = /* @__PURE__ */ new Set([...referenced, ...read]);
|
|
902
|
-
const undeclaredReferencedGuids = [...referenced].filter((guid) => !declared.has(guid));
|
|
903
|
-
const undeclaredReadGuids = [...read].filter((guid) => !declared.has(guid));
|
|
904
|
-
const unusedDeclaredGuids = [...declared].filter((guid) => !used.has(guid));
|
|
905
|
-
if (undeclaredReferencedGuids.length === 0 && undeclaredReadGuids.length === 0 && unusedDeclaredGuids.length === 0) {
|
|
906
|
-
return void 0;
|
|
907
|
-
}
|
|
908
|
-
return {
|
|
909
|
-
code: "pack-source-external-closure-mismatch",
|
|
910
|
-
expected: "externalAssets GUIDs to equal output external refs union AssetReader reads",
|
|
911
|
-
hint: "inspect refs and AssetReader reads, repair GUID declarations, then rebuild or cold-cook",
|
|
912
|
-
detail: {
|
|
913
|
-
undeclaredReferencedGuids: undeclaredReferencedGuids.sort(),
|
|
914
|
-
undeclaredReadGuids: undeclaredReadGuids.sort(),
|
|
915
|
-
unusedDeclaredGuids: unusedDeclaredGuids.sort()
|
|
916
|
-
}
|
|
917
|
-
};
|
|
918
|
-
}
|
|
919
|
-
function productContractError(sourceKey, kind, product) {
|
|
920
|
-
const mismatches = [];
|
|
921
|
-
if (product.payload.kind !== kind) {
|
|
922
|
-
mismatches.push({ sourceKey, expected: kind, actual: product.payload.kind });
|
|
923
|
-
}
|
|
924
|
-
for (const reference of product.refs) {
|
|
925
|
-
if (!AssetGuid.parse(reference.guid).ok) {
|
|
926
|
-
mismatches.push({
|
|
927
|
-
sourceKey,
|
|
928
|
-
expected: "every producer ref to contain a valid Asset GUID",
|
|
929
|
-
actual: reference.guid
|
|
930
|
-
});
|
|
931
|
-
break;
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
for (const [artifactKey, artifact] of Object.entries(product.artifacts)) {
|
|
935
|
-
if (artifactKey.length === 0 || artifactKey.startsWith("/") || artifactKey.includes("..") || artifactKey.includes("\\") || artifact.mediaType.trim().length === 0 || !(artifact.bytes instanceof Uint8Array)) {
|
|
936
|
-
mismatches.push({
|
|
937
|
-
sourceKey,
|
|
938
|
-
expected: "asset-local artifacts with safe keys, mediaType, and Uint8Array bytes",
|
|
939
|
-
actual: artifactKey
|
|
940
|
-
});
|
|
941
|
-
break;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
if (mismatches.length === 0) return void 0;
|
|
945
|
-
return {
|
|
946
|
-
code: "pack-source-output-invalid",
|
|
947
|
-
expected: "producer output refs and asset-local artifacts to satisfy the Pack v2 contract",
|
|
948
|
-
hint: "inspect sourceKey and artifact provenance, repair refs or bytes, then rebuild or cold-cook",
|
|
949
|
-
detail: { missingGuids: [], unexpectedSourceKeys: [], kindMismatches: mismatches }
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
async function buildScriptablePack(options) {
|
|
953
|
-
const observed = observedAssetReader(options.assetSource);
|
|
954
|
-
let built;
|
|
955
|
-
try {
|
|
956
|
-
const build = options.definition.build;
|
|
957
|
-
built = await build(observed.reader);
|
|
958
|
-
} catch (error) {
|
|
959
|
-
return err(
|
|
960
|
-
new ImportError({
|
|
961
|
-
code: "import-internal-error",
|
|
962
|
-
expected: "ScriptablePack build to return a structured Result without throwing",
|
|
963
|
-
hint: "fix the build implementation and return a structured failure for expected authoring errors",
|
|
964
|
-
detail: { reason: error instanceof Error ? error.message : String(error) }
|
|
965
|
-
})
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
if (!built.ok) {
|
|
969
|
-
const remappedBuildFailure = remapBuildFailureSource(built.error, options.sourcePath);
|
|
970
|
-
if (remappedBuildFailure !== void 0) return err(remappedBuildFailure);
|
|
971
|
-
if (built.error instanceof ImportError || built.error instanceof AssetError || isStructuredDomainError(built.error)) {
|
|
972
|
-
return err(built.error);
|
|
973
|
-
}
|
|
974
|
-
return err(
|
|
975
|
-
new ImportError({
|
|
976
|
-
code: "import-internal-error",
|
|
977
|
-
expected: "ScriptablePack build failure to use a structured domain error",
|
|
978
|
-
hint: "return an error with code, expected, hint, and optional detail fields",
|
|
979
|
-
detail: { reason: String(built.error) }
|
|
980
|
-
})
|
|
981
|
-
);
|
|
982
|
-
}
|
|
983
|
-
if (!record(built.value)) {
|
|
984
|
-
return err(
|
|
985
|
-
new ImportError({
|
|
986
|
-
code: "import-internal-error",
|
|
987
|
-
expected: "ScriptablePack build output to be an object keyed by declared sourceKey",
|
|
988
|
-
hint: "return one concrete Asset payload for each declared sourceKey",
|
|
989
|
-
detail: { reason: "build output is not an object" }
|
|
990
|
-
})
|
|
991
|
-
);
|
|
992
|
-
}
|
|
993
|
-
const output = built.value;
|
|
994
|
-
const invalidOutput = outputError(options.definition, output);
|
|
995
|
-
if (invalidOutput !== void 0) return err(invalidOutput);
|
|
996
|
-
const assets = [];
|
|
997
|
-
for (const sourceKey of Object.keys(options.definition.assets).sort()) {
|
|
998
|
-
const descriptor = options.definition.assets[sourceKey];
|
|
999
|
-
const asset = output[sourceKey];
|
|
1000
|
-
if (descriptor === void 0) continue;
|
|
1001
|
-
if (!isAsset(asset)) {
|
|
1002
|
-
return err(
|
|
1003
|
-
new ImportError({
|
|
1004
|
-
code: "import-internal-error",
|
|
1005
|
-
expected: `a concrete Asset payload for sourceKey ${sourceKey}`,
|
|
1006
|
-
hint: "return a durable Asset with a kind from SCRIPTABLE_PACK_ASSET_KINDS",
|
|
1007
|
-
detail: { reason: "build output payload is missing or has an unknown kind" }
|
|
1008
|
-
})
|
|
1009
|
-
);
|
|
1010
|
-
}
|
|
1011
|
-
const producer = options.outputs.get(descriptor.kind);
|
|
1012
|
-
if (producer === void 0) {
|
|
1013
|
-
return err({
|
|
1014
|
-
code: "pack-source-output-invalid",
|
|
1015
|
-
expected: `a domain output producer registered for kind ${descriptor.kind}`,
|
|
1016
|
-
hint: "attach the owning producer capability, inspect registration, then rebuild or cold-cook",
|
|
1017
|
-
detail: {
|
|
1018
|
-
missingGuids: [AssetGuid.format(descriptor.guid)],
|
|
1019
|
-
unexpectedSourceKeys: [],
|
|
1020
|
-
kindMismatches: []
|
|
1021
|
-
}
|
|
1022
|
-
});
|
|
1023
|
-
}
|
|
1024
|
-
const product2 = await producer.produce({
|
|
1025
|
-
guid: AssetGuid.format(descriptor.guid),
|
|
1026
|
-
sourceKey,
|
|
1027
|
-
asset
|
|
1028
|
-
});
|
|
1029
|
-
if (!product2.ok) return err(product2.error);
|
|
1030
|
-
const productError = productContractError(sourceKey, descriptor.kind, product2.value);
|
|
1031
|
-
if (productError !== void 0) return err(productError);
|
|
1032
|
-
assets.push({
|
|
1033
|
-
guid: AssetGuid.format(descriptor.guid),
|
|
1034
|
-
kind: descriptor.kind,
|
|
1035
|
-
...descriptor.name === void 0 ? {} : { name: descriptor.name },
|
|
1036
|
-
payload: product2.value.payload,
|
|
1037
|
-
refs: product2.value.refs,
|
|
1038
|
-
artifacts: product2.value.artifacts
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
|
-
const local = new Set(assets.map((asset) => asset.guid.toLowerCase()));
|
|
1042
|
-
const referenced = new Set(
|
|
1043
|
-
assets.flatMap((asset) => asset.refs.map((reference) => reference.guid.toLowerCase())).filter((guid) => !local.has(guid))
|
|
1044
|
-
);
|
|
1045
|
-
const read = new Set([...observed.reads.keys()].map((guid) => guid.toLowerCase()));
|
|
1046
|
-
const declared = new Set(
|
|
1047
|
-
Object.values(options.definition.externalAssets).map(
|
|
1048
|
-
(guid) => AssetGuid.format(guid).toLowerCase()
|
|
1049
|
-
)
|
|
1050
|
-
);
|
|
1051
|
-
const closureError2 = externalClosureError(declared, referenced, read);
|
|
1052
|
-
if (closureError2 !== void 0) return err(closureError2);
|
|
1053
|
-
const externalEvidence = [...declared].sort().map((guid) => {
|
|
1054
|
-
const observedRead = observed.reads.get(guid);
|
|
1055
|
-
const isReference = referenced.has(guid);
|
|
1056
|
-
return {
|
|
1057
|
-
guid,
|
|
1058
|
-
usage: observedRead === void 0 ? "reference" : isReference ? "both" : "content",
|
|
1059
|
-
...observedRead === void 0 ? {} : { generation: observedRead.generation, digest: observedRead.digest }
|
|
1060
|
-
};
|
|
1061
|
-
});
|
|
1062
|
-
const fingerprintEvidence = externalEvidence.map(({ guid, usage, digest }) => ({
|
|
1063
|
-
guid,
|
|
1064
|
-
usage,
|
|
1065
|
-
...digest === void 0 ? {} : { digest }
|
|
1066
|
-
}));
|
|
1067
|
-
const inputFingerprint = await fingerprint({
|
|
1068
|
-
meta: projectScriptablePackMeta(options.definition, options.sourcePath),
|
|
1069
|
-
sceneComponents: projectScriptablePackSceneComponents(options.definition.sceneComponents),
|
|
1070
|
-
sourceClosure: stableSourceClosure(options.sourceClosure),
|
|
1071
|
-
externalEvidence: fingerprintEvidence,
|
|
1072
|
-
authoringContractVersion: options.authoringContractVersion,
|
|
1073
|
-
producerVersions: options.outputs.versions()
|
|
1074
|
-
});
|
|
1075
|
-
const refs = assets.flatMap((asset) => asset.refs);
|
|
1076
|
-
const artifacts = Object.fromEntries(
|
|
1077
|
-
assets.flatMap(
|
|
1078
|
-
(asset) => Object.entries(asset.artifacts).map(([key, artifact]) => [`${asset.guid}/${key}`, artifact])
|
|
1079
|
-
)
|
|
1080
|
-
);
|
|
1081
|
-
const product = createImportProduct({
|
|
1082
|
-
assets,
|
|
1083
|
-
sourceDependencies: options.sourceClosure.map((entry) => entry.path),
|
|
1084
|
-
refs,
|
|
1085
|
-
artifacts,
|
|
1086
|
-
receipts: assets.map((asset) => ({
|
|
1087
|
-
guid: asset.guid,
|
|
1088
|
-
origin: "sourceMeta",
|
|
1089
|
-
status: "succeeded",
|
|
1090
|
-
inputFingerprint
|
|
1091
|
-
})),
|
|
1092
|
-
diagnostics: [],
|
|
1093
|
-
sourceRevision: inputFingerprint,
|
|
1094
|
-
sourceKey: options.sourcePath
|
|
1095
|
-
});
|
|
1096
|
-
if (!product.ok) return err(product.error);
|
|
1097
|
-
return ok({
|
|
1098
|
-
product: product.value,
|
|
1099
|
-
stagedOutputs: await Promise.all(
|
|
1100
|
-
Object.keys(options.definition.assets).sort().map(async (sourceKey) => {
|
|
1101
|
-
const descriptor = options.definition.assets[sourceKey];
|
|
1102
|
-
const asset = output[sourceKey];
|
|
1103
|
-
if (descriptor === void 0 || !isAsset(asset)) {
|
|
1104
|
-
throw new Error(`validated ScriptablePack output ${sourceKey} is not an Asset`);
|
|
1105
|
-
}
|
|
1106
|
-
return {
|
|
1107
|
-
guid: descriptor.guid,
|
|
1108
|
-
sourceKey,
|
|
1109
|
-
asset: privateClone(asset),
|
|
1110
|
-
digest: await fingerprint(asset)
|
|
1111
|
-
};
|
|
1112
|
-
})
|
|
1113
|
-
),
|
|
1114
|
-
externalEvidence,
|
|
1115
|
-
inputFingerprint
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
1515
|
|
|
1119
1516
|
// src/scriptable-pack-output-producers.ts
|
|
1120
1517
|
function producerError(input, reason) {
|
|
@@ -1127,11 +1524,11 @@ function producerError(input, reason) {
|
|
|
1127
1524
|
}
|
|
1128
1525
|
function formatGuid(value) {
|
|
1129
1526
|
if (typeof value === "string") {
|
|
1130
|
-
const parsed = AssetGuid.parse(value);
|
|
1527
|
+
const parsed = AssetGuid$1.parse(value);
|
|
1131
1528
|
if (!parsed.ok) throw parsed.error;
|
|
1132
|
-
return AssetGuid.format(parsed.value);
|
|
1529
|
+
return AssetGuid$1.format(parsed.value);
|
|
1133
1530
|
}
|
|
1134
|
-
return AssetGuid.format(value);
|
|
1531
|
+
return AssetGuid$1.format(value);
|
|
1135
1532
|
}
|
|
1136
1533
|
function canonical(value) {
|
|
1137
1534
|
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
@@ -1143,11 +1540,11 @@ function canonical(value) {
|
|
|
1143
1540
|
function particleProgramArtifact(effect) {
|
|
1144
1541
|
const artifactProgram = { format: effect.program.format, emitters: effect.program.emitters };
|
|
1145
1542
|
const bytes2 = new TextEncoder().encode(canonical(artifactProgram));
|
|
1146
|
-
const
|
|
1543
|
+
const fingerprint = `sha256:${bytesToHex(sha256(bytes2))}`;
|
|
1147
1544
|
return {
|
|
1148
|
-
program: { ...effect.program, fingerprint
|
|
1545
|
+
program: { ...effect.program, fingerprint },
|
|
1149
1546
|
bytes: bytes2,
|
|
1150
|
-
fingerprint
|
|
1547
|
+
fingerprint
|
|
1151
1548
|
};
|
|
1152
1549
|
}
|
|
1153
1550
|
function materialProduct(input) {
|
|
@@ -1165,7 +1562,9 @@ function materialProduct(input) {
|
|
|
1165
1562
|
return addRef(value, sourceField);
|
|
1166
1563
|
};
|
|
1167
1564
|
const textureFields = material.parameters === void 0 ? new Set(MATERIAL_TEXTURE_SLOTS) : new Set(
|
|
1168
|
-
material.parameters.filter(
|
|
1565
|
+
material.parameters.filter(
|
|
1566
|
+
(parameter) => parameter.type === "texture" || parameter.type === "texture_cube"
|
|
1567
|
+
).map((parameter) => parameter.name)
|
|
1169
1568
|
);
|
|
1170
1569
|
const parent = material.parent === void 0 ? void 0 : addRef(material.parent, { fieldName: "parent" });
|
|
1171
1570
|
const values = {};
|
|
@@ -1212,6 +1611,13 @@ function meshProduct(input) {
|
|
|
1212
1611
|
sourceField: { fieldName: "materialSlots", arrayIndex: slotIndex }
|
|
1213
1612
|
});
|
|
1214
1613
|
}
|
|
1614
|
+
const seenRefs = new Set(refs.map((reference) => reference.guid.toLowerCase()));
|
|
1615
|
+
for (const [lodIndex, lod] of (mesh.lods ?? []).entries()) {
|
|
1616
|
+
const guid = formatGuid(lod.mesh);
|
|
1617
|
+
if (seenRefs.has(guid.toLowerCase())) continue;
|
|
1618
|
+
seenRefs.add(guid.toLowerCase());
|
|
1619
|
+
refs.push({ guid, sourceField: { fieldName: "lods", arrayIndex: lodIndex } });
|
|
1620
|
+
}
|
|
1215
1621
|
return {
|
|
1216
1622
|
payload: mesh,
|
|
1217
1623
|
refs,
|
|
@@ -1252,6 +1658,25 @@ function sceneProduct(input, components) {
|
|
|
1252
1658
|
artifacts: {}
|
|
1253
1659
|
};
|
|
1254
1660
|
}
|
|
1661
|
+
function containsAssetGuid(value) {
|
|
1662
|
+
if (typeof value === "string") return AssetGuid$1.parse(value).ok;
|
|
1663
|
+
if (Array.isArray(value)) return value.some(containsAssetGuid);
|
|
1664
|
+
if (value !== null && typeof value === "object") {
|
|
1665
|
+
return Object.values(value).some(containsAssetGuid);
|
|
1666
|
+
}
|
|
1667
|
+
return false;
|
|
1668
|
+
}
|
|
1669
|
+
function preExternalizedSceneProduct(input) {
|
|
1670
|
+
if (input.asset.kind !== "scene") throw new TypeError("expected SceneAsset");
|
|
1671
|
+
if (containsAssetGuid(input.asset)) {
|
|
1672
|
+
throw new TypeError("direct scene payload must use explicit refs with runtime indices");
|
|
1673
|
+
}
|
|
1674
|
+
return {
|
|
1675
|
+
payload: { ...input.asset, kind: "scene" },
|
|
1676
|
+
refs: [],
|
|
1677
|
+
artifacts: {}
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1255
1680
|
function createSafeProducer(kind, version, product) {
|
|
1256
1681
|
return {
|
|
1257
1682
|
kind,
|
|
@@ -1360,7 +1785,7 @@ function ordinaryPodProduct(input) {
|
|
|
1360
1785
|
case "tileset": {
|
|
1361
1786
|
const tileset = asset;
|
|
1362
1787
|
const refs = tileset.atlases.map((atlas, index) => {
|
|
1363
|
-
const parsed = AssetGuid.parse(atlas);
|
|
1788
|
+
const parsed = AssetGuid$1.parse(atlas);
|
|
1364
1789
|
if (!parsed.ok) throw parsed.error;
|
|
1365
1790
|
return ref(parsed.value, "atlases", index);
|
|
1366
1791
|
});
|
|
@@ -1393,7 +1818,7 @@ function ordinaryPodProduct(input) {
|
|
|
1393
1818
|
}
|
|
1394
1819
|
case "skin": {
|
|
1395
1820
|
const skin = asset;
|
|
1396
|
-
const skeletonGuid = AssetGuid.parse(skin.skeletonGuid);
|
|
1821
|
+
const skeletonGuid = AssetGuid$1.parse(skin.skeletonGuid);
|
|
1397
1822
|
if (!skeletonGuid.ok) throw skeletonGuid.error;
|
|
1398
1823
|
return {
|
|
1399
1824
|
payload: skin,
|
|
@@ -1412,7 +1837,7 @@ function ordinaryPodProduct(input) {
|
|
|
1412
1837
|
const refs = [];
|
|
1413
1838
|
const nodes = graph.nodes.map((node, index) => {
|
|
1414
1839
|
if (node.type !== "clip") return node;
|
|
1415
|
-
const parsed = AssetGuid.parse(node.clip);
|
|
1840
|
+
const parsed = AssetGuid$1.parse(node.clip);
|
|
1416
1841
|
if (!parsed.ok) throw parsed.error;
|
|
1417
1842
|
const referenceIndex = refs.push(ref(parsed.value, "nodes", index)) - 1;
|
|
1418
1843
|
return { ...node, clip: referenceIndex };
|
|
@@ -1457,6 +1882,20 @@ function ordinaryPodProduct(input) {
|
|
|
1457
1882
|
}
|
|
1458
1883
|
};
|
|
1459
1884
|
}
|
|
1885
|
+
case "ies-profile": {
|
|
1886
|
+
const profile = asset;
|
|
1887
|
+
return {
|
|
1888
|
+
payload: profile,
|
|
1889
|
+
refs: [],
|
|
1890
|
+
artifacts: {
|
|
1891
|
+
body: {
|
|
1892
|
+
mediaType: "application/octet-stream",
|
|
1893
|
+
assetCodec: { name: "forgeax-ies-profile", version: "1" },
|
|
1894
|
+
bytes: bytes(profile.data)
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1460
1899
|
case "material":
|
|
1461
1900
|
case "mesh":
|
|
1462
1901
|
case "scene":
|
|
@@ -1471,201 +1910,42 @@ var materialAssetOutputProducer = createSafeProducer(
|
|
|
1471
1910
|
var meshAssetOutputProducer = createSafeProducer("mesh", "mesh-binary/4", meshProduct);
|
|
1472
1911
|
var textureAssetOutputProducer = createSafeProducer(
|
|
1473
1912
|
"texture",
|
|
1474
|
-
"texture-pack/1",
|
|
1475
|
-
textureProduct
|
|
1476
|
-
);
|
|
1477
|
-
function createSceneAssetOutputProducer(sceneComponents = []) {
|
|
1478
|
-
const schemas = new Map(
|
|
1479
|
-
sceneComponents.map((component) => [component.name, component.fields])
|
|
1480
|
-
);
|
|
1481
|
-
return createSafeProducer("scene", "scene-pack/3", (input) => sceneProduct(input, schemas));
|
|
1482
|
-
}
|
|
1483
|
-
function createStandardAssetOutputProducerRegistry(sceneComponents = []) {
|
|
1484
|
-
const registry = new AssetOutputProducerRegistry();
|
|
1485
|
-
registry.register(materialAssetOutputProducer);
|
|
1486
|
-
registry.register(meshAssetOutputProducer);
|
|
1487
|
-
registry.register(textureAssetOutputProducer);
|
|
1488
|
-
registry.register(createSceneAssetOutputProducer(sceneComponents));
|
|
1489
|
-
for (const kind of [
|
|
1490
|
-
"equirect",
|
|
1491
|
-
"sampler",
|
|
1492
|
-
"font",
|
|
1493
|
-
"render-pipeline",
|
|
1494
|
-
"tileset",
|
|
1495
|
-
"video",
|
|
1496
|
-
"skeleton",
|
|
1497
|
-
"skin",
|
|
1498
|
-
"animation-clip",
|
|
1499
|
-
"animation-graph",
|
|
1500
|
-
"audio",
|
|
1501
|
-
"particle-effect"
|
|
1502
|
-
]) {
|
|
1503
|
-
registry.register(createSafeProducer(kind, "ordinary-pod/1", ordinaryPodProduct));
|
|
1504
|
-
}
|
|
1505
|
-
return registry;
|
|
1506
|
-
}
|
|
1507
|
-
function stable2(value) {
|
|
1508
|
-
if (ArrayBuffer.isView(value)) {
|
|
1509
|
-
return `${value.constructor.name}:${JSON.stringify(Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)))}`;
|
|
1510
|
-
}
|
|
1511
|
-
if (Array.isArray(value)) return `[${value.map(stable2).join(",")}]`;
|
|
1512
|
-
if (value !== null && typeof value === "object") {
|
|
1513
|
-
const record2 = value;
|
|
1514
|
-
return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stable2(record2[key])}`).join(",")}}`;
|
|
1515
|
-
}
|
|
1516
|
-
return JSON.stringify(value) ?? "null";
|
|
1517
|
-
}
|
|
1518
|
-
async function assetDigest(asset) {
|
|
1519
|
-
const bytes2 = new TextEncoder().encode(stable2(asset));
|
|
1520
|
-
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes2);
|
|
1521
|
-
return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
1522
|
-
}
|
|
1523
|
-
function cycleError(stack, owner, guid) {
|
|
1524
|
-
const cycleStart = stack.indexOf(owner);
|
|
1525
|
-
const cycle = [...stack.slice(cycleStart), owner];
|
|
1526
|
-
return {
|
|
1527
|
-
code: "pack-source-build-cycle",
|
|
1528
|
-
expected: "an acyclic graph of ScriptablePack content dependencies",
|
|
1529
|
-
actual: cycle.join(" -> "),
|
|
1530
|
-
hint: "break one content read edge or convert it to a reference-only dependency",
|
|
1531
|
-
retryable: false,
|
|
1532
|
-
recoveryActions: ["inspect-content-dependency-graph", "edit-source"],
|
|
1533
|
-
detail: { sourcePath: owner, sourceKey: guid, incomingRefs: cycle }
|
|
1534
|
-
};
|
|
1535
|
-
}
|
|
1536
|
-
function missingOutputError(owner, guid) {
|
|
1537
|
-
return {
|
|
1538
|
-
code: "pack-source-output-invalid",
|
|
1539
|
-
expected: `owner ${owner} to stage every declared output including ${guid}`,
|
|
1540
|
-
hint: "return one output for every GUID declared by the staged owner",
|
|
1541
|
-
detail: { missingGuids: [guid], unexpectedSourceKeys: [], kindMismatches: [] }
|
|
1542
|
-
};
|
|
1543
|
-
}
|
|
1544
|
-
function createScriptablePackStagedAssetSnapshotSource(options) {
|
|
1545
|
-
const owners = options.declaredExternalOutputs === void 0 || options.declaredExternalOutputs.length === 0 ? options.owners : [
|
|
1546
|
-
{
|
|
1547
|
-
id: "<declared-pack-external>",
|
|
1548
|
-
guids: options.declaredExternalOutputs.map((output) => output.guid),
|
|
1549
|
-
async build() {
|
|
1550
|
-
return ok(options.declaredExternalOutputs ?? []);
|
|
1551
|
-
}
|
|
1552
|
-
},
|
|
1553
|
-
...options.owners
|
|
1554
|
-
];
|
|
1555
|
-
const ownerByGuid = /* @__PURE__ */ new Map();
|
|
1556
|
-
for (const owner of owners) {
|
|
1557
|
-
if (owner.id.trim().length === 0) throw new TypeError("staged owner id must be non-empty");
|
|
1558
|
-
for (const guid of owner.guids) {
|
|
1559
|
-
const key = AssetGuid.format(guid).toLowerCase();
|
|
1560
|
-
const existing = ownerByGuid.get(key);
|
|
1561
|
-
if (existing !== void 0) {
|
|
1562
|
-
throw new TypeError(`staged GUID ${key} is owned by both ${existing.id} and ${owner.id}`);
|
|
1563
|
-
}
|
|
1564
|
-
ownerByGuid.set(key, owner);
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
const snapshots = /* @__PURE__ */ new Map();
|
|
1568
|
-
const builds = /* @__PURE__ */ new Map();
|
|
1569
|
-
const sourceFor = (stack) => ({
|
|
1570
|
-
async readByGuid(guid) {
|
|
1571
|
-
const key = AssetGuid.format(guid).toLowerCase();
|
|
1572
|
-
const cached = snapshots.get(key);
|
|
1573
|
-
if (cached !== void 0) return ok(structuredClone(cached));
|
|
1574
|
-
const owner = ownerByGuid.get(key);
|
|
1575
|
-
if (owner === void 0) {
|
|
1576
|
-
return err(
|
|
1577
|
-
new AssetError({
|
|
1578
|
-
code: "asset-not-imported",
|
|
1579
|
-
expected: "a staged local owner for the requested GUID",
|
|
1580
|
-
hint: "declare the local ScriptablePack output before rebuilding the generation"
|
|
1581
|
-
})
|
|
1582
|
-
);
|
|
1583
|
-
}
|
|
1584
|
-
if (stack.includes(owner.id)) return err(cycleError(stack, owner.id, key));
|
|
1585
|
-
let building = builds.get(owner.id);
|
|
1586
|
-
if (building === void 0) {
|
|
1587
|
-
building = (async () => {
|
|
1588
|
-
const built2 = await owner.build(sourceFor([...stack, owner.id]));
|
|
1589
|
-
if (!built2.ok) {
|
|
1590
|
-
builds.delete(owner.id);
|
|
1591
|
-
return built2;
|
|
1592
|
-
}
|
|
1593
|
-
const next = /* @__PURE__ */ new Map();
|
|
1594
|
-
for (const output of built2.value) {
|
|
1595
|
-
const outputGuid = AssetGuid.format(output.guid).toLowerCase();
|
|
1596
|
-
if (ownerByGuid.get(outputGuid) !== owner) {
|
|
1597
|
-
builds.delete(owner.id);
|
|
1598
|
-
return err(missingOutputError(owner.id, outputGuid));
|
|
1599
|
-
}
|
|
1600
|
-
next.set(outputGuid, {
|
|
1601
|
-
asset: structuredClone(output.asset),
|
|
1602
|
-
generation: options.generation,
|
|
1603
|
-
digest: output.digest ?? await assetDigest(output.asset)
|
|
1604
|
-
});
|
|
1605
|
-
}
|
|
1606
|
-
for (const declared of owner.guids) {
|
|
1607
|
-
const declaredGuid = AssetGuid.format(declared).toLowerCase();
|
|
1608
|
-
if (!next.has(declaredGuid)) {
|
|
1609
|
-
builds.delete(owner.id);
|
|
1610
|
-
return err(missingOutputError(owner.id, declaredGuid));
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
for (const [outputGuid, snapshot] of next) snapshots.set(outputGuid, snapshot);
|
|
1614
|
-
return ok(void 0);
|
|
1615
|
-
})();
|
|
1616
|
-
builds.set(owner.id, building);
|
|
1617
|
-
}
|
|
1618
|
-
const built = await building;
|
|
1619
|
-
if (!built.ok) return built;
|
|
1620
|
-
const staged = snapshots.get(key);
|
|
1621
|
-
return staged === void 0 ? err(missingOutputError(owner.id, key)) : ok(structuredClone(staged));
|
|
1622
|
-
}
|
|
1623
|
-
});
|
|
1624
|
-
return sourceFor([]);
|
|
1913
|
+
"texture-pack/1",
|
|
1914
|
+
textureProduct
|
|
1915
|
+
);
|
|
1916
|
+
function createSceneAssetOutputProducer(sceneComponents = []) {
|
|
1917
|
+
const schemas = new Map(
|
|
1918
|
+
sceneComponents.map((component) => [component.name, component.fields])
|
|
1919
|
+
);
|
|
1920
|
+
return createSafeProducer("scene", "scene-pack/3", (input) => sceneProduct(input, schemas));
|
|
1625
1921
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
const bytes2 = new TextEncoder().encode(`${JSON.stringify(meta)}
|
|
1629
|
-
`);
|
|
1630
|
-
return {
|
|
1631
|
-
...product,
|
|
1632
|
-
assets: product.assets.map(
|
|
1633
|
-
(asset) => asset.guid.toLowerCase() === anchorGuid ? {
|
|
1634
|
-
...asset,
|
|
1635
|
-
artifacts: {
|
|
1636
|
-
...asset.artifacts,
|
|
1637
|
-
[META_ARTIFACT_KEY]: { mediaType: "application/json", bytes: bytes2 }
|
|
1638
|
-
}
|
|
1639
|
-
} : asset
|
|
1640
|
-
)
|
|
1641
|
-
};
|
|
1922
|
+
function createPreExternalizedSceneAssetOutputProducer() {
|
|
1923
|
+
return createSafeProducer("scene", "scene-pack/3", preExternalizedSceneProduct);
|
|
1642
1924
|
}
|
|
1643
|
-
|
|
1644
|
-
const
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1925
|
+
function createStandardAssetOutputProducerRegistry(sceneComponents = []) {
|
|
1926
|
+
const registry = new AssetOutputProducerRegistry();
|
|
1927
|
+
registry.register(materialAssetOutputProducer);
|
|
1928
|
+
registry.register(meshAssetOutputProducer);
|
|
1929
|
+
registry.register(textureAssetOutputProducer);
|
|
1930
|
+
registry.register(createSceneAssetOutputProducer(sceneComponents));
|
|
1931
|
+
for (const kind of [
|
|
1932
|
+
"equirect",
|
|
1933
|
+
"sampler",
|
|
1934
|
+
"font",
|
|
1935
|
+
"render-pipeline",
|
|
1936
|
+
"tileset",
|
|
1937
|
+
"video",
|
|
1938
|
+
"skeleton",
|
|
1939
|
+
"skin",
|
|
1940
|
+
"animation-clip",
|
|
1941
|
+
"animation-graph",
|
|
1942
|
+
"audio",
|
|
1943
|
+
"particle-effect",
|
|
1944
|
+
"ies-profile"
|
|
1945
|
+
]) {
|
|
1946
|
+
registry.register(createSafeProducer(kind, "ordinary-pod/1", ordinaryPodProduct));
|
|
1655
1947
|
}
|
|
1656
|
-
|
|
1657
|
-
built.value.product,
|
|
1658
|
-
anchorGuid,
|
|
1659
|
-
projectScriptablePackMeta(options.definition, options.sourcePath)
|
|
1660
|
-
);
|
|
1661
|
-
return ok({
|
|
1662
|
-
anchorGuid,
|
|
1663
|
-
declaredGuids,
|
|
1664
|
-
product,
|
|
1665
|
-
stagedOutputs: built.value.stagedOutputs,
|
|
1666
|
-
inputFingerprint: built.value.inputFingerprint,
|
|
1667
|
-
externalEvidence: built.value.externalEvidence
|
|
1668
|
-
});
|
|
1948
|
+
return registry;
|
|
1669
1949
|
}
|
|
1670
1950
|
function parseProducerReadiness(value) {
|
|
1671
1951
|
if (value === void 0 || value === "before-consume" || value === "on-demand") {
|
|
@@ -1765,108 +2045,18 @@ function canonicalScriptableSourcePath(sourcePath) {
|
|
|
1765
2045
|
const markerIndex = normalized.indexOf(marker);
|
|
1766
2046
|
return markerIndex < 0 ? normalized : normalized.slice(markerIndex + 1);
|
|
1767
2047
|
}
|
|
1768
|
-
function scriptablePackInputs(sourcePaths, declarations, cwd, generationFor, policyFor, sourceIdentityFor) {
|
|
1769
|
-
return sourcePaths.flatMap((sourcePath) => {
|
|
1770
|
-
const declaration = declarations.get(resolve(cwd, sourcePath));
|
|
1771
|
-
if (declaration?.format !== "pack.ts") return [];
|
|
1772
|
-
const projected = sourceIdentityFor?.(sourcePath);
|
|
1773
|
-
const catalogSourcePath = projected === void 0 || isAbsolute(projected) ? relative(cwd, sourcePath) : projected;
|
|
1774
|
-
const displaySourcePath = canonicalScriptableSourcePath(catalogSourcePath);
|
|
1775
|
-
return [
|
|
1776
|
-
{
|
|
1777
|
-
sourcePath: resolve(cwd, sourcePath),
|
|
1778
|
-
displaySourcePath,
|
|
1779
|
-
definition: declaration.definition,
|
|
1780
|
-
sourceClosure: declaration.sourceClosure,
|
|
1781
|
-
publicationGeneration: generationFor(displaySourcePath),
|
|
1782
|
-
policy: policyFor(displaySourcePath)
|
|
1783
|
-
}
|
|
1784
|
-
];
|
|
1785
|
-
});
|
|
1786
|
-
}
|
|
1787
|
-
async function materializePreparedScriptablePack(prepared, paths, sink) {
|
|
1788
|
-
const { product, finalized } = prepared;
|
|
1789
|
-
await sink.writePackage(paths.packagePath, JSON.stringify(finalized.pack));
|
|
1790
|
-
for (const artifact of finalized.artifacts) {
|
|
1791
|
-
await sink.writeArtifact(
|
|
1792
|
-
paths.artifactPath(artifact.path),
|
|
1793
|
-
artifact.bytes,
|
|
1794
|
-
artifact.path.endsWith(".json") ? "application/json" : artifact.mediaType
|
|
1795
|
-
);
|
|
1796
|
-
}
|
|
1797
|
-
const receipts = /* @__PURE__ */ new Map();
|
|
1798
|
-
for (const guid of product.declaredGuids) {
|
|
1799
|
-
const path = paths.receiptPath(guid);
|
|
1800
|
-
await sink.writeReceipt(
|
|
1801
|
-
path,
|
|
1802
|
-
JSON.stringify({
|
|
1803
|
-
guid,
|
|
1804
|
-
origin: "sourceMeta",
|
|
1805
|
-
status: "succeeded",
|
|
1806
|
-
inputFingerprint: product.inputFingerprint,
|
|
1807
|
-
outputDigest: finalized.digest
|
|
1808
|
-
})
|
|
1809
|
-
);
|
|
1810
|
-
receipts.set(guid, path);
|
|
1811
|
-
}
|
|
1812
|
-
return receipts;
|
|
1813
|
-
}
|
|
1814
|
-
async function readCookedAuthoredPack(authoredPack, cookers = [], sourcePath) {
|
|
1815
|
-
const parsed = upgradeLegacyAuthoredPack(authoredPack);
|
|
1816
|
-
if (parsed.schemaVersion !== "2.0.0" || parsed.assets === void 0) return void 0;
|
|
1817
|
-
const registry = new NativeCookerRegistry();
|
|
1818
|
-
for (const cooker of cookers) registry.register(cooker);
|
|
1819
|
-
const assets = [];
|
|
1820
|
-
const refsByGuid = /* @__PURE__ */ new Map();
|
|
1821
|
-
let hasCookedAsset = false;
|
|
1822
|
-
for (const asset of parsed.assets) {
|
|
1823
|
-
const shouldCook = asset.execution === "cooked";
|
|
1824
|
-
if (!shouldCook) {
|
|
1825
|
-
assets.push({
|
|
1826
|
-
guid: asset.guid,
|
|
1827
|
-
kind: asset.kind,
|
|
1828
|
-
...asset.name === void 0 ? {} : { name: asset.name },
|
|
1829
|
-
...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey },
|
|
1830
|
-
...asset.sourceIndex === void 0 ? {} : { sourceIndex: asset.sourceIndex },
|
|
1831
|
-
...asset.relations === void 0 ? {} : { relations: asset.relations },
|
|
1832
|
-
payload: asset.payload,
|
|
1833
|
-
refs: asset.refs ?? [],
|
|
1834
|
-
artifacts: {}
|
|
1835
|
-
});
|
|
1836
|
-
continue;
|
|
1837
|
-
}
|
|
1838
|
-
hasCookedAsset = true;
|
|
1839
|
-
const result = await registry.runDraft(asset.kind, {
|
|
1840
|
-
guid: asset.guid,
|
|
1841
|
-
source: asset.payload,
|
|
1842
|
-
...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey },
|
|
1843
|
-
...sourcePath === void 0 ? {} : { sourcePath },
|
|
1844
|
-
refs: asset.refs ?? []
|
|
1845
|
-
});
|
|
1846
|
-
if (!result.ok) throw result.error;
|
|
1847
|
-
const draft = result.value;
|
|
1848
|
-
const refs = [...draft.refs];
|
|
1849
|
-
refsByGuid.set(asset.guid.toLowerCase(), refs);
|
|
1850
|
-
assets.push({
|
|
1851
|
-
guid: draft.guid,
|
|
1852
|
-
kind: asset.kind,
|
|
1853
|
-
...asset.name === void 0 ? {} : { name: asset.name },
|
|
1854
|
-
...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey },
|
|
1855
|
-
...asset.sourceIndex === void 0 ? {} : { sourceIndex: asset.sourceIndex },
|
|
1856
|
-
...asset.relations === void 0 ? {} : { relations: asset.relations },
|
|
1857
|
-
payload: draft.payload,
|
|
1858
|
-
refs,
|
|
1859
|
-
artifacts: draft.artifacts
|
|
1860
|
-
});
|
|
1861
|
-
}
|
|
1862
|
-
return hasCookedAsset ? {
|
|
1863
|
-
logicalPackage: { schemaVersion: "2.0.0", kind: "internal-text-package", assets },
|
|
1864
|
-
refsByGuid
|
|
1865
|
-
} : void 0;
|
|
1866
|
-
}
|
|
1867
2048
|
async function declaredPackExternalOutputs(declarations, cookers = [], requiredGuids = [], externalImport) {
|
|
1868
2049
|
if (requiredGuids.length === 0) return [];
|
|
1869
|
-
const required = new Set(requiredGuids.map((guid) => AssetGuid.format(guid).toLowerCase()));
|
|
2050
|
+
const required = new Set(requiredGuids.map((guid) => AssetGuid$1.format(guid).toLowerCase()));
|
|
2051
|
+
const available = new Set(required);
|
|
2052
|
+
for (const declaration of declarations.values()) {
|
|
2053
|
+
if (declaration.format !== "meta.json") continue;
|
|
2054
|
+
for (const asset of declaration.value.subAssets) {
|
|
2055
|
+
const parsed = AssetGuid$1.parse(asset.guid);
|
|
2056
|
+
if (!parsed.ok) throw parsed.error;
|
|
2057
|
+
available.add(AssetGuid$1.format(parsed.value).toLowerCase());
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
1870
2060
|
const outputs = [];
|
|
1871
2061
|
for (const declaration of declarations.values()) {
|
|
1872
2062
|
if (declaration.format === "meta.json") {
|
|
@@ -1874,22 +2064,10 @@ async function declaredPackExternalOutputs(declarations, cookers = [], requiredG
|
|
|
1874
2064
|
if (!declaration.value.subAssets.some((asset) => required.has(asset.guid.toLowerCase()))) {
|
|
1875
2065
|
continue;
|
|
1876
2066
|
}
|
|
1877
|
-
const
|
|
1878
|
-
const resolved = resolveAssetSource(
|
|
1879
|
-
declaration.sourcePath,
|
|
1880
|
-
declaration.value.source,
|
|
1881
|
-
assetPaths
|
|
1882
|
-
);
|
|
1883
|
-
if (!resolved.ok) {
|
|
1884
|
-
throw new AssetError({
|
|
1885
|
-
code: "asset-not-imported",
|
|
1886
|
-
expected: `a resolvable source for Meta dependency ${declaration.sourcePath}`,
|
|
1887
|
-
hint: "repair the Meta source path before rebuilding the ScriptablePack"
|
|
1888
|
-
});
|
|
1889
|
-
}
|
|
2067
|
+
const resolved = resolveAssetSource(declaration.sourcePath, declaration.value.source);
|
|
1890
2068
|
const meta = {
|
|
1891
2069
|
importer: declaration.value.importer,
|
|
1892
|
-
source: resolved
|
|
2070
|
+
source: resolved,
|
|
1893
2071
|
sourceRevision: declaration.sourceRevision,
|
|
1894
2072
|
...declaration.value.packageId === void 0 ? {} : { packageId: declaration.value.packageId },
|
|
1895
2073
|
...declaration.value.provenance === void 0 ? {} : { provenance: declaration.value.provenance },
|
|
@@ -1913,70 +2091,515 @@ async function declaredPackExternalOutputs(declarations, cookers = [], requiredG
|
|
|
1913
2091
|
if (!sourcePackage.ok) {
|
|
1914
2092
|
throw new AssetError({
|
|
1915
2093
|
code: "asset-not-imported",
|
|
1916
|
-
expected:
|
|
1917
|
-
hint:
|
|
2094
|
+
expected: "the Meta importer to produce the requested dependency",
|
|
2095
|
+
hint: "repair the Meta source and rerun the Pack build",
|
|
2096
|
+
detail: { sourcePath: declaration.sourcePath }
|
|
1918
2097
|
});
|
|
1919
2098
|
}
|
|
1920
2099
|
for (const asset of sourcePackage.value.product.assets) {
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
const parsed = AssetGuid.parse(asset.guid);
|
|
2100
|
+
if (!required.has(asset.guid.toLowerCase())) continue;
|
|
2101
|
+
const parsed = AssetGuid$1.parse(asset.guid);
|
|
1924
2102
|
if (!parsed.ok) throw parsed.error;
|
|
2103
|
+
const declared = declaration.value.subAssets.find(
|
|
2104
|
+
(candidate) => candidate.guid.toLowerCase() === asset.guid.toLowerCase()
|
|
2105
|
+
);
|
|
1925
2106
|
outputs.push({
|
|
1926
2107
|
guid: parsed.value,
|
|
1927
|
-
|
|
2108
|
+
sourceKey: declared?.sourceKey ?? asset.guid,
|
|
2109
|
+
asset: { ...asset.payload, kind: asset.kind }
|
|
1928
2110
|
});
|
|
1929
2111
|
}
|
|
1930
2112
|
continue;
|
|
1931
2113
|
}
|
|
1932
2114
|
if (declaration.format !== "pack.json") continue;
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
2115
|
+
if (declaration.value.schemaVersion === "3.0.0") {
|
|
2116
|
+
const parsed = parseParameterizedPackJson(declaration.value);
|
|
2117
|
+
if (!parsed.ok || parsed.value.format !== "direct") continue;
|
|
2118
|
+
const projected = projectDirectPackJson(parsed.value);
|
|
2119
|
+
if (!projected.ok) continue;
|
|
2120
|
+
const prepared = await prepareDirectPackTransport({
|
|
2121
|
+
projected: projected.value,
|
|
2122
|
+
sourcePath: declaration.sourcePath,
|
|
2123
|
+
sourceRevision: declaration.sourceRevision,
|
|
2124
|
+
availableGuids: available,
|
|
2125
|
+
...cookers.length === 0 ? {} : { cookers },
|
|
2126
|
+
policy: {
|
|
2127
|
+
base: "/",
|
|
2128
|
+
packagePath: `assets/${projected.value.packageId}.pack.json`,
|
|
2129
|
+
artifactPath: (assetGuid, key) => `${assetGuid}/${key}.bin`,
|
|
2130
|
+
sink: () => {
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
});
|
|
2134
|
+
if (!prepared.ok) throw prepared.error;
|
|
2135
|
+
const cookedByGuid = new Map(
|
|
2136
|
+
prepared.value.finalized.pack.assets.map((asset) => [asset.guid.toLowerCase(), asset])
|
|
2137
|
+
);
|
|
2138
|
+
for (const asset of projected.value.assets) {
|
|
2139
|
+
if (!required.has(asset.guid.toLowerCase())) continue;
|
|
2140
|
+
const parsedGuid = AssetGuid$1.parse(asset.guid);
|
|
2141
|
+
if (!parsedGuid.ok) throw parsedGuid.error;
|
|
2142
|
+
const cooked2 = cookedByGuid.get(asset.guid.toLowerCase());
|
|
2143
|
+
if (cooked2 === void 0) {
|
|
2144
|
+
throw new AssetError({
|
|
2145
|
+
code: "asset-not-imported",
|
|
2146
|
+
expected: "the direct Pack producer to retain every declared output",
|
|
2147
|
+
hint: "repair the direct Pack output and rerun the Pack build",
|
|
2148
|
+
detail: { sourcePath: declaration.sourcePath }
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
outputs.push({
|
|
2152
|
+
guid: parsedGuid.value,
|
|
2153
|
+
sourceKey: asset.sourceKey,
|
|
2154
|
+
asset: { kind: cooked2.kind, ...cooked2.payload }
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
continue;
|
|
2158
|
+
}
|
|
1937
2159
|
const cooked = await readCookedAuthoredPack(declaration.value, cookers, declaration.sourcePath);
|
|
1938
|
-
for (const asset of cooked?.logicalPackage.assets.
|
|
1939
|
-
(
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
2160
|
+
for (const asset of cooked?.logicalPackage.assets ?? declaration.value.assets) {
|
|
2161
|
+
if (!required.has(asset.guid.toLowerCase())) continue;
|
|
2162
|
+
const parsedGuid = AssetGuid$1.parse(asset.guid);
|
|
2163
|
+
if (!parsedGuid.ok) throw parsedGuid.error;
|
|
2164
|
+
outputs.push({
|
|
2165
|
+
guid: parsedGuid.value,
|
|
2166
|
+
sourceKey: asset.sourceKey ?? asset.guid,
|
|
2167
|
+
asset: { ...asset.payload, kind: asset.kind }
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
return outputs;
|
|
2172
|
+
}
|
|
2173
|
+
async function readCookedAuthoredPack(authoredPack, cookers = [], sourcePath) {
|
|
2174
|
+
if (authoredPack.schemaVersion !== "2.0.0") return void 0;
|
|
2175
|
+
const registry = new NativeCookerRegistry();
|
|
2176
|
+
for (const cooker of cookers) registry.register(cooker);
|
|
2177
|
+
const assets = [];
|
|
2178
|
+
const refsByGuid = /* @__PURE__ */ new Map();
|
|
2179
|
+
let hasCookedAsset = false;
|
|
2180
|
+
for (const asset of authoredPack.assets) {
|
|
2181
|
+
if (asset.execution !== "cooked") {
|
|
2182
|
+
assets.push({
|
|
2183
|
+
guid: asset.guid,
|
|
2184
|
+
kind: asset.kind,
|
|
2185
|
+
...asset.name === void 0 ? {} : { name: asset.name },
|
|
2186
|
+
payload: asset.payload,
|
|
2187
|
+
refs: asset.refs,
|
|
2188
|
+
artifacts: {}
|
|
2189
|
+
});
|
|
2190
|
+
continue;
|
|
2191
|
+
}
|
|
2192
|
+
if (isEngineOwnedMaterial(asset)) {
|
|
2193
|
+
assets.push({
|
|
2194
|
+
guid: asset.guid,
|
|
2195
|
+
kind: asset.kind,
|
|
2196
|
+
...asset.name === void 0 ? {} : { name: asset.name },
|
|
2197
|
+
payload: asset.payload,
|
|
2198
|
+
refs: asset.refs,
|
|
2199
|
+
artifacts: {}
|
|
2200
|
+
});
|
|
2201
|
+
continue;
|
|
2202
|
+
}
|
|
2203
|
+
hasCookedAsset = true;
|
|
2204
|
+
const result = await registry.runDraft(asset.kind, {
|
|
2205
|
+
guid: asset.guid,
|
|
2206
|
+
// Pack v2 stores the asset kind beside payload. Native producer APIs
|
|
2207
|
+
// consume the runtime Asset shape, so reconstruct that one boundary
|
|
2208
|
+
// field before invoking the cooker.
|
|
2209
|
+
source: nativeCookSource(asset),
|
|
2210
|
+
...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey },
|
|
2211
|
+
...sourcePath === void 0 ? {} : { sourcePath },
|
|
2212
|
+
refs: asset.refs
|
|
2213
|
+
});
|
|
2214
|
+
if (!result.ok) throw result.error;
|
|
2215
|
+
const draft = result.value;
|
|
2216
|
+
refsByGuid.set(asset.guid.toLowerCase(), [...draft.refs]);
|
|
2217
|
+
assets.push({
|
|
2218
|
+
guid: draft.guid,
|
|
2219
|
+
kind: asset.kind,
|
|
2220
|
+
...asset.name === void 0 ? {} : { name: asset.name },
|
|
2221
|
+
payload: draft.payload,
|
|
2222
|
+
refs: [...draft.refs],
|
|
2223
|
+
artifacts: draft.artifacts
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
return hasCookedAsset ? {
|
|
2227
|
+
logicalPackage: { assets },
|
|
2228
|
+
refsByGuid
|
|
2229
|
+
} : void 0;
|
|
2230
|
+
}
|
|
2231
|
+
function directReference(guid) {
|
|
2232
|
+
return { guid, sourceField: { fieldName: "refs" } };
|
|
2233
|
+
}
|
|
2234
|
+
function mergeDirectReferences(produced, declared) {
|
|
2235
|
+
const merged = [...produced];
|
|
2236
|
+
const seen = new Set(produced.map((reference) => reference.guid.toLowerCase()));
|
|
2237
|
+
for (const guid of declared) {
|
|
2238
|
+
const parsed = AssetGuid$1.parse(guid);
|
|
2239
|
+
if (!parsed.ok) throw parsed.error;
|
|
2240
|
+
const normalized = AssetGuid$1.format(parsed.value);
|
|
2241
|
+
if (seen.has(normalized.toLowerCase())) continue;
|
|
2242
|
+
seen.add(normalized.toLowerCase());
|
|
2243
|
+
merged.push(directReference(normalized));
|
|
2244
|
+
}
|
|
2245
|
+
return merged;
|
|
2246
|
+
}
|
|
2247
|
+
function directReferenceFailure(sourcePath, guids) {
|
|
2248
|
+
return {
|
|
2249
|
+
code: "pack-output-reference-missing",
|
|
2250
|
+
expected: "every direct Pack reference to resolve to a local or published AssetGuid",
|
|
2251
|
+
hint: "publish the referenced Pack or repair the direct refs before rebuilding the Pack",
|
|
2252
|
+
detail: { sourcePath, guids: [...guids].sort() }
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
function directInputFingerprint(sourceRevision, nativeFingerprints) {
|
|
2256
|
+
if (nativeFingerprints.size === 0) return sourceRevision;
|
|
2257
|
+
return `sha256:${packageTransportRevision({
|
|
2258
|
+
sourceRevision,
|
|
2259
|
+
nativeCookers: [...nativeFingerprints.entries()].sort(
|
|
2260
|
+
([left], [right]) => left.localeCompare(right)
|
|
2261
|
+
)
|
|
2262
|
+
})}`;
|
|
2263
|
+
}
|
|
2264
|
+
function importedAssetArtifacts(assets) {
|
|
2265
|
+
return Object.fromEntries(
|
|
2266
|
+
assets.flatMap(
|
|
2267
|
+
(asset) => Object.entries(asset.artifacts).map(([key, artifact]) => [`${asset.guid}/${key}`, artifact])
|
|
2268
|
+
)
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
function rawDirectProduct(input) {
|
|
2272
|
+
const inputFingerprint = `sha256:${packageTransportRevision({
|
|
2273
|
+
packageId: input.projected.packageId,
|
|
2274
|
+
sourceRevision: input.sourceRevision,
|
|
2275
|
+
assets: input.projected.assets
|
|
2276
|
+
})}`;
|
|
2277
|
+
const assets = input.projected.assets.map((asset) => ({
|
|
2278
|
+
guid: asset.guid,
|
|
2279
|
+
kind: asset.kind,
|
|
2280
|
+
...asset.name === void 0 ? {} : { name: asset.name },
|
|
2281
|
+
payload: directRuntimePayload(asset),
|
|
2282
|
+
refs: asset.refs.map(directReference),
|
|
2283
|
+
artifacts: {}
|
|
2284
|
+
}));
|
|
2285
|
+
return {
|
|
2286
|
+
product: {
|
|
2287
|
+
assets,
|
|
2288
|
+
sourceDependencies: [input.sourcePath],
|
|
2289
|
+
refs: assets.flatMap((asset) => asset.refs),
|
|
2290
|
+
artifacts: importedAssetArtifacts(assets),
|
|
2291
|
+
receipts: assets.map((asset) => ({
|
|
2292
|
+
guid: asset.guid,
|
|
2293
|
+
origin: "authoredPack",
|
|
2294
|
+
status: "succeeded",
|
|
2295
|
+
inputFingerprint
|
|
2296
|
+
})),
|
|
2297
|
+
diagnostics: [],
|
|
2298
|
+
sourceRevision: inputFingerprint,
|
|
2299
|
+
sourceKey: input.sourcePath
|
|
2300
|
+
},
|
|
2301
|
+
stagedOutputs: input.projected.assets.map((asset) => ({
|
|
2302
|
+
guid: directGuid(asset.guid),
|
|
2303
|
+
sourceKey: asset.sourceKey,
|
|
2304
|
+
asset: {
|
|
2305
|
+
...directRuntimePayload(asset),
|
|
2306
|
+
kind: asset.kind
|
|
2307
|
+
},
|
|
2308
|
+
digest: `sha256:${packageTransportRevision({ kind: asset.kind, payload: asset.payload })}`
|
|
2309
|
+
})),
|
|
2310
|
+
externalEvidence: [],
|
|
2311
|
+
inputFingerprint
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
function directGuid(value) {
|
|
2315
|
+
const parsed = AssetGuid$1.parse(value);
|
|
2316
|
+
if (!parsed.ok) throw parsed.error;
|
|
2317
|
+
return parsed.value;
|
|
2318
|
+
}
|
|
2319
|
+
function nativeRefs(refs) {
|
|
2320
|
+
return refs.map((guid) => {
|
|
2321
|
+
const parsed = AssetGuid$1.parse(guid);
|
|
2322
|
+
if (!parsed.ok) throw parsed.error;
|
|
2323
|
+
return { guid: AssetGuid$1.format(parsed.value) };
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
function nativeCookSource(asset) {
|
|
2327
|
+
if (asset.kind !== "material" || asset.payload === null || typeof asset.payload !== "object" || Array.isArray(asset.payload)) {
|
|
2328
|
+
return asset.payload;
|
|
2329
|
+
}
|
|
2330
|
+
return { ...asset.payload, kind: asset.kind };
|
|
2331
|
+
}
|
|
2332
|
+
function isEngineOwnedMaterial(asset) {
|
|
2333
|
+
if (asset.kind !== "material" || asset.payload === null || typeof asset.payload !== "object" || Array.isArray(asset.payload)) {
|
|
2334
|
+
return false;
|
|
2335
|
+
}
|
|
2336
|
+
return isEngineMaterial(asset.payload);
|
|
2337
|
+
}
|
|
2338
|
+
function directRuntimePayload(asset) {
|
|
2339
|
+
if (asset.kind !== "ui" || asset.payload === null || typeof asset.payload !== "object" || Array.isArray(asset.payload)) {
|
|
2340
|
+
return asset.payload;
|
|
2341
|
+
}
|
|
2342
|
+
return { ...asset.payload, guid: asset.guid };
|
|
2343
|
+
}
|
|
2344
|
+
function directAssetForProducer(asset) {
|
|
2345
|
+
const payload = {
|
|
2346
|
+
...directRuntimePayload(asset),
|
|
2347
|
+
kind: asset.kind
|
|
2348
|
+
};
|
|
2349
|
+
if (asset.kind !== "mesh") return payload;
|
|
2350
|
+
return normalizeMeshPayload(payload, asset.refs) ?? payload;
|
|
2351
|
+
}
|
|
2352
|
+
async function prepareDirectPackTransport(input) {
|
|
2353
|
+
const localGuids = new Set(input.projected.assets.map((asset) => asset.guid.toLowerCase()));
|
|
2354
|
+
const availableGuids = new Set(
|
|
2355
|
+
[...input.availableGuids ?? [], ...BUILTIN_MESH_ASSETS.map((asset) => asset.guid)].filter(
|
|
2356
|
+
(guid) => !localGuids.has(guid.toLowerCase())
|
|
2357
|
+
)
|
|
2358
|
+
);
|
|
2359
|
+
const directArtifacts = input.projected.assets.find(
|
|
2360
|
+
(asset) => asset.artifacts !== void 0 && Object.keys(asset.artifacts).length > 0
|
|
2361
|
+
);
|
|
2362
|
+
if (directArtifacts !== void 0) {
|
|
2363
|
+
return err({
|
|
2364
|
+
code: "pack-parameter-invalid",
|
|
2365
|
+
expected: "direct v3 authoring entries to leave artifacts empty for the producer",
|
|
2366
|
+
hint: "remove authored artifact data and let the registered Asset producer create it",
|
|
2367
|
+
detail: { sourcePath: input.sourcePath, sourceKey: directArtifacts.sourceKey }
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
const packageId = PackageId$1.parse(input.projected.packageId);
|
|
2371
|
+
if (!packageId.ok) return err(packageId.error);
|
|
2372
|
+
const cookerRegistry = new NativeCookerRegistry();
|
|
2373
|
+
for (const cooker of input.cookers ?? []) cookerRegistry.register(cooker);
|
|
2374
|
+
const nativeDrafts = /* @__PURE__ */ new Map();
|
|
2375
|
+
const nativeFingerprints = /* @__PURE__ */ new Map();
|
|
2376
|
+
for (const asset of input.projected.assets) {
|
|
2377
|
+
if (!isScriptablePackAssetKind(asset.kind)) continue;
|
|
2378
|
+
if (cookerRegistry.get(asset.kind) === void 0) continue;
|
|
2379
|
+
if (isEngineOwnedMaterial(asset)) continue;
|
|
2380
|
+
const draft = await cookerRegistry.runDraft(asset.kind, {
|
|
2381
|
+
guid: asset.guid,
|
|
2382
|
+
// Direct v3 entries keep `kind` at the entry boundary. Reattach it for
|
|
2383
|
+
// producer contracts such as MaterialAsset, whose payload is otherwise
|
|
2384
|
+
// intentionally allowed to omit the discriminant.
|
|
2385
|
+
source: nativeCookSource(asset),
|
|
2386
|
+
sourceKey: asset.sourceKey,
|
|
2387
|
+
sourcePath: input.sourcePath,
|
|
2388
|
+
refs: asset.refs
|
|
2389
|
+
});
|
|
2390
|
+
if (!draft.ok) return err(draft.error);
|
|
2391
|
+
if (draft.value.guid.toLowerCase() !== asset.guid.toLowerCase()) {
|
|
2392
|
+
return err({
|
|
2393
|
+
code: "pack-parameter-invalid",
|
|
2394
|
+
expected: "a direct Pack cooker to preserve the derived AssetGuid",
|
|
2395
|
+
hint: "repair the native cooker output GUID and rebuild the direct Pack",
|
|
2396
|
+
detail: {
|
|
2397
|
+
sourcePath: input.sourcePath,
|
|
2398
|
+
sourceKey: asset.sourceKey,
|
|
2399
|
+
expectedGuid: asset.guid,
|
|
2400
|
+
actualGuid: draft.value.guid
|
|
2401
|
+
}
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
nativeDrafts.set(asset.guid.toLowerCase(), draft.value);
|
|
2405
|
+
nativeFingerprints.set(asset.sourceKey, draft.value.inputFingerprint);
|
|
2406
|
+
}
|
|
2407
|
+
const unsupported = input.projected.assets.filter(
|
|
2408
|
+
(asset) => !isScriptablePackAssetKind(asset.kind)
|
|
2409
|
+
);
|
|
2410
|
+
if (unsupported.length > 0 && unsupported.length !== input.projected.assets.length) {
|
|
2411
|
+
return err({
|
|
2412
|
+
code: "pack-parameter-invalid",
|
|
2413
|
+
expected: "a direct Pack to contain only producer-backed Engine Assets or only direct PODs",
|
|
2414
|
+
hint: "split custom direct PODs from producer-backed Assets into separate Packs",
|
|
2415
|
+
detail: {
|
|
2416
|
+
sourcePath: input.sourcePath,
|
|
2417
|
+
unsupportedKinds: [...new Set(unsupported.map((asset) => asset.kind))].sort()
|
|
2418
|
+
}
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
if (unsupported.length === input.projected.assets.length) {
|
|
2422
|
+
const raw = rawDirectProduct(input);
|
|
2423
|
+
const known2 = /* @__PURE__ */ new Set([...availableGuids, ...localGuids]);
|
|
2424
|
+
const missing2 = /* @__PURE__ */ new Set();
|
|
2425
|
+
for (const asset of raw.product.assets) {
|
|
2426
|
+
for (const reference of asset.refs) {
|
|
2427
|
+
if (!known2.has(reference.guid.toLowerCase())) missing2.add(reference.guid.toLowerCase());
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
if (missing2.size > 0) return err(directReferenceFailure(input.sourcePath, [...missing2]));
|
|
2431
|
+
const finalized2 = await finalizePackageTransportSource(
|
|
2432
|
+
projectImportProductForBuild(raw.product),
|
|
2433
|
+
input.policy
|
|
2434
|
+
);
|
|
2435
|
+
const facts2 = projectParameterizedPackPublication(raw);
|
|
2436
|
+
if (!facts2.ok) return facts2;
|
|
2437
|
+
const displaySourcePath2 = input.displaySourcePath ?? input.sourcePath;
|
|
2438
|
+
const revision2 = await scriptablePackResourceRevision(displaySourcePath2, raw.inputFingerprint, [
|
|
2439
|
+
{ path: input.sourcePath, digest: input.sourceRevision }
|
|
2440
|
+
]);
|
|
2441
|
+
return ok({
|
|
2442
|
+
projected: input.projected,
|
|
2443
|
+
product: raw,
|
|
2444
|
+
finalized: finalized2,
|
|
2445
|
+
facts: facts2.value,
|
|
2446
|
+
revision: revision2
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2449
|
+
const definition = {
|
|
2450
|
+
schemaVersion: "2.0.0",
|
|
2451
|
+
packageId: packageId.value,
|
|
2452
|
+
build: () => ok(
|
|
2453
|
+
Object.fromEntries(
|
|
2454
|
+
input.projected.assets.map((asset) => [
|
|
2455
|
+
asset.sourceKey,
|
|
2456
|
+
{
|
|
2457
|
+
...nativeDrafts.get(asset.guid.toLowerCase())?.payload ?? directAssetForProducer(asset),
|
|
2458
|
+
kind: asset.kind
|
|
2459
|
+
}
|
|
2460
|
+
])
|
|
2461
|
+
)
|
|
2462
|
+
)
|
|
2463
|
+
};
|
|
2464
|
+
const outputs = createStandardAssetOutputProducerRegistry();
|
|
2465
|
+
outputs.register(createPreExternalizedSceneAssetOutputProducer());
|
|
2466
|
+
const built = await buildParameterizedScriptablePack({
|
|
2467
|
+
definition,
|
|
2468
|
+
sourcePath: input.sourcePath,
|
|
2469
|
+
outputs,
|
|
2470
|
+
sourceClosure: [{ path: input.sourcePath, digest: input.sourceRevision }],
|
|
2471
|
+
availableGuids,
|
|
2472
|
+
deferReferenceValidation: true
|
|
2473
|
+
});
|
|
2474
|
+
if (!built.ok) return built;
|
|
2475
|
+
const declaredByGuid = new Map(
|
|
2476
|
+
input.projected.assets.map((asset) => [asset.guid.toLowerCase(), asset.refs])
|
|
2477
|
+
);
|
|
2478
|
+
const assets = built.value.product.assets.map((asset) => ({
|
|
2479
|
+
...asset,
|
|
2480
|
+
...nativeDrafts.has(asset.guid.toLowerCase()) ? {
|
|
2481
|
+
payload: nativeDrafts.get(asset.guid.toLowerCase())?.payload,
|
|
2482
|
+
refs: mergeDirectReferences(
|
|
2483
|
+
nativeRefs(nativeDrafts.get(asset.guid.toLowerCase())?.refs ?? []),
|
|
2484
|
+
declaredByGuid.get(asset.guid.toLowerCase()) ?? []
|
|
2485
|
+
),
|
|
2486
|
+
artifacts: nativeDrafts.get(asset.guid.toLowerCase())?.artifacts ?? {}
|
|
2487
|
+
} : {
|
|
2488
|
+
refs: mergeDirectReferences(
|
|
2489
|
+
asset.refs,
|
|
2490
|
+
declaredByGuid.get(asset.guid.toLowerCase()) ?? []
|
|
2491
|
+
)
|
|
2492
|
+
}
|
|
2493
|
+
}));
|
|
2494
|
+
const missing = /* @__PURE__ */ new Set();
|
|
2495
|
+
const known = /* @__PURE__ */ new Set([...availableGuids, ...localGuids]);
|
|
2496
|
+
for (const asset of assets) {
|
|
2497
|
+
for (const reference of asset.refs) {
|
|
2498
|
+
if (!known.has(reference.guid.toLowerCase())) missing.add(reference.guid.toLowerCase());
|
|
1944
2499
|
}
|
|
1945
2500
|
}
|
|
1946
|
-
return
|
|
2501
|
+
if (missing.size > 0) return err(directReferenceFailure(input.sourcePath, [...missing]));
|
|
2502
|
+
const inputFingerprint = directInputFingerprint(built.value.inputFingerprint, nativeFingerprints);
|
|
2503
|
+
const stagedOutputs = input.projected.assets.map((asset) => ({
|
|
2504
|
+
guid: directGuid(asset.guid),
|
|
2505
|
+
sourceKey: asset.sourceKey,
|
|
2506
|
+
asset: directAssetForProducer(asset),
|
|
2507
|
+
digest: `sha256:${packageTransportRevision({ kind: asset.kind, payload: asset.payload })}`
|
|
2508
|
+
}));
|
|
2509
|
+
const product = {
|
|
2510
|
+
...built.value,
|
|
2511
|
+
inputFingerprint,
|
|
2512
|
+
stagedOutputs,
|
|
2513
|
+
product: {
|
|
2514
|
+
...built.value.product,
|
|
2515
|
+
assets,
|
|
2516
|
+
refs: assets.flatMap((asset) => asset.refs),
|
|
2517
|
+
artifacts: importedAssetArtifacts(assets),
|
|
2518
|
+
receipts: built.value.product.receipts.map((receipt) => ({
|
|
2519
|
+
...receipt,
|
|
2520
|
+
inputFingerprint
|
|
2521
|
+
})),
|
|
2522
|
+
sourceRevision: inputFingerprint
|
|
2523
|
+
}
|
|
2524
|
+
};
|
|
2525
|
+
const finalized = await finalizePackageTransportSource(
|
|
2526
|
+
projectImportProductForBuild(product.product),
|
|
2527
|
+
input.policy
|
|
2528
|
+
);
|
|
2529
|
+
const facts = projectParameterizedPackPublication(product);
|
|
2530
|
+
if (!facts.ok) return facts;
|
|
2531
|
+
const displaySourcePath = input.displaySourcePath ?? input.sourcePath;
|
|
2532
|
+
const revision = await scriptablePackResourceRevision(
|
|
2533
|
+
displaySourcePath,
|
|
2534
|
+
product.inputFingerprint,
|
|
2535
|
+
[{ path: input.sourcePath, digest: input.sourceRevision }]
|
|
2536
|
+
);
|
|
2537
|
+
return ok({
|
|
2538
|
+
projected: input.projected,
|
|
2539
|
+
product,
|
|
2540
|
+
finalized,
|
|
2541
|
+
facts: facts.value,
|
|
2542
|
+
revision
|
|
2543
|
+
});
|
|
1947
2544
|
}
|
|
1948
|
-
async function
|
|
1949
|
-
const
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
return { pack, ...firstGuid === void 0 ? {} : { firstGuid } };
|
|
2545
|
+
async function prepareLegacyPackTransport(authoredPack, cookers, policyFor, sourcePath) {
|
|
2546
|
+
const firstGuid = authoredPack.assets[0]?.guid?.toLowerCase();
|
|
2547
|
+
if (authoredPack.schemaVersion !== "2.0.0" || firstGuid === void 0) {
|
|
2548
|
+
return { pack: authoredPack, ...firstGuid === void 0 ? {} : { firstGuid } };
|
|
1953
2549
|
}
|
|
1954
|
-
const cooked = await readCookedAuthoredPack(
|
|
1955
|
-
if (cooked === void 0) return { pack, firstGuid };
|
|
2550
|
+
const cooked = await readCookedAuthoredPack(authoredPack, cookers, sourcePath);
|
|
2551
|
+
if (cooked === void 0) return { pack: authoredPack, firstGuid };
|
|
1956
2552
|
return {
|
|
1957
|
-
pack,
|
|
2553
|
+
pack: authoredPack,
|
|
1958
2554
|
firstGuid,
|
|
1959
2555
|
cooked,
|
|
1960
2556
|
finalized: await finalizePackageTransportSource(cooked.logicalPackage, policyFor(firstGuid))
|
|
1961
2557
|
};
|
|
1962
2558
|
}
|
|
1963
|
-
function
|
|
2559
|
+
async function materializePreparedParameterizedScriptablePack(prepared, paths, sink) {
|
|
2560
|
+
const { product, finalized } = prepared;
|
|
2561
|
+
await sink.writePackage(paths.packagePath, JSON.stringify(finalized.pack));
|
|
2562
|
+
for (const artifact of finalized.artifacts) {
|
|
2563
|
+
await sink.writeArtifact(
|
|
2564
|
+
paths.artifactPath(artifact.path),
|
|
2565
|
+
artifact.bytes,
|
|
2566
|
+
artifact.path.endsWith(".json") ? "application/json" : artifact.mediaType
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
const receipts = /* @__PURE__ */ new Map();
|
|
2570
|
+
for (const asset of product.product.assets) {
|
|
2571
|
+
const path = paths.receiptPath(asset.guid);
|
|
2572
|
+
await sink.writeReceipt(
|
|
2573
|
+
path,
|
|
2574
|
+
JSON.stringify({
|
|
2575
|
+
guid: asset.guid,
|
|
2576
|
+
origin: "sourceMeta",
|
|
2577
|
+
status: "succeeded",
|
|
2578
|
+
inputFingerprint: product.inputFingerprint,
|
|
2579
|
+
outputDigest: finalized.digest
|
|
2580
|
+
})
|
|
2581
|
+
);
|
|
2582
|
+
receipts.set(asset.guid.toLowerCase(), path);
|
|
2583
|
+
}
|
|
2584
|
+
return receipts;
|
|
2585
|
+
}
|
|
2586
|
+
function projectParameterizedPackPublication(product) {
|
|
1964
2587
|
const assets = new Map(product.product.assets.map((asset) => [asset.guid.toLowerCase(), asset]));
|
|
1965
2588
|
const outputs = [];
|
|
1966
2589
|
for (const staged of product.stagedOutputs) {
|
|
1967
|
-
const guid = AssetGuid.format(staged.guid).toLowerCase();
|
|
2590
|
+
const guid = AssetGuid$1.format(staged.guid).toLowerCase();
|
|
1968
2591
|
const asset = assets.get(guid);
|
|
1969
|
-
if (asset === void 0 || staged.digest === void 0) {
|
|
2592
|
+
if (asset === void 0 || staged.digest === void 0 || staged.sourceKey === void 0) {
|
|
1970
2593
|
return err({
|
|
1971
2594
|
code: "pack-source-output-invalid",
|
|
1972
|
-
expected: `
|
|
1973
|
-
hint: "repair the
|
|
2595
|
+
expected: `parameterized Pack publication output ${guid} to include a product, sourceKey, and digest`,
|
|
2596
|
+
hint: "repair the dynamic Pack output and rebuild the current generation",
|
|
1974
2597
|
detail: { stage: "publication", guid }
|
|
1975
2598
|
});
|
|
1976
2599
|
}
|
|
1977
2600
|
outputs.push({
|
|
1978
2601
|
guid,
|
|
1979
|
-
sourceKey: staged.sourceKey
|
|
2602
|
+
sourceKey: staged.sourceKey,
|
|
1980
2603
|
kind: asset.kind,
|
|
1981
2604
|
digest: staged.digest,
|
|
1982
2605
|
refs: asset.refs.map((reference) => reference.guid)
|
|
@@ -1992,130 +2615,115 @@ function projectScriptablePackPublication(product) {
|
|
|
1992
2615
|
)
|
|
1993
2616
|
});
|
|
1994
2617
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
)
|
|
2618
|
+
function snapshotSourceForStagedOutputs(outputs) {
|
|
2619
|
+
if (outputs.length === 0) return void 0;
|
|
2620
|
+
const byGuid = new Map(
|
|
2621
|
+
outputs.map((output) => [AssetGuid$1.format(output.guid).toLowerCase(), output])
|
|
2000
2622
|
);
|
|
2001
|
-
return {
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
const products = /* @__PURE__ */ new Map();
|
|
2014
|
-
const owners = [...entries.entries()].map(
|
|
2015
|
-
([displaySourcePath, entry]) => ({
|
|
2016
|
-
id: displaySourcePath,
|
|
2017
|
-
guids: Object.values(entry.definition.assets).map((asset) => asset.guid),
|
|
2018
|
-
async build(source) {
|
|
2019
|
-
const produced = await produceScriptableSourcePackage({
|
|
2020
|
-
definition: entry.definition,
|
|
2021
|
-
sourcePath: displaySourcePath,
|
|
2022
|
-
assetSource: source,
|
|
2023
|
-
outputs: createStandardAssetOutputProducerRegistry(
|
|
2024
|
-
projectScriptablePackSceneComponents(entry.definition.sceneComponents)
|
|
2025
|
-
),
|
|
2026
|
-
sourceClosure: entry.closure,
|
|
2027
|
-
authoringContractVersion: "scriptable-pack-production/1"
|
|
2028
|
-
});
|
|
2029
|
-
if (!produced.ok) return produced;
|
|
2030
|
-
const observedClosure = await inventoryScriptablePackSource(entry.source.sourcePath);
|
|
2031
|
-
if (JSON.stringify(observedClosure) !== JSON.stringify(entry.closure)) {
|
|
2032
|
-
return err({
|
|
2033
|
-
code: "pack-source-load-failed",
|
|
2034
|
-
expected: "the complete ScriptablePack module closure to remain fixed during one build",
|
|
2035
|
-
hint: "retry the build after source and helper writes have settled",
|
|
2036
|
-
detail: {
|
|
2037
|
-
sourcePath: displaySourcePath,
|
|
2038
|
-
reason: "source-changed",
|
|
2039
|
-
phase: "build",
|
|
2040
|
-
diagnostic: "module closure changed while the staged generation was building"
|
|
2041
|
-
}
|
|
2042
|
-
});
|
|
2043
|
-
}
|
|
2044
|
-
products.set(displaySourcePath, produced.value);
|
|
2045
|
-
return ok(produced.value.stagedOutputs);
|
|
2623
|
+
return {
|
|
2624
|
+
async readByGuid(guid) {
|
|
2625
|
+
const key = AssetGuid$1.format(guid).toLowerCase();
|
|
2626
|
+
const output = byGuid.get(key);
|
|
2627
|
+
if (output === void 0) {
|
|
2628
|
+
return err(
|
|
2629
|
+
new AssetError({
|
|
2630
|
+
code: "asset-not-found",
|
|
2631
|
+
expected: `an available Asset snapshot for ${key}`,
|
|
2632
|
+
hint: "publish the referenced Pack or repair the content dependency"
|
|
2633
|
+
})
|
|
2634
|
+
);
|
|
2046
2635
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
[...entries.values()].flatMap((entry) => entry.closure).sort((a, b) => a.path.localeCompare(b.path))
|
|
2052
|
-
)
|
|
2053
|
-
).digest();
|
|
2054
|
-
const stagedSource = createScriptablePackStagedAssetSnapshotSource({
|
|
2055
|
-
generation: generationDigest.readUInt32BE(0),
|
|
2056
|
-
owners,
|
|
2057
|
-
declaredExternalOutputs
|
|
2058
|
-
});
|
|
2059
|
-
const prepared = /* @__PURE__ */ new Map();
|
|
2060
|
-
for (const source of sources) {
|
|
2061
|
-
const entry = entries.get(source.displaySourcePath);
|
|
2062
|
-
if (entry === void 0) {
|
|
2063
|
-
return err({
|
|
2064
|
-
code: "pack-source-path-invalid",
|
|
2065
|
-
expected: "a ScriptablePack source registered in the current inventory",
|
|
2066
|
-
actual: source.displaySourcePath,
|
|
2067
|
-
hint: "rebuild the source inventory before producing this path",
|
|
2068
|
-
retryable: true,
|
|
2069
|
-
recoveryActions: ["rebuild-source-inventory"],
|
|
2070
|
-
detail: { sourcePath: source.displaySourcePath }
|
|
2636
|
+
return ok({
|
|
2637
|
+
asset: structuredClone(output.asset),
|
|
2638
|
+
generation: 1,
|
|
2639
|
+
digest: output.digest ?? "sha256:staged"
|
|
2071
2640
|
});
|
|
2072
2641
|
}
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2644
|
+
function composeParameterizedAssetSource(staged, published) {
|
|
2645
|
+
if (staged === void 0) return published;
|
|
2646
|
+
if (published === void 0) return staged;
|
|
2647
|
+
return {
|
|
2648
|
+
async readByGuid(guid) {
|
|
2649
|
+
const local = await staged.readByGuid(guid);
|
|
2650
|
+
if (local.ok || local.error.code !== "asset-not-found") return local;
|
|
2651
|
+
return published.readByGuid(guid);
|
|
2081
2652
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2655
|
+
async function produceParameterizedScriptablePackProducts(options) {
|
|
2656
|
+
if (options.sources.length === 0) return ok(/* @__PURE__ */ new Map());
|
|
2657
|
+
const external = options.declaredExternalOutputs ?? [];
|
|
2658
|
+
const assetSource = composeParameterizedAssetSource(
|
|
2659
|
+
snapshotSourceForStagedOutputs(external),
|
|
2660
|
+
options.assetSource
|
|
2661
|
+
);
|
|
2662
|
+
const availableGuids = /* @__PURE__ */ new Set([
|
|
2663
|
+
...options.availableGuids ?? [],
|
|
2664
|
+
...external.map((output) => AssetGuid$1.format(output.guid).toLowerCase())
|
|
2665
|
+
]);
|
|
2666
|
+
const workItems = options.sources.map((source) => ({
|
|
2667
|
+
definition: source.definition,
|
|
2668
|
+
sourcePath: source.sourcePath,
|
|
2669
|
+
...source.subjectPackageId === void 0 ? {} : { subjectPackageId: source.subjectPackageId },
|
|
2670
|
+
...source.values === void 0 ? {} : { values: source.values },
|
|
2671
|
+
...source.inheritedValues === void 0 ? {} : { inheritedValues: source.inheritedValues },
|
|
2672
|
+
sourceClosure: source.sourceClosure
|
|
2673
|
+
}));
|
|
2674
|
+
const worklist = await buildParameterizedScriptablePackWorklist({
|
|
2675
|
+
subjects: workItems,
|
|
2676
|
+
outputs: createStandardAssetOutputProducerRegistry(
|
|
2677
|
+
options.sources.flatMap(
|
|
2678
|
+
(source) => projectScriptablePackSceneComponents(source.definition.sceneComponents)
|
|
2679
|
+
)
|
|
2680
|
+
),
|
|
2681
|
+
...assetSource === void 0 ? {} : { assetSource },
|
|
2682
|
+
availableGuids,
|
|
2683
|
+
...options.incomingRefs === void 0 ? {} : { incomingRefs: options.incomingRefs },
|
|
2684
|
+
...options.maxPasses === void 0 ? {} : { maxPasses: options.maxPasses }
|
|
2685
|
+
});
|
|
2686
|
+
if (!worklist.ok) return worklist;
|
|
2687
|
+
const productsBySource = new Map(
|
|
2688
|
+
worklist.value.buildProducts.filter((product) => product.product.sourceKey !== void 0).map((product) => [product.product.sourceKey, product])
|
|
2689
|
+
);
|
|
2690
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
2691
|
+
for (const source of options.sources) {
|
|
2692
|
+
const product = productsBySource.get(source.sourcePath);
|
|
2693
|
+
if (product === void 0) {
|
|
2086
2694
|
return err({
|
|
2087
2695
|
code: "pack-source-output-invalid",
|
|
2088
|
-
expected: "
|
|
2089
|
-
hint: "
|
|
2090
|
-
detail: {
|
|
2091
|
-
missingGuids: [AssetGuid.format(first.guid)],
|
|
2092
|
-
unexpectedSourceKeys: [],
|
|
2093
|
-
kindMismatches: []
|
|
2094
|
-
}
|
|
2696
|
+
expected: "one terminal dynamic Pack product for every source subject",
|
|
2697
|
+
hint: "rerun the bounded generation worklist and inspect its subject map",
|
|
2698
|
+
detail: { sourcePath: source.sourcePath }
|
|
2095
2699
|
});
|
|
2096
2700
|
}
|
|
2097
|
-
const
|
|
2098
|
-
const
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2701
|
+
const policy = typeof source.policy === "function" ? source.policy(product) : source.policy;
|
|
2702
|
+
const finalized = await finalizePackageTransportSource(
|
|
2703
|
+
projectImportProductForBuild(product.product),
|
|
2704
|
+
policy
|
|
2705
|
+
);
|
|
2706
|
+
const facts = projectParameterizedPackPublication(product);
|
|
2707
|
+
if (!facts.ok) return facts;
|
|
2102
2708
|
const revision = await scriptablePackResourceRevision(
|
|
2103
2709
|
source.displaySourcePath,
|
|
2104
|
-
|
|
2710
|
+
product.inputFingerprint,
|
|
2105
2711
|
source.sourceClosure
|
|
2106
2712
|
);
|
|
2107
2713
|
const publication = createAcceptedPublication({
|
|
2108
2714
|
sourcePath: source.displaySourcePath,
|
|
2109
|
-
sourceRevision:
|
|
2715
|
+
sourceRevision: product.inputFingerprint,
|
|
2110
2716
|
generation: source.publicationGeneration,
|
|
2111
2717
|
digest: finalized.digest,
|
|
2112
2718
|
packageUrl: finalized.packageUrl,
|
|
2113
|
-
inputFingerprint:
|
|
2719
|
+
inputFingerprint: product.inputFingerprint,
|
|
2114
2720
|
outputs: facts.value.outputs,
|
|
2115
|
-
externalEvidence:
|
|
2721
|
+
externalEvidence: product.externalEvidence
|
|
2116
2722
|
});
|
|
2117
2723
|
prepared.set(source.displaySourcePath, {
|
|
2118
|
-
product:
|
|
2724
|
+
product: {
|
|
2725
|
+
...product
|
|
2726
|
+
},
|
|
2119
2727
|
finalized,
|
|
2120
2728
|
facts: facts.value,
|
|
2121
2729
|
revision,
|
|
@@ -2124,6 +2732,22 @@ async function produceScriptablePackProducts(sources, declaredExternalOutputs =
|
|
|
2124
2732
|
}
|
|
2125
2733
|
return ok(prepared);
|
|
2126
2734
|
}
|
|
2735
|
+
async function scriptablePackResourceRevision(displaySourcePath, digest3, sourceClosure) {
|
|
2736
|
+
const mtimes = await Promise.all(
|
|
2737
|
+
sourceClosure.map(async (entry) => {
|
|
2738
|
+
try {
|
|
2739
|
+
return (await stat(entry.path)).mtimeMs;
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
if (error !== null && typeof error === "object" && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
2742
|
+
return 0;
|
|
2743
|
+
}
|
|
2744
|
+
throw error;
|
|
2745
|
+
}
|
|
2746
|
+
})
|
|
2747
|
+
);
|
|
2748
|
+
const observedAt = mtimes.length === 0 ? 0 : Math.trunc(Math.max(...mtimes));
|
|
2749
|
+
return { digest: digest3, observedAt, rootId: displaySourcePath };
|
|
2750
|
+
}
|
|
2127
2751
|
function normalizeCatalogPath(path) {
|
|
2128
2752
|
return path.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
2129
2753
|
}
|
|
@@ -2146,120 +2770,273 @@ function scriptableArtifactPath(guid, key) {
|
|
|
2146
2770
|
return `${guid.toLowerCase()}/${key.includes(".") ? key : `${key}.bin`}`;
|
|
2147
2771
|
}
|
|
2148
2772
|
function runtimePublicationFor(context, input) {
|
|
2149
|
-
|
|
2773
|
+
const publicationInput = {
|
|
2150
2774
|
pack: input.pack,
|
|
2151
2775
|
scopeId: context.runtimeBinding?.scopeId ?? "asset-runtime",
|
|
2152
2776
|
sourcePath: input.sourcePath,
|
|
2153
2777
|
sourceRevision: input.sourceRevision,
|
|
2154
2778
|
packageUrl: input.packageUrl,
|
|
2155
2779
|
...input.digest === void 0 ? {} : { digest: input.digest },
|
|
2780
|
+
...input.inputFingerprint === void 0 ? {} : { inputFingerprint: input.inputFingerprint },
|
|
2781
|
+
...input.outputs === void 0 ? {} : { outputs: input.outputs },
|
|
2782
|
+
...input.externalEvidence === void 0 ? {} : { externalEvidence: input.externalEvidence },
|
|
2156
2783
|
generation: context.runtimeBinding?.generation ?? context.generation
|
|
2784
|
+
};
|
|
2785
|
+
if (input.sourceKeys === void 0) return createRuntimePackPublication(publicationInput);
|
|
2786
|
+
const derived = createRuntimePackPublication(publicationInput);
|
|
2787
|
+
const outputs = derived.publication.outputs.map((output) => ({
|
|
2788
|
+
...output,
|
|
2789
|
+
sourceKey: input.sourceKeys?.get(output.guid.toLowerCase()) ?? output.sourceKey
|
|
2790
|
+
}));
|
|
2791
|
+
return createRuntimePackPublication({ ...publicationInput, outputs });
|
|
2792
|
+
}
|
|
2793
|
+
function sourceKeysFor(declarations) {
|
|
2794
|
+
return new Map(
|
|
2795
|
+
declarations.flatMap(
|
|
2796
|
+
(declaration) => declaration.sourceKey === void 0 ? [] : [[declaration.guid.toLowerCase(), declaration.sourceKey]]
|
|
2797
|
+
)
|
|
2798
|
+
);
|
|
2799
|
+
}
|
|
2800
|
+
function dynamicFailure(context, value) {
|
|
2801
|
+
const candidate = value !== null && typeof value === "object" ? value : {};
|
|
2802
|
+
const error = candidate;
|
|
2803
|
+
throw context.fail({
|
|
2804
|
+
code: typeof error.code === "string" ? error.code : "parameterized-pack-build-failed",
|
|
2805
|
+
expected: typeof error.expected === "string" ? error.expected : "the parameterized Pack generation to produce a valid terminal result",
|
|
2806
|
+
hint: typeof error.hint === "string" ? error.hint : "inspect the parameterized Pack subject and rebuild the current generation",
|
|
2807
|
+
...error.detail === void 0 ? {} : { detail: error.detail }
|
|
2157
2808
|
});
|
|
2158
2809
|
}
|
|
2159
|
-
|
|
2160
|
-
const
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
(
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2810
|
+
function parameterizedCatalogSourcePath(context, sourcePath) {
|
|
2811
|
+
const projected = context.fsForImport.sourceIdentityFor?.(sourcePath);
|
|
2812
|
+
const logical = projected === void 0 || isAbsolute(projected) ? relative(context.cwd, sourcePath) : projected;
|
|
2813
|
+
return canonicalScriptableSourcePath(logical).replaceAll("\\", "/");
|
|
2814
|
+
}
|
|
2815
|
+
async function buildParameterizedPackages(context) {
|
|
2816
|
+
const subjects = /* @__PURE__ */ new Map();
|
|
2817
|
+
for (const [sourcePath, declaration] of context.inventory.sourceDeclarations) {
|
|
2818
|
+
if (declaration.format === "pack.ts-parameterized") {
|
|
2819
|
+
const key = PackageId.format(declaration.definition.packageId).toLowerCase();
|
|
2820
|
+
subjects.set(key, {
|
|
2821
|
+
kind: "source",
|
|
2822
|
+
packageId: declaration.definition.packageId,
|
|
2823
|
+
sourcePath,
|
|
2824
|
+
definition: declaration.definition,
|
|
2825
|
+
sourceClosure: declaration.sourceClosure
|
|
2826
|
+
});
|
|
2827
|
+
continue;
|
|
2828
|
+
}
|
|
2829
|
+
if (declaration.format !== "pack.json" || declaration.value.schemaVersion !== "3.0.0") {
|
|
2830
|
+
continue;
|
|
2831
|
+
}
|
|
2832
|
+
const parsed = parseParameterizedPackJson(declaration.value);
|
|
2833
|
+
if (!parsed.ok) dynamicFailure(context, parsed.error);
|
|
2834
|
+
if (parsed.value.format === "direct") {
|
|
2835
|
+
subjects.set(PackageId.format(parsed.value.packageId).toLowerCase(), {
|
|
2836
|
+
kind: "direct",
|
|
2837
|
+
packageId: parsed.value.packageId,
|
|
2838
|
+
sourcePath
|
|
2839
|
+
});
|
|
2840
|
+
} else {
|
|
2841
|
+
subjects.set(PackageId.format(parsed.value.packageId).toLowerCase(), {
|
|
2842
|
+
kind: "instance",
|
|
2843
|
+
packageId: parsed.value.packageId,
|
|
2844
|
+
parent: parsed.value.parent,
|
|
2845
|
+
values: parsed.value.values,
|
|
2846
|
+
sourcePath
|
|
2847
|
+
});
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
const sourceSubject = (packageId) => {
|
|
2851
|
+
const subject = subjects.get(PackageId.format(packageId).toLowerCase());
|
|
2852
|
+
return subject?.kind === "source" ? subject : void 0;
|
|
2853
|
+
};
|
|
2854
|
+
const readSubject = async (packageId) => {
|
|
2855
|
+
const subject = subjects.get(PackageId.format(packageId).toLowerCase());
|
|
2856
|
+
if (subject === void 0) return void 0;
|
|
2857
|
+
if (subject.kind === "source") {
|
|
2858
|
+
return {
|
|
2859
|
+
format: "source",
|
|
2860
|
+
packageId: subject.packageId,
|
|
2861
|
+
parameters: "parameters" in subject.definition ? subject.definition.parameters : []
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
if (subject.kind === "instance") {
|
|
2865
|
+
return {
|
|
2866
|
+
format: "instance",
|
|
2867
|
+
packageId: subject.packageId,
|
|
2868
|
+
parent: subject.parent,
|
|
2869
|
+
values: subject.values
|
|
2870
|
+
};
|
|
2871
|
+
}
|
|
2872
|
+
return {
|
|
2873
|
+
format: "direct",
|
|
2874
|
+
packageId: subject.packageId
|
|
2875
|
+
};
|
|
2876
|
+
};
|
|
2877
|
+
const orderedSubjects = [...subjects.values()].sort(
|
|
2878
|
+
(left, right) => left.sourcePath.localeCompare(right.sourcePath)
|
|
2173
2879
|
);
|
|
2174
|
-
const
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
{
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2880
|
+
const inputs = [];
|
|
2881
|
+
for (const subject of orderedSubjects) {
|
|
2882
|
+
if (subject.kind === "source") {
|
|
2883
|
+
const packageId = PackageId.format(subject.packageId);
|
|
2884
|
+
const loaded = await loadParameterizedScriptablePack(subject.sourcePath);
|
|
2885
|
+
if (!loaded.ok) dynamicFailure(context, loaded.error);
|
|
2886
|
+
if (PackageId.format(loaded.value.packageId) !== packageId) {
|
|
2887
|
+
dynamicFailure(context, {
|
|
2888
|
+
code: "pack-source-revision-conflict",
|
|
2889
|
+
expected: "the parameterized source packageId to remain fixed during production",
|
|
2890
|
+
hint: "retry after source writes settle and rebuild the current generation",
|
|
2891
|
+
detail: {
|
|
2892
|
+
sourcePath: subject.sourcePath,
|
|
2893
|
+
scannedPackageId: packageId,
|
|
2894
|
+
loadedPackageId: PackageId.format(loaded.value.packageId)
|
|
2895
|
+
}
|
|
2896
|
+
});
|
|
2184
2897
|
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
hint: "rerun the source inventory and rebuild from the accepted declaration set",
|
|
2197
|
-
detail: { stage: "scan", sourcePath: entry.sourcePath }
|
|
2898
|
+
inputs.push({
|
|
2899
|
+
sourcePath: subject.sourcePath,
|
|
2900
|
+
displaySourcePath: parameterizedCatalogSourcePath(context, subject.sourcePath),
|
|
2901
|
+
definition: loaded.value,
|
|
2902
|
+
sourceClosure: subject.sourceClosure,
|
|
2903
|
+
publicationGeneration: context.generation,
|
|
2904
|
+
policy: {
|
|
2905
|
+
base: context.basePrefix === "" ? "/" : context.basePrefix,
|
|
2906
|
+
packagePath: `assets/${packageId}.pack.json`,
|
|
2907
|
+
artifactPath: scriptableArtifactPath
|
|
2908
|
+
}
|
|
2198
2909
|
});
|
|
2199
2910
|
}
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
scopeId: context.runtimeBinding?.scopeId ?? "asset-runtime",
|
|
2205
|
-
sourcePath: publication.sourcePath,
|
|
2206
|
-
sourceRevision: publication.sourceRevision,
|
|
2207
|
-
packageUrl: finalized.packageUrl,
|
|
2208
|
-
inputFingerprint: publication.receipt.inputFingerprint,
|
|
2209
|
-
digest: finalized.digest,
|
|
2210
|
-
generation: context.runtimeBinding?.generation ?? publication.generation,
|
|
2211
|
-
outputs: facts.outputs,
|
|
2212
|
-
externalEvidence: publication.externalEvidence
|
|
2213
|
-
});
|
|
2214
|
-
const receiptPaths = await materializePreparedScriptablePack(
|
|
2215
|
-
prepared,
|
|
2911
|
+
}
|
|
2912
|
+
for (const subject of orderedSubjects) {
|
|
2913
|
+
if (subject.kind !== "instance") continue;
|
|
2914
|
+
const resolved = await resolvePackParameterInheritance(
|
|
2216
2915
|
{
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2916
|
+
format: "instance",
|
|
2917
|
+
packageId: subject.packageId,
|
|
2918
|
+
parent: subject.parent,
|
|
2919
|
+
values: subject.values
|
|
2220
2920
|
},
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2921
|
+
readSubject
|
|
2922
|
+
);
|
|
2923
|
+
if (!resolved.ok) dynamicFailure(context, resolved.error);
|
|
2924
|
+
const root = sourceSubject(resolved.value.rootPackageId);
|
|
2925
|
+
if (root === void 0) {
|
|
2926
|
+
dynamicFailure(context, {
|
|
2927
|
+
code: "pack-parent-not-parameterized",
|
|
2928
|
+
expected: "the instance parent chain to terminate at a parameterized source",
|
|
2929
|
+
hint: "point the instance at a parameterized pack.ts source",
|
|
2930
|
+
detail: { packageId: PackageId.format(subject.packageId) }
|
|
2931
|
+
});
|
|
2932
|
+
}
|
|
2933
|
+
const loaded = await loadParameterizedScriptablePack(root.sourcePath);
|
|
2934
|
+
if (!loaded.ok) dynamicFailure(context, loaded.error);
|
|
2935
|
+
if (PackageId.format(loaded.value.packageId) !== PackageId.format(root.packageId)) {
|
|
2936
|
+
dynamicFailure(context, {
|
|
2937
|
+
code: "pack-source-revision-conflict",
|
|
2938
|
+
expected: "the parameterized parent packageId to remain fixed during production",
|
|
2939
|
+
hint: "retry after source writes settle and rebuild the current generation",
|
|
2940
|
+
detail: {
|
|
2941
|
+
sourcePath: root.sourcePath,
|
|
2942
|
+
scannedPackageId: PackageId.format(root.packageId),
|
|
2943
|
+
loadedPackageId: PackageId.format(loaded.value.packageId)
|
|
2235
2944
|
}
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2947
|
+
const packageId = PackageId.format(subject.packageId);
|
|
2948
|
+
inputs.push({
|
|
2949
|
+
sourcePath: subject.sourcePath,
|
|
2950
|
+
displaySourcePath: parameterizedCatalogSourcePath(context, subject.sourcePath),
|
|
2951
|
+
definition: loaded.value,
|
|
2952
|
+
sourceClosure: root.sourceClosure,
|
|
2953
|
+
subjectPackageId: subject.packageId,
|
|
2954
|
+
values: resolved.value.values,
|
|
2955
|
+
publicationGeneration: context.generation,
|
|
2956
|
+
policy: {
|
|
2957
|
+
base: context.basePrefix === "" ? "/" : context.basePrefix,
|
|
2958
|
+
packagePath: `assets/${packageId}.pack.json`,
|
|
2959
|
+
artifactPath: scriptableArtifactPath
|
|
2236
2960
|
}
|
|
2237
|
-
);
|
|
2238
|
-
result.set(entry.sourcePath, {
|
|
2239
|
-
packageUrl: finalized.packageUrl,
|
|
2240
|
-
receiptUrls: new Map(
|
|
2241
|
-
[...receiptPaths].map(([guid, path]) => [guid, context.sink.fileUrl(path)])
|
|
2242
|
-
),
|
|
2243
|
-
revision,
|
|
2244
|
-
publication: runtimePublication.publication,
|
|
2245
|
-
refs: facts.refs
|
|
2246
2961
|
});
|
|
2247
2962
|
}
|
|
2248
|
-
return
|
|
2963
|
+
if (inputs.length === 0) return [];
|
|
2964
|
+
const requiredGuids = context.inventory.entries.flatMap((entry) => {
|
|
2965
|
+
const parsed = AssetGuid$1.parse(entry.guid);
|
|
2966
|
+
if (!parsed.ok) dynamicFailure(context, parsed.error);
|
|
2967
|
+
return [parsed.value];
|
|
2968
|
+
});
|
|
2969
|
+
const externalOutputs = await declaredPackExternalOutputs(
|
|
2970
|
+
context.inventory.sourceDeclarations,
|
|
2971
|
+
context.cookers,
|
|
2972
|
+
requiredGuids,
|
|
2973
|
+
{
|
|
2974
|
+
importerRegistry: context.importerRegistry,
|
|
2975
|
+
fsForImport: context.fsForImport
|
|
2976
|
+
}
|
|
2977
|
+
);
|
|
2978
|
+
const availableGuids = new Set(requiredGuids.map((guid) => AssetGuid$1.format(guid).toLowerCase()));
|
|
2979
|
+
const built = await produceParameterizedScriptablePackProducts({
|
|
2980
|
+
sources: inputs,
|
|
2981
|
+
declaredExternalOutputs: externalOutputs,
|
|
2982
|
+
availableGuids
|
|
2983
|
+
});
|
|
2984
|
+
if (!built.ok) dynamicFailure(context, built.error);
|
|
2985
|
+
const bundles = [];
|
|
2986
|
+
for (const input of inputs) {
|
|
2987
|
+
const prepared = built.value.get(input.displaySourcePath);
|
|
2988
|
+
if (prepared === void 0) {
|
|
2989
|
+
dynamicFailure(context, {
|
|
2990
|
+
code: "parameterized-pack-build-failed",
|
|
2991
|
+
expected: "the worklist to return one prepared result per source subject",
|
|
2992
|
+
hint: "rerun the parameterized Pack generation from a clean inventory",
|
|
2993
|
+
detail: { sourcePath: input.sourcePath }
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
const packageId = PackageId.format(input.subjectPackageId ?? input.definition.packageId);
|
|
2997
|
+
const stagedByGuid = new Map(
|
|
2998
|
+
prepared.product.stagedOutputs.map((output) => [
|
|
2999
|
+
AssetGuid$1.format(output.guid).toLowerCase(),
|
|
3000
|
+
output
|
|
3001
|
+
])
|
|
3002
|
+
);
|
|
3003
|
+
const entries = projectPackageCatalog(
|
|
3004
|
+
prepared.product.product.assets.map((asset, sourceIndex) => {
|
|
3005
|
+
const staged = stagedByGuid.get(asset.guid.toLowerCase());
|
|
3006
|
+
return {
|
|
3007
|
+
guid: asset.guid,
|
|
3008
|
+
kind: asset.kind,
|
|
3009
|
+
sourcePath: input.displaySourcePath,
|
|
3010
|
+
sourceIndex,
|
|
3011
|
+
...staged?.sourceKey === void 0 ? {} : { sourceKey: staged.sourceKey },
|
|
3012
|
+
refs: asset.refs.map((reference) => reference.guid),
|
|
3013
|
+
execution: "cooked",
|
|
3014
|
+
packageId,
|
|
3015
|
+
provenance: { provider: "pack-ts", version: "2.0.0" }
|
|
3016
|
+
};
|
|
3017
|
+
}),
|
|
3018
|
+
`${context.basePrefix === "/" ? "" : context.basePrefix}/${input.displaySourcePath}`
|
|
3019
|
+
);
|
|
3020
|
+
bundles.push({ input, prepared, entries });
|
|
3021
|
+
}
|
|
3022
|
+
return bundles;
|
|
2249
3023
|
}
|
|
2250
|
-
function updateImportedEntries(entries, guids, packageUrl, projection = {}, refsByGuid, publication, sourcePath) {
|
|
3024
|
+
function updateImportedEntries(entries, guids, packageUrl, projection = {}, refsByGuid, publication, sourcePath, receiptUrls, revision) {
|
|
2251
3025
|
const selected = new Set(guids.map((guid) => guid.toLowerCase()));
|
|
2252
3026
|
for (let index = 0; index < entries.length; index += 1) {
|
|
2253
3027
|
const entry = entries[index];
|
|
2254
3028
|
if (entry === void 0 || !selected.has(entry.guid.toLowerCase())) continue;
|
|
2255
3029
|
const refs = refsByGuid?.get(entry.guid.toLowerCase());
|
|
3030
|
+
const cookReceiptUrl = receiptUrls?.get(entry.guid.toLowerCase());
|
|
2256
3031
|
entries[index] = {
|
|
2257
3032
|
...entry,
|
|
2258
3033
|
packageUrl,
|
|
2259
3034
|
...projection,
|
|
2260
3035
|
...sourcePath === void 0 ? {} : { sourcePath },
|
|
2261
3036
|
...publication === void 0 ? {} : { publication },
|
|
2262
|
-
...refs === void 0 ? {} : { refs }
|
|
3037
|
+
...refs === void 0 ? {} : { refs },
|
|
3038
|
+
...cookReceiptUrl === void 0 ? {} : { cookReceiptUrl },
|
|
3039
|
+
...revision === void 0 ? {} : { revision }
|
|
2263
3040
|
};
|
|
2264
3041
|
}
|
|
2265
3042
|
}
|
|
@@ -2273,11 +3050,30 @@ async function emitPackDocument(work, options) {
|
|
|
2273
3050
|
}
|
|
2274
3051
|
const referenceId = work.context.sink.emitFile({
|
|
2275
3052
|
type: "asset",
|
|
3053
|
+
fileName: options.packageName.startsWith("assets/") ? options.packageName : `assets/${options.packageName}`,
|
|
2276
3054
|
name: options.packageName,
|
|
2277
3055
|
originalFileName: options.originalFileName,
|
|
2278
3056
|
source: JSON.stringify(options.pack)
|
|
2279
3057
|
});
|
|
2280
3058
|
const packageUrl = work.context.sink.fileUrl(work.context.sink.getFileName(referenceId));
|
|
3059
|
+
const receiptUrls = /* @__PURE__ */ new Map();
|
|
3060
|
+
for (const receipt of options.receipts ?? []) {
|
|
3061
|
+
const guid = receipt.guid.toLowerCase();
|
|
3062
|
+
const receiptReferenceId = work.context.sink.emitFile({
|
|
3063
|
+
type: "asset",
|
|
3064
|
+
fileName: `assets/${guid}.receipt.json`,
|
|
3065
|
+
name: `${guid}.receipt.json`,
|
|
3066
|
+
originalFileName: options.originalFileName,
|
|
3067
|
+
source: JSON.stringify({
|
|
3068
|
+
...receipt,
|
|
3069
|
+
...receipt.outputDigest === void 0 && options.receiptOutputDigest === void 0 ? {} : { outputDigest: receipt.outputDigest ?? options.receiptOutputDigest }
|
|
3070
|
+
})
|
|
3071
|
+
});
|
|
3072
|
+
receiptUrls.set(
|
|
3073
|
+
guid,
|
|
3074
|
+
work.context.sink.fileUrl(work.context.sink.getFileName(receiptReferenceId))
|
|
3075
|
+
);
|
|
3076
|
+
}
|
|
2281
3077
|
updateImportedEntries(
|
|
2282
3078
|
work.importedEntries,
|
|
2283
3079
|
options.guids,
|
|
@@ -2285,11 +3081,13 @@ async function emitPackDocument(work, options) {
|
|
|
2285
3081
|
options.projection,
|
|
2286
3082
|
options.refsByGuid,
|
|
2287
3083
|
options.publication,
|
|
2288
|
-
options.sourcePath
|
|
3084
|
+
options.sourcePath,
|
|
3085
|
+
receiptUrls,
|
|
3086
|
+
options.revision
|
|
2289
3087
|
);
|
|
2290
3088
|
return packageUrl;
|
|
2291
3089
|
}
|
|
2292
|
-
async function emitAuthoredPack(work, guidSeen, entry) {
|
|
3090
|
+
async function emitAuthoredPack(work, guidSeen, entry, availableGuids) {
|
|
2293
3091
|
const sourceDeclaration = sourceDeclarationForCatalogPath(
|
|
2294
3092
|
entry.sourcePath,
|
|
2295
3093
|
work.context.inventory.sourceDeclarations,
|
|
@@ -2306,39 +3104,143 @@ async function emitAuthoredPack(work, guidSeen, entry) {
|
|
|
2306
3104
|
detail: { stage: "scan", sourcePath: packPath }
|
|
2307
3105
|
});
|
|
2308
3106
|
}
|
|
2309
|
-
|
|
2310
|
-
declaration.value
|
|
3107
|
+
if (declaration.value.schemaVersion === "3.0.0") {
|
|
3108
|
+
const parsed = parseParameterizedPackJson(declaration.value);
|
|
3109
|
+
if (!parsed.ok) dynamicFailure(work.context, parsed.error);
|
|
3110
|
+
if (parsed.value.format !== "direct") {
|
|
3111
|
+
dynamicFailure(work.context, {
|
|
3112
|
+
code: "catalog-declaration-missing",
|
|
3113
|
+
expected: "a direct v3 Pack declaration for an indexed authored output",
|
|
3114
|
+
hint: "instances are built from their parameterized parent and do not have direct Catalog rows",
|
|
3115
|
+
detail: { stage: "scan", sourcePath: packPath }
|
|
3116
|
+
});
|
|
3117
|
+
}
|
|
3118
|
+
const projected = projectDirectPackJson(parsed.value);
|
|
3119
|
+
if (!projected.ok) dynamicFailure(work.context, projected.error);
|
|
3120
|
+
const prepared2 = await prepareDirectPackTransport({
|
|
3121
|
+
projected: projected.value,
|
|
3122
|
+
sourcePath: packPath,
|
|
3123
|
+
sourceRevision: declaration.sourceRevision,
|
|
3124
|
+
availableGuids,
|
|
3125
|
+
cookers: work.context.cookers,
|
|
3126
|
+
policy: {
|
|
3127
|
+
base: work.context.basePrefix === "" ? "/" : work.context.basePrefix,
|
|
3128
|
+
packagePath: `assets/${projected.value.packageId}.pack.json`,
|
|
3129
|
+
artifactPath: (assetGuid, key) => `${assetGuid}/${key}.bin`,
|
|
3130
|
+
sink: () => {
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
});
|
|
3134
|
+
if (!prepared2.ok) dynamicFailure(work.context, prepared2.error);
|
|
3135
|
+
const { product, finalized, facts, revision } = prepared2.value;
|
|
3136
|
+
const publication = runtimePublicationFor(work.context, {
|
|
3137
|
+
pack: finalized.pack,
|
|
3138
|
+
sourcePath: entry.sourcePath,
|
|
3139
|
+
sourceRevision: product.inputFingerprint,
|
|
3140
|
+
packageUrl: finalized.packageUrl,
|
|
3141
|
+
digest: finalized.digest,
|
|
3142
|
+
inputFingerprint: product.inputFingerprint,
|
|
3143
|
+
outputs: facts.outputs,
|
|
3144
|
+
externalEvidence: product.externalEvidence
|
|
3145
|
+
});
|
|
3146
|
+
await emitPackDocument(work, {
|
|
3147
|
+
pack: publication.pack,
|
|
3148
|
+
artifacts: finalized.artifacts,
|
|
3149
|
+
packageName: `${prepared2.value.projected.packageId}.pack.json`,
|
|
3150
|
+
originalFileName: packPath,
|
|
3151
|
+
guids: product.product.assets.map((asset) => asset.guid),
|
|
3152
|
+
projection: work.context.authoredCookedCurrentProjection,
|
|
3153
|
+
refsByGuid: facts.refs,
|
|
3154
|
+
receipts: product.product.receipts,
|
|
3155
|
+
receiptOutputDigest: finalized.digest,
|
|
3156
|
+
publication: publication.publication,
|
|
3157
|
+
sourcePath: entry.sourcePath,
|
|
3158
|
+
revision
|
|
3159
|
+
});
|
|
3160
|
+
for (const asset of product.product.assets) guidSeen.add(asset.guid.toLowerCase());
|
|
3161
|
+
return;
|
|
3162
|
+
}
|
|
3163
|
+
const legacy = declaration.value;
|
|
3164
|
+
const prepared = await prepareLegacyPackTransport(
|
|
3165
|
+
legacy,
|
|
2311
3166
|
work.context.cookers,
|
|
2312
3167
|
(guid) => ({
|
|
2313
3168
|
base: work.context.basePrefix === "" ? "/" : work.context.basePrefix,
|
|
2314
3169
|
packagePath: `assets/${guid}.pack.json`,
|
|
2315
|
-
artifactPath: (
|
|
3170
|
+
artifactPath: (assetGuid, key) => `${assetGuid}/${key}.bin`
|
|
2316
3171
|
}),
|
|
2317
3172
|
packPath
|
|
2318
3173
|
);
|
|
2319
3174
|
const outputGuid = prepared.firstGuid ?? entry.guid;
|
|
2320
|
-
const authoredPack = prepared.finalized?.pack ??
|
|
2321
|
-
|
|
2322
|
-
|
|
3175
|
+
const authoredPack = prepared.finalized?.pack ?? {
|
|
3176
|
+
schemaVersion: "2.0.0",
|
|
3177
|
+
kind: "internal-text-package",
|
|
3178
|
+
assets: legacy.assets.map((asset) => ({
|
|
3179
|
+
guid: asset.guid,
|
|
3180
|
+
kind: asset.kind,
|
|
3181
|
+
...asset.name === void 0 ? {} : { name: asset.name },
|
|
3182
|
+
payload: asset.payload,
|
|
3183
|
+
refs: asset.refs,
|
|
3184
|
+
artifacts: asset.artifacts ?? {}
|
|
3185
|
+
}))
|
|
3186
|
+
};
|
|
3187
|
+
const runtimePublication = runtimePublicationFor(work.context, {
|
|
3188
|
+
pack: authoredPack,
|
|
2323
3189
|
sourcePath: entry.sourcePath,
|
|
2324
3190
|
sourceRevision: declaration.sourceRevision,
|
|
2325
3191
|
packageUrl: prepared.finalized?.packageUrl ?? `${work.context.basePrefix === "/" ? "" : work.context.basePrefix}/assets/${outputGuid}.pack.json`,
|
|
2326
3192
|
...prepared.finalized?.digest === void 0 ? {} : { digest: prepared.finalized.digest }
|
|
2327
3193
|
});
|
|
2328
3194
|
await emitPackDocument(work, {
|
|
2329
|
-
pack: runtimePublication
|
|
3195
|
+
pack: runtimePublication.pack,
|
|
2330
3196
|
artifacts: prepared.finalized?.artifacts ?? [],
|
|
2331
3197
|
packageName: `${outputGuid}.pack.json`,
|
|
2332
3198
|
originalFileName: packPath,
|
|
2333
|
-
guids:
|
|
3199
|
+
guids: authoredPack.assets.map((asset) => asset.guid),
|
|
2334
3200
|
projection: prepared.finalized === void 0 ? work.context.directCurrentProjection : work.context.authoredCookedCurrentProjection,
|
|
2335
|
-
|
|
2336
|
-
...
|
|
3201
|
+
refsByGuid: new Map(authoredPack.assets.map((asset) => [asset.guid.toLowerCase(), asset.refs])),
|
|
3202
|
+
...prepared.finalized?.receipts === void 0 ? {} : {
|
|
3203
|
+
receipts: prepared.finalized.receipts,
|
|
3204
|
+
receiptOutputDigest: prepared.finalized.digest
|
|
3205
|
+
},
|
|
3206
|
+
publication: runtimePublication.publication,
|
|
3207
|
+
sourcePath: entry.sourcePath
|
|
2337
3208
|
});
|
|
2338
|
-
for (const guid of
|
|
3209
|
+
for (const guid of authoredPack.assets.map((asset) => asset.guid)) {
|
|
2339
3210
|
guidSeen.add(guid.toLowerCase());
|
|
2340
3211
|
}
|
|
2341
3212
|
}
|
|
3213
|
+
async function emitParameterizedPack(work, guidSeen, bundle) {
|
|
3214
|
+
const { input, prepared } = bundle;
|
|
3215
|
+
const packageId = PackageId.format(input.subjectPackageId ?? input.definition.packageId);
|
|
3216
|
+
const runtimePublication = createRuntimePackPublication({
|
|
3217
|
+
pack: { assets: prepared.finalized.pack.assets },
|
|
3218
|
+
scopeId: work.context.runtimeBinding?.scopeId ?? "asset-runtime",
|
|
3219
|
+
sourcePath: input.displaySourcePath,
|
|
3220
|
+
sourceRevision: prepared.product.inputFingerprint,
|
|
3221
|
+
packageUrl: prepared.finalized.packageUrl,
|
|
3222
|
+
inputFingerprint: prepared.product.inputFingerprint,
|
|
3223
|
+
digest: prepared.finalized.digest,
|
|
3224
|
+
generation: work.context.runtimeBinding?.generation ?? prepared.publication.generation,
|
|
3225
|
+
outputs: prepared.facts.outputs,
|
|
3226
|
+
externalEvidence: prepared.product.externalEvidence
|
|
3227
|
+
});
|
|
3228
|
+
await emitPackDocument(work, {
|
|
3229
|
+
pack: runtimePublication.pack,
|
|
3230
|
+
artifacts: prepared.finalized.artifacts,
|
|
3231
|
+
packageName: `${packageId}.pack.json`,
|
|
3232
|
+
originalFileName: input.sourcePath,
|
|
3233
|
+
guids: prepared.product.product.assets.map((asset) => asset.guid),
|
|
3234
|
+
projection: work.context.authoredCookedCurrentProjection,
|
|
3235
|
+
refsByGuid: prepared.facts.refs,
|
|
3236
|
+
receipts: prepared.product.product.receipts,
|
|
3237
|
+
receiptOutputDigest: prepared.finalized.digest,
|
|
3238
|
+
publication: runtimePublication.publication,
|
|
3239
|
+
sourcePath: input.displaySourcePath,
|
|
3240
|
+
revision: prepared.revision
|
|
3241
|
+
});
|
|
3242
|
+
for (const asset of prepared.product.product.assets) guidSeen.add(asset.guid.toLowerCase());
|
|
3243
|
+
}
|
|
2342
3244
|
async function emitFinalizedOwner(work, metaPath, sourcePath, subAssets, sourcePackage, ownerFinalizer) {
|
|
2343
3245
|
const ownerGuid = subAssets[0]?.guid;
|
|
2344
3246
|
if (ownerGuid === void 0) return;
|
|
@@ -2407,11 +3309,12 @@ async function emitFinalizedOwner(work, metaPath, sourcePath, subAssets, sourceP
|
|
|
2407
3309
|
sourcePath
|
|
2408
3310
|
});
|
|
2409
3311
|
}
|
|
2410
|
-
async function emitBuildEntry(work, guidSeen, entry) {
|
|
3312
|
+
async function emitBuildEntry(work, guidSeen, entry, availableGuids) {
|
|
2411
3313
|
const guid = entry.guid.toLowerCase();
|
|
2412
3314
|
const metaPath = metaPathForGuid(work.context.inventory.declarations, guid);
|
|
2413
3315
|
if (metaPath === void 0) {
|
|
2414
|
-
if (entry.sourcePath.endsWith(".pack.json"))
|
|
3316
|
+
if (entry.sourcePath.endsWith(".pack.json"))
|
|
3317
|
+
await emitAuthoredPack(work, guidSeen, entry, availableGuids);
|
|
2415
3318
|
else guidSeen.add(guid);
|
|
2416
3319
|
return;
|
|
2417
3320
|
}
|
|
@@ -2464,7 +3367,8 @@ async function emitBuildEntry(work, guidSeen, entry) {
|
|
|
2464
3367
|
sourcePath: canonicalSourcePath,
|
|
2465
3368
|
sourceRevision: finalized.sourceRevision,
|
|
2466
3369
|
packageUrl: finalized.packageUrl,
|
|
2467
|
-
digest: finalized.digest
|
|
3370
|
+
digest: finalized.digest,
|
|
3371
|
+
sourceKeys: sourceKeysFor(subAssets)
|
|
2468
3372
|
});
|
|
2469
3373
|
await emitPackDocument(work, {
|
|
2470
3374
|
pack: runtimePublication.pack,
|
|
@@ -2481,24 +3385,21 @@ async function emitBuildEntry(work, guidSeen, entry) {
|
|
|
2481
3385
|
});
|
|
2482
3386
|
}
|
|
2483
3387
|
async function produceBuildAssets(context) {
|
|
2484
|
-
const
|
|
2485
|
-
const
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
packageUrl: scriptable.packageUrl,
|
|
2492
|
-
revision: scriptable.revision,
|
|
2493
|
-
refs: scriptable.refs.get(entry.guid.toLowerCase()) ?? [],
|
|
2494
|
-
...cookReceiptUrl === void 0 ? {} : { cookReceiptUrl },
|
|
2495
|
-
publication: scriptable.publication
|
|
2496
|
-
});
|
|
2497
|
-
});
|
|
3388
|
+
const dynamicBundles = await buildParameterizedPackages(context);
|
|
3389
|
+
const entries = [
|
|
3390
|
+
...context.inventory.entries,
|
|
3391
|
+
...dynamicBundles.flatMap((bundle) => bundle.entries)
|
|
3392
|
+
];
|
|
3393
|
+
const availableGuids = new Set(entries.map((entry) => entry.guid.toLowerCase()));
|
|
3394
|
+
const importedEntries = [...entries];
|
|
2498
3395
|
const guidSeen = /* @__PURE__ */ new Set();
|
|
2499
3396
|
const work = { importedEntries, context };
|
|
3397
|
+
for (const bundle of dynamicBundles) {
|
|
3398
|
+
await emitParameterizedPack(work, guidSeen, bundle);
|
|
3399
|
+
}
|
|
2500
3400
|
for (const entry of importedEntries) {
|
|
2501
|
-
if (!guidSeen.has(entry.guid.toLowerCase()))
|
|
3401
|
+
if (!guidSeen.has(entry.guid.toLowerCase()))
|
|
3402
|
+
await emitBuildEntry(work, guidSeen, entry, availableGuids);
|
|
2502
3403
|
}
|
|
2503
3404
|
return importedEntries;
|
|
2504
3405
|
}
|
|
@@ -2521,39 +3422,291 @@ function catalogImporterPolicy(importer, hostImporterKeys) {
|
|
|
2521
3422
|
hint: "remove the source declaration from Pack roots or publish its cooked runtime module"
|
|
2522
3423
|
};
|
|
2523
3424
|
}
|
|
2524
|
-
if (DEFAULT_CATALOG_IMPORTER_KEYS.includes(importer)) {
|
|
3425
|
+
if (DEFAULT_CATALOG_IMPORTER_KEYS.includes(importer)) {
|
|
3426
|
+
return {
|
|
3427
|
+
disposition: "publish",
|
|
3428
|
+
hostProvided: false,
|
|
3429
|
+
expected: "a standard Catalog provider declaration",
|
|
3430
|
+
hint: "let the provider producer materialize the declared output"
|
|
3431
|
+
};
|
|
3432
|
+
}
|
|
3433
|
+
if (hostImporterKeys.has(importer)) {
|
|
3434
|
+
return {
|
|
3435
|
+
disposition: "publish",
|
|
3436
|
+
hostProvided: true,
|
|
3437
|
+
expected: "a registered host Catalog provider declaration",
|
|
3438
|
+
hint: "let the registered provider materialize the declared output"
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
return {
|
|
3442
|
+
disposition: "missing",
|
|
3443
|
+
hostProvided: false,
|
|
3444
|
+
expected: "the sidecar importer must be present in the registered provider set",
|
|
3445
|
+
hint: "wire the provider through the host importer registry before building the Catalog"
|
|
3446
|
+
};
|
|
3447
|
+
}
|
|
3448
|
+
async function buildCatalogResult(roots, base = "/", registeredImporterKeys = /* @__PURE__ */ new Set(), scanOptions = {}, catalogVisibility = () => true, sourceIdentityFor) {
|
|
3449
|
+
const options = {
|
|
3450
|
+
base,
|
|
3451
|
+
scanOptions,
|
|
3452
|
+
importerPolicy: (importer) => catalogImporterPolicy(importer, registeredImporterKeys),
|
|
3453
|
+
visibility: catalogVisibility,
|
|
3454
|
+
...sourceIdentityFor === void 0 ? {} : { sourceIdentityFor }
|
|
3455
|
+
};
|
|
3456
|
+
return buildCatalogProjection(roots, options);
|
|
3457
|
+
}
|
|
3458
|
+
function numericTokens(source, startLine) {
|
|
3459
|
+
return source.split(/\r?\n/).slice(startLine).join(" ").replaceAll(",", " ").split(/\s+/).filter((token) => token.length > 0).map(Number);
|
|
3460
|
+
}
|
|
3461
|
+
function allFinite(values) {
|
|
3462
|
+
return values.every(Number.isFinite);
|
|
3463
|
+
}
|
|
3464
|
+
function parseLm63TypeC(source) {
|
|
3465
|
+
const lines = source.split(/\r?\n/);
|
|
3466
|
+
const tiltLine = lines.findIndex((line) => /^\s*TILT\s*=/i.test(line));
|
|
3467
|
+
if (tiltLine < 0) return err({ code: "invalid-ies", reason: "missing TILT declaration" });
|
|
3468
|
+
const tiltSource = lines[tiltLine];
|
|
3469
|
+
if (tiltSource === void 0)
|
|
3470
|
+
return err({ code: "invalid-ies", reason: "missing TILT declaration" });
|
|
3471
|
+
const tilt = tiltSource.slice(tiltSource.indexOf("=") + 1).trim().toUpperCase();
|
|
3472
|
+
if (tilt !== "NONE") return err({ code: "unsupported-tilt", reason: `TILT=${tilt}` });
|
|
3473
|
+
const values = numericTokens(source, tiltLine + 1);
|
|
3474
|
+
if (values.length < 12 || !allFinite(values.slice(0, 12))) {
|
|
3475
|
+
return err({ code: "invalid-ies", reason: "numeric header is incomplete or non-finite" });
|
|
3476
|
+
}
|
|
3477
|
+
const verticalCount = values[3];
|
|
3478
|
+
const horizontalCount = values[4];
|
|
3479
|
+
const photometricType = values[5];
|
|
3480
|
+
if (verticalCount === void 0 || horizontalCount === void 0 || photometricType === void 0) {
|
|
3481
|
+
return err({ code: "invalid-ies", reason: "numeric header is incomplete or non-finite" });
|
|
3482
|
+
}
|
|
3483
|
+
if (photometricType !== 1) {
|
|
3484
|
+
return err({
|
|
3485
|
+
code: "unsupported-photometric-type",
|
|
3486
|
+
reason: `photometric type ${photometricType} is not Type C`
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
if (!Number.isInteger(verticalCount) || !Number.isInteger(horizontalCount) || verticalCount < 2 || horizontalCount < 1) {
|
|
3490
|
+
return err({ code: "invalid-ies", reason: "angle counts are invalid" });
|
|
3491
|
+
}
|
|
3492
|
+
const angleOffset = 12;
|
|
3493
|
+
const verticalEnd = angleOffset + verticalCount;
|
|
3494
|
+
const horizontalEnd = verticalEnd + horizontalCount;
|
|
3495
|
+
const candelaEnd = horizontalEnd + verticalCount * horizontalCount;
|
|
3496
|
+
if (values.length < candelaEnd) {
|
|
3497
|
+
return err({ code: "invalid-ies", reason: "angle or candela table is incomplete" });
|
|
3498
|
+
}
|
|
3499
|
+
const verticalAnglesDeg = values.slice(angleOffset, verticalEnd);
|
|
3500
|
+
const horizontalAnglesDeg = values.slice(verticalEnd, horizontalEnd);
|
|
3501
|
+
const candelaMultiplier = values[2];
|
|
3502
|
+
if (candelaMultiplier === void 0) {
|
|
3503
|
+
return err({ code: "invalid-ies", reason: "numeric header is incomplete or non-finite" });
|
|
3504
|
+
}
|
|
3505
|
+
const candela = values.slice(horizontalEnd, candelaEnd).map((value) => value * candelaMultiplier);
|
|
3506
|
+
if (!allFinite(verticalAnglesDeg) || !allFinite(horizontalAnglesDeg) || !allFinite(candela) || verticalAnglesDeg[0] !== 0 || verticalAnglesDeg[verticalAnglesDeg.length - 1] !== 180 || horizontalAnglesDeg[0] !== 0) {
|
|
3507
|
+
return err({ code: "invalid-ies", reason: "Type C angles or candela values are invalid" });
|
|
3508
|
+
}
|
|
3509
|
+
return ok({ tilt: "NONE", verticalAnglesDeg, horizontalAnglesDeg, candela });
|
|
3510
|
+
}
|
|
3511
|
+
function horizontalTable(source) {
|
|
3512
|
+
const angles = [...source.horizontalAnglesDeg];
|
|
3513
|
+
const values = [...source.candela];
|
|
3514
|
+
const verticalCount = source.verticalAnglesDeg.length;
|
|
3515
|
+
const lastAngle = angles.at(-1);
|
|
3516
|
+
if (lastAngle === void 0) return { angles, values };
|
|
3517
|
+
if (lastAngle < 360) {
|
|
3518
|
+
for (let index = angles.length - 2; index > 0; index--) {
|
|
3519
|
+
const angle = angles[index];
|
|
3520
|
+
if (angle === void 0) continue;
|
|
3521
|
+
angles.push(360 - angle);
|
|
3522
|
+
const row = source.horizontalAnglesDeg.length - 1 - index;
|
|
3523
|
+
values.push(
|
|
3524
|
+
...source.candela.slice(row * verticalCount, row * verticalCount + verticalCount)
|
|
3525
|
+
);
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
return { angles, values };
|
|
3529
|
+
}
|
|
3530
|
+
function interpolate(values, angles, angle, verticalIndex, verticalCount) {
|
|
3531
|
+
const wrapped = (angle % 360 + 360) % 360;
|
|
3532
|
+
const last = angles.length - 1;
|
|
3533
|
+
const finalAngle = angles[last];
|
|
3534
|
+
const firstAngle = angles[0];
|
|
3535
|
+
if (finalAngle === void 0 || firstAngle === void 0) return Number.NaN;
|
|
3536
|
+
for (let index = 0; index < last; index++) {
|
|
3537
|
+
const left = angles[index];
|
|
3538
|
+
const right = angles[index + 1];
|
|
3539
|
+
if (left === void 0 || right === void 0) continue;
|
|
3540
|
+
if (wrapped < left || wrapped > right) continue;
|
|
3541
|
+
const span2 = right - left;
|
|
3542
|
+
const factor2 = span2 === 0 ? 0 : (wrapped - left) / span2;
|
|
3543
|
+
const a = values[index * verticalCount + verticalIndex] ?? 0;
|
|
3544
|
+
const b = values[(index + 1) * verticalCount + verticalIndex] ?? 0;
|
|
3545
|
+
return a + (b - a) * factor2;
|
|
3546
|
+
}
|
|
3547
|
+
const first = values[verticalIndex] ?? 0;
|
|
3548
|
+
const finalRow = (angles.length - 1) * verticalCount + verticalIndex;
|
|
3549
|
+
const final = values[finalRow] ?? 0;
|
|
3550
|
+
const span = 360 - finalAngle + firstAngle;
|
|
3551
|
+
const factor = span === 0 ? 0 : (wrapped - finalAngle + 360) / span;
|
|
3552
|
+
return final + (first - final) * factor;
|
|
3553
|
+
}
|
|
3554
|
+
function toFloat16(value) {
|
|
3555
|
+
if (value === 0) return 0;
|
|
3556
|
+
const sign = value < 0 ? 32768 : 0;
|
|
3557
|
+
const absolute = Math.abs(value);
|
|
3558
|
+
if (!Number.isFinite(absolute)) return sign | 31744;
|
|
3559
|
+
const exponent = Math.floor(Math.log2(absolute));
|
|
3560
|
+
if (exponent < -14) return sign | Math.round(absolute / 2 ** -24);
|
|
3561
|
+
if (exponent > 15) return sign | 31744;
|
|
3562
|
+
const mantissa = Math.round((absolute / 2 ** exponent - 1) * 1024);
|
|
3563
|
+
return sign | exponent + 15 << 10 | Math.min(mantissa, 1023);
|
|
3564
|
+
}
|
|
3565
|
+
function readFloat16LE(bytes2, offset) {
|
|
3566
|
+
const bits = (bytes2[offset] ?? 0) | (bytes2[offset + 1] ?? 0) << 8;
|
|
3567
|
+
const sign = (bits & 32768) === 0 ? 1 : -1;
|
|
3568
|
+
const exponent = bits >>> 10 & 31;
|
|
3569
|
+
const fraction = bits & 1023;
|
|
3570
|
+
if (exponent === 0) return sign * 2 ** -14 * (fraction / 2 ** 10);
|
|
3571
|
+
if (exponent === 31) return fraction === 0 ? sign * Infinity : Number.NaN;
|
|
3572
|
+
return sign * 2 ** (exponent - 15) * (1 + fraction / 2 ** 10);
|
|
3573
|
+
}
|
|
3574
|
+
function resampleTypeC(source) {
|
|
3575
|
+
const table = horizontalTable(source);
|
|
3576
|
+
const verticalCount = source.verticalAnglesDeg.length;
|
|
3577
|
+
const output = new Uint8Array(IES_PROFILE_WIDTH * IES_PROFILE_HEIGHT * 2);
|
|
3578
|
+
let peak = 0;
|
|
3579
|
+
const samples = [];
|
|
3580
|
+
for (let y = 0; y < IES_PROFILE_HEIGHT; y++) {
|
|
3581
|
+
const vertical = y / (IES_PROFILE_HEIGHT - 1) * 180;
|
|
3582
|
+
let lower = 0;
|
|
3583
|
+
while (lower + 1 < verticalCount && (source.verticalAnglesDeg[lower + 1] ?? 0) < vertical)
|
|
3584
|
+
lower++;
|
|
3585
|
+
const upper = Math.min(lower + 1, verticalCount - 1);
|
|
3586
|
+
const left = source.verticalAnglesDeg[lower] ?? 0;
|
|
3587
|
+
const right = source.verticalAnglesDeg[upper] ?? 0;
|
|
3588
|
+
const factor = right === left ? 0 : (vertical - left) / (right - left);
|
|
3589
|
+
for (let x = 0; x < IES_PROFILE_WIDTH; x++) {
|
|
3590
|
+
const horizontal = x / IES_PROFILE_WIDTH * 360;
|
|
3591
|
+
const low = interpolate(table.values, table.angles, horizontal, lower, verticalCount);
|
|
3592
|
+
const high = interpolate(table.values, table.angles, horizontal, upper, verticalCount);
|
|
3593
|
+
const value = low + (high - low) * factor;
|
|
3594
|
+
samples.push(Math.max(0, value));
|
|
3595
|
+
peak = Math.max(peak, value);
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
const divisor = peak > 0 ? peak : 1;
|
|
3599
|
+
for (let index = 0; index < samples.length; index++) {
|
|
3600
|
+
const bits = toFloat16((samples[index] ?? 0) / divisor);
|
|
3601
|
+
output[index * 2] = bits & 255;
|
|
3602
|
+
output[index * 2 + 1] = bits >>> 8;
|
|
3603
|
+
}
|
|
3604
|
+
return output;
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
// src/ies/ies-importer.ts
|
|
3608
|
+
function isRecord(value) {
|
|
3609
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3610
|
+
}
|
|
3611
|
+
function decodeFloat16(bytes2, offset) {
|
|
3612
|
+
const bits = (bytes2[offset] ?? 0) | (bytes2[offset + 1] ?? 0) << 8;
|
|
3613
|
+
const sign = (bits & 32768) === 0 ? 1 : -1;
|
|
3614
|
+
const exponent = bits >>> 10 & 31;
|
|
3615
|
+
const fraction = bits & 1023;
|
|
3616
|
+
if (exponent === 0) return sign * 2 ** -14 * (fraction / 2 ** 10);
|
|
3617
|
+
if (exponent === 31) return fraction === 0 ? sign * Infinity : Number.NaN;
|
|
3618
|
+
return sign * 2 ** (exponent - 15) * (1 + fraction / 2 ** 10);
|
|
3619
|
+
}
|
|
3620
|
+
function validateIesProfilePayload(value) {
|
|
3621
|
+
if (!isRecord(value) || value.kind !== "ies-profile") {
|
|
3622
|
+
return err(new Error("IES payload kind must be ies-profile"));
|
|
3623
|
+
}
|
|
3624
|
+
const data = value.data;
|
|
3625
|
+
if (!(data instanceof Uint8Array) || data.byteLength !== IES_PROFILE_BYTE_LENGTH) {
|
|
3626
|
+
return err(
|
|
3627
|
+
new Error(
|
|
3628
|
+
`IES payload must contain ${IES_PROFILE_WIDTH}x${IES_PROFILE_HEIGHT} little-endian f16 samples`
|
|
3629
|
+
)
|
|
3630
|
+
);
|
|
3631
|
+
}
|
|
3632
|
+
for (let offset = 0; offset < data.byteLength; offset += 2) {
|
|
3633
|
+
if (!Number.isFinite(decodeFloat16(data, offset))) {
|
|
3634
|
+
return err(new Error("IES payload contains a non-finite f16 sample"));
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
return ok(value);
|
|
3638
|
+
}
|
|
3639
|
+
function sourceValidationError(ctx, reason) {
|
|
3640
|
+
return new ImportError({
|
|
3641
|
+
code: "source-validation-failed",
|
|
3642
|
+
expected: "LM-63 Type C source with TILT=NONE",
|
|
3643
|
+
hint: IMPORT_ERROR_HINTS["source-validation-failed"],
|
|
3644
|
+
detail: {
|
|
3645
|
+
diagnostics: [
|
|
3646
|
+
{
|
|
3647
|
+
code: "ies-source-invalid",
|
|
3648
|
+
severity: "error",
|
|
3649
|
+
sourcePath: ctx.source,
|
|
3650
|
+
sourceRange: { start: 0, end: 0, line: 1, column: 1 },
|
|
3651
|
+
rule: "LM-63 Type C TILT=NONE",
|
|
3652
|
+
expected: "LM-63 Type C source with TILT=NONE",
|
|
3653
|
+
actual: reason,
|
|
3654
|
+
hint: "repair the IES source and rerun the build-time importer"
|
|
3655
|
+
}
|
|
3656
|
+
]
|
|
3657
|
+
}
|
|
3658
|
+
});
|
|
3659
|
+
}
|
|
3660
|
+
async function importIes(ctx) {
|
|
3661
|
+
const source = await ctx.readSource();
|
|
3662
|
+
if (!source.ok) {
|
|
2525
3663
|
return {
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
3664
|
+
ok: false,
|
|
3665
|
+
error: new ImportError({
|
|
3666
|
+
code: "source-read-failed",
|
|
3667
|
+
expected: `readable IES source at ${ctx.source}`,
|
|
3668
|
+
hint: IMPORT_ERROR_HINTS["source-read-failed"],
|
|
3669
|
+
detail: { source: ctx.source, reason: String(source.error) }
|
|
3670
|
+
})
|
|
2530
3671
|
};
|
|
2531
3672
|
}
|
|
2532
|
-
|
|
3673
|
+
const parsed = parseLm63TypeC(new TextDecoder().decode(source.value));
|
|
3674
|
+
if (!parsed.ok) return { ok: false, error: sourceValidationError(ctx, parsed.error.reason) };
|
|
3675
|
+
const declaration = ctx.subAssets.find((asset) => asset.kind === "ies-profile");
|
|
3676
|
+
if (declaration === void 0) {
|
|
2533
3677
|
return {
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
expected: "a registered host Catalog provider declaration",
|
|
2537
|
-
hint: "let the registered provider materialize the declared output"
|
|
3678
|
+
ok: false,
|
|
3679
|
+
error: sourceValidationError(ctx, "missing ies-profile sub-asset declaration")
|
|
2538
3680
|
};
|
|
2539
3681
|
}
|
|
3682
|
+
const data = resampleTypeC(parsed.value);
|
|
3683
|
+
const payload = { kind: "ies-profile", data };
|
|
2540
3684
|
return {
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
3685
|
+
ok: true,
|
|
3686
|
+
value: {
|
|
3687
|
+
assets: [
|
|
3688
|
+
{
|
|
3689
|
+
guid: declaration.guid,
|
|
3690
|
+
kind: "ies-profile",
|
|
3691
|
+
payload,
|
|
3692
|
+
refs: [],
|
|
3693
|
+
artifacts: {
|
|
3694
|
+
body: {
|
|
3695
|
+
mediaType: "application/octet-stream",
|
|
3696
|
+
assetCodec: { name: "forgeax-ies-profile", version: "1" },
|
|
3697
|
+
bytes: data
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
],
|
|
3702
|
+
sourceDependencies: [ctx.source]
|
|
3703
|
+
}
|
|
2554
3704
|
};
|
|
2555
|
-
return buildCatalogProjection(roots, options);
|
|
2556
3705
|
}
|
|
3706
|
+
var iesImporter = {
|
|
3707
|
+
key: "ies",
|
|
3708
|
+
import: importIes
|
|
3709
|
+
};
|
|
2557
3710
|
|
|
2558
3711
|
// src/importer-registry.ts
|
|
2559
3712
|
var ImporterRegistry = class {
|
|
@@ -2612,6 +3765,339 @@ var ImporterRegistry = class {
|
|
|
2612
3765
|
}) ?? true;
|
|
2613
3766
|
}
|
|
2614
3767
|
};
|
|
3768
|
+
function isRecord2(value) {
|
|
3769
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3770
|
+
}
|
|
3771
|
+
function stable2(value) {
|
|
3772
|
+
if (value instanceof ArrayBuffer) {
|
|
3773
|
+
return `ArrayBuffer:${JSON.stringify(Array.from(new Uint8Array(value)))}`;
|
|
3774
|
+
}
|
|
3775
|
+
if (ArrayBuffer.isView(value)) {
|
|
3776
|
+
return `${value.constructor.name}:${JSON.stringify(
|
|
3777
|
+
Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength))
|
|
3778
|
+
)}`;
|
|
3779
|
+
}
|
|
3780
|
+
if (Array.isArray(value)) return `[${value.map(stable2).join(",")}]`;
|
|
3781
|
+
if (value !== null && typeof value === "object") {
|
|
3782
|
+
const record2 = value;
|
|
3783
|
+
return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stable2(record2[key])}`).join(",")}}`;
|
|
3784
|
+
}
|
|
3785
|
+
return JSON.stringify(value) ?? "null";
|
|
3786
|
+
}
|
|
3787
|
+
function digest2(value) {
|
|
3788
|
+
return `sha256:${createHash("sha256").update(stable2(value)).digest("hex")}`;
|
|
3789
|
+
}
|
|
3790
|
+
function generationFromDigest(value) {
|
|
3791
|
+
const parsed = Number.parseInt(value.slice("sha256:".length, "sha256:".length + 8), 16);
|
|
3792
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : 1;
|
|
3793
|
+
}
|
|
3794
|
+
function reasonOf(error) {
|
|
3795
|
+
return error instanceof Error ? error.message : String(error);
|
|
3796
|
+
}
|
|
3797
|
+
async function collectPackFiles(assetRoots) {
|
|
3798
|
+
const files = /* @__PURE__ */ new Set();
|
|
3799
|
+
async function visit(directory) {
|
|
3800
|
+
let entries;
|
|
3801
|
+
try {
|
|
3802
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
3803
|
+
} catch (error) {
|
|
3804
|
+
return err({
|
|
3805
|
+
code: "scriptable-pack-file-root-unreadable",
|
|
3806
|
+
expected: "every configured ScriptablePack asset root to be readable",
|
|
3807
|
+
hint: "restore the asset root or configure a readable directory, then retry the ScriptablePack build",
|
|
3808
|
+
detail: { root: directory, reason: reasonOf(error) }
|
|
3809
|
+
});
|
|
3810
|
+
}
|
|
3811
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
3812
|
+
const child = resolve(directory, entry.name);
|
|
3813
|
+
if (entry.isDirectory()) {
|
|
3814
|
+
const nested = await visit(child);
|
|
3815
|
+
if (!nested.ok) return nested;
|
|
3816
|
+
} else if (entry.isFile() && entry.name.endsWith(".pack.json")) {
|
|
3817
|
+
files.add(child);
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
return ok(void 0);
|
|
3821
|
+
}
|
|
3822
|
+
for (const rawRoot of [...new Set(assetRoots.map((root) => resolve(root)))].sort(
|
|
3823
|
+
(a, b) => a.localeCompare(b)
|
|
3824
|
+
)) {
|
|
3825
|
+
let info;
|
|
3826
|
+
try {
|
|
3827
|
+
info = await stat(rawRoot);
|
|
3828
|
+
} catch (error) {
|
|
3829
|
+
return err({
|
|
3830
|
+
code: "scriptable-pack-file-root-unreadable",
|
|
3831
|
+
expected: "every configured ScriptablePack asset root to exist and be readable",
|
|
3832
|
+
hint: "restore the asset root or configure a readable directory, then retry the ScriptablePack build",
|
|
3833
|
+
detail: { root: rawRoot, reason: reasonOf(error) }
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
if (info.isFile()) {
|
|
3837
|
+
if (rawRoot.endsWith(".pack.json")) files.add(rawRoot);
|
|
3838
|
+
continue;
|
|
3839
|
+
}
|
|
3840
|
+
if (!info.isDirectory()) {
|
|
3841
|
+
return err({
|
|
3842
|
+
code: "scriptable-pack-file-root-unreadable",
|
|
3843
|
+
expected: "every configured ScriptablePack asset root to be a file or directory",
|
|
3844
|
+
hint: "configure an asset root containing ordinary .pack.json files, then retry the ScriptablePack build",
|
|
3845
|
+
detail: { root: rawRoot, reason: "root is neither a file nor a directory" }
|
|
3846
|
+
});
|
|
3847
|
+
}
|
|
3848
|
+
const visited = await visit(rawRoot);
|
|
3849
|
+
if (!visited.ok) return visited;
|
|
3850
|
+
}
|
|
3851
|
+
return ok([...files].sort((left, right) => left.localeCompare(right)));
|
|
3852
|
+
}
|
|
3853
|
+
function invalidPack(path, errors) {
|
|
3854
|
+
return {
|
|
3855
|
+
code: "scriptable-pack-file-invalid",
|
|
3856
|
+
expected: "a schema-valid ordinary .pack.json package",
|
|
3857
|
+
hint: "repair the Pack JSON against the package schema, then retry the ScriptablePack build",
|
|
3858
|
+
detail: { path, ajvErrors: errors }
|
|
3859
|
+
};
|
|
3860
|
+
}
|
|
3861
|
+
function assetFromRow(path, row) {
|
|
3862
|
+
if (!isScriptablePackAssetKind$1(row.kind)) {
|
|
3863
|
+
return err({
|
|
3864
|
+
code: "scriptable-pack-file-asset-invalid",
|
|
3865
|
+
expected: "an ordinary ScriptablePack Asset kind from SCRIPTABLE_PACK_ASSET_KINDS",
|
|
3866
|
+
hint: "use an ordinary engine Asset kind or keep the custom payload outside this Asset snapshot source",
|
|
3867
|
+
detail: { path, guid: row.guid, reason: `unsupported kind ${JSON.stringify(row.kind)}` }
|
|
3868
|
+
});
|
|
3869
|
+
}
|
|
3870
|
+
if (!isRecord2(row.payload)) {
|
|
3871
|
+
return err({
|
|
3872
|
+
code: "scriptable-pack-file-asset-invalid",
|
|
3873
|
+
expected: "the Pack row payload to be a JSON object",
|
|
3874
|
+
hint: "write a complete ordinary Asset payload in the .pack.json row, then retry the ScriptablePack build",
|
|
3875
|
+
detail: { path, guid: row.guid, reason: "payload is not an object" }
|
|
3876
|
+
});
|
|
3877
|
+
}
|
|
3878
|
+
const payloadKind = row.payload.kind;
|
|
3879
|
+
if (payloadKind !== void 0 && payloadKind !== row.kind) {
|
|
3880
|
+
return err({
|
|
3881
|
+
code: "scriptable-pack-file-asset-invalid",
|
|
3882
|
+
expected: "the row kind and payload.kind to agree",
|
|
3883
|
+
hint: "repair the duplicated kind discriminant in the .pack.json row, then retry the ScriptablePack build",
|
|
3884
|
+
detail: {
|
|
3885
|
+
path,
|
|
3886
|
+
guid: row.guid,
|
|
3887
|
+
reason: `row kind ${JSON.stringify(row.kind)} does not match payload.kind ${JSON.stringify(payloadKind)}`
|
|
3888
|
+
}
|
|
3889
|
+
});
|
|
3890
|
+
}
|
|
3891
|
+
const asset = { ...row.payload, kind: row.kind };
|
|
3892
|
+
return ok({ asset, digest: digest2(asset) });
|
|
3893
|
+
}
|
|
3894
|
+
async function indexPackFiles(assetRoots) {
|
|
3895
|
+
const files = await collectPackFiles(assetRoots);
|
|
3896
|
+
if (!files.ok) return files;
|
|
3897
|
+
const byGuid = /* @__PURE__ */ new Map();
|
|
3898
|
+
const pathsByGuid = /* @__PURE__ */ new Map();
|
|
3899
|
+
const documents = [];
|
|
3900
|
+
for (const path of files.value) {
|
|
3901
|
+
let body;
|
|
3902
|
+
try {
|
|
3903
|
+
body = await readFile(path, "utf8");
|
|
3904
|
+
} catch (error) {
|
|
3905
|
+
return err({
|
|
3906
|
+
code: "scriptable-pack-file-unreadable",
|
|
3907
|
+
expected: "a readable .pack.json file",
|
|
3908
|
+
hint: "restore the .pack.json file, then retry the ScriptablePack build",
|
|
3909
|
+
detail: { path, reason: reasonOf(error) }
|
|
3910
|
+
});
|
|
3911
|
+
}
|
|
3912
|
+
let parsed;
|
|
3913
|
+
try {
|
|
3914
|
+
parsed = JSON.parse(body);
|
|
3915
|
+
} catch (error) {
|
|
3916
|
+
return err({
|
|
3917
|
+
code: "scriptable-pack-file-json-invalid",
|
|
3918
|
+
expected: "a parseable JSON .pack.json file",
|
|
3919
|
+
hint: "repair or restore the .pack.json file, then retry the ScriptablePack build",
|
|
3920
|
+
detail: { path, reason: reasonOf(error) }
|
|
3921
|
+
});
|
|
3922
|
+
}
|
|
3923
|
+
if (!validatePack(parsed)) {
|
|
3924
|
+
return err(
|
|
3925
|
+
invalidPack(
|
|
3926
|
+
path,
|
|
3927
|
+
(validatePack.errors ?? []).map((error) => ({
|
|
3928
|
+
instancePath: error.instancePath,
|
|
3929
|
+
message: error.message ?? "unknown schema validation error"
|
|
3930
|
+
}))
|
|
3931
|
+
)
|
|
3932
|
+
);
|
|
3933
|
+
}
|
|
3934
|
+
const document = parsed;
|
|
3935
|
+
documents.push(document);
|
|
3936
|
+
for (const row of document.assets) {
|
|
3937
|
+
const existingPath = pathsByGuid.get(row.guid.toLowerCase());
|
|
3938
|
+
if (existingPath !== void 0) {
|
|
3939
|
+
return err({
|
|
3940
|
+
code: "scriptable-pack-file-guid-collision",
|
|
3941
|
+
expected: "each ordinary Pack asset GUID to be declared by exactly one .pack.json file",
|
|
3942
|
+
hint: "remove the duplicate GUID declaration or keep one authoritative .pack.json package, then retry",
|
|
3943
|
+
detail: { guid: row.guid.toLowerCase(), paths: [existingPath, path] }
|
|
3944
|
+
});
|
|
3945
|
+
}
|
|
3946
|
+
const asset = assetFromRow(path, row);
|
|
3947
|
+
if (!asset.ok) return asset;
|
|
3948
|
+
const key = row.guid.toLowerCase();
|
|
3949
|
+
pathsByGuid.set(key, path);
|
|
3950
|
+
byGuid.set(key, asset.value);
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
const sourceDigest = digest2(
|
|
3954
|
+
[...documents].sort((left, right) => stable2(left).localeCompare(stable2(right)))
|
|
3955
|
+
);
|
|
3956
|
+
return ok({
|
|
3957
|
+
generation: generationFromDigest(sourceDigest),
|
|
3958
|
+
assets: byGuid
|
|
3959
|
+
});
|
|
3960
|
+
}
|
|
3961
|
+
function createScriptablePackFileAssetSnapshotSource(options) {
|
|
3962
|
+
const index = indexPackFiles(options.assetRoots);
|
|
3963
|
+
return {
|
|
3964
|
+
async readByGuid(guid) {
|
|
3965
|
+
const key = AssetGuid$1.format(guid).toLowerCase();
|
|
3966
|
+
const indexed = await index;
|
|
3967
|
+
if (!indexed.ok) return indexed;
|
|
3968
|
+
const asset = indexed.value.assets.get(key);
|
|
3969
|
+
if (asset === void 0) {
|
|
3970
|
+
return err({
|
|
3971
|
+
code: "asset-not-found",
|
|
3972
|
+
expected: `an ordinary .pack.json asset with GUID ${key} under the configured asset roots`,
|
|
3973
|
+
hint: "add the dependency to an asset root or declare it as a ScriptablePack external reference, then retry",
|
|
3974
|
+
detail: { guid: key }
|
|
3975
|
+
});
|
|
3976
|
+
}
|
|
3977
|
+
return ok({
|
|
3978
|
+
asset: structuredClone(asset.asset),
|
|
3979
|
+
generation: indexed.value.generation,
|
|
3980
|
+
digest: asset.digest
|
|
3981
|
+
});
|
|
3982
|
+
}
|
|
3983
|
+
};
|
|
3984
|
+
}
|
|
3985
|
+
function stable3(value) {
|
|
3986
|
+
if (ArrayBuffer.isView(value)) {
|
|
3987
|
+
return `${value.constructor.name}:${JSON.stringify(Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)))}`;
|
|
3988
|
+
}
|
|
3989
|
+
if (Array.isArray(value)) return `[${value.map(stable3).join(",")}]`;
|
|
3990
|
+
if (value !== null && typeof value === "object") {
|
|
3991
|
+
const record2 = value;
|
|
3992
|
+
return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stable3(record2[key])}`).join(",")}}`;
|
|
3993
|
+
}
|
|
3994
|
+
return JSON.stringify(value) ?? "null";
|
|
3995
|
+
}
|
|
3996
|
+
async function assetDigest(asset) {
|
|
3997
|
+
const bytes2 = new TextEncoder().encode(stable3(asset));
|
|
3998
|
+
const digest3 = await globalThis.crypto.subtle.digest("SHA-256", bytes2);
|
|
3999
|
+
return `sha256:${Array.from(new Uint8Array(digest3), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
4000
|
+
}
|
|
4001
|
+
function cycleError(stack, owner, guid) {
|
|
4002
|
+
const cycleStart = stack.indexOf(owner);
|
|
4003
|
+
const cycle = [...stack.slice(cycleStart), owner];
|
|
4004
|
+
return {
|
|
4005
|
+
code: "pack-content-dependency-stalled",
|
|
4006
|
+
expected: "the content dependency worklist to make progress",
|
|
4007
|
+
hint: "inspect the waiting GUID and pending subjects, then break the content-read cycle",
|
|
4008
|
+
detail: { waitingGuids: [guid], pendingSubjects: cycle, iterations: 1 }
|
|
4009
|
+
};
|
|
4010
|
+
}
|
|
4011
|
+
function missingOutputError(owner, guid) {
|
|
4012
|
+
return {
|
|
4013
|
+
code: "pack-source-output-invalid",
|
|
4014
|
+
expected: `owner ${owner} to stage every declared output including ${guid}`,
|
|
4015
|
+
hint: "return one output for every GUID declared by the staged owner",
|
|
4016
|
+
detail: { missingGuids: [guid], unexpectedSourceKeys: [], kindMismatches: [] }
|
|
4017
|
+
};
|
|
4018
|
+
}
|
|
4019
|
+
function createScriptablePackStagedAssetSnapshotSource(options) {
|
|
4020
|
+
const owners = options.declaredExternalOutputs === void 0 || options.declaredExternalOutputs.length === 0 ? options.owners : [
|
|
4021
|
+
{
|
|
4022
|
+
id: "<declared-pack-external>",
|
|
4023
|
+
guids: options.declaredExternalOutputs.map((output) => output.guid),
|
|
4024
|
+
async build() {
|
|
4025
|
+
return ok(options.declaredExternalOutputs ?? []);
|
|
4026
|
+
}
|
|
4027
|
+
},
|
|
4028
|
+
...options.owners
|
|
4029
|
+
];
|
|
4030
|
+
const ownerByGuid = /* @__PURE__ */ new Map();
|
|
4031
|
+
for (const owner of owners) {
|
|
4032
|
+
if (owner.id.trim().length === 0) throw new TypeError("staged owner id must be non-empty");
|
|
4033
|
+
for (const guid of owner.guids) {
|
|
4034
|
+
const key = AssetGuid$1.format(guid).toLowerCase();
|
|
4035
|
+
const existing = ownerByGuid.get(key);
|
|
4036
|
+
if (existing !== void 0) {
|
|
4037
|
+
throw new TypeError(`staged GUID ${key} is owned by both ${existing.id} and ${owner.id}`);
|
|
4038
|
+
}
|
|
4039
|
+
ownerByGuid.set(key, owner);
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
4043
|
+
const builds = /* @__PURE__ */ new Map();
|
|
4044
|
+
const sourceFor = (stack) => ({
|
|
4045
|
+
async readByGuid(guid) {
|
|
4046
|
+
const key = AssetGuid$1.format(guid).toLowerCase();
|
|
4047
|
+
const cached = snapshots.get(key);
|
|
4048
|
+
if (cached !== void 0) return ok(structuredClone(cached));
|
|
4049
|
+
const owner = ownerByGuid.get(key);
|
|
4050
|
+
if (owner === void 0) {
|
|
4051
|
+
return err(
|
|
4052
|
+
new AssetError({
|
|
4053
|
+
code: "asset-not-imported",
|
|
4054
|
+
expected: "a staged local owner for the requested GUID",
|
|
4055
|
+
hint: "declare the local ScriptablePack output before rebuilding the generation"
|
|
4056
|
+
})
|
|
4057
|
+
);
|
|
4058
|
+
}
|
|
4059
|
+
if (stack.includes(owner.id)) return err(cycleError(stack, owner.id, key));
|
|
4060
|
+
let building = builds.get(owner.id);
|
|
4061
|
+
if (building === void 0) {
|
|
4062
|
+
building = (async () => {
|
|
4063
|
+
const built2 = await owner.build(sourceFor([...stack, owner.id]));
|
|
4064
|
+
if (!built2.ok) {
|
|
4065
|
+
builds.delete(owner.id);
|
|
4066
|
+
return built2;
|
|
4067
|
+
}
|
|
4068
|
+
const next = /* @__PURE__ */ new Map();
|
|
4069
|
+
for (const output of built2.value) {
|
|
4070
|
+
const outputGuid = AssetGuid$1.format(output.guid).toLowerCase();
|
|
4071
|
+
if (ownerByGuid.get(outputGuid) !== owner) {
|
|
4072
|
+
builds.delete(owner.id);
|
|
4073
|
+
return err(missingOutputError(owner.id, outputGuid));
|
|
4074
|
+
}
|
|
4075
|
+
next.set(outputGuid, {
|
|
4076
|
+
asset: structuredClone(output.asset),
|
|
4077
|
+
generation: options.generation,
|
|
4078
|
+
digest: output.digest ?? await assetDigest(output.asset)
|
|
4079
|
+
});
|
|
4080
|
+
}
|
|
4081
|
+
for (const declared of owner.guids) {
|
|
4082
|
+
const declaredGuid = AssetGuid$1.format(declared).toLowerCase();
|
|
4083
|
+
if (!next.has(declaredGuid)) {
|
|
4084
|
+
builds.delete(owner.id);
|
|
4085
|
+
return err(missingOutputError(owner.id, declaredGuid));
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
for (const [outputGuid, snapshot] of next) snapshots.set(outputGuid, snapshot);
|
|
4089
|
+
return ok(void 0);
|
|
4090
|
+
})();
|
|
4091
|
+
builds.set(owner.id, building);
|
|
4092
|
+
}
|
|
4093
|
+
const built = await building;
|
|
4094
|
+
if (!built.ok) return built;
|
|
4095
|
+
const staged = snapshots.get(key);
|
|
4096
|
+
return staged === void 0 ? err(missingOutputError(owner.id, key)) : ok(structuredClone(staged));
|
|
4097
|
+
}
|
|
4098
|
+
});
|
|
4099
|
+
return sourceFor([]);
|
|
4100
|
+
}
|
|
2615
4101
|
|
|
2616
4102
|
// src/source-package-errors.ts
|
|
2617
4103
|
var SOURCE_PACKAGE_ERROR_POLICY = {
|
|
@@ -2672,6 +4158,9 @@ function importFailureCode(error) {
|
|
|
2672
4158
|
case "source-read-failed":
|
|
2673
4159
|
case "import-internal-error":
|
|
2674
4160
|
case "mesh-material-slot-topology-change":
|
|
4161
|
+
case "mesh-lod-contract-invalid":
|
|
4162
|
+
case "mesh-lod-topology-change":
|
|
4163
|
+
case "mesh-lod-authority-conflict":
|
|
2675
4164
|
case "unknown-source-key":
|
|
2676
4165
|
case "duplicate-source-key":
|
|
2677
4166
|
case "invalid-source-overrides":
|
|
@@ -2685,6 +4174,9 @@ var IMPORT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
2685
4174
|
"import-produced-no-assets",
|
|
2686
4175
|
"guid-mismatch",
|
|
2687
4176
|
"mesh-material-slot-topology-change",
|
|
4177
|
+
"mesh-lod-contract-invalid",
|
|
4178
|
+
"mesh-lod-topology-change",
|
|
4179
|
+
"mesh-lod-authority-conflict",
|
|
2688
4180
|
"import-internal-error",
|
|
2689
4181
|
"source-validation-failed",
|
|
2690
4182
|
"unknown-source-key",
|
|
@@ -2802,7 +4294,7 @@ async function commitSourcePackageDdc(candidate) {
|
|
|
2802
4294
|
async function persistTransport(transport) {
|
|
2803
4295
|
if (transport === void 0) return true;
|
|
2804
4296
|
const directory = dirname(transport.path);
|
|
2805
|
-
const temporary = `${transport.path}.tmp`;
|
|
4297
|
+
const temporary = `${transport.path}.${randomUUID()}.tmp`;
|
|
2806
4298
|
try {
|
|
2807
4299
|
await mkdir(directory, { recursive: true });
|
|
2808
4300
|
if (((await stat(directory)).mode & 146) === 0) {
|
|
@@ -2827,8 +4319,8 @@ function importPublicationFailure(error) {
|
|
|
2827
4319
|
};
|
|
2828
4320
|
}
|
|
2829
4321
|
function projectImportPublication(input, head, observedAt) {
|
|
2830
|
-
const
|
|
2831
|
-
const revision = { digest, observedAt, rootId: input.root };
|
|
4322
|
+
const digest3 = head.currentKey ?? input.desiredKey;
|
|
4323
|
+
const revision = { digest: digest3, observedAt, rootId: input.root };
|
|
2832
4324
|
const published = new Set(input.publishedGuids.map((guid) => guid.toLowerCase()));
|
|
2833
4325
|
const catalog = input.nextCatalog.map((row) => {
|
|
2834
4326
|
if (!published.has(row.guid.toLowerCase())) return row;
|
|
@@ -2922,6 +4414,35 @@ async function commitImportPublication(candidate) {
|
|
|
2922
4414
|
)
|
|
2923
4415
|
};
|
|
2924
4416
|
}
|
|
4417
|
+
const transportPersisted = await persistTransport(candidate.input.transport);
|
|
4418
|
+
if (!transportPersisted) {
|
|
4419
|
+
await restoreImportPublication(candidate);
|
|
4420
|
+
return {
|
|
4421
|
+
ok: false,
|
|
4422
|
+
error: {
|
|
4423
|
+
code: "source-package-publication-invalid",
|
|
4424
|
+
expected: "the sidecar and DDC publication to commit atomically",
|
|
4425
|
+
hint: "repair the sidecar destination, then rebuild or cold-cook the source package",
|
|
4426
|
+
detail: "sidecar transport persistence failed; restored the previous DDC/LKG generation",
|
|
4427
|
+
diagnostic: sourcePackageError(
|
|
4428
|
+
"source-package-publication-invalid",
|
|
4429
|
+
{
|
|
4430
|
+
sourceMeta: "<import-publication>",
|
|
4431
|
+
anchorGuid: candidate.input.guid,
|
|
4432
|
+
affectedGuids: candidate.input.publishedGuids,
|
|
4433
|
+
producer: "source-package/import-publication",
|
|
4434
|
+
importer: "import-publication"
|
|
4435
|
+
},
|
|
4436
|
+
{ stage: "route-integrity", reason: "sidecar transport persistence failed" }
|
|
4437
|
+
)
|
|
4438
|
+
},
|
|
4439
|
+
head: await inspectHead(
|
|
4440
|
+
candidate.input.root,
|
|
4441
|
+
candidate.input.guid,
|
|
4442
|
+
candidate.input.desiredKey
|
|
4443
|
+
)
|
|
4444
|
+
};
|
|
4445
|
+
}
|
|
2925
4446
|
const projected = projectImportPublication(candidate.input, publication.value, Date.now());
|
|
2926
4447
|
return {
|
|
2927
4448
|
ok: true,
|
|
@@ -2929,7 +4450,7 @@ async function commitImportPublication(candidate) {
|
|
|
2929
4450
|
head: publication.value,
|
|
2930
4451
|
catalog: projected.catalog,
|
|
2931
4452
|
revision: projected.revision,
|
|
2932
|
-
transportPersisted
|
|
4453
|
+
transportPersisted
|
|
2933
4454
|
};
|
|
2934
4455
|
}
|
|
2935
4456
|
async function discardImportPublication(candidate) {
|
|
@@ -2939,6 +4460,6 @@ async function restoreImportPublication(candidate) {
|
|
|
2939
4460
|
await candidate.ddc.session.restoreEntry(candidate.ddc);
|
|
2940
4461
|
}
|
|
2941
4462
|
|
|
2942
|
-
export { AssetOutputProducerRegistry, DEFAULT_CATALOG_IMPORTER_KEYS, ImporterRegistry, SHADER_RESERVED_IMPORTER_KEY, buildCatalogResult,
|
|
4463
|
+
export { AssetOutputProducerRegistry, DEFAULT_CATALOG_IMPORTER_KEYS, ImporterRegistry, SHADER_RESERVED_IMPORTER_KEY, buildCatalogResult, buildParameterizedScriptablePack, buildParameterizedScriptablePackWorklist, canonicalScriptableSourcePath, catalogImporterPolicy, commitImportPublication, containsSourcePackageError, createImportProduct, createPreExternalizedSceneAssetOutputProducer, createSceneAssetOutputProducer, createScriptablePackFileAssetSnapshotSource, createScriptablePackStagedAssetSnapshotSource, createStandardAssetOutputProducerRegistry, declaredPackExternalOutputs, deriveDefaultLodScreenCoverages, discardImportPublication, finalizeImportProducts, finalizeSourcePackage, iesImporter, materialAssetOutputProducer, materializePreparedParameterizedScriptablePack, meshAssetOutputProducer, normaliseForPack, normalizeSourcePackageError, packMeshBinV4, parseLm63TypeC, parseProducerReadiness, prepareDirectPackTransport, prepareLegacyPackTransport, produceBuildAssets, produceParameterizedScriptablePackProducts, produceSourcePackage, projectImportProductForBuild, publishImportPublication, readCookedAuthoredPack, readFloat16LE, reconcileMeshLodMeta, resampleTypeC, restoreImportPublication, runImport, sourceDeclarationForCatalogPath, sourcePackageAssetsByGuid, sourcePackageError, stageImportPublication, textureAssetOutputProducer, validateIesProfilePayload, validateMeshLodContract };
|
|
2943
4464
|
//# sourceMappingURL=index.mjs.map
|
|
2944
4465
|
//# sourceMappingURL=index.mjs.map
|