@forgeax/engine-import 0.0.0-dev.8d955ade1c79

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 (118) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +227 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/decode-image-owner.test-d.d.ts +2 -0
  5. package/dist/__tests__/decode-image-owner.test-d.d.ts.map +1 -0
  6. package/dist/__tests__/import-contract-migration.test.d.ts +2 -0
  7. package/dist/__tests__/import-contract-migration.test.d.ts.map +1 -0
  8. package/dist/__tests__/import-dependencies.test.d.ts +2 -0
  9. package/dist/__tests__/import-dependencies.test.d.ts.map +1 -0
  10. package/dist/__tests__/import-finalizer-digest-retry.test.d.ts +2 -0
  11. package/dist/__tests__/import-finalizer-digest-retry.test.d.ts.map +1 -0
  12. package/dist/__tests__/import-local-artifacts.test.d.ts +2 -0
  13. package/dist/__tests__/import-local-artifacts.test.d.ts.map +1 -0
  14. package/dist/__tests__/import.unit.test.d.ts +2 -0
  15. package/dist/__tests__/import.unit.test.d.ts.map +1 -0
  16. package/dist/__tests__/mesh-bin-contract.test.d.ts +2 -0
  17. package/dist/__tests__/mesh-bin-contract.test.d.ts.map +1 -0
  18. package/dist/__tests__/mesh-bin-morph-v3.test.d.ts +2 -0
  19. package/dist/__tests__/mesh-bin-morph-v3.test.d.ts.map +1 -0
  20. package/dist/__tests__/mesh-bin.test.d.ts +2 -0
  21. package/dist/__tests__/mesh-bin.test.d.ts.map +1 -0
  22. package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
  23. package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
  24. package/dist/__tests__/scriptable-pack-host.unit.test.d.ts +2 -0
  25. package/dist/__tests__/scriptable-pack-host.unit.test.d.ts.map +1 -0
  26. package/dist/__tests__/scriptable-pack-output-producers.unit.test.d.ts +2 -0
  27. package/dist/__tests__/scriptable-pack-output-producers.unit.test.d.ts.map +1 -0
  28. package/dist/__tests__/scriptable-pack-product.contract.test.d.ts +2 -0
  29. package/dist/__tests__/scriptable-pack-product.contract.test.d.ts.map +1 -0
  30. package/dist/__tests__/scriptable-pack-production-producers.unit.test.d.ts +2 -0
  31. package/dist/__tests__/scriptable-pack-production-producers.unit.test.d.ts.map +1 -0
  32. package/dist/__tests__/scriptable-pack-staged-snapshot.unit.test.d.ts +2 -0
  33. package/dist/__tests__/scriptable-pack-staged-snapshot.unit.test.d.ts.map +1 -0
  34. package/dist/__tests__/scriptable-pack.integration.test.d.ts +2 -0
  35. package/dist/__tests__/scriptable-pack.integration.test.d.ts.map +1 -0
  36. package/dist/__tests__/source-overrides-import-context.unit.test.d.ts +2 -0
  37. package/dist/__tests__/source-overrides-import-context.unit.test.d.ts.map +1 -0
  38. package/dist/__tests__/source-package-publication.integration.test.d.ts +2 -0
  39. package/dist/__tests__/source-package-publication.integration.test.d.ts.map +1 -0
  40. package/dist/browser.d.ts +5 -0
  41. package/dist/browser.d.ts.map +1 -0
  42. package/dist/browser.mjs +782 -0
  43. package/dist/browser.mjs.map +1 -0
  44. package/dist/build-production.d.ts +48 -0
  45. package/dist/build-production.d.ts.map +1 -0
  46. package/dist/catalog-importer-policy.d.ts +13 -0
  47. package/dist/catalog-importer-policy.d.ts.map +1 -0
  48. package/dist/catalog-inventory.d.ts +4 -0
  49. package/dist/catalog-inventory.d.ts.map +1 -0
  50. package/dist/import-product.d.ts +27 -0
  51. package/dist/import-product.d.ts.map +1 -0
  52. package/dist/import-runner.d.ts +136 -0
  53. package/dist/import-runner.d.ts.map +1 -0
  54. package/dist/importer-registry.d.ts +48 -0
  55. package/dist/importer-registry.d.ts.map +1 -0
  56. package/dist/index.d.ts +19 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.mjs +2849 -0
  59. package/dist/index.mjs.map +1 -0
  60. package/dist/mesh-bin.d.ts +19 -0
  61. package/dist/mesh-bin.d.ts.map +1 -0
  62. package/dist/mesh-bin.mjs +182 -0
  63. package/dist/mesh-bin.mjs.map +1 -0
  64. package/dist/pack-projection.d.ts +5 -0
  65. package/dist/pack-projection.d.ts.map +1 -0
  66. package/dist/scriptable-pack-host.d.ts +72 -0
  67. package/dist/scriptable-pack-host.d.ts.map +1 -0
  68. package/dist/scriptable-pack-output-producers.d.ts +7 -0
  69. package/dist/scriptable-pack-output-producers.d.ts.map +1 -0
  70. package/dist/scriptable-pack-staged-snapshot.d.ts +21 -0
  71. package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -0
  72. package/dist/scriptable-pack.d.ts +141 -0
  73. package/dist/scriptable-pack.d.ts.map +1 -0
  74. package/dist/scriptable-source-package.d.ts +14 -0
  75. package/dist/scriptable-source-package.d.ts.map +1 -0
  76. package/dist/source-package-errors.d.ts +52 -0
  77. package/dist/source-package-errors.d.ts.map +1 -0
  78. package/dist/source-package-publication.d.ts +63 -0
  79. package/dist/source-package-publication.d.ts.map +1 -0
  80. package/dist/source-package.d.ts +60 -0
  81. package/dist/source-package.d.ts.map +1 -0
  82. package/package.json +69 -0
  83. package/src/__tests__/decode-image-owner.test-d.ts +53 -0
  84. package/src/__tests__/import-contract-migration.test.ts +223 -0
  85. package/src/__tests__/import-dependencies.test.ts +65 -0
  86. package/src/__tests__/import-finalizer-digest-retry.test.ts +113 -0
  87. package/src/__tests__/import-local-artifacts.test.ts +102 -0
  88. package/src/__tests__/import.unit.test.ts +429 -0
  89. package/src/__tests__/mesh-bin-contract.test.ts +95 -0
  90. package/src/__tests__/mesh-bin-morph-v3.test.ts +44 -0
  91. package/src/__tests__/mesh-bin.test.ts +102 -0
  92. package/src/__tests__/owner-chain.integration.test.ts +37 -0
  93. package/src/__tests__/scriptable-pack-host.unit.test.ts +86 -0
  94. package/src/__tests__/scriptable-pack-output-producers.unit.test.ts +190 -0
  95. package/src/__tests__/scriptable-pack-product.contract.test.ts +47 -0
  96. package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +488 -0
  97. package/src/__tests__/scriptable-pack-staged-snapshot.unit.test.ts +290 -0
  98. package/src/__tests__/scriptable-pack.integration.test.ts +435 -0
  99. package/src/__tests__/source-overrides-import-context.unit.test.ts +92 -0
  100. package/src/__tests__/source-package-publication.integration.test.ts +75 -0
  101. package/src/browser.ts +14 -0
  102. package/src/build-production.ts +493 -0
  103. package/src/catalog-importer-policy.ts +58 -0
  104. package/src/catalog-inventory.ts +24 -0
  105. package/src/import-product.ts +163 -0
  106. package/src/import-runner.ts +749 -0
  107. package/src/importer-registry.ts +103 -0
  108. package/src/index.ts +175 -0
  109. package/src/mesh-bin.ts +229 -0
  110. package/src/pack-projection.ts +21 -0
  111. package/src/scriptable-pack-host.ts +544 -0
  112. package/src/scriptable-pack-output-producers.ts +456 -0
  113. package/src/scriptable-pack-staged-snapshot.ts +172 -0
  114. package/src/scriptable-pack.ts +626 -0
  115. package/src/scriptable-source-package.ts +88 -0
  116. package/src/source-package-errors.ts +201 -0
  117. package/src/source-package-publication.ts +326 -0
  118. package/src/source-package.ts +187 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,2849 @@
1
+ import { ok, validateSourceOverrideMap, ImportError, IMPORT_ERROR_HINTS, canonicalizeSourceOverrides, err, AssetError, MATERIAL_TEXTURE_SLOTS } from '@forgeax/engine-types';
2
+ export { IMPORT_ERROR_HINTS, ImportError } from '@forgeax/engine-types';
3
+ import { resolve, dirname } from 'path';
4
+ import { upgradeLegacyAuthoredPack, loadAssetConfig, resolveAssetSource, finalizePackageTransportSource, 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';
9
+ 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';
12
+ import { externalizeSceneAsset } from '@forgeax/engine-scene';
13
+ import { sha256 } from '@noble/hashes/sha2.js';
14
+ import { bytesToHex } from '@noble/hashes/utils.js';
15
+ import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
16
+ import { MESH_BIN_HEADER_V4_BYTES, writeMeshBinHeader } from '@forgeax/engine-pack/mesh-bin-contract';
17
+ import { isScriptablePackAssetKind } from '@forgeax/engine-pack';
18
+
19
+ // src/index.ts
20
+ function invalidProduct(field) {
21
+ return err({
22
+ code: "import-product-invalid",
23
+ expected: "a complete terminal import product with source identity",
24
+ hint: "preserve refs, artifacts, receipts, diagnostics, and source revision at the product boundary",
25
+ detail: { field }
26
+ });
27
+ }
28
+ function createImportProduct(input) {
29
+ if (!Array.isArray(input.assets)) return invalidProduct("assets");
30
+ if (!Array.isArray(input.sourceDependencies)) return invalidProduct("sourceDependencies");
31
+ if (input.sourceRevision.trim().length === 0) return invalidProduct("sourceRevision");
32
+ if (!Array.isArray(input.refs)) return invalidProduct("refs");
33
+ if (input.artifacts === null || typeof input.artifacts !== "object") {
34
+ return invalidProduct("artifacts");
35
+ }
36
+ if (!Array.isArray(input.receipts)) return invalidProduct("receipts");
37
+ if (!Array.isArray(input.diagnostics)) return invalidProduct("diagnostics");
38
+ return ok({ ...input });
39
+ }
40
+ async function sha256Hex(bytes2) {
41
+ const subtle = globalThis.crypto?.subtle;
42
+ if (subtle === void 0) throw new Error("Web Crypto API is required for importer digests");
43
+ const owned = new Uint8Array(bytes2.byteLength);
44
+ 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("");
47
+ }
48
+ async function artifactDigest(bytes2) {
49
+ return `sha256:${await sha256Hex(bytes2)}`;
50
+ }
51
+ function concatBytes(chunks) {
52
+ const encoder = new TextEncoder();
53
+ const encoded = chunks.map(
54
+ (chunk) => typeof chunk === "string" ? encoder.encode(chunk) : chunk
55
+ );
56
+ const totalLength = encoded.reduce((total, chunk) => total + chunk.byteLength, 0);
57
+ const bytes2 = new Uint8Array(totalLength);
58
+ let offset = 0;
59
+ for (const chunk of encoded) {
60
+ bytes2.set(chunk, offset);
61
+ offset += chunk.byteLength;
62
+ }
63
+ return bytes2;
64
+ }
65
+ async function productDigest(asset, artifacts) {
66
+ const chunks = [
67
+ JSON.stringify(projectImportedAssetPayload(asset)),
68
+ JSON.stringify(asset.refs.map((ref2) => ref2.guid)),
69
+ JSON.stringify(artifacts)
70
+ ];
71
+ for (const [key, artifact] of Object.entries(asset.artifacts).sort(
72
+ ([left], [right]) => left.localeCompare(right)
73
+ )) {
74
+ chunks.push(key, artifact.bytes);
75
+ }
76
+ return `sha256:${await sha256Hex(concatBytes(chunks))}`;
77
+ }
78
+ async function artifactDescriptors(artifacts) {
79
+ const entries = [];
80
+ for (const [path, artifact] of Object.entries(artifacts)) {
81
+ entries.push([
82
+ path,
83
+ {
84
+ path,
85
+ mediaType: artifact.mediaType,
86
+ byteLength: artifact.bytes.byteLength,
87
+ integrity: { algorithm: "sha256", digest: await artifactDigest(artifact.bytes) },
88
+ ...artifact.assetCodec === void 0 ? {} : { assetCodec: artifact.assetCodec }
89
+ }
90
+ ]);
91
+ }
92
+ return Object.fromEntries(entries);
93
+ }
94
+ function projectImportedAssetPayload(asset) {
95
+ const payload = asset.payload;
96
+ if (Object.keys(asset.artifacts).length === 0) return payload;
97
+ if (asset.kind === "mesh") return { kind: "mesh" };
98
+ if (asset.kind === "texture" || asset.kind === "equirect") {
99
+ const { data: _runtimeBytes, ...metadata } = payload;
100
+ return metadata;
101
+ }
102
+ return payload;
103
+ }
104
+ function finalizeImportProducts(product, inputFingerprint) {
105
+ return (async () => {
106
+ const products = [];
107
+ for (const asset of product.assets) {
108
+ const artifacts = await artifactDescriptors(asset.artifacts);
109
+ const digest = await productDigest(asset, artifacts);
110
+ products.push({
111
+ guid: asset.guid,
112
+ payload: asset.payload,
113
+ refs: asset.refs.map((ref2) => ref2.guid),
114
+ artifacts,
115
+ digest,
116
+ receipt: {
117
+ guid: asset.guid,
118
+ origin: "sourceMeta",
119
+ status: "succeeded",
120
+ inputFingerprint,
121
+ outputDigest: digest
122
+ }
123
+ });
124
+ }
125
+ return products;
126
+ })();
127
+ }
128
+ var SHADER_RESERVED_IMPORTER_KEY = "shader";
129
+ function isModuleLoadFailure(e) {
130
+ if (!(e instanceof Error)) return false;
131
+ const code = e.code;
132
+ if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND" || code === "ERR_DLOPEN_FAILED") {
133
+ return true;
134
+ }
135
+ const msg = e.message;
136
+ return msg.includes("Cannot find module") || msg.includes("native addon") || msg.includes(".node");
137
+ }
138
+ function normaliseForPack(value) {
139
+ if (value === null || value === void 0) return value;
140
+ 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) {
141
+ return Array.from(value);
142
+ }
143
+ if (Array.isArray(value)) {
144
+ return value.map(normaliseForPack);
145
+ }
146
+ if (typeof value === "object") {
147
+ const out = {};
148
+ for (const [k, v] of Object.entries(value)) {
149
+ out[k] = normaliseForPack(v);
150
+ }
151
+ return out;
152
+ }
153
+ return value;
154
+ }
155
+ function declarationFields(declaration) {
156
+ if (declaration === void 0) return {};
157
+ return {
158
+ sourceIndex: declaration.sourceIndex,
159
+ ...declaration.sourceKey !== void 0 ? { sourceKey: declaration.sourceKey } : {},
160
+ ...declaration.relations !== void 0 ? { relations: declaration.relations } : {}
161
+ };
162
+ }
163
+ function joinSiblingPath(sourcePath, uri) {
164
+ const slash = Math.max(sourcePath.lastIndexOf("/"), sourcePath.lastIndexOf("\\"));
165
+ const dir = slash >= 0 ? sourcePath.slice(0, slash + 1) : "";
166
+ return `${dir}${uri}`;
167
+ }
168
+ function normalizeDependencyPath(path) {
169
+ const slash = path.replaceAll("\\", "/");
170
+ const parts = [];
171
+ for (const part of slash.split("/")) {
172
+ if (part === "" || part === ".") continue;
173
+ if (part === "..") {
174
+ parts.pop();
175
+ } else {
176
+ parts.push(part);
177
+ }
178
+ }
179
+ return parts.join("/");
180
+ }
181
+ function errResult(error) {
182
+ return { ok: false, error };
183
+ }
184
+ function sourceKeyActual(value) {
185
+ if (typeof value === "string") return JSON.stringify(value);
186
+ if (value === void 0) return "missing";
187
+ return typeof value;
188
+ }
189
+ function sourceReadFailureReason(error) {
190
+ if (typeof error !== "object" || error === null) {
191
+ return typeof error === "string" ? error : "unknown";
192
+ }
193
+ const metadata = error;
194
+ const token = typeof metadata.code === "string" ? metadata.code : typeof metadata.name === "string" ? metadata.name : void 0;
195
+ switch (token) {
196
+ case "ENOENT":
197
+ case "NotFoundError":
198
+ return "not-found";
199
+ case "EACCES":
200
+ case "EPERM":
201
+ case "PermissionDeniedError":
202
+ case "NotAllowedError":
203
+ case "SecurityError":
204
+ return "permission-denied";
205
+ case "EAGAIN":
206
+ case "EBUSY":
207
+ case "EINTR":
208
+ case "ECONNABORTED":
209
+ case "ECONNREFUSED":
210
+ case "ECONNRESET":
211
+ case "EHOSTUNREACH":
212
+ case "ENETDOWN":
213
+ case "ENETUNREACH":
214
+ case "ETIMEDOUT":
215
+ case "EPIPE":
216
+ case "ABORT_ERR":
217
+ case "AbortError":
218
+ case "TimeoutError":
219
+ return "transient";
220
+ default:
221
+ if (typeof metadata.detail === "object" && metadata.detail !== null) {
222
+ const reason = metadata.detail.reason;
223
+ if (typeof reason === "string" && reason.length > 0) return reason;
224
+ }
225
+ if (typeof metadata.message === "string" && metadata.message.length > 0) {
226
+ return metadata.message;
227
+ }
228
+ return "unknown";
229
+ }
230
+ }
231
+ function validateOutputSourceKeys(meta) {
232
+ if (meta.subAssets.length <= 1) return void 0;
233
+ const diagnostics = [];
234
+ const seen = /* @__PURE__ */ new Map();
235
+ for (const [index, declaration] of meta.subAssets.entries()) {
236
+ const sourcePath = `${meta.source}#subAssets[${index}]`;
237
+ const sourceRange = { start: 0, end: 0, line: 1, column: 1 };
238
+ const sourceKey = declaration.sourceKey;
239
+ if (typeof sourceKey !== "string" || sourceKey.trim().length === 0) {
240
+ diagnostics.push({
241
+ code: "source-key-required",
242
+ severity: "error",
243
+ sourcePath,
244
+ sourceRange,
245
+ rule: "import-output-source-key",
246
+ expected: "a non-empty producer-owned sourceKey",
247
+ actual: sourceKeyActual(sourceKey),
248
+ hint: "publish a stable semantic sourceKey; sourceIndex is only a locator"
249
+ });
250
+ continue;
251
+ }
252
+ const prior = seen.get(sourceKey);
253
+ if (prior !== void 0) {
254
+ diagnostics.push({
255
+ code: "duplicate-source-key",
256
+ severity: "error",
257
+ sourcePath,
258
+ sourceRange,
259
+ rule: "import-output-source-key-unique",
260
+ expected: "sourceKey to be unique within one imported package",
261
+ actual: `${JSON.stringify(sourceKey)} duplicates subAssets[${prior}]`,
262
+ hint: "rename the duplicate semantic output before writing Meta"
263
+ });
264
+ continue;
265
+ }
266
+ seen.set(sourceKey, index);
267
+ }
268
+ if (diagnostics.length === 0) return void 0;
269
+ return new ImportError({
270
+ code: "source-validation-failed",
271
+ expected: "every writable imported output to declare a unique non-empty sourceKey",
272
+ hint: IMPORT_ERROR_HINTS["source-validation-failed"],
273
+ detail: { diagnostics }
274
+ });
275
+ }
276
+ async function runImport(meta, registry, fs) {
277
+ if (meta.importer === SHADER_RESERVED_IMPORTER_KEY) {
278
+ return { ok: true, value: { skipped: "shader" } };
279
+ }
280
+ const sourceKeyError = validateOutputSourceKeys(meta);
281
+ if (sourceKeyError !== void 0) return errResult(sourceKeyError);
282
+ const declaredSourceKeys = meta.subAssets.flatMap(
283
+ (subAsset) => subAsset.sourceKey === void 0 ? [] : [subAsset.sourceKey]
284
+ );
285
+ const sourceOverridesResult = validateSourceOverrideMap(meta.sourceOverrides, declaredSourceKeys);
286
+ if (!sourceOverridesResult.ok) {
287
+ const error = new ImportError({
288
+ code: sourceOverridesResult.error.code,
289
+ expected: sourceOverridesResult.error.expected,
290
+ hint: IMPORT_ERROR_HINTS[sourceOverridesResult.error.code],
291
+ detail: {
292
+ sourceKey: sourceOverridesResult.error.actual,
293
+ declaredSourceKeys,
294
+ reason: sourceOverridesResult.error.hint
295
+ }
296
+ });
297
+ if (sourceOverridesResult.error.actual !== void 0) {
298
+ Object.assign(error, { actual: sourceOverridesResult.error.actual });
299
+ }
300
+ return errResult(error);
301
+ }
302
+ const importer = registry.get(meta.importer);
303
+ if (importer === void 0) {
304
+ return errResult(
305
+ new ImportError({
306
+ code: "importer-not-registered",
307
+ expected: `an importer registered for meta.importer "${meta.importer}"`,
308
+ hint: IMPORT_ERROR_HINTS["importer-not-registered"],
309
+ detail: {
310
+ importer: meta.importer,
311
+ registeredImporters: registry.registeredImporters()
312
+ }
313
+ })
314
+ );
315
+ }
316
+ const dependencies = /* @__PURE__ */ new Set();
317
+ const readSource = async (sourcePath) => {
318
+ dependencies.add(normalizeDependencyPath(sourcePath));
319
+ try {
320
+ return await fs.readSource(sourcePath);
321
+ } catch (error) {
322
+ return { ok: false, error };
323
+ }
324
+ };
325
+ const readSibling = async (uri) => {
326
+ let inner;
327
+ try {
328
+ if (fs.readSibling) {
329
+ dependencies.add(normalizeDependencyPath(joinSiblingPath(meta.source, uri)));
330
+ inner = await fs.readSibling(meta.source, uri);
331
+ } else {
332
+ inner = await readSource(joinSiblingPath(meta.source, uri));
333
+ }
334
+ } catch (error) {
335
+ inner = { ok: false, error };
336
+ }
337
+ if (inner.ok) {
338
+ return { ok: true, value: inner.value };
339
+ }
340
+ return {
341
+ ok: false,
342
+ error: new ImportError({
343
+ code: "source-read-failed",
344
+ expected: `readable sibling file "${uri}" co-located with meta.source "${meta.source}"`,
345
+ hint: IMPORT_ERROR_HINTS["source-read-failed"],
346
+ detail: {
347
+ source: uri,
348
+ reason: sourceReadFailureReason(inner.error)
349
+ }
350
+ })
351
+ };
352
+ };
353
+ const decodeImage = fs.decodeImage ?? (async () => {
354
+ throw new Error(
355
+ "ImportRunnerFs.decodeImage was not provided; gltfImporter texture extraction requires the host (vite-plugin-pack / cli-gltf / test) to bind decodeImage when constructing the ImportRunnerFs"
356
+ );
357
+ });
358
+ const canonicalSourceOverrides = canonicalizeSourceOverrides(sourceOverridesResult.value);
359
+ const ctx = {
360
+ source: meta.source,
361
+ readSource: () => readSource(meta.source),
362
+ readSibling,
363
+ decodeImage,
364
+ subAssets: meta.subAssets.map(({ guid, sourceIndex, sourceKey, kind }) => ({
365
+ guid,
366
+ sourceIndex,
367
+ ...sourceKey === void 0 ? {} : { sourceKey },
368
+ kind
369
+ })),
370
+ importSettings: meta.importSettings ?? {},
371
+ ...canonicalSourceOverrides === void 0 ? {} : { sourceOverrides: canonicalSourceOverrides }
372
+ };
373
+ const sourceProbe = await readSource(meta.source);
374
+ if (!sourceProbe.ok) {
375
+ return errResult(
376
+ new ImportError({
377
+ code: "source-read-failed",
378
+ expected: `readable source file at meta.source "${meta.source}"`,
379
+ hint: IMPORT_ERROR_HINTS["source-read-failed"],
380
+ detail: {
381
+ source: meta.source,
382
+ reason: sourceReadFailureReason(sourceProbe.error)
383
+ }
384
+ })
385
+ );
386
+ }
387
+ let product;
388
+ try {
389
+ const imported = await importer.import(ctx);
390
+ if (imported === void 0 || imported === null || typeof imported !== "object" || !("ok" in imported)) {
391
+ throw new Error(
392
+ "importer returned a legacy or malformed result; expected ImportResult<ImportProduct>"
393
+ );
394
+ } else {
395
+ if (!imported.ok) {
396
+ if (imported.error === void 0)
397
+ throw new Error("importer returned an invalid failure result");
398
+ if (imported.error instanceof ImportError) return errResult(imported.error);
399
+ return errResult(
400
+ new ImportError({
401
+ code: "import-internal-error",
402
+ expected: `importer "${meta.importer}" to return a structured ImportError`,
403
+ hint: IMPORT_ERROR_HINTS["import-internal-error"],
404
+ detail: { reason: String(imported.error) }
405
+ })
406
+ );
407
+ }
408
+ if (imported.value === void 0)
409
+ throw new Error("importer returned an invalid success result");
410
+ const value = imported.value;
411
+ if (value === void 0 || !Array.isArray(value.assets) || !Array.isArray(value.sourceDependencies) || "artifacts" in value) {
412
+ throw new Error("importer returned an invalid ImportProduct");
413
+ }
414
+ for (const asset of value.assets) {
415
+ if (asset === null || typeof asset !== "object" || !("artifacts" in asset) || asset.artifacts === null || typeof asset.artifacts !== "object" || Array.isArray(asset.artifacts)) {
416
+ throw new Error("importer returned an asset without local artifacts");
417
+ }
418
+ }
419
+ product = value;
420
+ }
421
+ } catch (e) {
422
+ if (e instanceof ImportError) return errResult(e);
423
+ const message = e instanceof Error ? e.message : String(e);
424
+ if (isModuleLoadFailure(e)) {
425
+ return errResult(
426
+ new ImportError({
427
+ code: "import-internal-error",
428
+ expected: `importer module "${meta.importer}" to load (module + native addon present)`,
429
+ hint: IMPORT_ERROR_HINTS["import-internal-error"],
430
+ detail: { loadError: message }
431
+ })
432
+ );
433
+ }
434
+ return errResult(
435
+ new ImportError({
436
+ code: "import-internal-error",
437
+ expected: `importer "${meta.importer}" to convert the source without throwing`,
438
+ hint: IMPORT_ERROR_HINTS["import-internal-error"],
439
+ detail: { reason: message }
440
+ })
441
+ );
442
+ }
443
+ const produced = product.assets;
444
+ const declared = new Set(meta.subAssets.map((s) => s.guid));
445
+ const producedGuids = new Set(produced.map((a) => a.guid));
446
+ const unexpectedGuids = [...producedGuids].filter((g) => !declared.has(g));
447
+ if (unexpectedGuids.length > 0) {
448
+ return errResult(
449
+ new ImportError({
450
+ code: "guid-mismatch",
451
+ expected: "every produced GUID to be declared in meta.subAssets[]",
452
+ hint: IMPORT_ERROR_HINTS["guid-mismatch"],
453
+ detail: { unexpectedGuids }
454
+ })
455
+ );
456
+ }
457
+ const missingGuids = [...declared].filter((g) => !producedGuids.has(g));
458
+ if (produced.length === 0 || missingGuids.length > 0) {
459
+ return errResult(
460
+ new ImportError({
461
+ code: "import-produced-no-assets",
462
+ expected: produced.length === 0 ? "the importer to produce at least one ImportedAsset" : "the produced GUID set to be a superset of meta.subAssets[]",
463
+ hint: IMPORT_ERROR_HINTS["import-produced-no-assets"],
464
+ detail: { missingGuids }
465
+ })
466
+ );
467
+ }
468
+ const declarations = new Map(
469
+ meta.subAssets.map((declaration) => [declaration.guid, declaration])
470
+ );
471
+ const productWithDependencies = {
472
+ ...product,
473
+ sourceDependencies: [...dependencies]
474
+ };
475
+ const inputFingerprint = `source:${[...dependencies].sort().join("|")}`;
476
+ let cookProducts;
477
+ try {
478
+ cookProducts = await finalizeImportProducts(productWithDependencies, inputFingerprint);
479
+ } catch (e) {
480
+ const reason = e instanceof Error ? e.message : String(e);
481
+ return errResult(
482
+ new ImportError({
483
+ code: "import-internal-error",
484
+ expected: `importer "${meta.importer}" finalization to produce complete CookProduct digests`,
485
+ hint: IMPORT_ERROR_HINTS["import-internal-error"],
486
+ detail: { reason: `finalization/digest: ${reason}` }
487
+ })
488
+ );
489
+ }
490
+ const terminalProduct = createImportProduct({
491
+ ...productWithDependencies,
492
+ refs: productWithDependencies.assets.flatMap((asset) => asset.refs),
493
+ artifacts: Object.fromEntries(
494
+ productWithDependencies.assets.flatMap(
495
+ (asset) => Object.entries(asset.artifacts).map(([key, artifact]) => [
496
+ `${asset.guid}/${key}`,
497
+ artifact
498
+ ])
499
+ )
500
+ ),
501
+ receipts: cookProducts.map((product2) => product2.receipt),
502
+ diagnostics: meta.diagnostics ?? [],
503
+ sourceRevision: inputFingerprint
504
+ });
505
+ if (!terminalProduct.ok) {
506
+ return errResult(
507
+ new ImportError({
508
+ code: "import-internal-error",
509
+ expected: "the import product to retain complete terminal producer facts",
510
+ hint: "preserve source identity and producer evidence when returning the import product",
511
+ detail: { reason: terminalProduct.error.detail.field }
512
+ })
513
+ );
514
+ }
515
+ if (meta.buildPack === false) {
516
+ return {
517
+ ok: true,
518
+ value: { product: terminalProduct.value, cookProducts }
519
+ };
520
+ }
521
+ const assets = produced.map((a) => {
522
+ const outputFields = declarationFields(declarations.get(a.guid));
523
+ return {
524
+ guid: a.guid,
525
+ kind: a.kind,
526
+ ...outputFields,
527
+ ...a.name !== void 0 ? { name: a.name } : {},
528
+ // bug-20260610: mesh / scene / animation-clip payloads carry Float32Array
529
+ // / Uint16Array / Uint32Array fields. JSON.stringify on a typed array
530
+ // serialises to `{ "0": v0, "1": v1, ... }` (a plain object), which the
531
+ // runtime mesh / animation loaders reject (`vertexData instanceof
532
+ // Float32Array` and `Array.isArray(vertexData)` both fail). Convert
533
+ // every typed-array field to a plain Array here so the pack is JSON-
534
+ // roundtrip safe end-to-end. This matches the convention every
535
+ // existing pack-fixture test uses (`vertices: Array.from(...)`).
536
+ payload: normaliseForPack(a.payload),
537
+ refs: a.refs.map((r) => r.guid),
538
+ artifacts: a.artifacts
539
+ };
540
+ });
541
+ const pack = {
542
+ schemaVersion: "2.0.0",
543
+ kind: "internal-text-package",
544
+ ...meta.packageId !== void 0 ? { packageId: meta.packageId } : {},
545
+ ...meta.provenance !== void 0 ? { provenance: meta.provenance } : {},
546
+ ...meta.revision !== void 0 ? { revision: meta.revision } : {},
547
+ ...meta.diagnostics !== void 0 ? { diagnostics: meta.diagnostics } : {},
548
+ assets
549
+ };
550
+ return {
551
+ ok: true,
552
+ value: {
553
+ product: terminalProduct.value,
554
+ cookProducts,
555
+ pack
556
+ }
557
+ };
558
+ }
559
+
560
+ // src/pack-projection.ts
561
+ function projectImportProductForBuild(product) {
562
+ return {
563
+ schemaVersion: "2.0.0",
564
+ kind: "internal-text-package",
565
+ assets: product.assets.map((asset) => ({
566
+ guid: asset.guid,
567
+ kind: asset.kind,
568
+ ...asset.name === void 0 ? {} : { name: asset.name },
569
+ payload: normaliseForPack(projectImportedAssetPayload(asset)),
570
+ refs: asset.refs.map((reference) => reference.guid),
571
+ artifacts: asset.artifacts
572
+ }))
573
+ };
574
+ }
575
+ function failure(sourceKey, expected, actual) {
576
+ return {
577
+ code: "mesh-bin-payload-invalid",
578
+ subject: "mesh-bin",
579
+ sourceKey,
580
+ expected,
581
+ actual,
582
+ recovery: "re-cook the source with its Meta sidecar through the build-time importer"
583
+ };
584
+ }
585
+ function asAttributeMap(value) {
586
+ return value ?? {};
587
+ }
588
+ function jsonValue(value) {
589
+ if (value instanceof Float32Array || value instanceof Uint16Array) return Array.from(value);
590
+ if (Array.isArray(value)) return value.map(jsonValue);
591
+ if (value !== null && typeof value === "object") {
592
+ return Object.fromEntries(
593
+ Object.entries(value).map(([key, nested]) => [key, jsonValue(nested)])
594
+ );
595
+ }
596
+ return value;
597
+ }
598
+ function refsMeta(payload, refs) {
599
+ const materialSlots = (payload.materialSlots ?? [{ slotName: "Default" }]).map(
600
+ (slot, slotIndex) => {
601
+ const defaultMaterial = slot.defaultMaterial;
602
+ let defaultMaterialRef;
603
+ if (defaultMaterial !== void 0) {
604
+ const guid = AssetGuid.format(defaultMaterial);
605
+ defaultMaterialRef = refs.findIndex((candidate) => candidate.toLowerCase() === guid);
606
+ if (defaultMaterialRef < 0) {
607
+ throw new Error(
608
+ `material slot ${slotIndex} default material ${guid} is absent from refs`
609
+ );
610
+ }
611
+ }
612
+ return {
613
+ slotName: slot.slotName,
614
+ ...slot.sourceKey === void 0 ? {} : { sourceKey: slot.sourceKey },
615
+ ...defaultMaterialRef === void 0 ? {} : { defaultMaterialRef }
616
+ };
617
+ }
618
+ );
619
+ return {
620
+ submeshes: payload.submeshes === void 0 || payload.submeshes.length === 0 ? [{ indexOffset: 0, indexCount: payload.indices?.length ?? 0, materialSlot: 0 }] : payload.submeshes,
621
+ materialSlots,
622
+ ...payload.aabb === void 0 ? {} : { aabb: jsonValue(payload.aabb) },
623
+ ...payload.morphTargets === void 0 ? {} : { morphTargets: jsonValue(payload.morphTargets) },
624
+ ...payload.morphWeights === void 0 ? {} : { morphWeights: jsonValue(payload.morphWeights) }
625
+ };
626
+ }
627
+ function packMeshBinV4(payload, sourceKey, refs = []) {
628
+ try {
629
+ const vertices = payload.vertices;
630
+ const indices = payload.indices;
631
+ if (!(vertices instanceof Float32Array)) {
632
+ return err(
633
+ failure(sourceKey, "Float32Array interleaved vertices", "vertices is not Float32Array")
634
+ );
635
+ }
636
+ if (indices !== void 0 && !(indices instanceof Uint16Array || indices instanceof Uint32Array)) {
637
+ return err(
638
+ failure(sourceKey, "Uint16Array or Uint32Array indices", "indices has an unsupported type")
639
+ );
640
+ }
641
+ const attributes = asAttributeMap(payload.attributes);
642
+ const projection = deriveVertexLayoutProjection(attributes);
643
+ if (projection.attributes.length === 0 || projection.arrayStride === 0) {
644
+ return err(
645
+ failure(
646
+ sourceKey,
647
+ "a non-empty canonical geometry projection",
648
+ "projection has no attributes"
649
+ )
650
+ );
651
+ }
652
+ const vertexCount = payload.vertexCount ?? vertices.byteLength / projection.arrayStride;
653
+ if (!Number.isSafeInteger(vertexCount) || vertexCount < 0) {
654
+ return err(
655
+ failure(sourceKey, "a non-negative safe vertex cardinality", `vertexCount=${vertexCount}`)
656
+ );
657
+ }
658
+ if (vertices.byteLength !== vertexCount * projection.arrayStride) {
659
+ return err(
660
+ failure(
661
+ sourceKey,
662
+ `vertices.byteLength=${vertexCount * projection.arrayStride}`,
663
+ `vertices.byteLength=${vertices.byteLength}; stride=${projection.arrayStride}`
664
+ )
665
+ );
666
+ }
667
+ for (const attribute of projection.attributes) {
668
+ const value = attributes[attribute.key];
669
+ const components = attribute.byteLength / (attribute.format === "uint16x4" ? 2 : 4);
670
+ if (value === void 0 || !(value instanceof Float32Array) && !(value instanceof Uint16Array) || value.length !== vertexCount * components) {
671
+ return err(
672
+ failure(
673
+ sourceKey,
674
+ `${attribute.key} cardinality=${vertexCount * components}`,
675
+ `${attribute.key} cardinality=${value?.byteLength ?? "missing"}`
676
+ )
677
+ );
678
+ }
679
+ }
680
+ const interleaved = new Uint8Array(vertexCount * projection.arrayStride);
681
+ const interleavedView = new DataView(interleaved.buffer);
682
+ for (const attribute of projection.attributes) {
683
+ const value = attributes[attribute.key];
684
+ if (value === void 0) continue;
685
+ const components = attribute.byteLength / (attribute.format === "uint16x4" ? 2 : 4);
686
+ for (let vertex = 0; vertex < vertexCount; vertex++) {
687
+ for (let component = 0; component < components; component++) {
688
+ const sourceIndex = vertex * components + component;
689
+ const targetOffset = vertex * projection.arrayStride + attribute.offset + component * (attribute.format === "uint16x4" ? 2 : 4);
690
+ if (attribute.format === "uint16x4") {
691
+ interleavedView.setUint16(targetOffset, value[sourceIndex] ?? 0, true);
692
+ } else {
693
+ interleavedView.setFloat32(
694
+ targetOffset,
695
+ value[sourceIndex] ?? 0,
696
+ true
697
+ );
698
+ }
699
+ }
700
+ }
701
+ }
702
+ const indexCount = indices?.length ?? 0;
703
+ const indexWidth = indices === void 0 || indexCount === 0 ? 0 : indices.BYTES_PER_ELEMENT;
704
+ const indexBytes = indexCount * indexWidth;
705
+ if (!Number.isSafeInteger(indexBytes) || indexBytes > 4294967295) {
706
+ return err(failure(sourceKey, "safe index payload byte length", `indexBytes=${indexBytes}`));
707
+ }
708
+ const meta = new TextEncoder().encode(JSON.stringify(refsMeta(payload, refs)));
709
+ const header = {
710
+ version: 4,
711
+ projectionVersion: projection.schemaVersion,
712
+ mask: projection.mask,
713
+ digest: projection.digest,
714
+ stride: projection.arrayStride,
715
+ vertexCount,
716
+ vertexBytes: interleaved.byteLength,
717
+ indexCount,
718
+ indexWidth,
719
+ indexBytes,
720
+ jsonBytes: meta.byteLength
721
+ };
722
+ const total = MESH_BIN_HEADER_V4_BYTES + interleaved.byteLength + indexBytes + meta.byteLength;
723
+ if (!Number.isSafeInteger(total) || total > 4294967295) {
724
+ return err(failure(sourceKey, "safe mesh binary byte length", `total=${total}`));
725
+ }
726
+ const out = new Uint8Array(total);
727
+ writeMeshBinHeader(header, out);
728
+ let offset = MESH_BIN_HEADER_V4_BYTES;
729
+ out.set(interleaved, offset);
730
+ offset += interleaved.byteLength;
731
+ if (indices !== void 0 && indexBytes > 0) {
732
+ out.set(new Uint8Array(indices.buffer, indices.byteOffset, indices.byteLength), offset);
733
+ offset += indexBytes;
734
+ }
735
+ out.set(meta, offset);
736
+ return ok(out);
737
+ } catch (error) {
738
+ return err(
739
+ failure(
740
+ sourceKey,
741
+ "valid canonical mesh payload",
742
+ error instanceof Error ? error.message : String(error)
743
+ )
744
+ );
745
+ }
746
+ }
747
+ function commonPathPrefix(paths) {
748
+ const first = paths[0];
749
+ if (first === void 0) return "";
750
+ const firstNormalized = first.replaceAll("\\", "/");
751
+ const firstDirectory = firstNormalized.slice(0, firstNormalized.lastIndexOf("/"));
752
+ const parts = firstDirectory.split("/");
753
+ let length = parts.length;
754
+ for (const path of paths.slice(1)) {
755
+ const normalized = path.replaceAll("\\", "/");
756
+ const candidate = normalized.slice(0, normalized.lastIndexOf("/")).split("/");
757
+ length = Math.min(length, candidate.length);
758
+ for (let index = 0; index < length; index += 1) {
759
+ if (parts[index] !== candidate[index]) {
760
+ length = index;
761
+ break;
762
+ }
763
+ }
764
+ }
765
+ return parts.slice(0, length).join("/");
766
+ }
767
+ function stableSourceClosure(closure) {
768
+ const root = commonPathPrefix(closure.map((entry) => entry.path));
769
+ return closure.map((entry) => ({
770
+ path: entry.path.replaceAll("\\", "/").slice(root.length).replace(/^\/+/, ""),
771
+ digest: entry.digest
772
+ })).sort((left, right) => left.path.localeCompare(right.path));
773
+ }
774
+ var AssetOutputProducerRegistry = class {
775
+ producers = /* @__PURE__ */ new Map();
776
+ register(producer) {
777
+ if (producer.kind.trim().length === 0 || producer.version.trim().length === 0) {
778
+ throw new TypeError("ScriptablePack output producer kind and version must be non-empty");
779
+ }
780
+ if (typeof producer.produce !== "function") {
781
+ throw new TypeError(`ScriptablePack output producer ${producer.kind} must expose produce`);
782
+ }
783
+ this.producers.set(producer.kind, producer);
784
+ }
785
+ get(kind) {
786
+ return this.producers.get(kind);
787
+ }
788
+ versions() {
789
+ return Object.fromEntries(
790
+ [...this.producers.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([kind, producer]) => [kind, producer.version])
791
+ );
792
+ }
793
+ };
794
+ function privateClone(value) {
795
+ return structuredClone(value);
796
+ }
797
+ function record(value) {
798
+ return value !== null && typeof value === "object" && !Array.isArray(value);
799
+ }
800
+ function isAsset(value) {
801
+ return record(value) && typeof value.kind === "string" && isScriptablePackAssetKind(value.kind);
802
+ }
803
+ function isStructuredDomainError(value) {
804
+ return value !== null && typeof value === "object" && typeof value.code === "string" && typeof value.expected === "string" && typeof value.hint === "string";
805
+ }
806
+ function remapBuildFailureSource(value, sourcePath) {
807
+ if (value === null || typeof value !== "object") return void 0;
808
+ const error = value;
809
+ if (error.code !== "pack-source-load-failed") return void 0;
810
+ if (error.detail === null || typeof error.detail !== "object") return void 0;
811
+ const detail = error.detail;
812
+ if (detail.phase !== "build") return void 0;
813
+ return {
814
+ ...value,
815
+ detail: { ...detail, sourcePath }
816
+ };
817
+ }
818
+ function observedAssetReader(source) {
819
+ const reads = /* @__PURE__ */ new Map();
820
+ const reader = {
821
+ async readByGuid(guid) {
822
+ const key = AssetGuid.format(guid);
823
+ const cached = reads.get(key);
824
+ if (cached !== void 0) return ok(privateClone(cached.asset));
825
+ if (source === void 0) {
826
+ return err(
827
+ new AssetError({
828
+ code: "asset-not-found",
829
+ expected: `a host Asset snapshot source for ScriptablePack content read ${key}`,
830
+ hint: "configure the standard ScriptablePack asset source or remove the content read"
831
+ })
832
+ );
833
+ }
834
+ const result = await source.readByGuid(guid);
835
+ if (!result.ok) return result;
836
+ const fixed = privateClone(result.value.asset);
837
+ reads.set(key, {
838
+ guid: key,
839
+ asset: fixed,
840
+ generation: result.value.generation,
841
+ digest: result.value.digest
842
+ });
843
+ return ok(privateClone(fixed));
844
+ }
845
+ };
846
+ return { reader, reads };
847
+ }
848
+ function stable(value) {
849
+ if (value instanceof Uint8Array) return JSON.stringify(Array.from(value));
850
+ if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
851
+ if (value !== null && typeof value === "object") {
852
+ const record2 = value;
853
+ return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stable(record2[key])}`).join(",")}}`;
854
+ }
855
+ return JSON.stringify(value) ?? "null";
856
+ }
857
+ async function fingerprint(value) {
858
+ const subtle = globalThis.crypto?.subtle;
859
+ if (subtle === void 0)
860
+ throw new Error("Web Crypto API is required for ScriptablePack fingerprints");
861
+ const digest = await subtle.digest("SHA-256", new TextEncoder().encode(stable(value)));
862
+ const hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join(
863
+ ""
864
+ );
865
+ return `sha256:${hex}`;
866
+ }
867
+ function outputError(definition, output) {
868
+ const declaredKeys = Object.keys(definition.assets);
869
+ const outputKeys = Object.keys(output);
870
+ const outputKeySet = new Set(outputKeys);
871
+ const declaredKeySet = new Set(declaredKeys);
872
+ const missingKeys = declaredKeys.filter((key) => !outputKeySet.has(key));
873
+ const unexpectedSourceKeys = outputKeys.filter((key) => !declaredKeySet.has(key));
874
+ const kindMismatches = declaredKeys.flatMap((sourceKey) => {
875
+ const descriptor = definition.assets[sourceKey];
876
+ const value = output[sourceKey];
877
+ if (descriptor === void 0 || value === void 0) return [];
878
+ const actualKind = value !== null && typeof value === "object" && "kind" in value ? String(value.kind) : typeof value;
879
+ return actualKind === descriptor.kind ? [] : [{ sourceKey, expected: descriptor.kind, actual: actualKind }];
880
+ });
881
+ if (missingKeys.length === 0 && unexpectedSourceKeys.length === 0 && kindMismatches.length === 0) {
882
+ return void 0;
883
+ }
884
+ return {
885
+ code: "pack-source-output-invalid",
886
+ expected: "build output keys and kinds to exactly match definition.assets",
887
+ hint: "inspect sourceKey and GUID topology, then rebuild or cold-cook the ScriptablePack",
888
+ detail: {
889
+ missingGuids: missingKeys.map(
890
+ (key) => AssetGuid.format(definition.assets[key]?.guid)
891
+ ),
892
+ unexpectedSourceKeys,
893
+ kindMismatches
894
+ }
895
+ };
896
+ }
897
+ function externalClosureError(declared, referenced, read) {
898
+ const used = /* @__PURE__ */ new Set([...referenced, ...read]);
899
+ const undeclaredReferencedGuids = [...referenced].filter((guid) => !declared.has(guid));
900
+ const undeclaredReadGuids = [...read].filter((guid) => !declared.has(guid));
901
+ const unusedDeclaredGuids = [...declared].filter((guid) => !used.has(guid));
902
+ if (undeclaredReferencedGuids.length === 0 && undeclaredReadGuids.length === 0 && unusedDeclaredGuids.length === 0) {
903
+ return void 0;
904
+ }
905
+ return {
906
+ code: "pack-source-external-closure-mismatch",
907
+ expected: "externalAssets GUIDs to equal output external refs union AssetReader reads",
908
+ hint: "inspect refs and AssetReader reads, repair GUID declarations, then rebuild or cold-cook",
909
+ detail: {
910
+ undeclaredReferencedGuids: undeclaredReferencedGuids.sort(),
911
+ undeclaredReadGuids: undeclaredReadGuids.sort(),
912
+ unusedDeclaredGuids: unusedDeclaredGuids.sort()
913
+ }
914
+ };
915
+ }
916
+ function productContractError(sourceKey, kind, product) {
917
+ const mismatches = [];
918
+ if (product.payload.kind !== kind) {
919
+ mismatches.push({ sourceKey, expected: kind, actual: product.payload.kind });
920
+ }
921
+ for (const reference of product.refs) {
922
+ if (!AssetGuid.parse(reference.guid).ok) {
923
+ mismatches.push({
924
+ sourceKey,
925
+ expected: "every producer ref to contain a valid Asset GUID",
926
+ actual: reference.guid
927
+ });
928
+ break;
929
+ }
930
+ }
931
+ for (const [artifactKey, artifact] of Object.entries(product.artifacts)) {
932
+ if (artifactKey.length === 0 || artifactKey.startsWith("/") || artifactKey.includes("..") || artifactKey.includes("\\") || artifact.mediaType.trim().length === 0 || !(artifact.bytes instanceof Uint8Array)) {
933
+ mismatches.push({
934
+ sourceKey,
935
+ expected: "asset-local artifacts with safe keys, mediaType, and Uint8Array bytes",
936
+ actual: artifactKey
937
+ });
938
+ break;
939
+ }
940
+ }
941
+ if (mismatches.length === 0) return void 0;
942
+ return {
943
+ code: "pack-source-output-invalid",
944
+ expected: "producer output refs and asset-local artifacts to satisfy the Pack v2 contract",
945
+ hint: "inspect sourceKey and artifact provenance, repair refs or bytes, then rebuild or cold-cook",
946
+ detail: { missingGuids: [], unexpectedSourceKeys: [], kindMismatches: mismatches }
947
+ };
948
+ }
949
+ async function buildScriptablePack(options) {
950
+ const observed = observedAssetReader(options.assetSource);
951
+ let built;
952
+ try {
953
+ const build = options.definition.build;
954
+ built = await build(observed.reader);
955
+ } catch (error) {
956
+ return err(
957
+ new ImportError({
958
+ code: "import-internal-error",
959
+ expected: "ScriptablePack build to return a structured Result without throwing",
960
+ hint: "fix the build implementation and return a structured failure for expected authoring errors",
961
+ detail: { reason: error instanceof Error ? error.message : String(error) }
962
+ })
963
+ );
964
+ }
965
+ if (!built.ok) {
966
+ const remappedBuildFailure = remapBuildFailureSource(built.error, options.sourcePath);
967
+ if (remappedBuildFailure !== void 0) return err(remappedBuildFailure);
968
+ if (built.error instanceof ImportError || built.error instanceof AssetError || isStructuredDomainError(built.error)) {
969
+ return err(built.error);
970
+ }
971
+ return err(
972
+ new ImportError({
973
+ code: "import-internal-error",
974
+ expected: "ScriptablePack build failure to use a structured domain error",
975
+ hint: "return an error with code, expected, hint, and optional detail fields",
976
+ detail: { reason: String(built.error) }
977
+ })
978
+ );
979
+ }
980
+ if (!record(built.value)) {
981
+ return err(
982
+ new ImportError({
983
+ code: "import-internal-error",
984
+ expected: "ScriptablePack build output to be an object keyed by declared sourceKey",
985
+ hint: "return one concrete Asset payload for each declared sourceKey",
986
+ detail: { reason: "build output is not an object" }
987
+ })
988
+ );
989
+ }
990
+ const output = built.value;
991
+ const invalidOutput = outputError(options.definition, output);
992
+ if (invalidOutput !== void 0) return err(invalidOutput);
993
+ const assets = [];
994
+ for (const sourceKey of Object.keys(options.definition.assets).sort()) {
995
+ const descriptor = options.definition.assets[sourceKey];
996
+ const asset = output[sourceKey];
997
+ if (descriptor === void 0) continue;
998
+ if (!isAsset(asset)) {
999
+ return err(
1000
+ new ImportError({
1001
+ code: "import-internal-error",
1002
+ expected: `a concrete Asset payload for sourceKey ${sourceKey}`,
1003
+ hint: "return a durable Asset with a kind from SCRIPTABLE_PACK_ASSET_KINDS",
1004
+ detail: { reason: "build output payload is missing or has an unknown kind" }
1005
+ })
1006
+ );
1007
+ }
1008
+ const producer = options.outputs.get(descriptor.kind);
1009
+ if (producer === void 0) {
1010
+ return err({
1011
+ code: "pack-source-output-invalid",
1012
+ expected: `a domain output producer registered for kind ${descriptor.kind}`,
1013
+ hint: "attach the owning producer capability, inspect registration, then rebuild or cold-cook",
1014
+ detail: {
1015
+ missingGuids: [AssetGuid.format(descriptor.guid)],
1016
+ unexpectedSourceKeys: [],
1017
+ kindMismatches: []
1018
+ }
1019
+ });
1020
+ }
1021
+ const product2 = await producer.produce({
1022
+ guid: AssetGuid.format(descriptor.guid),
1023
+ sourceKey,
1024
+ asset
1025
+ });
1026
+ if (!product2.ok) return err(product2.error);
1027
+ const productError = productContractError(sourceKey, descriptor.kind, product2.value);
1028
+ if (productError !== void 0) return err(productError);
1029
+ assets.push({
1030
+ guid: AssetGuid.format(descriptor.guid),
1031
+ kind: descriptor.kind,
1032
+ ...descriptor.name === void 0 ? {} : { name: descriptor.name },
1033
+ payload: product2.value.payload,
1034
+ refs: product2.value.refs,
1035
+ artifacts: product2.value.artifacts
1036
+ });
1037
+ }
1038
+ const local = new Set(assets.map((asset) => asset.guid.toLowerCase()));
1039
+ const referenced = new Set(
1040
+ assets.flatMap((asset) => asset.refs.map((reference) => reference.guid.toLowerCase())).filter((guid) => !local.has(guid))
1041
+ );
1042
+ const read = new Set([...observed.reads.keys()].map((guid) => guid.toLowerCase()));
1043
+ const declared = new Set(
1044
+ Object.values(options.definition.externalAssets).map(
1045
+ (guid) => AssetGuid.format(guid).toLowerCase()
1046
+ )
1047
+ );
1048
+ const closureError2 = externalClosureError(declared, referenced, read);
1049
+ if (closureError2 !== void 0) return err(closureError2);
1050
+ const externalEvidence = [...declared].sort().map((guid) => {
1051
+ const observedRead = observed.reads.get(guid);
1052
+ const isReference = referenced.has(guid);
1053
+ return {
1054
+ guid,
1055
+ usage: observedRead === void 0 ? "reference" : isReference ? "both" : "content",
1056
+ ...observedRead === void 0 ? {} : { generation: observedRead.generation, digest: observedRead.digest }
1057
+ };
1058
+ });
1059
+ const fingerprintEvidence = externalEvidence.map(({ guid, usage, digest }) => ({
1060
+ guid,
1061
+ usage,
1062
+ ...digest === void 0 ? {} : { digest }
1063
+ }));
1064
+ const inputFingerprint = await fingerprint({
1065
+ meta: projectScriptablePackMeta(options.definition, options.sourcePath),
1066
+ sceneComponents: projectScriptablePackSceneComponents(options.definition.sceneComponents),
1067
+ sourceClosure: stableSourceClosure(options.sourceClosure),
1068
+ externalEvidence: fingerprintEvidence,
1069
+ authoringContractVersion: options.authoringContractVersion,
1070
+ producerVersions: options.outputs.versions()
1071
+ });
1072
+ const refs = assets.flatMap((asset) => asset.refs);
1073
+ const artifacts = Object.fromEntries(
1074
+ assets.flatMap(
1075
+ (asset) => Object.entries(asset.artifacts).map(([key, artifact]) => [`${asset.guid}/${key}`, artifact])
1076
+ )
1077
+ );
1078
+ const product = createImportProduct({
1079
+ assets,
1080
+ sourceDependencies: options.sourceClosure.map((entry) => entry.path),
1081
+ refs,
1082
+ artifacts,
1083
+ receipts: assets.map((asset) => ({
1084
+ guid: asset.guid,
1085
+ origin: "sourceMeta",
1086
+ status: "succeeded",
1087
+ inputFingerprint
1088
+ })),
1089
+ diagnostics: [],
1090
+ sourceRevision: inputFingerprint,
1091
+ sourceKey: options.sourcePath
1092
+ });
1093
+ if (!product.ok) return err(product.error);
1094
+ return ok({
1095
+ product: product.value,
1096
+ stagedOutputs: await Promise.all(
1097
+ Object.keys(options.definition.assets).sort().map(async (sourceKey) => {
1098
+ const descriptor = options.definition.assets[sourceKey];
1099
+ const asset = output[sourceKey];
1100
+ if (descriptor === void 0 || !isAsset(asset)) {
1101
+ throw new Error(`validated ScriptablePack output ${sourceKey} is not an Asset`);
1102
+ }
1103
+ return {
1104
+ guid: descriptor.guid,
1105
+ sourceKey,
1106
+ asset: privateClone(asset),
1107
+ digest: await fingerprint(asset)
1108
+ };
1109
+ })
1110
+ ),
1111
+ externalEvidence,
1112
+ inputFingerprint
1113
+ });
1114
+ }
1115
+
1116
+ // src/scriptable-pack-output-producers.ts
1117
+ function producerError(input, reason) {
1118
+ return new ImportError({
1119
+ code: "import-internal-error",
1120
+ expected: `ScriptablePack ${input.asset.kind} output ${input.sourceKey} to satisfy its domain producer contract`,
1121
+ hint: "fix the generated Asset payload and rebuild the ScriptablePack",
1122
+ detail: { reason: reason instanceof Error ? reason.message : String(reason) }
1123
+ });
1124
+ }
1125
+ function formatGuid(value) {
1126
+ if (typeof value === "string") {
1127
+ const parsed = AssetGuid.parse(value);
1128
+ if (!parsed.ok) throw parsed.error;
1129
+ return AssetGuid.format(parsed.value);
1130
+ }
1131
+ return AssetGuid.format(value);
1132
+ }
1133
+ function canonical(value) {
1134
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
1135
+ if (value !== null && typeof value === "object") {
1136
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`).join(",")}}`;
1137
+ }
1138
+ return JSON.stringify(value) ?? "null";
1139
+ }
1140
+ function particleProgramArtifact(effect) {
1141
+ const artifactProgram = { format: effect.program.format, emitters: effect.program.emitters };
1142
+ const bytes2 = new TextEncoder().encode(canonical(artifactProgram));
1143
+ const fingerprint2 = `sha256:${bytesToHex(sha256(bytes2))}`;
1144
+ return {
1145
+ program: { ...effect.program, fingerprint: fingerprint2 },
1146
+ bytes: bytes2,
1147
+ fingerprint: fingerprint2
1148
+ };
1149
+ }
1150
+ function materialProduct(input) {
1151
+ if (input.asset.kind !== "material") throw new TypeError("expected MaterialAsset");
1152
+ const material = input.asset;
1153
+ const refs = [];
1154
+ const addRef = (guid, sourceField) => {
1155
+ refs.push({ guid: formatGuid(guid), sourceField });
1156
+ return refs.length - 1;
1157
+ };
1158
+ const addMaterialRef = (value, sourceField) => {
1159
+ if (typeof value === "number") {
1160
+ throw new TypeError("material texture references must be GUIDs, not runtime handles");
1161
+ }
1162
+ return addRef(value, sourceField);
1163
+ };
1164
+ const textureFields = material.parameters === void 0 ? new Set(MATERIAL_TEXTURE_SLOTS) : new Set(
1165
+ material.parameters.filter((parameter) => parameter.type === "texture").map((parameter) => parameter.name)
1166
+ );
1167
+ const parent = material.parent === void 0 ? void 0 : addRef(material.parent, { fieldName: "parent" });
1168
+ const values = {};
1169
+ for (const fieldName of Object.keys(material.values ?? {}).sort()) {
1170
+ const value = material.values?.[fieldName];
1171
+ if (typeof value === "string" && textureFields.has(fieldName)) {
1172
+ values[fieldName] = addMaterialRef(value, { componentName: "<material>", fieldName });
1173
+ } else if (value !== null && typeof value === "object" && !Array.isArray(value) && "texture" in value) {
1174
+ const texture = value;
1175
+ values[fieldName] = {
1176
+ ...texture,
1177
+ texture: addMaterialRef(texture.texture, { componentName: "<material>", fieldName }),
1178
+ ...texture.sampler === void 0 ? {} : {
1179
+ sampler: addMaterialRef(texture.sampler, {
1180
+ componentName: "<material>",
1181
+ fieldName: `${fieldName}.sampler`
1182
+ })
1183
+ }
1184
+ };
1185
+ } else {
1186
+ values[fieldName] = value;
1187
+ }
1188
+ }
1189
+ const { parent: _parent, values: _values, ...materialShape } = material;
1190
+ return {
1191
+ payload: {
1192
+ ...materialShape,
1193
+ ...parent === void 0 ? {} : { parent },
1194
+ ...material.values === void 0 ? {} : { values }
1195
+ },
1196
+ refs,
1197
+ artifacts: {}
1198
+ };
1199
+ }
1200
+ function meshProduct(input) {
1201
+ if (input.asset.kind !== "mesh") throw new TypeError("expected MeshAsset");
1202
+ const mesh = input.asset;
1203
+ const refs = [];
1204
+ for (let slotIndex = 0; slotIndex < mesh.materialSlots.length; slotIndex++) {
1205
+ const defaultMaterial = mesh.materialSlots[slotIndex]?.defaultMaterial;
1206
+ if (defaultMaterial === void 0) continue;
1207
+ refs.push({
1208
+ guid: formatGuid(defaultMaterial),
1209
+ sourceField: { fieldName: "materialSlots", arrayIndex: slotIndex }
1210
+ });
1211
+ }
1212
+ return {
1213
+ payload: mesh,
1214
+ refs,
1215
+ artifacts: {
1216
+ body: {
1217
+ mediaType: "application/x-forgeax-mesh",
1218
+ assetCodec: { name: "mesh-binary", version: "4" },
1219
+ bytes: (() => {
1220
+ const packed = packMeshBinV4(
1221
+ mesh,
1222
+ input.sourceKey,
1223
+ refs.map((reference) => reference.guid)
1224
+ );
1225
+ if (!packed.ok) {
1226
+ throw packed.error;
1227
+ }
1228
+ return packed.value;
1229
+ })()
1230
+ }
1231
+ }
1232
+ };
1233
+ }
1234
+ function sceneProduct(input, components) {
1235
+ if (input.asset.kind !== "scene") throw new TypeError("expected SceneAsset");
1236
+ const externalized = externalizeSceneAsset(input.asset, (componentName) => {
1237
+ const schema = components.get(componentName);
1238
+ if (schema === void 0) {
1239
+ throw new TypeError(
1240
+ `ScriptablePack scene component ${componentName} is missing from sceneComponents`
1241
+ );
1242
+ }
1243
+ return schema;
1244
+ });
1245
+ if (!externalized.ok) throw new TypeError(`scene field ${externalized.error.field} is invalid`);
1246
+ return {
1247
+ payload: { kind: "scene", ...externalized.value.payload },
1248
+ refs: externalized.value.refs,
1249
+ artifacts: {}
1250
+ };
1251
+ }
1252
+ function createSafeProducer(kind, version, product) {
1253
+ return {
1254
+ kind,
1255
+ version,
1256
+ produce(input) {
1257
+ try {
1258
+ return ok(product(input));
1259
+ } catch (error) {
1260
+ return err(producerError(input, error));
1261
+ }
1262
+ }
1263
+ };
1264
+ }
1265
+ function jsonArtifact(value) {
1266
+ return {
1267
+ mediaType: "application/json",
1268
+ assetCodec: { name: "forgeax-json", version: "1" },
1269
+ bytes: new TextEncoder().encode(JSON.stringify(value))
1270
+ };
1271
+ }
1272
+ function ref(guid, fieldName, arrayIndex) {
1273
+ return {
1274
+ guid: formatGuid(guid),
1275
+ sourceField: { fieldName, ...arrayIndex === void 0 ? {} : { arrayIndex } }
1276
+ };
1277
+ }
1278
+ function bytes(value) {
1279
+ return Uint8Array.from(value);
1280
+ }
1281
+ function ordinaryPodProduct(input) {
1282
+ const asset = input.asset;
1283
+ switch (asset.kind) {
1284
+ case "texture": {
1285
+ const texture = asset;
1286
+ return {
1287
+ payload: texture,
1288
+ refs: [],
1289
+ artifacts: {
1290
+ body: {
1291
+ mediaType: "image/raw",
1292
+ assetCodec: { name: "raw-image", version: "1" },
1293
+ bytes: bytes(texture.data)
1294
+ }
1295
+ }
1296
+ };
1297
+ }
1298
+ case "equirect": {
1299
+ const equirect = asset;
1300
+ return {
1301
+ payload: equirect,
1302
+ refs: [],
1303
+ artifacts: {
1304
+ body: {
1305
+ mediaType: "image/raw",
1306
+ assetCodec: { name: "raw-image", version: "1" },
1307
+ bytes: bytes(equirect.data)
1308
+ }
1309
+ }
1310
+ };
1311
+ }
1312
+ case "sampler": {
1313
+ const sampler = asset;
1314
+ return { payload: sampler, refs: [], artifacts: { body: jsonArtifact(sampler) } };
1315
+ }
1316
+ case "font": {
1317
+ const font = asset;
1318
+ const atlas = ref(font.atlas, "atlas");
1319
+ const sampler = ref(font.sampler, "sampler");
1320
+ return {
1321
+ payload: {
1322
+ kind: font.kind,
1323
+ glyphs: font.glyphs,
1324
+ common: font.common,
1325
+ atlasGuid: atlas.guid,
1326
+ samplerGuid: sampler.guid
1327
+ },
1328
+ refs: [atlas, sampler],
1329
+ artifacts: { body: jsonArtifact(font) }
1330
+ };
1331
+ }
1332
+ case "render-pipeline": {
1333
+ const pipeline = asset;
1334
+ return {
1335
+ payload: pipeline,
1336
+ refs: [],
1337
+ artifacts: { body: jsonArtifact(pipeline) }
1338
+ };
1339
+ }
1340
+ case "tileset": {
1341
+ const tileset = asset;
1342
+ const refs = tileset.atlases.map((atlas, index) => {
1343
+ const parsed = AssetGuid.parse(atlas);
1344
+ if (!parsed.ok) throw parsed.error;
1345
+ return ref(parsed.value, "atlases", index);
1346
+ });
1347
+ return {
1348
+ payload: { ...tileset, atlases: refs.map((_entry, index) => index) },
1349
+ refs,
1350
+ artifacts: { body: jsonArtifact(tileset) }
1351
+ };
1352
+ }
1353
+ case "video": {
1354
+ const video = asset;
1355
+ try {
1356
+ const url = new URL(video.url);
1357
+ if (url.protocol !== "http:" && url.protocol !== "https:")
1358
+ throw new Error("unsupported URL scheme");
1359
+ } catch (error) {
1360
+ throw new TypeError(
1361
+ `video URL is invalid: ${error instanceof Error ? error.message : String(error)}`
1362
+ );
1363
+ }
1364
+ return { payload: video, refs: [], artifacts: {} };
1365
+ }
1366
+ case "skeleton": {
1367
+ const skeleton = asset;
1368
+ return {
1369
+ payload: skeleton,
1370
+ refs: [],
1371
+ artifacts: { body: jsonArtifact(skeleton) }
1372
+ };
1373
+ }
1374
+ case "skin": {
1375
+ const skin = asset;
1376
+ const skeletonGuid = AssetGuid.parse(skin.skeletonGuid);
1377
+ if (!skeletonGuid.ok) throw skeletonGuid.error;
1378
+ return {
1379
+ payload: skin,
1380
+ refs: [ref(skeletonGuid.value, "skeletonGuid")],
1381
+ artifacts: { body: jsonArtifact(skin) }
1382
+ };
1383
+ }
1384
+ case "animation-clip":
1385
+ return {
1386
+ payload: asset,
1387
+ refs: [],
1388
+ artifacts: { body: jsonArtifact(asset) }
1389
+ };
1390
+ case "animation-graph": {
1391
+ const graph = asset;
1392
+ const refs = [];
1393
+ const nodes = graph.nodes.map((node, index) => {
1394
+ if (node.type !== "clip") return node;
1395
+ const parsed = AssetGuid.parse(node.clip);
1396
+ if (!parsed.ok) throw parsed.error;
1397
+ const referenceIndex = refs.push(ref(parsed.value, "nodes", index)) - 1;
1398
+ return { ...node, clip: referenceIndex };
1399
+ });
1400
+ return {
1401
+ payload: { kind: graph.kind, root: graph.root, nodes },
1402
+ refs,
1403
+ artifacts: { body: jsonArtifact(graph) }
1404
+ };
1405
+ }
1406
+ case "audio": {
1407
+ const audio = asset;
1408
+ return {
1409
+ payload: {
1410
+ kind: audio.kind,
1411
+ sourceKey: audio.sourceKey,
1412
+ mediaType: audio.mediaType,
1413
+ bytes: audio.bytes.slice()
1414
+ },
1415
+ refs: [],
1416
+ artifacts: {
1417
+ source: {
1418
+ mediaType: audio.mediaType,
1419
+ assetCodec: { name: "browser-audio", version: "1" },
1420
+ bytes: audio.bytes.slice()
1421
+ }
1422
+ }
1423
+ };
1424
+ }
1425
+ case "particle-effect": {
1426
+ const effect = asset;
1427
+ const cooked = particleProgramArtifact(effect);
1428
+ return {
1429
+ payload: { ...effect, programFingerprint: cooked.fingerprint, program: cooked.program },
1430
+ refs: [],
1431
+ artifacts: {
1432
+ "particle-effect/program.json": {
1433
+ mediaType: "application/json",
1434
+ assetCodec: { name: "forgeax-vfx-program", version: effect.program.format },
1435
+ bytes: cooked.bytes
1436
+ }
1437
+ }
1438
+ };
1439
+ }
1440
+ case "material":
1441
+ case "mesh":
1442
+ case "scene":
1443
+ throw new TypeError(`ordinary producer received already-owned ${asset.kind} asset`);
1444
+ }
1445
+ }
1446
+ var materialAssetOutputProducer = createSafeProducer(
1447
+ "material",
1448
+ "material-pack/1",
1449
+ materialProduct
1450
+ );
1451
+ var meshAssetOutputProducer = createSafeProducer("mesh", "mesh-binary/4", meshProduct);
1452
+ function createSceneAssetOutputProducer(sceneComponents = []) {
1453
+ const schemas = new Map(
1454
+ sceneComponents.map((component) => [component.name, component.fields])
1455
+ );
1456
+ return createSafeProducer("scene", "scene-pack/3", (input) => sceneProduct(input, schemas));
1457
+ }
1458
+ function createStandardAssetOutputProducerRegistry(sceneComponents = []) {
1459
+ const registry = new AssetOutputProducerRegistry();
1460
+ registry.register(materialAssetOutputProducer);
1461
+ registry.register(meshAssetOutputProducer);
1462
+ registry.register(createSceneAssetOutputProducer(sceneComponents));
1463
+ for (const kind of [
1464
+ "texture",
1465
+ "equirect",
1466
+ "sampler",
1467
+ "font",
1468
+ "render-pipeline",
1469
+ "tileset",
1470
+ "video",
1471
+ "skeleton",
1472
+ "skin",
1473
+ "animation-clip",
1474
+ "animation-graph",
1475
+ "audio",
1476
+ "particle-effect"
1477
+ ]) {
1478
+ registry.register(createSafeProducer(kind, "ordinary-pod/1", ordinaryPodProduct));
1479
+ }
1480
+ return registry;
1481
+ }
1482
+ function stable2(value) {
1483
+ if (ArrayBuffer.isView(value)) {
1484
+ return `${value.constructor.name}:${JSON.stringify(Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)))}`;
1485
+ }
1486
+ if (Array.isArray(value)) return `[${value.map(stable2).join(",")}]`;
1487
+ if (value !== null && typeof value === "object") {
1488
+ const record2 = value;
1489
+ return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stable2(record2[key])}`).join(",")}}`;
1490
+ }
1491
+ return JSON.stringify(value) ?? "null";
1492
+ }
1493
+ async function assetDigest(asset) {
1494
+ const bytes2 = new TextEncoder().encode(stable2(asset));
1495
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes2);
1496
+ return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
1497
+ }
1498
+ function cycleError(stack, owner, guid) {
1499
+ const cycleStart = stack.indexOf(owner);
1500
+ const cycle = [...stack.slice(cycleStart), owner];
1501
+ return {
1502
+ code: "pack-source-build-cycle",
1503
+ expected: "an acyclic graph of ScriptablePack content dependencies",
1504
+ actual: cycle.join(" -> "),
1505
+ hint: "break one content read edge or convert it to a reference-only dependency",
1506
+ retryable: false,
1507
+ recoveryActions: ["inspect-content-dependency-graph", "edit-source"],
1508
+ detail: { sourcePath: owner, sourceKey: guid, incomingRefs: cycle }
1509
+ };
1510
+ }
1511
+ function missingOutputError(owner, guid) {
1512
+ return {
1513
+ code: "pack-source-output-invalid",
1514
+ expected: `owner ${owner} to stage every declared output including ${guid}`,
1515
+ hint: "return one output for every GUID declared by the staged owner",
1516
+ detail: { missingGuids: [guid], unexpectedSourceKeys: [], kindMismatches: [] }
1517
+ };
1518
+ }
1519
+ function createScriptablePackStagedAssetSnapshotSource(options) {
1520
+ const owners = options.declaredExternalOutputs === void 0 || options.declaredExternalOutputs.length === 0 ? options.owners : [
1521
+ {
1522
+ id: "<declared-pack-external>",
1523
+ guids: options.declaredExternalOutputs.map((output) => output.guid),
1524
+ async build() {
1525
+ return ok(options.declaredExternalOutputs ?? []);
1526
+ }
1527
+ },
1528
+ ...options.owners
1529
+ ];
1530
+ const ownerByGuid = /* @__PURE__ */ new Map();
1531
+ for (const owner of owners) {
1532
+ if (owner.id.trim().length === 0) throw new TypeError("staged owner id must be non-empty");
1533
+ for (const guid of owner.guids) {
1534
+ const key = AssetGuid.format(guid).toLowerCase();
1535
+ const existing = ownerByGuid.get(key);
1536
+ if (existing !== void 0) {
1537
+ throw new TypeError(`staged GUID ${key} is owned by both ${existing.id} and ${owner.id}`);
1538
+ }
1539
+ ownerByGuid.set(key, owner);
1540
+ }
1541
+ }
1542
+ const snapshots = /* @__PURE__ */ new Map();
1543
+ const builds = /* @__PURE__ */ new Map();
1544
+ const sourceFor = (stack) => ({
1545
+ async readByGuid(guid) {
1546
+ const key = AssetGuid.format(guid).toLowerCase();
1547
+ const cached = snapshots.get(key);
1548
+ if (cached !== void 0) return ok(structuredClone(cached));
1549
+ const owner = ownerByGuid.get(key);
1550
+ if (owner === void 0) {
1551
+ return err(
1552
+ new AssetError({
1553
+ code: "asset-not-imported",
1554
+ expected: "a staged local owner for the requested GUID",
1555
+ hint: "declare the local ScriptablePack output before rebuilding the generation"
1556
+ })
1557
+ );
1558
+ }
1559
+ if (stack.includes(owner.id)) return err(cycleError(stack, owner.id, key));
1560
+ let building = builds.get(owner.id);
1561
+ if (building === void 0) {
1562
+ building = (async () => {
1563
+ const built2 = await owner.build(sourceFor([...stack, owner.id]));
1564
+ if (!built2.ok) {
1565
+ builds.delete(owner.id);
1566
+ return built2;
1567
+ }
1568
+ const next = /* @__PURE__ */ new Map();
1569
+ for (const output of built2.value) {
1570
+ const outputGuid = AssetGuid.format(output.guid).toLowerCase();
1571
+ if (ownerByGuid.get(outputGuid) !== owner) {
1572
+ builds.delete(owner.id);
1573
+ return err(missingOutputError(owner.id, outputGuid));
1574
+ }
1575
+ next.set(outputGuid, {
1576
+ asset: structuredClone(output.asset),
1577
+ generation: options.generation,
1578
+ digest: output.digest ?? await assetDigest(output.asset)
1579
+ });
1580
+ }
1581
+ for (const declared of owner.guids) {
1582
+ const declaredGuid = AssetGuid.format(declared).toLowerCase();
1583
+ if (!next.has(declaredGuid)) {
1584
+ builds.delete(owner.id);
1585
+ return err(missingOutputError(owner.id, declaredGuid));
1586
+ }
1587
+ }
1588
+ for (const [outputGuid, snapshot] of next) snapshots.set(outputGuid, snapshot);
1589
+ return ok(void 0);
1590
+ })();
1591
+ builds.set(owner.id, building);
1592
+ }
1593
+ const built = await building;
1594
+ if (!built.ok) return built;
1595
+ const staged = snapshots.get(key);
1596
+ return staged === void 0 ? err(missingOutputError(owner.id, key)) : ok(structuredClone(staged));
1597
+ }
1598
+ });
1599
+ return sourceFor([]);
1600
+ }
1601
+ var META_ARTIFACT_KEY = "scriptable-pack.meta.json";
1602
+ function withPrebuiltMeta(product, anchorGuid, meta) {
1603
+ const bytes2 = new TextEncoder().encode(`${JSON.stringify(meta)}
1604
+ `);
1605
+ return {
1606
+ ...product,
1607
+ assets: product.assets.map(
1608
+ (asset) => asset.guid.toLowerCase() === anchorGuid ? {
1609
+ ...asset,
1610
+ artifacts: {
1611
+ ...asset.artifacts,
1612
+ [META_ARTIFACT_KEY]: { mediaType: "application/json", bytes: bytes2 }
1613
+ }
1614
+ } : asset
1615
+ )
1616
+ };
1617
+ }
1618
+ async function produceScriptableSourcePackage(options) {
1619
+ const built = await buildScriptablePack(options);
1620
+ if (!built.ok) return err(built.error);
1621
+ const declaredGuids = Object.values(options.definition.assets).map((descriptor) => AssetGuid.format(descriptor.guid).toLowerCase()).sort();
1622
+ const anchorGuid = declaredGuids[0];
1623
+ if (anchorGuid === void 0) {
1624
+ return err({
1625
+ code: "pack-source-output-invalid",
1626
+ expected: "at least one declared ScriptablePack output",
1627
+ hint: "add an output descriptor before building the package",
1628
+ detail: { missingGuids: [], unexpectedSourceKeys: [], kindMismatches: [] }
1629
+ });
1630
+ }
1631
+ const product = withPrebuiltMeta(
1632
+ built.value.product,
1633
+ anchorGuid,
1634
+ projectScriptablePackMeta(options.definition, options.sourcePath)
1635
+ );
1636
+ return ok({
1637
+ anchorGuid,
1638
+ declaredGuids,
1639
+ product,
1640
+ stagedOutputs: built.value.stagedOutputs,
1641
+ inputFingerprint: built.value.inputFingerprint,
1642
+ externalEvidence: built.value.externalEvidence
1643
+ });
1644
+ }
1645
+ function parseProducerReadiness(value) {
1646
+ if (value === void 0 || value === "before-consume" || value === "on-demand") {
1647
+ return { ok: true, value: value ?? "before-consume" };
1648
+ }
1649
+ return {
1650
+ ok: false,
1651
+ error: {
1652
+ code: "producer-readiness-invalid",
1653
+ expected: "'before-consume' or 'on-demand'",
1654
+ hint: "set producerReadiness to before-consume or on-demand",
1655
+ detail: { value }
1656
+ }
1657
+ };
1658
+ }
1659
+ function finalizeSourcePackage(product, finalizer, artifactUrl) {
1660
+ if (finalizer === void 0) return ok(product);
1661
+ const result = finalizer(product, { artifactUrl });
1662
+ if (!result.ok) return err(result.error);
1663
+ return ok({
1664
+ ...product,
1665
+ assets: product.assets.map(
1666
+ (asset, index) => index === 0 ? { ...asset, payload: result.value.asset } : asset
1667
+ )
1668
+ });
1669
+ }
1670
+ function closureError(declaredGuids, producedGuids) {
1671
+ const declared = new Set(declaredGuids.map((guid) => guid.toLowerCase()));
1672
+ const produced = new Set(producedGuids.map((guid) => guid.toLowerCase()));
1673
+ const counts = /* @__PURE__ */ new Map();
1674
+ for (const guid of producedGuids) {
1675
+ const key = guid.toLowerCase();
1676
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1677
+ }
1678
+ const duplicateGuids = [...counts.entries()].filter(([, count]) => count > 1).map(([guid]) => guid);
1679
+ return {
1680
+ code: "source-package-guid-closure-mismatch",
1681
+ expected: "the importer to return exactly one asset for every declared Meta GUID",
1682
+ hint: "repair the Meta declaration or importer output, then rebuild the whole source package",
1683
+ detail: {
1684
+ stage: "closure",
1685
+ declaredGuids,
1686
+ producedGuids,
1687
+ missingGuids: declaredGuids.filter((guid) => !produced.has(guid.toLowerCase())),
1688
+ unexpectedGuids: producedGuids.filter((guid) => !declared.has(guid.toLowerCase())),
1689
+ duplicateGuids
1690
+ }
1691
+ };
1692
+ }
1693
+ function producedGuidsFromImportFailure(declaredGuids, detail) {
1694
+ if ("missingGuids" in detail) {
1695
+ return declaredGuids.filter((guid) => !detail.missingGuids.includes(guid));
1696
+ }
1697
+ if ("unexpectedGuids" in detail) return [...declaredGuids, ...detail.unexpectedGuids];
1698
+ return [];
1699
+ }
1700
+ async function produceSourcePackage(input) {
1701
+ const declaredGuids = input.meta.subAssets.map((asset) => asset.guid);
1702
+ const result = await runImport(input.meta, input.registry, input.fs);
1703
+ if (!result.ok) {
1704
+ const producedGuids2 = producedGuidsFromImportFailure(declaredGuids, result.error.detail);
1705
+ return {
1706
+ ok: false,
1707
+ error: closureError(declaredGuids, producedGuids2)
1708
+ };
1709
+ }
1710
+ if ("skipped" in result.value) {
1711
+ return { ok: false, error: closureError(declaredGuids, []) };
1712
+ }
1713
+ const producedGuids = result.value.product.assets.map((asset) => asset.guid);
1714
+ const declaredSet = new Set(declaredGuids.map((guid) => guid.toLowerCase()));
1715
+ const producedSet = new Set(producedGuids.map((guid) => guid.toLowerCase()));
1716
+ const hasDuplicate = new Set(producedGuids.map((guid) => guid.toLowerCase())).size !== producedGuids.length;
1717
+ const complete = !hasDuplicate && declaredGuids.length === producedGuids.length && declaredGuids.every((guid) => producedSet.has(guid.toLowerCase())) && producedGuids.every((guid) => declaredSet.has(guid.toLowerCase()));
1718
+ if (!complete) return { ok: false, error: closureError(declaredGuids, producedGuids) };
1719
+ const product = result.value.product;
1720
+ const anchorGuid = [...declaredGuids].sort((left, right) => left.localeCompare(right))[0];
1721
+ if (anchorGuid === void 0)
1722
+ return { ok: false, error: closureError(declaredGuids, producedGuids) };
1723
+ return {
1724
+ ok: true,
1725
+ value: {
1726
+ anchorGuid,
1727
+ declaredGuids,
1728
+ product
1729
+ }
1730
+ };
1731
+ }
1732
+ function sourcePackageAssetsByGuid(source) {
1733
+ return new Map(source.product.assets.map((asset) => [asset.guid.toLowerCase(), asset]));
1734
+ }
1735
+
1736
+ // src/scriptable-pack-host.ts
1737
+ function canonicalScriptableSourcePath(sourcePath) {
1738
+ const normalized = sourcePath.replaceAll("\\", "/");
1739
+ const marker = "/assets/";
1740
+ const markerIndex = normalized.indexOf(marker);
1741
+ return markerIndex < 0 ? normalized : normalized.slice(markerIndex + 1);
1742
+ }
1743
+ function scriptablePackInputs(sourcePaths, declarations, cwd, generationFor, policyFor) {
1744
+ return sourcePaths.flatMap((sourcePath) => {
1745
+ const declaration = declarations.get(resolve(cwd, sourcePath));
1746
+ if (declaration?.format !== "pack.ts") return [];
1747
+ const displaySourcePath = canonicalScriptableSourcePath(sourcePath);
1748
+ return [
1749
+ {
1750
+ sourcePath: resolve(cwd, sourcePath),
1751
+ displaySourcePath,
1752
+ definition: declaration.definition,
1753
+ sourceClosure: declaration.sourceClosure,
1754
+ publicationGeneration: generationFor(displaySourcePath),
1755
+ policy: policyFor(displaySourcePath)
1756
+ }
1757
+ ];
1758
+ });
1759
+ }
1760
+ async function materializePreparedScriptablePack(prepared, paths, sink) {
1761
+ const { product, finalized } = prepared;
1762
+ await sink.writePackage(paths.packagePath, JSON.stringify(finalized.pack));
1763
+ for (const artifact of finalized.artifacts) {
1764
+ await sink.writeArtifact(
1765
+ paths.artifactPath(artifact.path),
1766
+ artifact.bytes,
1767
+ artifact.path.endsWith(".json") ? "application/json" : artifact.mediaType
1768
+ );
1769
+ }
1770
+ const receipts = /* @__PURE__ */ new Map();
1771
+ for (const guid of product.declaredGuids) {
1772
+ const path = paths.receiptPath(guid);
1773
+ await sink.writeReceipt(
1774
+ path,
1775
+ JSON.stringify({
1776
+ guid,
1777
+ origin: "sourceMeta",
1778
+ status: "succeeded",
1779
+ inputFingerprint: product.inputFingerprint,
1780
+ outputDigest: finalized.digest
1781
+ })
1782
+ );
1783
+ receipts.set(guid, path);
1784
+ }
1785
+ return receipts;
1786
+ }
1787
+ async function readCookedAuthoredPack(authoredPack, cookers = [], sourcePath) {
1788
+ const parsed = upgradeLegacyAuthoredPack(authoredPack);
1789
+ if (parsed.schemaVersion !== "2.0.0" || parsed.assets === void 0) return void 0;
1790
+ const registry = new NativeCookerRegistry();
1791
+ for (const cooker of cookers) registry.register(cooker);
1792
+ const assets = [];
1793
+ const refsByGuid = /* @__PURE__ */ new Map();
1794
+ let hasCookedAsset = false;
1795
+ for (const asset of parsed.assets) {
1796
+ const shouldCook = asset.execution === "cooked";
1797
+ if (!shouldCook) {
1798
+ assets.push({
1799
+ guid: asset.guid,
1800
+ kind: asset.kind,
1801
+ ...asset.name === void 0 ? {} : { name: asset.name },
1802
+ ...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey },
1803
+ ...asset.sourceIndex === void 0 ? {} : { sourceIndex: asset.sourceIndex },
1804
+ ...asset.relations === void 0 ? {} : { relations: asset.relations },
1805
+ payload: asset.payload,
1806
+ refs: asset.refs ?? [],
1807
+ artifacts: {}
1808
+ });
1809
+ continue;
1810
+ }
1811
+ hasCookedAsset = true;
1812
+ const result = await registry.runDraft(asset.kind, {
1813
+ guid: asset.guid,
1814
+ source: asset.payload,
1815
+ ...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey },
1816
+ ...sourcePath === void 0 ? {} : { sourcePath },
1817
+ refs: asset.refs ?? []
1818
+ });
1819
+ if (!result.ok) throw result.error;
1820
+ const draft = result.value;
1821
+ const refs = [...draft.refs];
1822
+ refsByGuid.set(asset.guid.toLowerCase(), refs);
1823
+ assets.push({
1824
+ guid: draft.guid,
1825
+ kind: asset.kind,
1826
+ ...asset.name === void 0 ? {} : { name: asset.name },
1827
+ ...asset.sourceKey === void 0 ? {} : { sourceKey: asset.sourceKey },
1828
+ ...asset.sourceIndex === void 0 ? {} : { sourceIndex: asset.sourceIndex },
1829
+ ...asset.relations === void 0 ? {} : { relations: asset.relations },
1830
+ payload: draft.payload,
1831
+ refs,
1832
+ artifacts: draft.artifacts
1833
+ });
1834
+ }
1835
+ return hasCookedAsset ? {
1836
+ logicalPackage: { schemaVersion: "2.0.0", kind: "internal-text-package", assets },
1837
+ refsByGuid
1838
+ } : void 0;
1839
+ }
1840
+ async function declaredPackExternalOutputs(declarations, cookers = [], requiredGuids = [], externalImport) {
1841
+ if (requiredGuids.length === 0) return [];
1842
+ const required = new Set(requiredGuids.map((guid) => AssetGuid.format(guid).toLowerCase()));
1843
+ const outputs = [];
1844
+ for (const declaration of declarations.values()) {
1845
+ if (declaration.format === "meta.json") {
1846
+ if (externalImport === void 0) continue;
1847
+ if (!declaration.value.subAssets.some((asset) => required.has(asset.guid.toLowerCase()))) {
1848
+ continue;
1849
+ }
1850
+ const assetPaths = externalImport.assetPaths ?? loadAssetConfig(process.cwd()).paths;
1851
+ const resolved = resolveAssetSource(
1852
+ declaration.sourcePath,
1853
+ declaration.value.source,
1854
+ assetPaths
1855
+ );
1856
+ if (!resolved.ok) {
1857
+ throw new AssetError({
1858
+ code: "asset-not-imported",
1859
+ expected: `a resolvable source for Meta dependency ${declaration.sourcePath}`,
1860
+ hint: "repair the Meta source path before rebuilding the ScriptablePack"
1861
+ });
1862
+ }
1863
+ const meta = {
1864
+ importer: declaration.value.importer,
1865
+ source: resolved.value,
1866
+ sourceRevision: declaration.sourceRevision,
1867
+ ...declaration.value.packageId === void 0 ? {} : { packageId: declaration.value.packageId },
1868
+ ...declaration.value.provenance === void 0 ? {} : { provenance: declaration.value.provenance },
1869
+ ...declaration.value.revision === void 0 ? {} : { revision: declaration.value.revision },
1870
+ ...declaration.value.diagnostics === void 0 ? {} : { diagnostics: declaration.value.diagnostics },
1871
+ importSettings: declaration.value.importSettings,
1872
+ ...declaration.value.sourceOverrides === void 0 ? {} : { sourceOverrides: declaration.value.sourceOverrides },
1873
+ subAssets: declaration.value.subAssets.map(({ guid, sourceIndex, sourceKey, kind }) => ({
1874
+ guid,
1875
+ sourceIndex,
1876
+ ...sourceKey === void 0 ? {} : { sourceKey },
1877
+ kind
1878
+ })),
1879
+ buildPack: false
1880
+ };
1881
+ const sourcePackage = await produceSourcePackage({
1882
+ meta,
1883
+ registry: externalImport.importerRegistry,
1884
+ fs: externalImport.fsForImport
1885
+ });
1886
+ if (!sourcePackage.ok) {
1887
+ throw new AssetError({
1888
+ code: "asset-not-imported",
1889
+ expected: `the ${declaration.value.importer} importer to produce Meta dependency outputs`,
1890
+ hint: `repair ${declaration.sourcePath} before rebuilding the ScriptablePack`
1891
+ });
1892
+ }
1893
+ for (const asset of sourcePackage.value.product.assets) {
1894
+ const key = asset.guid.toLowerCase();
1895
+ if (!required.has(key)) continue;
1896
+ const parsed = AssetGuid.parse(asset.guid);
1897
+ if (!parsed.ok) throw parsed.error;
1898
+ outputs.push({
1899
+ guid: parsed.value,
1900
+ asset: { kind: asset.kind, ...asset.payload }
1901
+ });
1902
+ }
1903
+ continue;
1904
+ }
1905
+ if (declaration.format !== "pack.json") continue;
1906
+ const declaredAssets = declaration.value.assets.filter(
1907
+ (asset) => required.has(asset.guid.toLowerCase())
1908
+ );
1909
+ if (declaredAssets.length === 0) continue;
1910
+ const cooked = await readCookedAuthoredPack(declaration.value, cookers, declaration.sourcePath);
1911
+ for (const asset of cooked?.logicalPackage.assets.filter(
1912
+ (item) => required.has(item.guid.toLowerCase())
1913
+ ) ?? declaredAssets) {
1914
+ const parsed = AssetGuid.parse(asset.guid);
1915
+ if (!parsed.ok) throw parsed.error;
1916
+ outputs.push({ guid: parsed.value, asset: { kind: asset.kind, ...asset.payload } });
1917
+ }
1918
+ }
1919
+ return outputs;
1920
+ }
1921
+ async function prepareAuthoredPackTransport(authoredPack, cookers, policyFor, sourcePath) {
1922
+ const pack = upgradeLegacyAuthoredPack(authoredPack);
1923
+ const firstGuid = pack.assets?.[0]?.guid?.toLowerCase();
1924
+ if (pack.schemaVersion !== "2.0.0" || firstGuid === void 0) {
1925
+ return { pack, ...firstGuid === void 0 ? {} : { firstGuid } };
1926
+ }
1927
+ const cooked = await readCookedAuthoredPack(pack, cookers, sourcePath);
1928
+ if (cooked === void 0) return { pack, firstGuid };
1929
+ return {
1930
+ pack,
1931
+ firstGuid,
1932
+ cooked,
1933
+ finalized: await finalizePackageTransportSource(cooked.logicalPackage, policyFor(firstGuid))
1934
+ };
1935
+ }
1936
+ function projectScriptablePackPublication(product) {
1937
+ const assets = new Map(product.product.assets.map((asset) => [asset.guid.toLowerCase(), asset]));
1938
+ const outputs = [];
1939
+ for (const staged of product.stagedOutputs) {
1940
+ const guid = AssetGuid.format(staged.guid).toLowerCase();
1941
+ const asset = assets.get(guid);
1942
+ if (asset === void 0 || staged.digest === void 0) {
1943
+ return err({
1944
+ code: "pack-source-output-invalid",
1945
+ expected: `ScriptablePack publication output ${guid} to include a product and digest`,
1946
+ hint: "repair the ScriptablePack producer output and rebuild",
1947
+ detail: { stage: "publication", guid }
1948
+ });
1949
+ }
1950
+ outputs.push({
1951
+ guid,
1952
+ sourceKey: staged.sourceKey ?? guid,
1953
+ kind: asset.kind,
1954
+ digest: staged.digest,
1955
+ refs: asset.refs.map((reference) => reference.guid)
1956
+ });
1957
+ }
1958
+ return ok({
1959
+ outputs,
1960
+ refs: new Map(
1961
+ product.product.assets.map((asset) => [
1962
+ asset.guid.toLowerCase(),
1963
+ asset.refs.map((reference) => reference.guid)
1964
+ ])
1965
+ )
1966
+ });
1967
+ }
1968
+ async function scriptablePackResourceRevision(displaySourcePath, digest, sourceClosure) {
1969
+ const observedAt = Math.trunc(
1970
+ Math.max(
1971
+ ...await Promise.all(sourceClosure.map(async (entry) => (await stat(entry.path)).mtimeMs))
1972
+ )
1973
+ );
1974
+ return { digest, observedAt, rootId: displaySourcePath };
1975
+ }
1976
+ async function produceScriptablePackProducts(sources, declaredExternalOutputs = []) {
1977
+ const entries = /* @__PURE__ */ new Map();
1978
+ for (const source of sources) {
1979
+ if (entries.has(source.displaySourcePath)) continue;
1980
+ entries.set(source.displaySourcePath, {
1981
+ source,
1982
+ definition: source.definition,
1983
+ closure: source.sourceClosure
1984
+ });
1985
+ }
1986
+ const products = /* @__PURE__ */ new Map();
1987
+ const owners = [...entries.entries()].map(
1988
+ ([displaySourcePath, entry]) => ({
1989
+ id: displaySourcePath,
1990
+ guids: Object.values(entry.definition.assets).map((asset) => asset.guid),
1991
+ async build(source) {
1992
+ const produced = await produceScriptableSourcePackage({
1993
+ definition: entry.definition,
1994
+ sourcePath: displaySourcePath,
1995
+ assetSource: source,
1996
+ outputs: createStandardAssetOutputProducerRegistry(
1997
+ projectScriptablePackSceneComponents(entry.definition.sceneComponents)
1998
+ ),
1999
+ sourceClosure: entry.closure,
2000
+ authoringContractVersion: "scriptable-pack-production/1"
2001
+ });
2002
+ if (!produced.ok) return produced;
2003
+ const observedClosure = await inventoryScriptablePackSource(entry.source.sourcePath);
2004
+ if (JSON.stringify(observedClosure) !== JSON.stringify(entry.closure)) {
2005
+ return err({
2006
+ code: "pack-source-load-failed",
2007
+ expected: "the complete ScriptablePack module closure to remain fixed during one build",
2008
+ hint: "retry the build after source and helper writes have settled",
2009
+ detail: {
2010
+ sourcePath: displaySourcePath,
2011
+ reason: "source-changed",
2012
+ phase: "build",
2013
+ diagnostic: "module closure changed while the staged generation was building"
2014
+ }
2015
+ });
2016
+ }
2017
+ products.set(displaySourcePath, produced.value);
2018
+ return ok(produced.value.stagedOutputs);
2019
+ }
2020
+ })
2021
+ );
2022
+ const generationDigest = createHash("sha256").update(
2023
+ JSON.stringify(
2024
+ [...entries.values()].flatMap((entry) => entry.closure).sort((a, b) => a.path.localeCompare(b.path))
2025
+ )
2026
+ ).digest();
2027
+ const stagedSource = createScriptablePackStagedAssetSnapshotSource({
2028
+ generation: generationDigest.readUInt32BE(0),
2029
+ owners,
2030
+ declaredExternalOutputs
2031
+ });
2032
+ const prepared = /* @__PURE__ */ new Map();
2033
+ for (const source of sources) {
2034
+ const entry = entries.get(source.displaySourcePath);
2035
+ if (entry === void 0) {
2036
+ return err({
2037
+ code: "pack-source-path-invalid",
2038
+ expected: "a ScriptablePack source registered in the current inventory",
2039
+ actual: source.displaySourcePath,
2040
+ hint: "rebuild the source inventory before producing this path",
2041
+ retryable: true,
2042
+ recoveryActions: ["rebuild-source-inventory"],
2043
+ detail: { sourcePath: source.displaySourcePath }
2044
+ });
2045
+ }
2046
+ const first = Object.values(entry.definition.assets)[0];
2047
+ if (first === void 0) {
2048
+ return err({
2049
+ code: "pack-source-output-invalid",
2050
+ expected: "at least one declared ScriptablePack output",
2051
+ hint: "add an output descriptor before building the package",
2052
+ detail: { missingGuids: [], unexpectedSourceKeys: [], kindMismatches: [] }
2053
+ });
2054
+ }
2055
+ const staged = await stagedSource.readByGuid(first.guid);
2056
+ if (!staged.ok) return err(staged.error);
2057
+ const produced = products.get(source.displaySourcePath);
2058
+ if (produced === void 0) {
2059
+ return err({
2060
+ code: "pack-source-output-invalid",
2061
+ expected: "the staged owner build to retain its source-package product",
2062
+ hint: "retry the ScriptablePack production attempt",
2063
+ detail: {
2064
+ missingGuids: [AssetGuid.format(first.guid)],
2065
+ unexpectedSourceKeys: [],
2066
+ kindMismatches: []
2067
+ }
2068
+ });
2069
+ }
2070
+ const transportPolicy = typeof source.policy === "function" ? source.policy(produced) : source.policy;
2071
+ const logicalPackage = projectImportProductForBuild(produced.product);
2072
+ const finalized = await finalizePackageTransportSource(logicalPackage, transportPolicy);
2073
+ const facts = projectScriptablePackPublication(produced);
2074
+ if (!facts.ok) return err(facts.error);
2075
+ const revision = await scriptablePackResourceRevision(
2076
+ source.displaySourcePath,
2077
+ produced.inputFingerprint,
2078
+ source.sourceClosure
2079
+ );
2080
+ const publication = createAcceptedPublication({
2081
+ sourcePath: source.displaySourcePath,
2082
+ sourceRevision: produced.inputFingerprint,
2083
+ generation: source.publicationGeneration,
2084
+ digest: finalized.digest,
2085
+ packageUrl: finalized.packageUrl,
2086
+ inputFingerprint: produced.inputFingerprint,
2087
+ outputs: facts.value.outputs,
2088
+ externalEvidence: produced.externalEvidence
2089
+ });
2090
+ prepared.set(source.displaySourcePath, {
2091
+ product: produced,
2092
+ finalized,
2093
+ facts: facts.value,
2094
+ revision,
2095
+ publication
2096
+ });
2097
+ }
2098
+ return ok(prepared);
2099
+ }
2100
+
2101
+ // src/build-production.ts
2102
+ function scriptableArtifactPath(guid, key) {
2103
+ return `${guid.toLowerCase()}/${key.includes(".") ? key : `${key}.bin`}`;
2104
+ }
2105
+ function runtimePublicationFor(context, input) {
2106
+ return createRuntimePackPublication({
2107
+ pack: input.pack,
2108
+ scopeId: context.runtimeBinding?.scopeId ?? "asset-runtime",
2109
+ sourcePath: input.sourcePath,
2110
+ sourceRevision: input.sourceRevision,
2111
+ packageUrl: input.packageUrl,
2112
+ ...input.digest === void 0 ? {} : { digest: input.digest },
2113
+ generation: context.runtimeBinding?.generation ?? context.generation
2114
+ });
2115
+ }
2116
+ async function buildAuthoredPackages(context) {
2117
+ const scriptableSources = [
2118
+ ...new Set(
2119
+ context.inventory.entries.filter((entry) => entry.sourcePath.endsWith(".pack.ts")).map((entry) => entry.sourcePath)
2120
+ )
2121
+ ];
2122
+ if (scriptableSources.length === 0) return /* @__PURE__ */ new Map();
2123
+ const inputs = scriptablePackInputs(
2124
+ scriptableSources,
2125
+ context.inventory.sourceDeclarations,
2126
+ context.cwd,
2127
+ () => context.generation,
2128
+ () => (product) => ({
2129
+ base: context.basePrefix === "" ? "/" : context.basePrefix,
2130
+ packagePath: `assets/${product.anchorGuid}.pack.json`,
2131
+ artifactPath: scriptableArtifactPath
2132
+ })
2133
+ );
2134
+ const preparedResult = await produceScriptablePackProducts(
2135
+ inputs,
2136
+ await declaredPackExternalOutputs(
2137
+ context.inventory.sourceDeclarations,
2138
+ context.cookers,
2139
+ inputs.flatMap((input) => Object.values(input.definition.externalAssets)),
2140
+ {
2141
+ importerRegistry: context.importerRegistry,
2142
+ fsForImport: context.fsForImport,
2143
+ assetPaths: loadAssetConfig(context.cwd).paths
2144
+ }
2145
+ )
2146
+ );
2147
+ if (!preparedResult.ok) throw context.fail(preparedResult.error);
2148
+ const result = /* @__PURE__ */ new Map();
2149
+ for (const entry of context.inventory.entries) {
2150
+ if (!entry.sourcePath.endsWith(".pack.ts") || result.has(entry.sourcePath)) continue;
2151
+ const prepared = preparedResult.value.get(canonicalScriptableSourcePath(entry.sourcePath));
2152
+ if (prepared === void 0) {
2153
+ throw context.fail({
2154
+ code: "catalog-declaration-missing",
2155
+ expected: "the ScriptablePack inventory to retain every authored Pack source",
2156
+ hint: "rerun the source inventory and rebuild from the accepted declaration set",
2157
+ detail: { stage: "scan", sourcePath: entry.sourcePath }
2158
+ });
2159
+ }
2160
+ const { product, finalized, facts, revision, publication } = prepared;
2161
+ const packagePath = `assets/${product.anchorGuid}.pack.json`;
2162
+ const runtimePublication = createRuntimePackPublication({
2163
+ pack: { assets: finalized.pack.assets },
2164
+ scopeId: context.runtimeBinding?.scopeId ?? "asset-runtime",
2165
+ sourcePath: publication.sourcePath,
2166
+ sourceRevision: publication.sourceRevision,
2167
+ packageUrl: finalized.packageUrl,
2168
+ inputFingerprint: publication.receipt.inputFingerprint,
2169
+ digest: finalized.digest,
2170
+ generation: context.runtimeBinding?.generation ?? publication.generation,
2171
+ outputs: facts.outputs,
2172
+ externalEvidence: publication.externalEvidence
2173
+ });
2174
+ const receiptPaths = await materializePreparedScriptablePack(
2175
+ prepared,
2176
+ {
2177
+ packagePath,
2178
+ artifactPath: (path) => `assets/${path}`,
2179
+ receiptPath: (guid) => `assets/${guid}.receipt.json`
2180
+ },
2181
+ {
2182
+ writePackage: (path) => {
2183
+ context.sink.emitFile({
2184
+ type: "asset",
2185
+ fileName: path,
2186
+ originalFileName: `${context.cwd}/${entry.sourcePath}`,
2187
+ source: JSON.stringify(runtimePublication.pack)
2188
+ });
2189
+ },
2190
+ writeArtifact: (path, bytes2) => {
2191
+ context.sink.emitFile({ type: "asset", fileName: path, source: bytes2 });
2192
+ },
2193
+ writeReceipt: (path, source) => {
2194
+ context.sink.emitFile({ type: "asset", fileName: path, source });
2195
+ }
2196
+ }
2197
+ );
2198
+ result.set(entry.sourcePath, {
2199
+ packageUrl: finalized.packageUrl,
2200
+ receiptUrls: new Map(
2201
+ [...receiptPaths].map(([guid, path]) => [guid, context.sink.fileUrl(path)])
2202
+ ),
2203
+ revision,
2204
+ publication: runtimePublication.publication,
2205
+ refs: facts.refs
2206
+ });
2207
+ }
2208
+ return result;
2209
+ }
2210
+ function updateImportedEntries(entries, guids, packageUrl, projection = {}, refsByGuid, publication) {
2211
+ const selected = new Set(guids.map((guid) => guid.toLowerCase()));
2212
+ for (let index = 0; index < entries.length; index += 1) {
2213
+ const entry = entries[index];
2214
+ if (entry === void 0 || !selected.has(entry.guid.toLowerCase())) continue;
2215
+ const refs = refsByGuid?.get(entry.guid.toLowerCase());
2216
+ entries[index] = {
2217
+ ...entry,
2218
+ packageUrl,
2219
+ ...projection,
2220
+ ...publication === void 0 ? {} : { publication },
2221
+ ...refs === void 0 ? {} : { refs }
2222
+ };
2223
+ }
2224
+ }
2225
+ async function emitPackDocument(work, options) {
2226
+ for (const artifact of options.artifacts) {
2227
+ work.context.sink.emitFile({
2228
+ type: "asset",
2229
+ fileName: `assets/${artifact.path}`,
2230
+ source: artifact.bytes
2231
+ });
2232
+ }
2233
+ const referenceId = work.context.sink.emitFile({
2234
+ type: "asset",
2235
+ name: options.packageName,
2236
+ originalFileName: options.originalFileName,
2237
+ source: JSON.stringify(options.pack)
2238
+ });
2239
+ const packageUrl = work.context.sink.fileUrl(work.context.sink.getFileName(referenceId));
2240
+ updateImportedEntries(
2241
+ work.importedEntries,
2242
+ options.guids,
2243
+ packageUrl,
2244
+ options.projection,
2245
+ options.refsByGuid,
2246
+ options.publication
2247
+ );
2248
+ return packageUrl;
2249
+ }
2250
+ async function emitAuthoredPack(work, guidSeen, entry) {
2251
+ const packPath = resolve(work.context.cwd, entry.sourcePath);
2252
+ const declaration = work.context.inventory.sourceDeclarations.get(packPath);
2253
+ if (declaration?.format !== "pack.json") {
2254
+ throw work.context.fail({
2255
+ code: "catalog-declaration-missing",
2256
+ expected: "the inventory to retain the authored Pack declaration",
2257
+ hint: "rerun Pack inventory before emitting the authored Pack",
2258
+ detail: { stage: "scan", sourcePath: packPath }
2259
+ });
2260
+ }
2261
+ const prepared = await prepareAuthoredPackTransport(
2262
+ declaration.value,
2263
+ work.context.cookers,
2264
+ (guid) => ({
2265
+ base: work.context.basePrefix === "" ? "/" : work.context.basePrefix,
2266
+ packagePath: `assets/${guid}.pack.json`,
2267
+ artifactPath: (artifactGuid, key) => `${artifactGuid}/${key}.bin`
2268
+ }),
2269
+ packPath
2270
+ );
2271
+ const outputGuid = prepared.firstGuid ?? entry.guid;
2272
+ const authoredPack = prepared.finalized?.pack ?? prepared.pack;
2273
+ const runtimePublication = authoredPack.assets === void 0 || authoredPack.assets.length === 0 ? void 0 : runtimePublicationFor(work.context, {
2274
+ pack: { assets: authoredPack.assets },
2275
+ sourcePath: packPath,
2276
+ sourceRevision: prepared.finalized?.sourceRevision ?? entry.sourcePath,
2277
+ packageUrl: prepared.finalized?.packageUrl ?? `${work.context.basePrefix === "/" ? "" : work.context.basePrefix}/assets/${outputGuid}.pack.json`,
2278
+ ...prepared.finalized?.digest === void 0 ? {} : { digest: prepared.finalized.digest }
2279
+ });
2280
+ await emitPackDocument(work, {
2281
+ pack: runtimePublication?.pack ?? authoredPack,
2282
+ artifacts: prepared.finalized?.artifacts ?? [],
2283
+ packageName: `${outputGuid}.pack.json`,
2284
+ originalFileName: packPath,
2285
+ guids: prepared.pack.assets?.map((asset) => asset.guid) ?? [outputGuid],
2286
+ projection: prepared.finalized === void 0 ? work.context.directCurrentProjection : work.context.authoredCookedCurrentProjection,
2287
+ ...prepared.cooked === void 0 ? {} : { refsByGuid: prepared.cooked.refsByGuid },
2288
+ ...runtimePublication === void 0 ? {} : { publication: runtimePublication.publication }
2289
+ });
2290
+ for (const guid of prepared.pack.assets?.map((asset) => asset.guid) ?? [outputGuid]) {
2291
+ guidSeen.add(guid.toLowerCase());
2292
+ }
2293
+ }
2294
+ async function emitFinalizedOwner(work, metaPath, subAssets, sourcePackage, ownerFinalizer) {
2295
+ const ownerGuid = subAssets[0]?.guid;
2296
+ if (ownerGuid === void 0) return;
2297
+ const artifactPaths = /* @__PURE__ */ new Map();
2298
+ const sourceAsset = sourcePackage.product.assets[0];
2299
+ for (const [path, artifact] of Object.entries(sourceAsset?.artifacts ?? {})) {
2300
+ const ref2 = work.context.sink.emitFile({
2301
+ type: "asset",
2302
+ name: path,
2303
+ originalFileName: path,
2304
+ source: artifact.bytes
2305
+ });
2306
+ artifactPaths.set(path, work.context.sink.getFileName(ref2));
2307
+ }
2308
+ const ownerProduct = finalizeSourcePackage(
2309
+ sourcePackage.product,
2310
+ ownerFinalizer,
2311
+ (artifact) => work.context.sink.fileUrl(artifactPaths.get(artifact.path) ?? artifact.path)
2312
+ );
2313
+ if (!ownerProduct.ok) throw work.context.fail(ownerProduct.error);
2314
+ const ownerPackage = await finalizePackageTransportSource(
2315
+ projectImportProductForBuild(ownerProduct.value),
2316
+ {
2317
+ base: work.context.basePrefix,
2318
+ packagePath: `assets/${ownerGuid}.pack.json`,
2319
+ artifactPath: (_guid, key) => {
2320
+ const emittedPath = artifactPaths.get(key);
2321
+ if (emittedPath === void 0) {
2322
+ throw work.context.fail({
2323
+ code: "producer-artifact-missing",
2324
+ expected: `producer artifact ${key} to be emitted before Pack finalization`,
2325
+ hint: "repair the importer finalizer artifact closure and rebuild",
2326
+ detail: { stage: "finalize", metaPath, artifact: key }
2327
+ });
2328
+ }
2329
+ return emittedPath.replace(/^assets\//, "");
2330
+ },
2331
+ sink: () => {
2332
+ }
2333
+ }
2334
+ );
2335
+ const runtimePublication = runtimePublicationFor(work.context, {
2336
+ pack: { assets: ownerPackage.pack.assets },
2337
+ sourcePath: metaPath,
2338
+ sourceRevision: ownerPackage.sourceRevision,
2339
+ packageUrl: ownerPackage.packageUrl,
2340
+ digest: ownerPackage.digest
2341
+ });
2342
+ await emitPackDocument(work, {
2343
+ pack: runtimePublication.pack,
2344
+ artifacts: ownerPackage.artifacts,
2345
+ packageName: `${ownerGuid}.pack.json`,
2346
+ originalFileName: metaPath,
2347
+ guids: subAssets.map((sub) => sub.guid),
2348
+ projection: work.context.cookedCurrentProjection,
2349
+ refsByGuid: new Map(
2350
+ ownerProduct.value.assets.map((asset) => [
2351
+ asset.guid.toLowerCase(),
2352
+ asset.refs.map((ref2) => ref2.guid)
2353
+ ])
2354
+ ),
2355
+ publication: runtimePublication.publication
2356
+ });
2357
+ }
2358
+ async function emitBuildEntry(work, guidSeen, entry) {
2359
+ const guid = entry.guid.toLowerCase();
2360
+ const metaPath = metaPathForGuid(work.context.inventory.declarations, guid);
2361
+ if (metaPath === void 0) {
2362
+ if (entry.sourcePath.endsWith(".pack.json")) await emitAuthoredPack(work, guidSeen, entry);
2363
+ else guidSeen.add(guid);
2364
+ return;
2365
+ }
2366
+ const declaration = work.context.inventory.declarations.get(metaPath);
2367
+ if (declaration === void 0) {
2368
+ throw work.context.fail({
2369
+ code: "catalog-declaration-missing",
2370
+ expected: "every indexed Meta path to have one validated producer declaration",
2371
+ hint: "rerun the inventory and repair the Catalog declaration index",
2372
+ detail: { metaPath }
2373
+ });
2374
+ }
2375
+ const runMeta = { ...declaration, buildPack: false };
2376
+ const subAssets = runMeta.subAssets;
2377
+ for (const sub of subAssets) guidSeen.add(sub.guid.toLowerCase());
2378
+ const sourcePackage = await produceSourcePackage({
2379
+ meta: runMeta,
2380
+ registry: work.context.importerRegistry,
2381
+ fs: work.context.fsForImport
2382
+ });
2383
+ if (!sourcePackage.ok) throw work.context.fail(sourcePackage.error);
2384
+ const ownerFinalizer = work.context.importerRegistry.get(runMeta.importer)?.finalize;
2385
+ if (ownerFinalizer !== void 0) {
2386
+ await emitFinalizedOwner(work, metaPath, subAssets, sourcePackage.value, ownerFinalizer);
2387
+ return;
2388
+ }
2389
+ const finalized = await finalizePackageTransportSource(
2390
+ projectImportProductForBuild(sourcePackage.value.product),
2391
+ {
2392
+ base: work.context.basePrefix,
2393
+ packagePath: `assets/${subAssets[0]?.guid ?? "pack"}.pack.json`,
2394
+ artifactPath: (assetGuid, key) => `${assetGuid}-${key}.bin`,
2395
+ sink: () => {
2396
+ }
2397
+ }
2398
+ );
2399
+ const productByGuid = sourcePackageAssetsByGuid(sourcePackage.value);
2400
+ const runtimePublication = runtimePublicationFor(work.context, {
2401
+ pack: { assets: finalized.pack.assets },
2402
+ sourcePath: metaPath,
2403
+ sourceRevision: finalized.sourceRevision,
2404
+ packageUrl: finalized.packageUrl,
2405
+ digest: finalized.digest
2406
+ });
2407
+ await emitPackDocument(work, {
2408
+ pack: runtimePublication.pack,
2409
+ artifacts: finalized.artifacts,
2410
+ packageName: `${subAssets[0]?.guid ?? "pack"}.pack.json`,
2411
+ originalFileName: metaPath,
2412
+ guids: subAssets.map((sub) => sub.guid),
2413
+ projection: work.context.cookedCurrentProjection,
2414
+ refsByGuid: new Map(
2415
+ [...productByGuid].map(([guid2, asset]) => [guid2, asset.refs.map((ref2) => ref2.guid)])
2416
+ ),
2417
+ publication: runtimePublication.publication
2418
+ });
2419
+ }
2420
+ async function produceBuildAssets(context) {
2421
+ const entries = [...context.inventory.entries];
2422
+ const authored = await buildAuthoredPackages(context);
2423
+ const importedEntries = entries.map((entry) => {
2424
+ const scriptable = authored.get(entry.sourcePath);
2425
+ if (scriptable === void 0) return entry;
2426
+ const cookReceiptUrl = scriptable.receiptUrls.get(entry.guid.toLowerCase());
2427
+ return projectCookedPackageEntry(entry, {
2428
+ packageUrl: scriptable.packageUrl,
2429
+ revision: scriptable.revision,
2430
+ refs: scriptable.refs.get(entry.guid.toLowerCase()) ?? [],
2431
+ ...cookReceiptUrl === void 0 ? {} : { cookReceiptUrl },
2432
+ publication: scriptable.publication
2433
+ });
2434
+ });
2435
+ const guidSeen = /* @__PURE__ */ new Set();
2436
+ const work = { importedEntries, context };
2437
+ for (const entry of importedEntries) {
2438
+ if (!guidSeen.has(entry.guid.toLowerCase())) await emitBuildEntry(work, guidSeen, entry);
2439
+ }
2440
+ return importedEntries;
2441
+ }
2442
+
2443
+ // src/catalog-importer-policy.ts
2444
+ var DEFAULT_CATALOG_IMPORTER_KEYS = [
2445
+ "image",
2446
+ "gltf",
2447
+ "fbx",
2448
+ "audio",
2449
+ "font",
2450
+ "ui"
2451
+ ];
2452
+ function catalogImporterPolicy(importer, hostImporterKeys) {
2453
+ if (importer === SHADER_RESERVED_IMPORTER_KEY) {
2454
+ return {
2455
+ disposition: "exclude",
2456
+ hostProvided: false,
2457
+ expected: "the provider must publish cooked runtime output before Catalog projection",
2458
+ hint: "remove the source declaration from Pack roots or publish its cooked runtime module"
2459
+ };
2460
+ }
2461
+ if (DEFAULT_CATALOG_IMPORTER_KEYS.includes(importer)) {
2462
+ return {
2463
+ disposition: "publish",
2464
+ hostProvided: false,
2465
+ expected: "a standard Catalog provider declaration",
2466
+ hint: "let the provider producer materialize the declared output"
2467
+ };
2468
+ }
2469
+ if (hostImporterKeys.has(importer)) {
2470
+ return {
2471
+ disposition: "publish",
2472
+ hostProvided: true,
2473
+ expected: "a registered host Catalog provider declaration",
2474
+ hint: "let the registered provider materialize the declared output"
2475
+ };
2476
+ }
2477
+ return {
2478
+ disposition: "missing",
2479
+ hostProvided: false,
2480
+ expected: "the sidecar importer must be present in the registered provider set",
2481
+ hint: "wire the provider through the host importer registry before building the Catalog"
2482
+ };
2483
+ }
2484
+ async function buildCatalogResult(roots, base = "/", registeredImporterKeys = /* @__PURE__ */ new Set(), scanOptions = {}, catalogVisibility = () => true) {
2485
+ const options = {
2486
+ base,
2487
+ scanOptions,
2488
+ importerPolicy: (importer) => catalogImporterPolicy(importer, registeredImporterKeys),
2489
+ visibility: catalogVisibility
2490
+ };
2491
+ return buildCatalogProjection(roots, options);
2492
+ }
2493
+
2494
+ // src/importer-registry.ts
2495
+ var ImporterRegistry = class {
2496
+ importers = /* @__PURE__ */ new Map();
2497
+ /**
2498
+ * Register an importer for its `importer.key`. Fail-fast on a malformed
2499
+ * importer (charter P3); idempotent on a repeated key (last write wins).
2500
+ *
2501
+ * @param importer the `{ key, import }` object to register.
2502
+ * @throws TypeError when `importer.key` is empty or `importer.import` is not
2503
+ * a function - a wire-time misconfiguration the host must fix.
2504
+ */
2505
+ register(importer) {
2506
+ if (typeof importer.key !== "string" || importer.key.length === 0) {
2507
+ throw new TypeError(
2508
+ `ImporterRegistry.register: importer.key must be a non-empty string (got ${JSON.stringify(importer.key)})`
2509
+ );
2510
+ }
2511
+ if (typeof importer.import !== "function") {
2512
+ throw new TypeError(
2513
+ `ImporterRegistry.register: importer.import must be a function for key "${importer.key}"`
2514
+ );
2515
+ }
2516
+ this.importers.set(importer.key, importer);
2517
+ }
2518
+ /**
2519
+ * Look up the importer registered for `key`. Returns `undefined` when no
2520
+ * importer is wired - the import runner maps that to a structured
2521
+ * `ImportError(code='importer-not-registered')` with the registered keys in
2522
+ * `.detail.registeredImporters` (charter P3).
2523
+ */
2524
+ get(key) {
2525
+ return this.importers.get(key);
2526
+ }
2527
+ /**
2528
+ * The importer keys currently wired, in insertion order. Fed into the
2529
+ * `importer-not-registered` error `.detail.registeredImporters` so AI users
2530
+ * see exactly what is injectable.
2531
+ */
2532
+ registeredImporters() {
2533
+ return [...this.importers.keys()];
2534
+ }
2535
+ /** Project the first registered producer capability into the runner context. */
2536
+ contextCapabilities() {
2537
+ for (const importer of this.importers.values()) {
2538
+ const decoder = importer.capabilities?.decodeImage;
2539
+ if (decoder !== void 0) return { decodeImage: decoder };
2540
+ }
2541
+ return {};
2542
+ }
2543
+ /** Ask the registered producer whether a declaration has a Catalog product. */
2544
+ shouldPublishCatalog(input) {
2545
+ return this.get(input.importer)?.capabilities?.catalog?.publish?.({
2546
+ importSettings: input.importSettings,
2547
+ subAssets: input.subAssets
2548
+ }) ?? true;
2549
+ }
2550
+ };
2551
+
2552
+ // src/source-package-errors.ts
2553
+ var SOURCE_PACKAGE_ERROR_POLICY = {
2554
+ "source-package-meta-invalid": {
2555
+ expected: "a valid source Meta declaration with complete GUID topology",
2556
+ hint: "repair the Meta declaration, then rebuild or cold-cook the source package"
2557
+ },
2558
+ "source-package-importer-missing": {
2559
+ expected: "a registered importer for the source Meta importer key",
2560
+ hint: "register the named importer, then rebuild or cold-cook the source package"
2561
+ },
2562
+ "source-package-conversion-failed": {
2563
+ expected: "the configured importer to convert the source successfully",
2564
+ hint: "repair the source or importer, then rebuild or cold-cook the source package"
2565
+ },
2566
+ "source-package-ddc-failed": {
2567
+ expected: "a readable persistent DDC entry with matching integrity evidence",
2568
+ hint: "discard the invalid derived entry, then rebuild or cold-cook the source package"
2569
+ },
2570
+ "source-package-publication-invalid": {
2571
+ expected: "a complete Pack body, refs, artifacts, and route integrity",
2572
+ hint: "repair the missing product bytes, then rebuild or cold-cook the source package"
2573
+ },
2574
+ "source-package-guid-closure-mismatch": {
2575
+ expected: "exactly one produced asset for every declared GUID",
2576
+ hint: "repair the Meta topology or importer output, then rebuild the whole source package"
2577
+ }
2578
+ };
2579
+ function sourcePackageError(code, context, detail) {
2580
+ const policy = SOURCE_PACKAGE_ERROR_POLICY[code];
2581
+ return {
2582
+ code,
2583
+ expected: policy?.expected,
2584
+ hint: policy?.hint,
2585
+ detail: { ...context, ...detail }
2586
+ };
2587
+ }
2588
+ function unknownReason(error) {
2589
+ if (error !== null && typeof error === "object") {
2590
+ const value = error;
2591
+ if (typeof value.code === "string") {
2592
+ const detail = value.detail;
2593
+ const reason = detail !== null && typeof detail === "object" && "reason" in detail ? detail.reason : void 0;
2594
+ const diagnostic = detail !== null && typeof detail === "object" && "diagnostic" in detail ? detail.diagnostic : void 0;
2595
+ return `${value.code}: ${typeof diagnostic === "string" ? diagnostic : typeof reason === "string" ? reason : String(value.expected ?? "producer failure")}`;
2596
+ }
2597
+ }
2598
+ return error instanceof Error ? error.message : String(error);
2599
+ }
2600
+ function importFailureCode(error) {
2601
+ switch (error.code) {
2602
+ case "importer-not-registered":
2603
+ return "source-package-importer-missing";
2604
+ case "import-produced-no-assets":
2605
+ case "guid-mismatch":
2606
+ case "source-validation-failed":
2607
+ return "source-package-guid-closure-mismatch";
2608
+ case "source-read-failed":
2609
+ case "import-internal-error":
2610
+ case "mesh-material-slot-topology-change":
2611
+ case "unknown-source-key":
2612
+ case "duplicate-source-key":
2613
+ case "invalid-source-overrides":
2614
+ case "invalid-source-override-payload":
2615
+ return "source-package-conversion-failed";
2616
+ }
2617
+ }
2618
+ function normalizeSourcePackageError(error, context) {
2619
+ if (isSourcePackageError(error)) return error;
2620
+ if (isScriptablePackFailure(error)) {
2621
+ return sourcePackageError("source-package-conversion-failed", context, {
2622
+ stage: "conversion",
2623
+ reason: unknownReason(error)
2624
+ });
2625
+ }
2626
+ if (isImportError(error)) {
2627
+ const code = importFailureCode(error);
2628
+ const detail = {
2629
+ stage: code === "source-package-importer-missing" ? "importer" : "conversion",
2630
+ reason: unknownReason(error),
2631
+ ...code === "source-package-importer-missing" && "registeredImporters" in error.detail ? { registeredImporters: error.detail.registeredImporters } : {}
2632
+ };
2633
+ return sourcePackageError(code, context, detail);
2634
+ }
2635
+ return sourcePackageError(contextErrorStage(error), context, {
2636
+ stage: "conversion",
2637
+ reason: unknownReason(error)
2638
+ });
2639
+ }
2640
+ function isSourcePackageError(error) {
2641
+ return error !== null && typeof error === "object" && "code" in error && typeof error.code === "string" && error.code in SOURCE_PACKAGE_ERROR_POLICY && "expected" in error && typeof error.expected === "string" && "hint" in error && typeof error.hint === "string" && "detail" in error && error.detail !== null && typeof error.detail === "object";
2642
+ }
2643
+ function contextErrorStage(error) {
2644
+ return error instanceof SyntaxError ? "source-package-meta-invalid" : "source-package-conversion-failed";
2645
+ }
2646
+ function isImportError(error) {
2647
+ return error !== null && typeof error === "object" && "code" in error && "expected" in error && "hint" in error && "detail" in error;
2648
+ }
2649
+ function isScriptablePackFailure(error) {
2650
+ return error !== null && typeof error === "object" && "code" in error && typeof error.code === "string" && error.code.startsWith("pack-source-") && "expected" in error && typeof error.expected === "string" && "hint" in error && typeof error.hint === "string" && "detail" in error;
2651
+ }
2652
+ function inspectHead(root, guid, desiredKey) {
2653
+ return new DdcLifecycle(root).inspect(guid, desiredKey);
2654
+ }
2655
+ var PUBLICATION_SETTLE_TIMEOUT_MS = 5e3;
2656
+ var PUBLICATION_SETTLE_POLL_MS = 10;
2657
+ async function inspectAfterSupersededCommit(candidate) {
2658
+ let head = await inspectHead(candidate.root, candidate.lease.guid, candidate.lease.desiredKey);
2659
+ if (head.state === "current" && head.currentKey === candidate.lease.desiredKey) return head;
2660
+ const deadline = Date.now() + PUBLICATION_SETTLE_TIMEOUT_MS;
2661
+ while (Date.now() < deadline && head.activeLease !== void 0 && head.activeLease.attempt !== candidate.lease.attempt && head.activeLease.desiredKey === candidate.lease.desiredKey) {
2662
+ await new Promise((resolve3) => setTimeout(resolve3, PUBLICATION_SETTLE_POLL_MS));
2663
+ head = await inspectHead(candidate.root, candidate.lease.guid, candidate.lease.desiredKey);
2664
+ if (head.state === "current" && head.currentKey === candidate.lease.desiredKey) return head;
2665
+ }
2666
+ return head;
2667
+ }
2668
+ async function stageSourcePackageDdc(input) {
2669
+ const session = new DdcGenerationSession(input.root, { generation: 1 });
2670
+ try {
2671
+ const candidate = await session.stageEntry(input.entry);
2672
+ return ok({ ...candidate, root: input.root, session, context: input.context });
2673
+ } catch (error) {
2674
+ return err(
2675
+ sourcePackageError("source-package-ddc-failed", input.context, {
2676
+ stage: "ddc",
2677
+ reason: error instanceof Error ? error.message : String(error)
2678
+ })
2679
+ );
2680
+ }
2681
+ }
2682
+ async function commitSourcePackageDdc(candidate) {
2683
+ try {
2684
+ const commit = await candidate.session.commitEntry(candidate, candidate.lease.desiredKey);
2685
+ if (commit.result !== "current") {
2686
+ const head = await inspectAfterSupersededCommit(candidate);
2687
+ if (head.state === "current" && head.currentKey === candidate.lease.desiredKey) {
2688
+ return ok(head);
2689
+ }
2690
+ return err(
2691
+ sourcePackageError("source-package-ddc-failed", candidate.context, {
2692
+ stage: "ddc",
2693
+ reason: `DDC lifecycle commit returned ${commit.result}`
2694
+ })
2695
+ );
2696
+ }
2697
+ return ok(await inspectHead(candidate.root, candidate.lease.guid, candidate.lease.desiredKey));
2698
+ } catch (error) {
2699
+ return err(
2700
+ sourcePackageError("source-package-ddc-failed", candidate.context, {
2701
+ stage: "ddc",
2702
+ reason: error instanceof Error ? error.message : String(error)
2703
+ })
2704
+ );
2705
+ }
2706
+ }
2707
+ async function persistTransport(transport) {
2708
+ if (transport === void 0) return true;
2709
+ const directory = dirname(transport.path);
2710
+ const temporary = `${transport.path}.tmp`;
2711
+ try {
2712
+ await mkdir(directory, { recursive: true });
2713
+ if (((await stat(directory)).mode & 146) === 0) {
2714
+ throw new Error("transport directory is read-only");
2715
+ }
2716
+ await writeFile(temporary, transport.body);
2717
+ await rename(temporary, transport.path);
2718
+ return true;
2719
+ } catch {
2720
+ await rm(temporary, { force: true }).catch(() => {
2721
+ });
2722
+ return false;
2723
+ }
2724
+ }
2725
+ function importPublicationFailure(error) {
2726
+ return {
2727
+ code: error.code,
2728
+ expected: error.expected,
2729
+ hint: error.hint,
2730
+ detail: error.detail.reason ?? error.detail.stage,
2731
+ diagnostic: error
2732
+ };
2733
+ }
2734
+ function projectImportPublication(input, head, observedAt) {
2735
+ const digest = head.currentKey ?? input.desiredKey;
2736
+ const revision = { digest, observedAt, rootId: input.root };
2737
+ const published = new Set(input.publishedGuids.map((guid) => guid.toLowerCase()));
2738
+ const catalog = input.nextCatalog.map((row) => {
2739
+ if (!published.has(row.guid.toLowerCase())) return row;
2740
+ const previousLkg = head.lastKnownGoodKey === void 0 ? void 0 : input.previousCatalog.find(
2741
+ (candidate) => candidate.guid.toLowerCase() === row.guid.toLowerCase() && candidate.revision?.digest === head.lastKnownGoodKey
2742
+ );
2743
+ const projection = row.projection === void 0 ? void 0 : (() => {
2744
+ const { lastKnownGood: _staleLastKnownGood, ...withoutLastKnownGood } = row.projection;
2745
+ return previousLkg?.packageUrl === void 0 ? withoutLastKnownGood : { ...withoutLastKnownGood, lastKnownGood: { packageUrl: previousLkg.packageUrl } };
2746
+ })();
2747
+ return {
2748
+ ...row,
2749
+ revision,
2750
+ ...projection === void 0 ? {} : { projection }
2751
+ };
2752
+ });
2753
+ return { catalog, revision };
2754
+ }
2755
+ async function publishImportPublication(input) {
2756
+ const staged = await stageImportPublication(input);
2757
+ if (!staged.ok) return staged;
2758
+ return commitImportPublication(staged.candidate);
2759
+ }
2760
+ async function stageImportPublication(input) {
2761
+ const staged = await stageSourcePackageDdc({
2762
+ root: input.root,
2763
+ entry: {
2764
+ key: input.desiredKey,
2765
+ guid: input.guid,
2766
+ payload: input.pack,
2767
+ refs: [],
2768
+ artifacts: {},
2769
+ receipt: {
2770
+ guid: input.guid,
2771
+ key: input.desiredKey,
2772
+ producer: "engine-import/source-package-publication",
2773
+ inputFingerprint: input.desiredKey,
2774
+ outputDigest: ddcOutputDigest({
2775
+ guid: input.guid,
2776
+ payload: input.pack,
2777
+ refs: [],
2778
+ artifacts: {}
2779
+ })
2780
+ }
2781
+ },
2782
+ context: {
2783
+ sourceMeta: "<import-publication>",
2784
+ anchorGuid: input.guid,
2785
+ affectedGuids: input.publishedGuids,
2786
+ producer: "source-package/import-publication",
2787
+ importer: "import-publication"
2788
+ }
2789
+ });
2790
+ if (!staged.ok) {
2791
+ return {
2792
+ ok: false,
2793
+ error: importPublicationFailure(staged.error),
2794
+ head: await inspectHead(input.root, input.guid, input.desiredKey)
2795
+ };
2796
+ }
2797
+ const lastKnownGoodKey = staged.value.previousHead.currentKey !== void 0 && staged.value.previousHead.currentKey !== input.desiredKey ? staged.value.previousHead.currentKey : staged.value.previousHead.lastKnownGoodKey;
2798
+ const projectedHead = {
2799
+ ...staged.value.previousHead,
2800
+ state: "cooking",
2801
+ currentKey: input.desiredKey,
2802
+ ...lastKnownGoodKey === void 0 ? {} : { lastKnownGoodKey }
2803
+ };
2804
+ const projected = projectImportPublication(input, projectedHead, Date.now());
2805
+ return {
2806
+ ok: true,
2807
+ candidate: {
2808
+ input,
2809
+ ddc: staged.value,
2810
+ key: input.desiredKey,
2811
+ head: projectedHead,
2812
+ catalog: projected.catalog,
2813
+ revision: projected.revision
2814
+ }
2815
+ };
2816
+ }
2817
+ async function commitImportPublication(candidate) {
2818
+ const publication = await commitSourcePackageDdc(candidate.ddc);
2819
+ if (!publication.ok) {
2820
+ return {
2821
+ ok: false,
2822
+ error: importPublicationFailure(publication.error),
2823
+ head: await inspectHead(
2824
+ candidate.input.root,
2825
+ candidate.input.guid,
2826
+ candidate.input.desiredKey
2827
+ )
2828
+ };
2829
+ }
2830
+ const projected = projectImportPublication(candidate.input, publication.value, Date.now());
2831
+ return {
2832
+ ok: true,
2833
+ key: candidate.input.desiredKey,
2834
+ head: publication.value,
2835
+ catalog: projected.catalog,
2836
+ revision: projected.revision,
2837
+ transportPersisted: await persistTransport(candidate.input.transport)
2838
+ };
2839
+ }
2840
+ async function discardImportPublication(candidate) {
2841
+ await candidate.ddc.session.discardEntry(candidate.ddc);
2842
+ }
2843
+ async function restoreImportPublication(candidate) {
2844
+ await candidate.ddc.session.restoreEntry(candidate.ddc);
2845
+ }
2846
+
2847
+ export { AssetOutputProducerRegistry, DEFAULT_CATALOG_IMPORTER_KEYS, ImporterRegistry, SHADER_RESERVED_IMPORTER_KEY, buildCatalogResult, buildScriptablePack, canonicalScriptableSourcePath, catalogImporterPolicy, commitImportPublication, 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, sourcePackageAssetsByGuid, sourcePackageError, stageImportPublication };
2848
+ //# sourceMappingURL=index.mjs.map
2849
+ //# sourceMappingURL=index.mjs.map