@forgeax/engine-import 0.1.21 → 0.1.24

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