@forgeax/engine-pack 0.1.20 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +159 -46
  2. package/dist/__tests__/inventory-schema.test.d.ts +2 -0
  3. package/dist/__tests__/inventory-schema.test.d.ts.map +1 -0
  4. package/dist/__tests__/parameterized-pack-authoring-gateway.unit.test.d.ts +2 -0
  5. package/dist/__tests__/parameterized-pack-authoring-gateway.unit.test.d.ts.map +1 -0
  6. package/dist/__tests__/parameterized-pack-scanner.unit.test.d.ts +2 -0
  7. package/dist/__tests__/parameterized-pack-scanner.unit.test.d.ts.map +1 -0
  8. package/dist/__tests__/parameterized-pack.unit.test.d.ts +2 -0
  9. package/dist/__tests__/parameterized-pack.unit.test.d.ts.map +1 -0
  10. package/dist/__tests__/scanner-blacklist.test.d.ts +2 -0
  11. package/dist/__tests__/scanner-blacklist.test.d.ts.map +1 -0
  12. package/dist/build.d.ts +9 -4
  13. package/dist/build.d.ts.map +1 -1
  14. package/dist/build.mjs +3317 -804
  15. package/dist/build.mjs.map +1 -1
  16. package/dist/builtin.mjs +37 -4
  17. package/dist/builtin.mjs.map +1 -1
  18. package/dist/catalog-builder.d.ts.map +1 -1
  19. package/dist/cli-asset.d.ts.map +1 -1
  20. package/dist/cli-asset.mjs +1368 -850
  21. package/dist/cli-asset.mjs.map +1 -1
  22. package/dist/deriveAssetName.d.ts +11 -7
  23. package/dist/deriveAssetName.d.ts.map +1 -1
  24. package/dist/evidence/material-cook.d.ts.map +1 -1
  25. package/dist/evidence/source-inventory.d.ts.map +1 -1
  26. package/dist/guid.d.ts +15 -0
  27. package/dist/guid.d.ts.map +1 -1
  28. package/dist/guid.mjs +53 -5
  29. package/dist/guid.mjs.map +1 -1
  30. package/dist/index.d.ts +3 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.mjs +1255 -561
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/inventory/binding.d.ts +6 -0
  35. package/dist/inventory/binding.d.ts.map +1 -0
  36. package/dist/inventory/declaration.d.ts +24 -0
  37. package/dist/inventory/declaration.d.ts.map +1 -0
  38. package/dist/inventory/index.d.ts +3 -0
  39. package/dist/inventory/index.d.ts.map +1 -0
  40. package/dist/inventory/sync.d.ts +5 -0
  41. package/dist/inventory/sync.d.ts.map +1 -0
  42. package/dist/material-cook.mjs +3 -1
  43. package/dist/material-cook.mjs.map +1 -1
  44. package/dist/name.mjs +4 -7
  45. package/dist/name.mjs.map +1 -1
  46. package/dist/package-finalizer.d.ts +2 -20
  47. package/dist/package-finalizer.d.ts.map +1 -1
  48. package/dist/parameterized-pack-node.d.ts +26 -0
  49. package/dist/parameterized-pack-node.d.ts.map +1 -0
  50. package/dist/parameterized-pack-node.mjs +11093 -0
  51. package/dist/parameterized-pack-node.mjs.map +1 -0
  52. package/dist/parameterized-pack.d.ts +233 -0
  53. package/dist/parameterized-pack.d.ts.map +1 -0
  54. package/dist/resolve-asset-source.d.ts +1 -3
  55. package/dist/resolve-asset-source.d.ts.map +1 -1
  56. package/dist/resolve-asset-source.mjs +6 -81
  57. package/dist/resolve-asset-source.mjs.map +1 -1
  58. package/dist/runtime.mjs +77 -7
  59. package/dist/runtime.mjs.map +1 -1
  60. package/dist/scanner.d.ts +19 -8
  61. package/dist/scanner.d.ts.map +1 -1
  62. package/dist/scanner.mjs +1169 -690
  63. package/dist/scanner.mjs.map +1 -1
  64. package/dist/schema-compiled.d.ts.map +1 -1
  65. package/dist/schema.mjs +79 -7
  66. package/dist/schema.mjs.map +1 -1
  67. package/dist/scriptable-pack-node.d.ts +8 -32
  68. package/dist/scriptable-pack-node.d.ts.map +1 -1
  69. package/dist/scriptable-pack-node.mjs +576 -1021
  70. package/dist/scriptable-pack-node.mjs.map +1 -1
  71. package/dist/scriptable-pack-worker.mjs +29 -11
  72. package/dist/scriptable-pack-worker.mjs.map +1 -1
  73. package/dist/scriptable-pack.d.ts +19 -532
  74. package/dist/scriptable-pack.d.ts.map +1 -1
  75. package/dist/scriptable-pack.mjs +1042 -527
  76. package/dist/scriptable-pack.mjs.map +1 -1
  77. package/package.json +7 -7
  78. package/schema/meta.schema.json +1 -1
  79. package/schema/pack.schema.json +76 -6
  80. package/src/__tests__/guid-collision.unit.test.ts +18 -9
  81. package/src/__tests__/inventory-schema.test.ts +109 -0
  82. package/src/__tests__/pack.unit.test.ts +1 -238
  83. package/src/__tests__/package-finalizer.contract.test.ts +55 -0
  84. package/src/__tests__/parameterized-pack-authoring-gateway.unit.test.ts +249 -0
  85. package/src/__tests__/parameterized-pack-scanner.unit.test.ts +123 -0
  86. package/src/__tests__/parameterized-pack.unit.test.ts +266 -0
  87. package/src/__tests__/resolve-asset-source.test.ts +15 -98
  88. package/src/__tests__/scanner-blacklist.test.ts +55 -0
  89. package/src/__tests__/scanner-inventory.contract.test.ts +14 -15
  90. package/src/__tests__/scanner-inventory.test.ts +24 -12
  91. package/src/__tests__/scriptable-pack-cli.integration.test.ts +4 -9
  92. package/src/__tests__/topology.unit.test.ts +23 -0
  93. package/src/build.ts +28 -18
  94. package/src/catalog-builder.ts +61 -34
  95. package/src/cli-asset.ts +70 -85
  96. package/src/deriveAssetName.ts +14 -13
  97. package/src/evidence/material-cook.ts +3 -1
  98. package/src/evidence/source-inventory.ts +34 -21
  99. package/src/guid.ts +65 -4
  100. package/src/index.ts +12 -11
  101. package/src/inventory/binding.ts +25 -0
  102. package/src/inventory/declaration.ts +168 -0
  103. package/src/inventory/index.ts +18 -0
  104. package/src/inventory/sync.ts +22 -0
  105. package/src/package-finalizer.ts +2 -42
  106. package/src/parameterized-pack-node.ts +1968 -0
  107. package/src/parameterized-pack.ts +1493 -0
  108. package/src/resolve-asset-source.ts +4 -76
  109. package/src/scanner.ts +241 -59
  110. package/src/schema-compiled.ts +2 -0
  111. package/src/scriptable-pack-node.ts +111 -722
  112. package/src/scriptable-pack-worker.ts +48 -16
  113. package/src/scriptable-pack.ts +27 -939
  114. package/dist/.tsbuildinfo +0 -1
  115. package/dist/__tests__/load-asset-config.test.d.ts +0 -2
  116. package/dist/__tests__/load-asset-config.test.d.ts.map +0 -1
  117. package/dist/__tests__/scriptable-pack.test-d.d.ts +0 -2
  118. package/dist/__tests__/scriptable-pack.test-d.d.ts.map +0 -1
  119. package/dist/__tests__/scriptable-pack.unit.test.d.ts +0 -2
  120. package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +0 -1
  121. package/dist/config.d.ts +0 -6
  122. package/dist/config.d.ts.map +0 -1
  123. package/dist/config.mjs +0 -29
  124. package/dist/config.mjs.map +0 -1
  125. package/src/__tests__/load-asset-config.test.ts +0 -121
  126. package/src/__tests__/scriptable-pack.test-d.ts +0 -131
  127. package/src/__tests__/scriptable-pack.unit.test.ts +0 -789
  128. package/src/config.ts +0 -36
package/dist/build.mjs CHANGED
@@ -1,9 +1,10 @@
1
- import { ok, err, catalogOperationsFor, authoringCapabilityForAssetKind, PACK_ERROR_HINTS, MATERIAL_TEXTURE_SLOTS, projectCookProductEvidence, projectAssetEvidence, validateSourceOverrideMap, catalogEntryDigest, AssetError, ImportError } from '@forgeax/engine-types';
2
- import { join, dirname, resolve, isAbsolute, posix, relative, basename, extname } from 'path';
1
+ import { ok, err, catalogOperationsFor, authoringCapabilityForAssetKind, MATERIAL_TEXTURE_SLOTS, projectCookProductEvidence, projectAssetEvidence, validateSourceOverrideMap, PACK_ERROR_HINTS, catalogEntryDigest, AssetError, ImportError } from '@forgeax/engine-types';
2
+ import { dirname, resolve, isAbsolute, relative, join, basename, sep, extname } from 'path';
3
+ import { sha1 } from '@noble/hashes/legacy.js';
3
4
  import { uuidv7obj } from 'uuidv7';
4
- import { readFileSync, existsSync } from 'fs';
5
5
  import { createHash } from 'crypto';
6
- import { stat, readFile, readdir, realpath, mkdtemp, rm } from 'fs/promises';
6
+ import { stat, readFile, readdir, realpath, mkdir, writeFile, rename, rm, mkdtemp } from 'fs/promises';
7
+ import { existsSync } from 'fs';
7
8
  import { tmpdir } from 'os';
8
9
  import { fileURLToPath } from 'url';
9
10
  import { Worker } from 'worker_threads';
@@ -1300,21 +1301,21 @@ var require_errors = __commonJS({
1300
1301
  function extendErrors({ gen, keyword, schemaValue, data, errsCount, it }) {
1301
1302
  if (errsCount === void 0)
1302
1303
  throw new Error("ajv implementation error");
1303
- const err6 = gen.name("err");
1304
+ const err8 = gen.name("err");
1304
1305
  gen.forRange("i", errsCount, names_1.default.errors, (i) => {
1305
- gen.const(err6, (0, codegen_1._)`${names_1.default.vErrors}[${i}]`);
1306
- gen.if((0, codegen_1._)`${err6}.instancePath === undefined`, () => gen.assign((0, codegen_1._)`${err6}.instancePath`, (0, codegen_1.strConcat)(names_1.default.instancePath, it.errorPath)));
1307
- gen.assign((0, codegen_1._)`${err6}.schemaPath`, (0, codegen_1.str)`${it.errSchemaPath}/${keyword}`);
1306
+ gen.const(err8, (0, codegen_1._)`${names_1.default.vErrors}[${i}]`);
1307
+ gen.if((0, codegen_1._)`${err8}.instancePath === undefined`, () => gen.assign((0, codegen_1._)`${err8}.instancePath`, (0, codegen_1.strConcat)(names_1.default.instancePath, it.errorPath)));
1308
+ gen.assign((0, codegen_1._)`${err8}.schemaPath`, (0, codegen_1.str)`${it.errSchemaPath}/${keyword}`);
1308
1309
  if (it.opts.verbose) {
1309
- gen.assign((0, codegen_1._)`${err6}.schema`, schemaValue);
1310
- gen.assign((0, codegen_1._)`${err6}.data`, data);
1310
+ gen.assign((0, codegen_1._)`${err8}.schema`, schemaValue);
1311
+ gen.assign((0, codegen_1._)`${err8}.data`, data);
1311
1312
  }
1312
1313
  });
1313
1314
  }
1314
1315
  exports.extendErrors = extendErrors;
1315
1316
  function addError(gen, errObj) {
1316
- const err6 = gen.const("err", errObj);
1317
- gen.if((0, codegen_1._)`${names_1.default.vErrors} === null`, () => gen.assign(names_1.default.vErrors, (0, codegen_1._)`[${err6}]`), (0, codegen_1._)`${names_1.default.vErrors}.push(${err6})`);
1317
+ const err8 = gen.const("err", errObj);
1318
+ gen.if((0, codegen_1._)`${names_1.default.vErrors} === null`, () => gen.assign(names_1.default.vErrors, (0, codegen_1._)`[${err8}]`), (0, codegen_1._)`${names_1.default.vErrors}.push(${err8})`);
1318
1319
  gen.code((0, codegen_1._)`${names_1.default.errors}++`);
1319
1320
  }
1320
1321
  function returnErrors(it, errs) {
@@ -2966,7 +2967,7 @@ var require_compile = __commonJS({
2966
2967
  const schOrFunc = root.refs[ref];
2967
2968
  if (schOrFunc)
2968
2969
  return schOrFunc;
2969
- let _sch = resolve3.call(this, root, ref);
2970
+ let _sch = resolve4.call(this, root, ref);
2970
2971
  if (_sch === void 0) {
2971
2972
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2972
2973
  const { schemaId } = this.opts;
@@ -2993,7 +2994,7 @@ var require_compile = __commonJS({
2993
2994
  function sameSchemaEnv(s1, s2) {
2994
2995
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
2995
2996
  }
2996
- function resolve3(root, ref) {
2997
+ function resolve4(root, ref) {
2997
2998
  let sch;
2998
2999
  while (typeof (sch = this.refs[ref]) == "string")
2999
3000
  ref = sch;
@@ -3621,7 +3622,7 @@ var require_fast_uri = __commonJS({
3621
3622
  }
3622
3623
  return uri;
3623
3624
  }
3624
- function resolve3(baseURI, relativeURI, options) {
3625
+ function resolve4(baseURI, relativeURI, options) {
3625
3626
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3626
3627
  const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
3627
3628
  schemelessOptions.skipEscape = true;
@@ -3879,7 +3880,7 @@ var require_fast_uri = __commonJS({
3879
3880
  var fastUri = {
3880
3881
  SCHEMES,
3881
3882
  normalize,
3882
- resolve: resolve3,
3883
+ resolve: resolve4,
3883
3884
  resolveComponent,
3884
3885
  equal,
3885
3886
  serialize,
@@ -4716,8 +4717,8 @@ var require_multipleOf = __commonJS({
4716
4717
  const { gen, data, schemaCode, it } = cxt;
4717
4718
  const prec = it.opts.multipleOfPrecision;
4718
4719
  const res = gen.let("res");
4719
- const invalid3 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
4720
- cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid3}))`);
4720
+ const invalid2 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
4721
+ cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid2}))`);
4721
4722
  }
4722
4723
  };
4723
4724
  exports.default = def;
@@ -6895,6 +6896,10 @@ function bytesToDashForm(bytes) {
6895
6896
  return `${h[bytes[0]]}${h[bytes[1]]}${h[bytes[2]]}${h[bytes[3]]}-${h[bytes[4]]}${h[bytes[5]]}-${h[bytes[6]]}${h[bytes[7]]}-${h[bytes[8]]}${h[bytes[9]]}-${h[bytes[10]]}${h[bytes[11]]}${h[bytes[12]]}${h[bytes[13]]}${h[bytes[14]]}${h[bytes[15]]}`;
6896
6897
  }
6897
6898
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6899
+ var PACK_SOURCE_KEY_RE = /^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*$/;
6900
+ function isValidPackSourceKey(value) {
6901
+ return typeof value === "string" && PACK_SOURCE_KEY_RE.test(value);
6902
+ }
6898
6903
  function isValidAssetGuidString(value) {
6899
6904
  return typeof value === "string" && UUID_RE.test(value);
6900
6905
  }
@@ -6906,6 +6911,48 @@ function dashFormToBytes(dashForm) {
6906
6911
  }
6907
6912
  return bytes;
6908
6913
  }
6914
+ function randomUuidBytes() {
6915
+ const uuid = uuidv7obj();
6916
+ const bytes = new Uint8Array(16);
6917
+ bytes.set(uuid.bytes);
6918
+ return bytes;
6919
+ }
6920
+ function packageId(value) {
6921
+ return value;
6922
+ }
6923
+ var PackageId = {
6924
+ parse(dashForm) {
6925
+ const parsed = AssetGuid.parse(dashForm);
6926
+ return parsed.ok ? { ok: true, value: packageId(parsed.value) } : parsed;
6927
+ },
6928
+ format(value) {
6929
+ return bytesToDashForm(value);
6930
+ },
6931
+ random() {
6932
+ return packageId(randomUuidBytes());
6933
+ }
6934
+ };
6935
+ function derivedGuid(namespace, sourceKey2) {
6936
+ if (!(namespace instanceof Uint8Array) || namespace.byteLength !== 16) {
6937
+ throw new TypeError("AssetGuid.derive requires a 16-byte PackageId");
6938
+ }
6939
+ if (!isValidPackSourceKey(sourceKey2)) {
6940
+ const error = new TypeError(
6941
+ `AssetGuid.derive received invalid sourceKey ${JSON.stringify(sourceKey2)}`
6942
+ );
6943
+ Object.defineProperty(error, "code", { value: "pack-source-key-invalid" });
6944
+ throw error;
6945
+ }
6946
+ const name = new TextEncoder().encode(sourceKey2);
6947
+ const input = new Uint8Array(namespace.byteLength + name.byteLength);
6948
+ input.set(namespace, 0);
6949
+ input.set(name, namespace.byteLength);
6950
+ const digest3 = sha1(input);
6951
+ const result = digest3.slice(0, 16);
6952
+ result[6] = (result[6] ?? 0) & 15 | 80;
6953
+ result[8] = (result[8] ?? 0) & 63 | 128;
6954
+ return brand(result);
6955
+ }
6909
6956
  var AssetGuid = {
6910
6957
  /**
6911
6958
  * Parse a 36-char RFC 4122 dash-form UUID string into an AssetGuid.
@@ -6949,507 +6996,1075 @@ var AssetGuid = {
6949
6996
  * Works in both Node.js and browser environments.
6950
6997
  */
6951
6998
  random() {
6952
- const uuid = uuidv7obj();
6953
- const bytes = new Uint8Array(16);
6954
- bytes.set(uuid.bytes);
6955
- return brand(bytes);
6999
+ return brand(randomUuidBytes());
7000
+ },
7001
+ /** Derive the stable UUIDv5 projection for one Pack subject and sourceKey. */
7002
+ derive(namespace, sourceKey2) {
7003
+ return derivedGuid(namespace, sourceKey2);
6956
7004
  }
6957
7005
  };
6958
-
6959
- // src/scriptable-pack.ts
6960
- var SCRIPTABLE_PACK_ASSET_KINDS = [
6961
- "mesh",
6962
- "material",
6963
- "scene",
6964
- "texture",
6965
- "equirect",
6966
- "sampler",
6967
- "font",
6968
- "render-pipeline",
6969
- "tileset",
6970
- "video",
6971
- "skeleton",
6972
- "skin",
6973
- "animation-clip",
6974
- "animation-graph",
6975
- "audio",
6976
- "particle-effect"
7006
+ var PACK_PARAMETER_TYPES = [
7007
+ "bool",
7008
+ "u32",
7009
+ "i32",
7010
+ "f32",
7011
+ "f64",
7012
+ "string",
7013
+ "enum",
7014
+ "vec2",
7015
+ "vec3",
7016
+ "vec4",
7017
+ "color",
7018
+ "asset-guid"
6977
7019
  ];
6978
- var SCRIPTABLE_PACK_CAPABILITY_MANIFEST = {
6979
- assetKinds: SCRIPTABLE_PACK_ASSET_KINDS,
6980
- durablePayload: true,
6981
- refs: true,
6982
- artifacts: true,
6983
- hostCapabilities: ["audio-install", "video-play", "particle-execute"]
6984
- };
6985
- function isScriptablePackAssetKind(value) {
6986
- return SCRIPTABLE_PACK_ASSET_KINDS.includes(value);
6987
- }
6988
- function sceneComponentFieldType(value) {
6989
- if (typeof value === "string") return value;
6990
- if (isRecord(value) && typeof value.type === "string") return value.type;
6991
- return void 0;
6992
- }
6993
- function projectScriptablePackSceneComponents(components) {
6994
- return (components ?? []).map((component) => ({
6995
- name: component.name,
6996
- fields: Object.fromEntries(
6997
- Object.entries(component.fields).map(([fieldName, field]) => [
6998
- fieldName,
6999
- sceneComponentFieldType(field)
7000
- ])
7001
- )
7002
- }));
7003
- }
7004
- var SCRIPTABLE_PACK_REQUEST_ID_SCHEMA = {
7005
- type: "string",
7006
- minLength: 1,
7007
- maxLength: 128,
7008
- pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$",
7009
- description: "Caller-minted identity for one idempotent ScriptablePack operation."
7010
- };
7011
- var SCRIPTABLE_PACK_SOURCE_PATH_SCHEMA = {
7012
- type: "string",
7013
- minLength: 8,
7014
- pattern: "^[^/].*\\.pack\\.ts$",
7015
- description: "Game-root-relative ScriptablePack source path."
7016
- };
7017
- var SCRIPTABLE_PACK_OWNER_GUID_SCHEMA = {
7018
- type: "string",
7019
- format: "uuid",
7020
- description: "Catalog GUID used to resolve the producer-owned source subject."
7021
- };
7022
- var SCRIPTABLE_PACK_REVISION_SCHEMA = {
7023
- type: "string",
7024
- minLength: 64,
7025
- maxLength: 64,
7026
- pattern: "^[a-f0-9]{64}$",
7027
- description: "SHA-256 source revision returned by preflight."
7028
- };
7029
- var SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA = { type: "string", minLength: 1 };
7030
- var SCRIPTABLE_PACK_ASSET_KIND_SCHEMA = {
7031
- enum: SCRIPTABLE_PACK_ASSET_KINDS,
7032
- description: "Ordinary Asset kind supported by the ScriptablePack producer/loader matrix."
7033
- };
7034
- var SCRIPTABLE_PACK_NAME_SCHEMA = { type: "string", minLength: 1 };
7035
- function scriptablePackArgsSchema(properties, required, options = {}) {
7036
- const subjectRequired = ["requestId", ...required];
7020
+ function authoringError(code, expected, hint, detail = {}, actual) {
7037
7021
  return {
7038
- type: "object",
7039
- properties: {
7040
- sourcePath: SCRIPTABLE_PACK_SOURCE_PATH_SCHEMA,
7041
- ownerGuid: SCRIPTABLE_PACK_OWNER_GUID_SCHEMA,
7042
- requestId: SCRIPTABLE_PACK_REQUEST_ID_SCHEMA,
7043
- ...properties
7044
- },
7045
- required: subjectRequired,
7046
- ...options.subject === false ? {} : {
7047
- anyOf: [
7048
- { required: [...subjectRequired, "sourcePath"] },
7049
- { required: [...subjectRequired, "ownerGuid"] },
7050
- ...options.guidSubject === false ? [] : [{ required: [...subjectRequired, "guid"] }]
7051
- ]
7052
- },
7053
- additionalProperties: false
7022
+ code,
7023
+ expected,
7024
+ hint,
7025
+ ...actual === void 0 ? {} : { actual },
7026
+ detail
7054
7027
  };
7055
7028
  }
7056
- [
7057
- {
7058
- id: "asset-source.create",
7059
- kind: "create",
7060
- domain: "session",
7061
- title: "Create Asset Source",
7062
- argsSchema: scriptablePackArgsSchema(
7063
- {
7064
- name: SCRIPTABLE_PACK_NAME_SCHEMA,
7065
- initialOutput: {
7066
- type: "object",
7067
- properties: {
7068
- sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
7069
- kind: SCRIPTABLE_PACK_ASSET_KIND_SCHEMA,
7070
- name: SCRIPTABLE_PACK_NAME_SCHEMA
7071
- },
7072
- required: ["sourceKey", "kind"],
7073
- additionalProperties: false
7074
- }
7075
- },
7076
- ["sourcePath", "initialOutput"],
7077
- { subject: false }
7078
- )
7079
- },
7080
- {
7081
- id: "asset-source.add-output",
7082
- kind: "add-output",
7083
- domain: "session",
7084
- title: "Add Asset Source Output",
7085
- argsSchema: scriptablePackArgsSchema(
7086
- {
7087
- sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
7088
- assetKind: SCRIPTABLE_PACK_ASSET_KIND_SCHEMA,
7089
- name: SCRIPTABLE_PACK_NAME_SCHEMA,
7090
- expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
7091
- },
7092
- ["sourceKey", "assetKind", "expectedRevision"]
7093
- )
7094
- },
7095
- {
7096
- id: "asset-source.add-external-asset",
7097
- kind: "add-external-asset",
7098
- domain: "session",
7099
- title: "Add Asset Source External Asset",
7100
- argsSchema: scriptablePackArgsSchema(
7101
- {
7102
- alias: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
7103
- guid: { type: "string", format: "uuid" },
7104
- expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
7105
- },
7106
- ["alias", "guid", "expectedRevision"],
7107
- { guidSubject: false }
7108
- )
7109
- },
7110
- {
7111
- id: "asset-source.rename",
7112
- kind: "rename",
7113
- domain: "session",
7114
- title: "Rename Asset Source Display Name",
7115
- argsSchema: scriptablePackArgsSchema(
7116
- {
7117
- target: {
7118
- oneOf: [
7119
- {
7120
- type: "object",
7121
- properties: { kind: { const: "package" } },
7122
- required: ["kind"],
7123
- additionalProperties: false
7124
- },
7125
- {
7126
- type: "object",
7127
- properties: {
7128
- kind: { const: "output" },
7129
- sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA
7130
- },
7131
- required: ["kind", "sourceKey"],
7132
- additionalProperties: false
7133
- }
7134
- ]
7135
- },
7136
- name: SCRIPTABLE_PACK_NAME_SCHEMA,
7137
- expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
7138
- },
7139
- ["target", "name", "expectedRevision"]
7140
- )
7141
- },
7142
- {
7143
- id: "asset-source.remove-output",
7144
- kind: "remove-output",
7145
- domain: "session",
7146
- title: "Remove Asset Source Output",
7147
- destructive: true,
7148
- argsSchema: scriptablePackArgsSchema(
7149
- {
7150
- sourceKey: SCRIPTABLE_PACK_SOURCE_KEY_SCHEMA,
7151
- confirmIncomingRefs: { type: "array", items: { type: "string" } },
7152
- expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
7153
- },
7154
- ["sourceKey", "expectedRevision"]
7155
- )
7156
- },
7157
- {
7158
- id: "asset-source.clone",
7159
- kind: "clone",
7160
- domain: "session",
7161
- title: "Clone Asset Source",
7162
- argsSchema: scriptablePackArgsSchema(
7163
- {
7164
- targetPath: SCRIPTABLE_PACK_SOURCE_PATH_SCHEMA,
7165
- expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA
7166
- },
7167
- ["targetPath", "expectedRevision"]
7168
- )
7169
- },
7170
- {
7171
- id: "asset-source.rebuild",
7172
- kind: "rebuild",
7173
- domain: "session",
7174
- title: "Rebuild Asset Source",
7175
- argsSchema: scriptablePackArgsSchema({ expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA }, [
7176
- "expectedRevision"
7177
- ])
7178
- },
7179
- {
7180
- id: "asset-source.cold-cook",
7181
- kind: "cold-cook",
7182
- domain: "session",
7183
- title: "Cold Cook Asset Source",
7184
- argsSchema: scriptablePackArgsSchema({ expectedRevision: SCRIPTABLE_PACK_REVISION_SCHEMA }, [
7185
- "expectedRevision"
7186
- ])
7187
- }
7188
- ];
7189
- function actual(value) {
7190
- if (value === null) return "null";
7191
- if (Array.isArray(value)) return "array";
7192
- if (ArrayBuffer.isView(value)) return value.constructor.name;
7193
- return typeof value;
7194
- }
7195
- function invalid(propertyPath, expected, value, sourcePath) {
7196
- return err({
7197
- code: "pack-source-definition-invalid",
7198
- expected,
7199
- hint: "repair the default exported ScriptablePack definition, then inspect Meta again",
7200
- detail: {
7201
- ...sourcePath === void 0 ? {} : { sourcePath },
7202
- propertyPath,
7203
- actual: actual(value)
7204
- }
7205
- });
7029
+ function failure(error) {
7030
+ return err(error);
7206
7031
  }
7207
7032
  function isRecord(value) {
7208
7033
  return value !== null && typeof value === "object" && !Array.isArray(value);
7209
7034
  }
7210
- function isAssetGuid(value) {
7035
+ function validateSceneComponents(value) {
7036
+ if (value === void 0) return ok(void 0);
7037
+ if (!Array.isArray(value)) {
7038
+ return failure(
7039
+ parameterFailure(
7040
+ "$.sceneComponents",
7041
+ "an array of component schema records",
7042
+ value,
7043
+ "sceneComponents is not an array"
7044
+ )
7045
+ );
7046
+ }
7047
+ const components = [];
7048
+ const names = /* @__PURE__ */ new Set();
7049
+ for (const [componentIndex, candidate] of value.entries()) {
7050
+ if (!isRecord(candidate) || typeof candidate.name !== "string" || !isRecord(candidate.fields)) {
7051
+ return failure(
7052
+ parameterFailure(
7053
+ `$.sceneComponents[${componentIndex}]`,
7054
+ "a component record with name and fields",
7055
+ candidate,
7056
+ "scene component schema is malformed"
7057
+ )
7058
+ );
7059
+ }
7060
+ if (candidate.name.length === 0 || names.has(candidate.name)) {
7061
+ return failure(
7062
+ parameterFailure(
7063
+ `$.sceneComponents[${componentIndex}].name`,
7064
+ "a non-empty unique component name",
7065
+ candidate.name,
7066
+ "scene component names must be unique"
7067
+ )
7068
+ );
7069
+ }
7070
+ names.add(candidate.name);
7071
+ const fields = {};
7072
+ for (const [fieldName, field] of Object.entries(candidate.fields)) {
7073
+ if (typeof field === "string" && field.length === 0 || typeof field !== "string" && (!isRecord(field) || typeof field.type !== "string" || field.type.length === 0)) {
7074
+ return failure(
7075
+ parameterFailure(
7076
+ `$.sceneComponents[${componentIndex}].fields.${fieldName}`,
7077
+ "a field type string or { type: string }",
7078
+ field,
7079
+ "scene component field schema is malformed"
7080
+ )
7081
+ );
7082
+ }
7083
+ fields[fieldName] = field;
7084
+ }
7085
+ components.push({ name: candidate.name, fields });
7086
+ }
7087
+ return ok(components);
7088
+ }
7089
+ function isPackageId(value) {
7211
7090
  return value instanceof Uint8Array && value.byteLength === 16;
7212
7091
  }
7213
- function guidKey(guid) {
7214
- return AssetGuid.format(guid);
7092
+ function cloneValue(value) {
7093
+ if (value instanceof Uint8Array) return value.slice();
7094
+ if (Array.isArray(value)) return value.map((item) => cloneValue(item));
7095
+ return value;
7215
7096
  }
7216
- function freezeDefinition(definition) {
7217
- const assets = Object.fromEntries(
7218
- Object.entries(definition.assets).map(([sourceKey2, descriptor]) => [
7219
- sourceKey2,
7220
- Object.freeze({ ...descriptor, guid: descriptor.guid.slice() })
7221
- ])
7222
- );
7223
- const externalAssets = Object.fromEntries(
7224
- Object.entries(definition.externalAssets).map(([alias, guid]) => [
7225
- alias,
7226
- guid.slice()
7227
- ])
7097
+ function sourceKeyFailure(sourceKey2, propertyPath = "$.sourceKey") {
7098
+ return authoringError(
7099
+ "pack-source-key-invalid",
7100
+ "a sourceKey matching ^[a-z0-9][a-z0-9._-]*(/[a-z0-9][a-z0-9._-]*)*$",
7101
+ "use a stable lower-case semantic key; do not derive it from file paths or output order",
7102
+ { propertyPath, sourceKey: sourceKey2 },
7103
+ typeof sourceKey2 === "string" ? sourceKey2 : void 0
7228
7104
  );
7229
- const sceneComponents = definition.sceneComponents === void 0 ? void 0 : Object.freeze(
7230
- projectScriptablePackSceneComponents(definition.sceneComponents).map(
7231
- (component) => Object.freeze({
7232
- ...component,
7233
- fields: Object.freeze({ ...component.fields })
7234
- })
7235
- )
7105
+ }
7106
+ function parameterFailure(propertyPath, expected, actual, reason) {
7107
+ return authoringError(
7108
+ "pack-parameter-invalid",
7109
+ expected,
7110
+ "repair the parameter declaration or the instance values, then inspect and rebuild",
7111
+ { propertyPath, reason, actual: typeof actual === "string" ? actual : JSON.stringify(actual) }
7236
7112
  );
7237
- return Object.freeze({
7238
- ...definition,
7239
- packageId: definition.packageId.slice(),
7240
- assets: Object.freeze(assets),
7241
- externalAssets: Object.freeze(externalAssets),
7242
- ...sceneComponents === void 0 ? {} : { sceneComponents }
7243
- });
7244
7113
  }
7245
- function validateScriptablePackDefinition(value, sourcePath) {
7246
- if (!isRecord(value))
7247
- return invalid("$", "a ScriptablePack definition object", value, sourcePath);
7248
- if (value.schemaVersion !== "1.0.0") {
7249
- return invalid("$.schemaVersion", "the literal '1.0.0'", value.schemaVersion, sourcePath);
7114
+ function numericType(type) {
7115
+ return type === "u32" || type === "i32" || type === "f32" || type === "f64";
7116
+ }
7117
+ function vectorLength(type) {
7118
+ switch (type) {
7119
+ case "vec2":
7120
+ return 2;
7121
+ case "vec3":
7122
+ return 3;
7123
+ case "vec4":
7124
+ case "color":
7125
+ return 4;
7126
+ default:
7127
+ return void 0;
7250
7128
  }
7251
- if (!isAssetGuid(value.packageId)) {
7252
- return invalid("$.packageId", "a 16-byte AssetGuid", value.packageId, sourcePath);
7129
+ }
7130
+ function parameterValueError(parameter, value, propertyPath) {
7131
+ const { type } = parameter;
7132
+ if (type === "bool" && typeof value !== "boolean") {
7133
+ return parameterFailure(propertyPath, "a boolean", value, "bool value has the wrong type");
7134
+ }
7135
+ if (numericType(type)) {
7136
+ if (typeof value !== "number" || !Number.isFinite(value)) {
7137
+ return parameterFailure(
7138
+ propertyPath,
7139
+ "a finite number",
7140
+ value,
7141
+ "numeric value is not finite"
7142
+ );
7143
+ }
7144
+ if ((type === "u32" || type === "i32") && !Number.isInteger(value)) {
7145
+ return parameterFailure(
7146
+ propertyPath,
7147
+ "an integer",
7148
+ value,
7149
+ "integer parameter received a fraction"
7150
+ );
7151
+ }
7152
+ if (type === "u32" && (value < 0 || value > 4294967295)) {
7153
+ return parameterFailure(
7154
+ propertyPath,
7155
+ "an unsigned 32-bit integer",
7156
+ value,
7157
+ "u32 value is outside [0, 2^32 - 1]"
7158
+ );
7159
+ }
7160
+ if (type === "i32" && (value < -2147483648 || value > 2147483647)) {
7161
+ return parameterFailure(
7162
+ propertyPath,
7163
+ "a signed 32-bit integer",
7164
+ value,
7165
+ "i32 value is outside [-2^31, 2^31 - 1]"
7166
+ );
7167
+ }
7168
+ if (type === "f32" && !Number.isFinite(Math.fround(value))) {
7169
+ return parameterFailure(
7170
+ propertyPath,
7171
+ "a finite IEEE-754 32-bit float",
7172
+ value,
7173
+ "f32 value overflows binary32"
7174
+ );
7175
+ }
7176
+ if (parameter.minimum !== void 0 && value < parameter.minimum) {
7177
+ return parameterFailure(
7178
+ propertyPath,
7179
+ `a number >= ${parameter.minimum}`,
7180
+ value,
7181
+ "value is below minimum"
7182
+ );
7183
+ }
7184
+ if (parameter.maximum !== void 0 && value > parameter.maximum) {
7185
+ return parameterFailure(
7186
+ propertyPath,
7187
+ `a number <= ${parameter.maximum}`,
7188
+ value,
7189
+ "value is above maximum"
7190
+ );
7191
+ }
7192
+ return void 0;
7253
7193
  }
7254
- if (value.name !== void 0 && typeof value.name !== "string") {
7255
- return invalid("$.name", "a string when present", value.name, sourcePath);
7256
- }
7257
- if (!isRecord(value.assets) || Object.keys(value.assets).length === 0) {
7258
- return invalid(
7259
- "$.assets",
7260
- "a non-empty sourceKey to descriptor object",
7261
- value.assets,
7262
- sourcePath
7263
- );
7194
+ if (type === "string" && typeof value !== "string") {
7195
+ return parameterFailure(propertyPath, "a string", value, "string parameter has the wrong type");
7264
7196
  }
7265
- if (!isRecord(value.externalAssets)) {
7266
- return invalid(
7267
- "$.externalAssets",
7268
- "an alias to AssetGuid object",
7269
- value.externalAssets,
7270
- sourcePath
7271
- );
7197
+ if (type === "enum") {
7198
+ if (typeof value !== "string") {
7199
+ return parameterFailure(
7200
+ propertyPath,
7201
+ "one of the declared enum strings",
7202
+ value,
7203
+ "enum value has the wrong type"
7204
+ );
7205
+ }
7206
+ if (parameter.values === void 0 || !parameter.values.includes(value)) {
7207
+ return parameterFailure(
7208
+ propertyPath,
7209
+ "one of the declared enum values",
7210
+ value,
7211
+ "enum value is not declared"
7212
+ );
7213
+ }
7214
+ return void 0;
7272
7215
  }
7273
- if (value.sceneComponents !== void 0) {
7274
- if (!Array.isArray(value.sceneComponents)) {
7275
- return invalid(
7276
- "$.sceneComponents",
7277
- "an array of component tokens or neutral schema rows when present",
7278
- value.sceneComponents,
7279
- sourcePath
7216
+ const length = vectorLength(type);
7217
+ if (length !== void 0) {
7218
+ if (!Array.isArray(value) || value.length !== length || value.some((component) => typeof component !== "number" || !Number.isFinite(component))) {
7219
+ return parameterFailure(
7220
+ propertyPath,
7221
+ `a finite numeric vector with ${length} components`,
7222
+ value,
7223
+ "vector value has the wrong shape"
7280
7224
  );
7281
7225
  }
7282
- const componentNames = /* @__PURE__ */ new Set();
7283
- for (const [componentIndex, component] of value.sceneComponents.entries()) {
7284
- if (!isRecord(component)) {
7285
- return invalid(
7286
- `$.sceneComponents[${componentIndex}]`,
7287
- "a component token or neutral schema row",
7288
- component,
7289
- sourcePath
7290
- );
7291
- }
7292
- if (typeof component.name !== "string" || component.name.trim().length === 0) {
7293
- return invalid(
7294
- `$.sceneComponents[${componentIndex}].name`,
7295
- "a non-empty component name",
7296
- component.name,
7297
- sourcePath
7298
- );
7299
- }
7300
- if (componentNames.has(component.name)) {
7301
- return invalid(
7302
- `$.sceneComponents[${componentIndex}].name`,
7303
- "component names to be unique within one ScriptablePack",
7304
- component.name,
7305
- sourcePath
7306
- );
7307
- }
7308
- componentNames.add(component.name);
7309
- if (!isRecord(component.fields)) {
7310
- return invalid(
7311
- `$.sceneComponents[${componentIndex}].fields`,
7312
- "a field-name to schema-type object",
7313
- component.fields,
7314
- sourcePath
7315
- );
7316
- }
7317
- for (const [fieldName, field] of Object.entries(component.fields)) {
7318
- const type = sceneComponentFieldType(field);
7319
- if (fieldName.trim().length === 0 || type === void 0 || type.trim().length === 0) {
7320
- return invalid(
7321
- `$.sceneComponents[${componentIndex}].fields`,
7322
- "field names with non-empty string schema types",
7323
- field,
7324
- sourcePath
7325
- );
7326
- }
7327
- }
7226
+ if (type === "color" && value.some((component) => component < 0 || component > 1)) {
7227
+ return parameterFailure(
7228
+ propertyPath,
7229
+ "an RGBA color with components in [0, 1]",
7230
+ value,
7231
+ "color component is outside [0, 1]"
7232
+ );
7328
7233
  }
7234
+ return void 0;
7329
7235
  }
7330
- if (typeof value.build !== "function") {
7331
- return invalid("$.build", "a build(assetReader) function", value.build, sourcePath);
7332
- }
7333
- const packageGuid = guidKey(value.packageId);
7334
- const localGuids = /* @__PURE__ */ new Set();
7335
- for (const [sourceKey2, descriptor] of Object.entries(value.assets)) {
7336
- if (sourceKey2.trim().length === 0) {
7337
- return invalid("$.assets", "non-empty sourceKey object keys", sourceKey2, sourcePath);
7338
- }
7339
- if (!isRecord(descriptor)) {
7340
- return invalid(
7341
- `$.assets[${JSON.stringify(sourceKey2)}]`,
7342
- "an asset descriptor",
7343
- descriptor,
7344
- sourcePath
7236
+ if (type === "asset-guid") {
7237
+ const validString = typeof value === "string" && isValidAssetGuidString(value);
7238
+ if (!(value instanceof Uint8Array && value.byteLength === 16) && !validString) {
7239
+ return parameterFailure(
7240
+ propertyPath,
7241
+ "a 16-byte AssetGuid or UUID string",
7242
+ value,
7243
+ "asset GUID value is malformed"
7244
+ );
7245
+ }
7246
+ return void 0;
7247
+ }
7248
+ return parameterFailure(
7249
+ propertyPath,
7250
+ "a supported Pack parameter type",
7251
+ value,
7252
+ "unknown parameter type"
7253
+ );
7254
+ }
7255
+ function normalizeParameterValue(parameter, value, propertyPath) {
7256
+ const error = parameterValueError(parameter, value, propertyPath);
7257
+ if (error !== void 0) return failure(error);
7258
+ if (parameter.type === "asset-guid" && typeof value === "string") {
7259
+ const parsed = AssetGuid.parse(value);
7260
+ if (!parsed.ok) {
7261
+ return failure(
7262
+ parameterFailure(
7263
+ propertyPath,
7264
+ "a valid AssetGuid UUID string",
7265
+ value,
7266
+ "asset GUID parser rejected the value"
7267
+ )
7345
7268
  );
7346
7269
  }
7347
- if (!isAssetGuid(descriptor.guid)) {
7348
- return invalid(
7349
- `$.assets[${JSON.stringify(sourceKey2)}].guid`,
7350
- "a 16-byte AssetGuid",
7351
- descriptor.guid,
7352
- sourcePath
7270
+ return ok(parsed.value);
7271
+ }
7272
+ return ok(cloneValue(value));
7273
+ }
7274
+ function validateParameterDefinitions(parameters, propertyPath = "$.parameters") {
7275
+ if (!Array.isArray(parameters) || parameters.length === 0) {
7276
+ return failure(
7277
+ parameterFailure(
7278
+ propertyPath,
7279
+ "a non-empty parameter descriptor array",
7280
+ parameters,
7281
+ "parameters must be explicit and non-empty"
7282
+ )
7283
+ );
7284
+ }
7285
+ const names = /* @__PURE__ */ new Set();
7286
+ const normalized = [];
7287
+ for (const [index, candidate] of parameters.entries()) {
7288
+ const path = `${propertyPath}[${index}]`;
7289
+ if (!isRecord(candidate))
7290
+ return failure(
7291
+ parameterFailure(
7292
+ path,
7293
+ "a parameter descriptor object",
7294
+ candidate,
7295
+ "descriptor is not an object"
7296
+ )
7297
+ );
7298
+ const name = candidate.name;
7299
+ if (typeof name !== "string" || !/^[A-Za-z][A-Za-z0-9_.-]*$/.test(name)) {
7300
+ return failure(
7301
+ parameterFailure(
7302
+ `${path}.name`,
7303
+ "a unique identifier-like parameter name",
7304
+ name,
7305
+ "parameter name is invalid"
7306
+ )
7353
7307
  );
7354
7308
  }
7355
- if (typeof descriptor.kind !== "string" || !isScriptablePackAssetKind(descriptor.kind)) {
7356
- return invalid(
7357
- `$.assets[${JSON.stringify(sourceKey2)}].kind`,
7358
- "one of SCRIPTABLE_PACK_ASSET_KINDS",
7359
- descriptor.kind,
7360
- sourcePath
7309
+ if (names.has(name))
7310
+ return failure(
7311
+ parameterFailure(
7312
+ `${path}.name`,
7313
+ "a unique parameter name",
7314
+ name,
7315
+ "parameter name is duplicated"
7316
+ )
7317
+ );
7318
+ names.add(name);
7319
+ const unknown = Object.keys(candidate).find(
7320
+ (key) => !["name", "type", "default", "minimum", "maximum", "values", "kind"].includes(key)
7321
+ );
7322
+ if (unknown !== void 0) {
7323
+ return failure(
7324
+ parameterFailure(
7325
+ `${path}.${unknown}`,
7326
+ "only name, type, default, minimum, maximum, values, and kind fields",
7327
+ candidate[unknown],
7328
+ "parameter descriptors are a closed authoring schema"
7329
+ )
7330
+ );
7331
+ }
7332
+ const type = candidate.type;
7333
+ if (typeof type !== "string" || !PACK_PARAMETER_TYPES.includes(type)) {
7334
+ return failure(
7335
+ parameterFailure(
7336
+ `${path}.type`,
7337
+ PACK_PARAMETER_TYPES.join(" | "),
7338
+ type,
7339
+ "parameter type is not supported"
7340
+ )
7361
7341
  );
7362
7342
  }
7363
- if (descriptor.name !== void 0 && typeof descriptor.name !== "string") {
7364
- return invalid(
7365
- `$.assets[${JSON.stringify(sourceKey2)}].name`,
7366
- "a string when present",
7367
- descriptor.name,
7368
- sourcePath
7343
+ const typed = {
7344
+ ...candidate,
7345
+ name,
7346
+ type
7347
+ };
7348
+ if (typed.values !== void 0) {
7349
+ if (typed.type !== "enum" || !Array.isArray(typed.values) || typed.values.length === 0 || typed.values.some((item) => typeof item !== "string") || new Set(typed.values).size !== typed.values.length) {
7350
+ return failure(
7351
+ parameterFailure(
7352
+ `${path}.values`,
7353
+ "unique non-empty strings for enum",
7354
+ typed.values,
7355
+ "enum values are malformed"
7356
+ )
7357
+ );
7358
+ }
7359
+ } else if (typed.type === "enum") {
7360
+ return failure(
7361
+ parameterFailure(
7362
+ `${path}.values`,
7363
+ "a non-empty enum choices array",
7364
+ typed.values,
7365
+ "enum has no choices"
7366
+ )
7369
7367
  );
7370
7368
  }
7371
- const guid = guidKey(descriptor.guid);
7372
- if (guid === packageGuid || localGuids.has(guid)) {
7373
- return invalid(
7374
- `$.assets[${JSON.stringify(sourceKey2)}].guid`,
7375
- "a GUID unique within the package and distinct from packageId",
7376
- descriptor.guid,
7377
- sourcePath
7369
+ if (typed.kind !== void 0 && (typed.type !== "asset-guid" || typeof typed.kind !== "string")) {
7370
+ return failure(
7371
+ parameterFailure(
7372
+ `${path}.kind`,
7373
+ "a kind constraint only on asset-guid parameters",
7374
+ typed.kind,
7375
+ "kind is misplaced"
7376
+ )
7378
7377
  );
7379
7378
  }
7380
- localGuids.add(guid);
7381
- }
7382
- const externalGuids = /* @__PURE__ */ new Set();
7383
- for (const [alias, guidValue] of Object.entries(value.externalAssets)) {
7384
- if (alias.trim().length === 0) {
7385
- return invalid("$.externalAssets", "non-empty alias object keys", alias, sourcePath);
7379
+ for (const field of ["minimum", "maximum"]) {
7380
+ const bound = typed[field];
7381
+ if (bound !== void 0 && (typeof bound !== "number" || !Number.isFinite(bound))) {
7382
+ return failure(
7383
+ parameterFailure(
7384
+ `${path}.${field}`,
7385
+ "a finite numeric bound",
7386
+ bound,
7387
+ "numeric bound is invalid"
7388
+ )
7389
+ );
7390
+ }
7386
7391
  }
7387
- if (!isAssetGuid(guidValue)) {
7388
- return invalid(
7389
- `$.externalAssets[${JSON.stringify(alias)}]`,
7390
- "a 16-byte AssetGuid",
7391
- guidValue,
7392
- sourcePath
7392
+ if (!numericType(typed.type) && (typed.minimum !== void 0 || typed.maximum !== void 0)) {
7393
+ return failure(
7394
+ parameterFailure(
7395
+ path,
7396
+ "minimum/maximum only on numeric parameters",
7397
+ typed,
7398
+ "numeric bounds are not meaningful for this parameter type"
7399
+ )
7393
7400
  );
7394
7401
  }
7395
- const guid = guidKey(guidValue);
7396
- if (guid === packageGuid || localGuids.has(guid) || externalGuids.has(guid)) {
7397
- return invalid(
7398
- `$.externalAssets[${JSON.stringify(alias)}]`,
7399
- "a unique foreign GUID not owned by this package",
7400
- guidValue,
7401
- sourcePath
7402
+ if (typed.minimum !== void 0 && typed.maximum !== void 0 && typed.minimum > typed.maximum) {
7403
+ return failure(
7404
+ parameterFailure(path, "minimum <= maximum", typed, "numeric bounds are reversed")
7402
7405
  );
7403
7406
  }
7404
- externalGuids.add(guid);
7407
+ const defaultResult = normalizeParameterValue(typed, typed.default, `${path}.default`);
7408
+ if (!defaultResult.ok) return defaultResult;
7409
+ normalized.push({
7410
+ ...typed,
7411
+ default: defaultResult.value,
7412
+ ...typed.values === void 0 ? {} : { values: Object.freeze([...typed.values]) }
7413
+ });
7405
7414
  }
7406
- return ok(freezeDefinition(value));
7415
+ return ok(Object.freeze(normalized));
7407
7416
  }
7408
- function projectScriptablePackMeta(definition, source) {
7409
- const subAssets = Object.entries(definition.assets).sort(([left], [right]) => left.localeCompare(right)).map(([sourceKey2, descriptor], sourceIndex) => ({
7410
- guid: guidKey(descriptor.guid),
7411
- sourceIndex,
7412
- sourceKey: sourceKey2,
7413
- kind: descriptor.kind,
7414
- ...descriptor.name === void 0 ? {} : { name: descriptor.name }
7415
- }));
7416
- const externalAssets = Object.entries(definition.externalAssets).sort(([left], [right]) => left.localeCompare(right)).map(([alias, guid]) => ({ alias, guid: guidKey(guid) }));
7417
- return {
7418
- schemaVersion: "1.0.0",
7419
- kind: "external-asset-package",
7420
- packageId: guidKey(definition.packageId),
7421
- ...definition.name === void 0 ? {} : { name: definition.name },
7422
- importer: "pack-ts",
7423
- source,
7424
- importSettings: { contractVersion: "1.0.0", externalAssets },
7425
- subAssets
7426
- };
7417
+ function definePackageId(value) {
7418
+ if (isPackageId(value)) return value.slice();
7419
+ const parsed = PackageId.parse(value);
7420
+ if (!parsed.ok) {
7421
+ const error = authoringError(
7422
+ "pack-package-id-invalid",
7423
+ "a 36-character RFC 4122 UUID packageId",
7424
+ "use PackageId.random() or repair the packageId literal before loading the Pack",
7425
+ { value },
7426
+ value
7427
+ );
7428
+ throw Object.assign(new TypeError(error.hint), error);
7429
+ }
7430
+ return parsed.value;
7427
7431
  }
7428
-
7429
- // src/catalog-projection.ts
7430
- function catalogProjectionFor(subject, execution, lifecycle) {
7432
+ function cloneDefinition(definition) {
7433
+ const parameters = "parameters" in definition ? Object.freeze(
7434
+ definition.parameters.map(
7435
+ (parameter) => Object.freeze({
7436
+ ...parameter,
7437
+ default: cloneValue(parameter.default),
7438
+ ...parameter.values === void 0 ? {} : { values: Object.freeze(parameter.values.map((value) => cloneValue(value))) }
7439
+ })
7440
+ )
7441
+ ) : void 0;
7442
+ const sceneComponents = definition.sceneComponents === void 0 ? void 0 : Object.freeze(
7443
+ definition.sceneComponents.map(
7444
+ (component) => Object.freeze({
7445
+ name: component.name,
7446
+ fields: Object.freeze({ ...component.fields })
7447
+ })
7448
+ )
7449
+ );
7450
+ return Object.freeze({
7451
+ ...definition,
7452
+ packageId: definition.packageId.slice(),
7453
+ ...parameters === void 0 ? {} : { parameters },
7454
+ ...sceneComponents === void 0 ? {} : { sceneComponents }
7455
+ });
7456
+ }
7457
+ function definePack(definition) {
7458
+ const validated = validatePackDefinition(definition);
7459
+ if (!validated.ok) throw Object.assign(new TypeError(validated.error.hint), validated.error);
7460
+ return cloneDefinition(
7461
+ validated.value
7462
+ );
7463
+ }
7464
+ function projectParameterizedPackMeta(definition, sourcePath) {
7431
7465
  return {
7432
- subject,
7433
- execution,
7434
- lifecycle,
7435
- operations: catalogOperationsFor({ subject, execution, lifecycle })
7466
+ schemaVersion: "2.0.0",
7467
+ kind: "parameterized-pack-source",
7468
+ packageId: PackageId.format(definition.packageId),
7469
+ source: sourcePath,
7470
+ ..."parameters" in definition ? {
7471
+ parameters: definition.parameters.map((parameter) => ({
7472
+ name: parameter.name,
7473
+ type: parameter.type,
7474
+ default: jsonParameterValue(parameter.default),
7475
+ ...parameter.minimum === void 0 ? {} : { minimum: parameter.minimum },
7476
+ ...parameter.maximum === void 0 ? {} : { maximum: parameter.maximum },
7477
+ ...parameter.values === void 0 ? {} : { values: parameter.values },
7478
+ ...parameter.kind === void 0 ? {} : { kind: parameter.kind }
7479
+ }))
7480
+ } : {}
7436
7481
  };
7437
7482
  }
7438
- function currentProjectionFor(subject, execution) {
7439
- return catalogRowProjectionFor(subject, execution, "current");
7440
- }
7441
- function catalogRowProjectionFor(subject, execution, lifecycle) {
7442
- const projection = catalogProjectionFor(subject, execution, lifecycle);
7443
- return { ...projection, projection };
7483
+ function jsonParameterValue(value) {
7484
+ return value instanceof Uint8Array ? AssetGuid.format(value) : value;
7444
7485
  }
7445
- function referenceRelations(guid, refs, provenance) {
7446
- if (refs === void 0 || refs.length === 0) return void 0;
7447
- return refs.map((ref) => ({
7448
- from: { type: "asset", id: guid },
7449
- to: { type: "asset", id: ref },
7450
- type: "references",
7451
- policy: { strength: "required" },
7452
- provenance
7486
+ function validatePackDefinition(value, sourcePath) {
7487
+ if (!isRecord(value))
7488
+ return failure(
7489
+ parameterFailure("$", "a Pack definition object", value, "definition is not an object")
7490
+ );
7491
+ const unknown = Object.keys(value).find(
7492
+ (key) => !["schemaVersion", "packageId", "name", "parameters", "sceneComponents", "build"].includes(
7493
+ key
7494
+ )
7495
+ );
7496
+ if (unknown !== void 0) {
7497
+ return failure(
7498
+ parameterFailure(
7499
+ `$.${unknown}`,
7500
+ "only schemaVersion, packageId, name, parameters, sceneComponents, and build fields",
7501
+ value[unknown],
7502
+ "legacy static output declarations and external asset tables are not part of v2 authoring"
7503
+ )
7504
+ );
7505
+ }
7506
+ if (value.schemaVersion !== "2.0.0") {
7507
+ return failure(
7508
+ parameterFailure(
7509
+ "$.schemaVersion",
7510
+ "the literal '2.0.0'",
7511
+ value.schemaVersion,
7512
+ "authoring source schema is unsupported"
7513
+ )
7514
+ );
7515
+ }
7516
+ if (!isPackageId(value.packageId)) {
7517
+ return failure(
7518
+ authoringError(
7519
+ "pack-package-id-invalid",
7520
+ "a 16-byte PackageId",
7521
+ "use definePackageId() for the stable source identity",
7522
+ { sourcePath, propertyPath: "$.packageId" },
7523
+ typeof value.packageId === "string" ? value.packageId : void 0
7524
+ )
7525
+ );
7526
+ }
7527
+ if (value.name !== void 0 && typeof value.name !== "string") {
7528
+ return failure(
7529
+ parameterFailure("$.name", "a string when present", value.name, "display name is malformed")
7530
+ );
7531
+ }
7532
+ if (typeof value.build !== "function") {
7533
+ return failure(
7534
+ parameterFailure("$.build", "a build function", value.build, "build is not callable")
7535
+ );
7536
+ }
7537
+ const sceneComponents = validateSceneComponents(value.sceneComponents);
7538
+ if (!sceneComponents.ok) return sceneComponents;
7539
+ const hasParameters = Object.hasOwn(value, "parameters");
7540
+ if (!hasParameters) {
7541
+ return ok(
7542
+ Object.freeze({
7543
+ schemaVersion: "2.0.0",
7544
+ packageId: value.packageId,
7545
+ ...value.name === void 0 ? {} : { name: value.name },
7546
+ ...sceneComponents.value === void 0 ? {} : { sceneComponents: sceneComponents.value },
7547
+ build: value.build
7548
+ })
7549
+ );
7550
+ }
7551
+ const parameters = validateParameterDefinitions(value.parameters);
7552
+ if (!parameters.ok) return parameters;
7553
+ return ok(
7554
+ Object.freeze({
7555
+ schemaVersion: "2.0.0",
7556
+ packageId: value.packageId,
7557
+ ...value.name === void 0 ? {} : { name: value.name },
7558
+ ...sceneComponents.value === void 0 ? {} : { sceneComponents: sceneComponents.value },
7559
+ parameters: parameters.value,
7560
+ build: value.build
7561
+ })
7562
+ );
7563
+ }
7564
+ function isParameterizedPack(value) {
7565
+ return isRecord(value) && Array.isArray(value.parameters) && value.parameters.length > 0;
7566
+ }
7567
+ function resolvePackParameterValues(definition, overrides = {}, inheritedValues) {
7568
+ if (!isRecord(overrides)) {
7569
+ return failure(
7570
+ parameterFailure(
7571
+ "$.values",
7572
+ "an object of sparse parameter overrides",
7573
+ overrides,
7574
+ "values is not an object"
7575
+ )
7576
+ );
7577
+ }
7578
+ const descriptors = new Map(
7579
+ definition.parameters.map((parameter) => [parameter.name, parameter])
7580
+ );
7581
+ for (const name of Object.keys(overrides)) {
7582
+ if (!descriptors.has(name)) {
7583
+ return failure(
7584
+ parameterFailure(
7585
+ `$.values.${name}`,
7586
+ "a declared parameter name",
7587
+ name,
7588
+ "instance contains an unknown parameter"
7589
+ )
7590
+ );
7591
+ }
7592
+ }
7593
+ const values = {};
7594
+ for (const parameter of definition.parameters) {
7595
+ const raw = Object.hasOwn(overrides, parameter.name) ? overrides[parameter.name] : inheritedValues?.[parameter.name] ?? parameter.default;
7596
+ const normalized = normalizeParameterValue(parameter, raw, `$.values.${parameter.name}`);
7597
+ if (!normalized.ok) return normalized;
7598
+ values[parameter.name] = normalized.value;
7599
+ }
7600
+ return ok(Object.freeze(values));
7601
+ }
7602
+ function jsonError(propertyPath, expected, actual, reason) {
7603
+ return failure(
7604
+ authoringError(
7605
+ "pack-parameter-invalid",
7606
+ expected,
7607
+ "repair the v3 pack.json authoring data, then rerun scan or rebuild",
7608
+ {
7609
+ propertyPath,
7610
+ reason,
7611
+ actual: typeof actual === "string" ? actual : JSON.stringify(actual)
7612
+ }
7613
+ )
7614
+ );
7615
+ }
7616
+ function parsePackageId(value, propertyPath) {
7617
+ if (typeof value !== "string") {
7618
+ return failure(
7619
+ authoringError(
7620
+ "pack-package-id-invalid",
7621
+ "a UUID string",
7622
+ "repair packageId in the authoring file",
7623
+ { propertyPath, value }
7624
+ )
7625
+ );
7626
+ }
7627
+ const parsed = PackageId.parse(value);
7628
+ if (!parsed.ok) {
7629
+ return failure(
7630
+ authoringError(
7631
+ "pack-package-id-invalid",
7632
+ "a 36-character RFC 4122 UUID",
7633
+ "repair packageId or use the authoring gateway to mint one",
7634
+ { propertyPath, value },
7635
+ value
7636
+ )
7637
+ );
7638
+ }
7639
+ return ok(parsed.value);
7640
+ }
7641
+ function parsePackJsonAsset(value, sourceKey2) {
7642
+ if (!isRecord(value))
7643
+ return jsonError(
7644
+ `$.assets.${sourceKey2}`,
7645
+ "an asset entry object",
7646
+ value,
7647
+ "asset entry is not an object"
7648
+ );
7649
+ const allowed = /* @__PURE__ */ new Set(["kind", "payload", "refs", "name", "artifacts"]);
7650
+ const unknown = Object.keys(value).find((key) => !allowed.has(key));
7651
+ if (unknown !== void 0)
7652
+ return jsonError(
7653
+ `$.assets.${sourceKey2}.${unknown}`,
7654
+ "no GUID or extra authoring field",
7655
+ value[unknown],
7656
+ "direct v3 entries derive identity from their object key"
7657
+ );
7658
+ if (typeof value.kind !== "string" || value.kind.length === 0)
7659
+ return jsonError(
7660
+ `$.assets.${sourceKey2}.kind`,
7661
+ "a non-empty asset kind",
7662
+ value.kind,
7663
+ "asset kind is missing"
7664
+ );
7665
+ if (!isRecord(value.payload))
7666
+ return jsonError(
7667
+ `$.assets.${sourceKey2}.payload`,
7668
+ "a JSON object payload",
7669
+ value.payload,
7670
+ "payload must be a plain object"
7671
+ );
7672
+ if (!Array.isArray(value.refs) || value.refs.some((ref) => typeof ref !== "string" || !isValidAssetGuidString(ref))) {
7673
+ return jsonError(
7674
+ `$.assets.${sourceKey2}.refs`,
7675
+ "an array of UUID references",
7676
+ value.refs,
7677
+ "references must already be real AssetGuids"
7678
+ );
7679
+ }
7680
+ if (value.name !== void 0 && (typeof value.name !== "string" || value.name.length === 0))
7681
+ return jsonError(
7682
+ `$.assets.${sourceKey2}.name`,
7683
+ "a non-empty display name when present",
7684
+ value.name,
7685
+ "asset name is malformed"
7686
+ );
7687
+ if (value.artifacts !== void 0 && !isRecord(value.artifacts))
7688
+ return jsonError(
7689
+ `$.assets.${sourceKey2}.artifacts`,
7690
+ "an artifact descriptor object when present",
7691
+ value.artifacts,
7692
+ "artifacts are not an object"
7693
+ );
7694
+ const base = {
7695
+ kind: value.kind,
7696
+ payload: value.payload,
7697
+ refs: Object.freeze([...value.refs]),
7698
+ ...value.name === void 0 ? {} : { name: value.name }
7699
+ };
7700
+ if (value.artifacts === void 0) return ok(base);
7701
+ const withArtifacts = {
7702
+ ...base,
7703
+ artifacts: value.artifacts
7704
+ };
7705
+ return ok(withArtifacts);
7706
+ }
7707
+ function parseParameterizedPackJson(value) {
7708
+ if (!isRecord(value))
7709
+ return jsonError("$", "a v3 pack.json object", value, "document is not an object");
7710
+ if (value.schemaVersion !== "3.0.0")
7711
+ return jsonError(
7712
+ "$.schemaVersion",
7713
+ "the literal '3.0.0'",
7714
+ value.schemaVersion,
7715
+ "document schema is not v3"
7716
+ );
7717
+ const unknown = Object.keys(value).find(
7718
+ (key) => !["schemaVersion", "packageId", "assets", "parent", "values"].includes(key)
7719
+ );
7720
+ if (unknown !== void 0)
7721
+ return jsonError(
7722
+ `$.${unknown}`,
7723
+ "no extra top-level authoring fields",
7724
+ value[unknown],
7725
+ "keep the v3 document to packageId+assets or packageId+parent+values"
7726
+ );
7727
+ const packageResult = parsePackageId(value.packageId, "$.packageId");
7728
+ if (!packageResult.ok) return packageResult;
7729
+ const hasAssets = Object.hasOwn(value, "assets");
7730
+ const hasParent = Object.hasOwn(value, "parent");
7731
+ const hasValues = Object.hasOwn(value, "values");
7732
+ if (hasAssets && (hasParent || hasValues))
7733
+ return jsonError(
7734
+ "$",
7735
+ "assets or parent+values, never both",
7736
+ value,
7737
+ "direct and instance branches are mutually exclusive"
7738
+ );
7739
+ if (hasAssets) {
7740
+ if (!isRecord(value.assets))
7741
+ return jsonError(
7742
+ "$.assets",
7743
+ "a sourceKey to asset object",
7744
+ value.assets,
7745
+ "direct assets are not an object"
7746
+ );
7747
+ const assets = {};
7748
+ for (const [sourceKey2, entry] of Object.entries(value.assets)) {
7749
+ if (!isValidPackSourceKey(sourceKey2))
7750
+ return failure(sourceKeyFailure(sourceKey2, `$.assets.${sourceKey2}`));
7751
+ const parsed = parsePackJsonAsset(entry, sourceKey2);
7752
+ if (!parsed.ok) return parsed;
7753
+ assets[sourceKey2] = parsed.value;
7754
+ }
7755
+ return ok({
7756
+ format: "direct",
7757
+ schemaVersion: "3.0.0",
7758
+ packageId: packageResult.value,
7759
+ assets: Object.freeze(assets)
7760
+ });
7761
+ }
7762
+ if (!hasParent || !hasValues || hasAssets)
7763
+ return jsonError(
7764
+ "$",
7765
+ "parent and values for an instance document",
7766
+ value,
7767
+ "instance branch is incomplete"
7768
+ );
7769
+ const parentResult = parsePackageId(value.parent, "$.parent");
7770
+ if (!parentResult.ok) return parentResult;
7771
+ if (!isRecord(value.values))
7772
+ return jsonError(
7773
+ "$.values",
7774
+ "a sparse parameter object",
7775
+ value.values,
7776
+ "instance values are not an object"
7777
+ );
7778
+ return ok({
7779
+ format: "instance",
7780
+ schemaVersion: "3.0.0",
7781
+ packageId: packageResult.value,
7782
+ parent: parentResult.value,
7783
+ values: Object.freeze({ ...value.values })
7784
+ });
7785
+ }
7786
+ var parsePackJson = parseParameterizedPackJson;
7787
+ var parsePackAuthoringJson = parseParameterizedPackJson;
7788
+ function projectDirectPackJson(value) {
7789
+ const parsed = "format" in value ? ok(value) : parseParameterizedPackJson(value);
7790
+ if (!parsed.ok) return parsed;
7791
+ if (parsed.value.format !== "direct") {
7792
+ return failure(
7793
+ authoringError(
7794
+ "pack-parameter-invalid",
7795
+ "a direct v3 pack.json",
7796
+ "projectDirectPackJson accepts the direct branch only",
7797
+ { observed: parsed.value.format }
7798
+ )
7799
+ );
7800
+ }
7801
+ const assets = [];
7802
+ for (const [sourceKey2, entry] of Object.entries(parsed.value.assets).sort(
7803
+ ([left], [right]) => left.localeCompare(right)
7804
+ )) {
7805
+ assets.push({
7806
+ ...entry,
7807
+ guid: AssetGuid.format(AssetGuid.derive(parsed.value.packageId, sourceKey2)),
7808
+ sourceKey: sourceKey2
7809
+ });
7810
+ }
7811
+ return ok({ packageId: PackageId.format(parsed.value.packageId), assets: Object.freeze(assets) });
7812
+ }
7813
+ function subjectId(subject) {
7814
+ return PackageId.format(subject.packageId).toLowerCase();
7815
+ }
7816
+ async function resolvePackParameterInheritance(subject, readParent) {
7817
+ const visited = /* @__PURE__ */ new Set();
7818
+ async function visit(current, path) {
7819
+ const currentId = subjectId(current);
7820
+ if (visited.has(currentId) || path.includes(currentId)) {
7821
+ return failure(
7822
+ authoringError(
7823
+ "pack-parent-cycle",
7824
+ "a finite acyclic Pack parent chain",
7825
+ "inspect parentChain and repair the first repeated packageId",
7826
+ { packageId: currentId, parentChain: [...path, currentId] }
7827
+ )
7828
+ );
7829
+ }
7830
+ visited.add(currentId);
7831
+ const nextPath = [...path, currentId];
7832
+ if (current.format === "direct") {
7833
+ return failure(
7834
+ authoringError(
7835
+ "pack-parent-not-parameterized",
7836
+ "a parameterized Pack source as the parent",
7837
+ "direct packs cannot be instance parents; point the instance at a parameterized source",
7838
+ { packageId: currentId }
7839
+ )
7840
+ );
7841
+ }
7842
+ if (current.format === "source") {
7843
+ const descriptors2 = validateParameterDefinitions(current.parameters, "$.parameters");
7844
+ if (!descriptors2.ok) return descriptors2;
7845
+ if (descriptors2.value.length === 0) {
7846
+ return failure(
7847
+ authoringError(
7848
+ "pack-parent-not-parameterized",
7849
+ "the parent source to declare a non-empty parameter list",
7850
+ "use clone for an independent zero-parameter Pack instead of creating an empty instance",
7851
+ { packageId: currentId }
7852
+ )
7853
+ );
7854
+ }
7855
+ const definition2 = {
7856
+ packageId: current.packageId,
7857
+ parameters: descriptors2.value};
7858
+ const values2 = resolvePackParameterValues(definition2);
7859
+ if (!values2.ok) return values2;
7860
+ return ok({
7861
+ packageId: current.packageId,
7862
+ rootPackageId: current.packageId,
7863
+ values: values2.value,
7864
+ parentChain: [],
7865
+ parameters: descriptors2.value
7866
+ });
7867
+ }
7868
+ const parent = await readParent(current.parent);
7869
+ if (parent === void 0) {
7870
+ return failure(
7871
+ authoringError(
7872
+ "pack-parent-not-found",
7873
+ "the parent packageId to resolve in the source index",
7874
+ "inspect the parent locator or recreate the instance from a live parameterized Pack",
7875
+ { packageId: currentId, parent: PackageId.format(current.parent) }
7876
+ )
7877
+ );
7878
+ }
7879
+ if (PackageId.format(parent.packageId).toLowerCase() !== PackageId.format(current.parent).toLowerCase()) {
7880
+ return failure(
7881
+ authoringError(
7882
+ "pack-parent-not-found",
7883
+ "the parent reader to return the requested packageId",
7884
+ "repair the Source Index locator and retry inheritance resolution",
7885
+ {
7886
+ packageId: currentId,
7887
+ requestedParent: PackageId.format(current.parent),
7888
+ observedParent: PackageId.format(parent.packageId)
7889
+ }
7890
+ )
7891
+ );
7892
+ }
7893
+ const resolvedParent = await visit(parent, nextPath);
7894
+ if (!resolvedParent.ok) return resolvedParent;
7895
+ const descriptors = resolvedParent.value.parameters;
7896
+ const definition = {
7897
+ packageId: resolvedParent.value.rootPackageId,
7898
+ parameters: descriptors};
7899
+ const values = resolvePackParameterValues(
7900
+ definition,
7901
+ current.values,
7902
+ resolvedParent.value.values
7903
+ );
7904
+ if (!values.ok) return values;
7905
+ return ok({
7906
+ packageId: current.packageId,
7907
+ rootPackageId: resolvedParent.value.rootPackageId,
7908
+ values: values.value,
7909
+ parentChain: [PackageId.format(current.parent), ...resolvedParent.value.parentChain],
7910
+ parameters: descriptors
7911
+ });
7912
+ }
7913
+ return visit(subject, []);
7914
+ }
7915
+ var resolvePackInheritance = resolvePackParameterInheritance;
7916
+ var PARAMETERIZED_PACK_OPERATION_IDS = [
7917
+ "asset.list",
7918
+ "asset.inspect",
7919
+ "asset.resolve",
7920
+ "asset.verify",
7921
+ "asset-source.create",
7922
+ "asset-source.clone",
7923
+ "asset-source.create-instance",
7924
+ "asset-source.apply-values",
7925
+ "asset-source.rebuild",
7926
+ "asset-source.cold-cook"
7927
+ ];
7928
+ function createParameterizedPackAuthoringGateway(port) {
7929
+ const requests = /* @__PURE__ */ new Map();
7930
+ return {
7931
+ execute(operation) {
7932
+ if (typeof operation.requestId !== "string" || operation.requestId.trim().length === 0) {
7933
+ return Promise.resolve(
7934
+ failure(
7935
+ authoringError(
7936
+ "pack-source-revision-conflict",
7937
+ "a non-empty caller-minted requestId",
7938
+ "mint a new requestId and retry",
7939
+ { requestId: operation.requestId }
7940
+ )
7941
+ )
7942
+ );
7943
+ }
7944
+ let fingerprint;
7945
+ try {
7946
+ fingerprint = JSON.stringify(operation);
7947
+ } catch (cause) {
7948
+ return Promise.resolve(
7949
+ failure(
7950
+ authoringError(
7951
+ "pack-parameter-invalid",
7952
+ "a JSON-serialisable authoring operation",
7953
+ "remove non-serialisable operation fields and retry with a new requestId",
7954
+ {
7955
+ requestId: operation.requestId,
7956
+ cause: cause instanceof Error ? cause.message : String(cause)
7957
+ }
7958
+ )
7959
+ )
7960
+ );
7961
+ }
7962
+ const previous = requests.get(operation.requestId);
7963
+ if (previous !== void 0) {
7964
+ if (previous.fingerprint === fingerprint) return previous.result;
7965
+ return Promise.resolve(
7966
+ failure(
7967
+ authoringError(
7968
+ "pack-source-revision-conflict",
7969
+ "one immutable operation per requestId",
7970
+ "read the original result or mint a new requestId",
7971
+ { requestId: operation.requestId }
7972
+ )
7973
+ )
7974
+ );
7975
+ }
7976
+ const result = Promise.resolve().then(() => port.execute(operation)).catch(
7977
+ (cause) => failure(
7978
+ authoringError(
7979
+ "pack-parameter-invalid",
7980
+ "the Pack authoring port to return a structured Result",
7981
+ "repair the gateway port failure, then retry with a new requestId",
7982
+ {
7983
+ requestId: operation.requestId,
7984
+ cause: cause instanceof Error ? cause.message : String(cause)
7985
+ }
7986
+ )
7987
+ )
7988
+ );
7989
+ requests.set(operation.requestId, { fingerprint, result });
7990
+ return result;
7991
+ }
7992
+ };
7993
+ }
7994
+ var PARAMETERIZED_PACK_OPERATION_DESCRIPTORS = PARAMETERIZED_PACK_OPERATION_IDS.map(
7995
+ (id) => ({
7996
+ id,
7997
+ domain: id.startsWith("asset-source.") ? "session" : "asset",
7998
+ readOnly: id.startsWith("asset."),
7999
+ requiresRevision: !id.startsWith("asset.") && id !== "asset-source.create"
8000
+ })
8001
+ );
8002
+
8003
+ // src/scriptable-pack.ts
8004
+ var SCRIPTABLE_PACK_ASSET_KINDS = [
8005
+ "mesh",
8006
+ "material",
8007
+ "scene",
8008
+ "texture",
8009
+ "equirect",
8010
+ "sampler",
8011
+ "font",
8012
+ "render-pipeline",
8013
+ "tileset",
8014
+ "video",
8015
+ "skeleton",
8016
+ "skin",
8017
+ "animation-clip",
8018
+ "animation-graph",
8019
+ "audio",
8020
+ "particle-effect",
8021
+ "ies-profile"
8022
+ ];
8023
+ function isScriptablePackAssetKind(value) {
8024
+ return SCRIPTABLE_PACK_ASSET_KINDS.includes(value);
8025
+ }
8026
+ function sceneComponentFieldType(value) {
8027
+ if (typeof value === "string") return value;
8028
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
8029
+ const type = value.type;
8030
+ return typeof type === "string" ? type : void 0;
8031
+ }
8032
+ function projectScriptablePackSceneComponents(components) {
8033
+ return (components ?? []).map((component) => ({
8034
+ name: component.name,
8035
+ fields: Object.fromEntries(
8036
+ Object.entries(component.fields).map(([fieldName, field]) => [
8037
+ fieldName,
8038
+ sceneComponentFieldType(field)
8039
+ ])
8040
+ )
8041
+ }));
8042
+ }
8043
+
8044
+ // src/catalog-projection.ts
8045
+ function catalogProjectionFor(subject, execution, lifecycle) {
8046
+ return {
8047
+ subject,
8048
+ execution,
8049
+ lifecycle,
8050
+ operations: catalogOperationsFor({ subject, execution, lifecycle })
8051
+ };
8052
+ }
8053
+ function currentProjectionFor(subject, execution) {
8054
+ return catalogRowProjectionFor(subject, execution, "current");
8055
+ }
8056
+ function catalogRowProjectionFor(subject, execution, lifecycle) {
8057
+ const projection = catalogProjectionFor(subject, execution, lifecycle);
8058
+ return { ...projection, projection };
8059
+ }
8060
+ function referenceRelations(guid, refs, provenance) {
8061
+ if (refs === void 0 || refs.length === 0) return void 0;
8062
+ return refs.map((ref) => ({
8063
+ from: { type: "asset", id: guid },
8064
+ to: { type: "asset", id: ref },
8065
+ type: "references",
8066
+ policy: { strength: "required" },
8067
+ provenance
7453
8068
  }));
7454
8069
  }
7455
8070
  function producerFields(producer, output) {
@@ -7531,10 +8146,10 @@ function projectCookedPackageEntry(entry, projection) {
7531
8146
  refs: projection.refs
7532
8147
  };
7533
8148
  }
7534
- function runtimeCatalogRowError(subjectId, expected) {
8149
+ function runtimeCatalogRowError(subjectId2, expected) {
7535
8150
  const diagnostic = {
7536
8151
  code: "invalid-producer-fact",
7537
- subject: { type: "asset", id: subjectId },
8152
+ subject: { type: "asset", id: subjectId2 },
7538
8153
  expected,
7539
8154
  hint: "recook the source and publish one current runtime Catalog row",
7540
8155
  authority: "producer"
@@ -7585,27 +8200,6 @@ function projectRuntimeCatalogRow(input) {
7585
8200
  }
7586
8201
  return projectRuntimeCatalogRowValue(input);
7587
8202
  }
7588
- function loadAssetConfig(cwd) {
7589
- let pkg;
7590
- try {
7591
- pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
7592
- } catch {
7593
- return { roots: [join(cwd, "assets")], paths: {} };
7594
- }
7595
- const assets = pkg.forgeax?.assets;
7596
- const rawRoots = assets?.roots;
7597
- const roots = Array.isArray(rawRoots) && rawRoots.length > 0 ? rawRoots.map((r) => join(cwd, r)) : [join(cwd, "assets")];
7598
- const rawPaths = assets?.paths;
7599
- const paths = {};
7600
- if (rawPaths !== void 0 && rawPaths !== null && typeof rawPaths === "object") {
7601
- for (const [name, dir] of Object.entries(rawPaths)) {
7602
- if (typeof dir === "string") {
7603
- paths[name] = join(cwd, dir);
7604
- }
7605
- }
7606
- }
7607
- return { roots, paths };
7608
- }
7609
8203
 
7610
8204
  // src/deriveAssetName.ts
7611
8205
  function baseName(p) {
@@ -7613,85 +8207,26 @@ function baseName(p) {
7613
8207
  const idx = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
7614
8208
  return idx === -1 ? trimmed : trimmed.slice(idx + 1);
7615
8209
  }
7616
- function deriveAssetName(packagePath, assetCount, storedName) {
7617
- if (packagePath === null) {
7618
- return storedName ?? "";
7619
- }
7620
- if (assetCount === 1) {
7621
- return baseName(packagePath);
7622
- }
8210
+ function deriveAssetName(packagePath, _assetCount, storedName) {
7623
8211
  if (storedName !== void 0) {
7624
8212
  return storedName;
7625
8213
  }
8214
+ if (packagePath === null) {
8215
+ return "";
8216
+ }
7626
8217
  return baseName(packagePath);
7627
8218
  }
7628
8219
  function deriveSourceName(metaFileName) {
7629
8220
  return metaFileName.replace(/\.meta\.json$/, "");
7630
8221
  }
7631
- var PATH_REF_RE = /^@([^/]+)\/(.+)$/;
7632
- function isPathEscape(pathDir, resolved) {
7633
- const rel = posix.relative(pathDir, resolved);
7634
- return rel.startsWith("..") || posix.isAbsolute(rel);
7635
- }
7636
- function resolveAssetSource(metaPath, source, paths) {
8222
+ function resolveAssetSource(metaPath, source) {
7637
8223
  if (source === void 0) {
7638
8224
  const metaDir = dirname(metaPath);
7639
- const metaName = deriveAssetName(metaPath, 1);
8225
+ const metaName = deriveAssetName(metaPath);
7640
8226
  const derived = deriveSourceName(metaName);
7641
- return ok(resolve(metaDir, derived));
7642
- }
7643
- if (source.startsWith("@")) {
7644
- const match = source.match(PATH_REF_RE);
7645
- if (!match) {
7646
- return err(
7647
- new PackError({
7648
- code: "pack-malformed-path-ref",
7649
- expected: "source in format @<name>/<rest>",
7650
- hint: PACK_ERROR_HINTS["pack-malformed-path-ref"],
7651
- detail: {
7652
- code: "pack-malformed-path-ref",
7653
- reason: "format",
7654
- rawSource: source,
7655
- expectedFormat: "@<name>/<rest>"
7656
- }
7657
- })
7658
- );
7659
- }
7660
- const [, name, rest] = match;
7661
- const pathDir = paths[name];
7662
- if (pathDir === void 0) {
7663
- return err(
7664
- new PackError({
7665
- code: "pack-unknown-path",
7666
- expected: "path name found in package.json#forgeax.assets.paths",
7667
- hint: PACK_ERROR_HINTS["pack-unknown-path"],
7668
- detail: {
7669
- code: "pack-unknown-path",
7670
- pathName: name,
7671
- knownNames: Object.keys(paths)
7672
- }
7673
- })
7674
- );
7675
- }
7676
- const resolved = resolve(pathDir, rest);
7677
- if (isPathEscape(pathDir, resolved)) {
7678
- return err(
7679
- new PackError({
7680
- code: "pack-malformed-path-ref",
7681
- expected: "rest path must not escape the declared path directory",
7682
- hint: PACK_ERROR_HINTS["pack-malformed-path-ref"],
7683
- detail: {
7684
- code: "pack-malformed-path-ref",
7685
- reason: "escape",
7686
- rawSource: source,
7687
- expectedFormat: "@<name>/<rest>"
7688
- }
7689
- })
7690
- );
7691
- }
7692
- return ok(resolved);
8227
+ return resolve(metaDir, derived);
7693
8228
  }
7694
- return ok(resolve(dirname(metaPath), source));
8229
+ return resolve(dirname(metaPath), source);
7695
8230
  }
7696
8231
  function isRecord2(value) {
7697
8232
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -7699,25 +8234,25 @@ function isRecord2(value) {
7699
8234
  function hasText(value) {
7700
8235
  return typeof value === "string" && value.length > 0;
7701
8236
  }
7702
- function issue(code, subjectId, expected, hint, actual2, authority = "producer") {
7703
- const subject = { type: "asset", id: subjectId };
8237
+ function issue(code, subjectId2, expected, hint, actual, authority = "producer") {
8238
+ const subject = { type: "asset", id: subjectId2 };
7704
8239
  return {
7705
8240
  ok: false,
7706
8241
  error: {
7707
8242
  code,
7708
8243
  subject,
7709
8244
  expected,
7710
- ...actual2 === void 0 ? {} : { actual: actual2 },
8245
+ ...actual === void 0 ? {} : { actual },
7711
8246
  hint,
7712
8247
  authority
7713
8248
  }
7714
8249
  };
7715
8250
  }
7716
- function validateFacts(value, subjectId) {
8251
+ function validateFacts(value, subjectId2) {
7717
8252
  if ("packageId" in value && !hasText(value.packageId)) {
7718
8253
  return issue(
7719
8254
  "invalid-producer-fact",
7720
- subjectId,
8255
+ subjectId2,
7721
8256
  "packageId must be a non-empty string",
7722
8257
  "publish a stable package identity"
7723
8258
  );
@@ -7727,7 +8262,7 @@ function validateFacts(value, subjectId) {
7727
8262
  if (!isRecord2(provenance) || !hasText(provenance.provider) || !hasText(provenance.version)) {
7728
8263
  return issue(
7729
8264
  "invalid-producer-fact",
7730
- subjectId,
8265
+ subjectId2,
7731
8266
  "provenance.provider and provenance.version must be non-empty",
7732
8267
  "publish provider identity and version"
7733
8268
  );
@@ -7738,7 +8273,7 @@ function validateFacts(value, subjectId) {
7738
8273
  if (!isRecord2(revision) || !hasText(revision.digest) || !hasText(revision.rootId) || typeof revision.observedAt !== "number" || !Number.isFinite(revision.observedAt)) {
7739
8274
  return issue(
7740
8275
  "invalid-producer-fact",
7741
- subjectId,
8276
+ subjectId2,
7742
8277
  "revision.digest, revision.rootId and finite revision.observedAt",
7743
8278
  "publish a complete resource revision"
7744
8279
  );
@@ -7748,7 +8283,7 @@ function validateFacts(value, subjectId) {
7748
8283
  if (!Array.isArray(value.diagnostics)) {
7749
8284
  return issue(
7750
8285
  "invalid-producer-fact",
7751
- subjectId,
8286
+ subjectId2,
7752
8287
  "diagnostics must be an array",
7753
8288
  "publish structured diagnostics as an array"
7754
8289
  );
@@ -7757,7 +8292,7 @@ function validateFacts(value, subjectId) {
7757
8292
  if (!isRecord2(diagnostic) || !hasText(diagnostic.code)) {
7758
8293
  return issue(
7759
8294
  "invalid-producer-fact",
7760
- subjectId,
8295
+ subjectId2,
7761
8296
  "each diagnostic needs a stable code",
7762
8297
  "publish a machine-readable diagnostic code"
7763
8298
  );
@@ -7766,7 +8301,7 @@ function validateFacts(value, subjectId) {
7766
8301
  if (!isRecord2(diagnostic.subject) || !hasText(diagnostic.subject.id)) {
7767
8302
  return issue(
7768
8303
  "invalid-producer-fact",
7769
- subjectId,
8304
+ subjectId2,
7770
8305
  "blocking diagnostic.subject must identify an object",
7771
8306
  "set subject.type and subject.id"
7772
8307
  );
@@ -7774,7 +8309,7 @@ function validateFacts(value, subjectId) {
7774
8309
  if (!hasText(diagnostic.expected) || !hasText(diagnostic.hint)) {
7775
8310
  return issue(
7776
8311
  "invalid-producer-fact",
7777
- subjectId,
8312
+ subjectId2,
7778
8313
  "blocking diagnostics need expected and hint",
7779
8314
  "provide an executable recovery hint"
7780
8315
  );
@@ -7784,7 +8319,7 @@ function validateFacts(value, subjectId) {
7784
8319
  }
7785
8320
  return { ok: true, value };
7786
8321
  }
7787
- function validateMetaSourceOverrides(value, subjectId) {
8322
+ function validateMetaSourceOverrides(value, subjectId2) {
7788
8323
  if (!("sourceOverrides" in value)) return { ok: true, value };
7789
8324
  const declaredSourceKeys = Array.isArray(value.subAssets) ? value.subAssets.flatMap(
7790
8325
  (subAsset) => isRecord2(subAsset) && hasText(subAsset.sourceKey) ? [subAsset.sourceKey] : []
@@ -7793,7 +8328,7 @@ function validateMetaSourceOverrides(value, subjectId) {
7793
8328
  if (!result.ok) {
7794
8329
  return issue(
7795
8330
  result.error.code,
7796
- subjectId,
8331
+ subjectId2,
7797
8332
  result.error.expected,
7798
8333
  result.error.hint,
7799
8334
  result.error.actual,
@@ -7814,15 +8349,15 @@ function validateProducerContract(value) {
7814
8349
  "publish a JSON object"
7815
8350
  );
7816
8351
  }
7817
- const subjectId = hasText(value.guid) ? value.guid : hasText(value.packageId) ? value.packageId : "unknown";
7818
- const facts = validateFacts(value, subjectId);
8352
+ const subjectId2 = hasText(value.guid) ? value.guid : hasText(value.packageId) ? value.packageId : "unknown";
8353
+ const facts = validateFacts(value, subjectId2);
7819
8354
  if (!facts.ok) return facts;
7820
- const metaOverrides = validateMetaSourceOverrides(value, subjectId);
8355
+ const metaOverrides = validateMetaSourceOverrides(value, subjectId2);
7821
8356
  if (!metaOverrides.ok) return metaOverrides;
7822
8357
  if ("kind" in value && !hasText(value.kind)) {
7823
8358
  return issue(
7824
8359
  "invalid-producer-fact",
7825
- subjectId,
8360
+ subjectId2,
7826
8361
  "kind must be a non-empty open string",
7827
8362
  "publish the provider-owned asset kind"
7828
8363
  );
@@ -7830,7 +8365,7 @@ function validateProducerContract(value) {
7830
8365
  if ("execution" in value && value.execution !== "direct" && value.execution !== "cooked") {
7831
8366
  return issue(
7832
8367
  "invalid-producer-fact",
7833
- subjectId,
8368
+ subjectId2,
7834
8369
  "execution must be either direct or cooked",
7835
8370
  "publish the explicit authoring execution mode"
7836
8371
  );
@@ -7840,7 +8375,7 @@ function validateProducerContract(value) {
7840
8375
  if (value.sourceKey !== void 0 && !hasText(value.sourceKey)) {
7841
8376
  return issue(
7842
8377
  "invalid-source-key",
7843
- subjectId,
8378
+ subjectId2,
7844
8379
  "sourceKey must be a non-empty producer-owned string",
7845
8380
  "publish a stable semantic sourceKey"
7846
8381
  );
@@ -7848,7 +8383,7 @@ function validateProducerContract(value) {
7848
8383
  if (value.sourceIndex !== void 0 && (typeof value.sourceIndex !== "number" || !Number.isInteger(value.sourceIndex) || value.sourceIndex < 0)) {
7849
8384
  return issue(
7850
8385
  "invalid-source-index",
7851
- subjectId,
8386
+ subjectId2,
7852
8387
  "sourceIndex must be a non-negative integer locator",
7853
8388
  "publish a non-negative sourceIndex without using it as identity"
7854
8389
  );
@@ -7856,7 +8391,7 @@ function validateProducerContract(value) {
7856
8391
  if (value.sourceKey === void 0 && value.sourceIndex !== void 0) {
7857
8392
  return issue(
7858
8393
  "missing-source-key",
7859
- subjectId,
8394
+ subjectId2,
7860
8395
  "sourceKey is required when an output declares sourceIndex",
7861
8396
  "publish a stable semantic key; do not derive identity from sourceIndex"
7862
8397
  );
@@ -7864,7 +8399,7 @@ function validateProducerContract(value) {
7864
8399
  if ("compatiblePreviousKinds" in value && (!Array.isArray(value.compatiblePreviousKinds) || value.compatiblePreviousKinds.some((kind) => !hasText(kind)))) {
7865
8400
  return issue(
7866
8401
  "invalid-producer-fact",
7867
- subjectId,
8402
+ subjectId2,
7868
8403
  "compatiblePreviousKinds must be an array of non-empty kind strings",
7869
8404
  "declare only producer-verified prior kinds that may preserve the GUID"
7870
8405
  );
@@ -7949,7 +8484,7 @@ var meta_schema_default = {
7949
8484
  source: {
7950
8485
  type: "string",
7951
8486
  minLength: 1,
7952
- description: "Optional. Omit to derive the companion file in the same directory (strip .meta.json suffix from the meta filename). Or write @<name>/<rest> to reference a declared path table entry (see package.json#forgeax.assets.paths). Existing relative paths remain unchanged."
8487
+ description: "Optional. Omit to derive the companion file in the same directory (strip .meta.json suffix from the meta filename). An explicit source is resolved relative to this sidecar."
7953
8488
  },
7954
8489
  importSettings: {
7955
8490
  type: "object",
@@ -8090,11 +8625,11 @@ var pack_schema_default = {
8090
8625
  $comment: "The ordinary durable consumer matrix is SCRIPTABLE_PACK_ASSET_KINDS in @forgeax/engine-pack/source. Pack kind fields remain open for registered producer extensions.",
8091
8626
  type: "object",
8092
8627
  additionalProperties: false,
8093
- required: ["schemaVersion", "kind", "assets"],
8628
+ required: ["schemaVersion"],
8094
8629
  properties: {
8095
8630
  schemaVersion: {
8096
8631
  type: "string",
8097
- enum: ["1.0.0", "2.0.0"]
8632
+ enum: ["1.0.0", "2.0.0", "3.0.0"]
8098
8633
  },
8099
8634
  kind: {
8100
8635
  type: "string",
@@ -8116,12 +8651,58 @@ var pack_schema_default = {
8116
8651
  items: { $ref: "#/$defs/diagnostic" }
8117
8652
  },
8118
8653
  assets: {
8119
- type: "array",
8120
- items: {
8121
- $ref: "#/$defs/asset"
8122
- }
8654
+ oneOf: [
8655
+ {
8656
+ type: "array",
8657
+ items: { $ref: "#/$defs/asset" }
8658
+ },
8659
+ { $ref: "#/$defs/authoringAssets" }
8660
+ ]
8661
+ },
8662
+ parent: {
8663
+ $ref: "#/$defs/guid36",
8664
+ description: "Parent parameterized Pack packageId for one v3 instance."
8665
+ },
8666
+ values: {
8667
+ type: "object",
8668
+ additionalProperties: true,
8669
+ description: "Sparse shallow parameter overrides for one v3 instance."
8123
8670
  }
8124
8671
  },
8672
+ oneOf: [
8673
+ {
8674
+ required: ["kind", "assets"],
8675
+ properties: {
8676
+ schemaVersion: { enum: ["1.0.0", "2.0.0"] },
8677
+ kind: { const: "internal-text-package" },
8678
+ assets: { type: "array" },
8679
+ parent: false,
8680
+ values: false
8681
+ }
8682
+ },
8683
+ {
8684
+ required: ["packageId", "assets"],
8685
+ properties: {
8686
+ schemaVersion: { const: "3.0.0" },
8687
+ kind: false,
8688
+ packageId: { $ref: "#/$defs/guid36" },
8689
+ assets: { $ref: "#/$defs/authoringAssets" },
8690
+ parent: false,
8691
+ values: false
8692
+ }
8693
+ },
8694
+ {
8695
+ required: ["packageId", "parent", "values"],
8696
+ properties: {
8697
+ schemaVersion: { const: "3.0.0" },
8698
+ kind: false,
8699
+ packageId: { $ref: "#/$defs/guid36" },
8700
+ parent: { $ref: "#/$defs/guid36" },
8701
+ values: { type: "object" },
8702
+ assets: false
8703
+ }
8704
+ }
8705
+ ],
8125
8706
  allOf: [
8126
8707
  {
8127
8708
  if: {
@@ -8213,16 +8794,40 @@ var pack_schema_default = {
8213
8794
  }
8214
8795
  ]
8215
8796
  },
8216
- artifacts: {
8797
+ authoringAssets: {
8217
8798
  type: "object",
8218
8799
  additionalProperties: false,
8219
8800
  patternProperties: {
8220
- "^[A-Za-z0-9][A-Za-z0-9._/-]*$": {
8221
- $ref: "#/$defs/artifactDescriptor"
8801
+ "^[a-z0-9][a-z0-9._-]*(/[a-z0-9][a-z0-9._-]*)*$": {
8802
+ $ref: "#/$defs/authoringAsset"
8222
8803
  }
8223
8804
  }
8224
8805
  },
8225
- artifactDescriptor: {
8806
+ authoringAsset: {
8807
+ type: "object",
8808
+ additionalProperties: false,
8809
+ required: ["kind", "payload", "refs"],
8810
+ properties: {
8811
+ kind: { type: "string", minLength: 1 },
8812
+ name: { type: "string", minLength: 1 },
8813
+ payload: { type: "object" },
8814
+ refs: {
8815
+ type: "array",
8816
+ items: { $ref: "#/$defs/guid36" }
8817
+ },
8818
+ artifacts: { $ref: "#/$defs/artifacts" }
8819
+ }
8820
+ },
8821
+ artifacts: {
8822
+ type: "object",
8823
+ additionalProperties: false,
8824
+ patternProperties: {
8825
+ "^[A-Za-z0-9][A-Za-z0-9._/-]*$": {
8826
+ $ref: "#/$defs/artifactDescriptor"
8827
+ }
8828
+ }
8829
+ },
8830
+ artifactDescriptor: {
8226
8831
  type: "object",
8227
8832
  additionalProperties: false,
8228
8833
  required: ["path", "mediaType"],
@@ -8379,50 +8984,51 @@ async function inventoryScriptablePackSource(sourcePath, initialSourceText) {
8379
8984
  }
8380
8985
  function serializeFailure(value) {
8381
8986
  if (value !== null && typeof value === "object") {
8382
- const failure3 = value;
8987
+ const failure5 = value;
8383
8988
  return {
8384
- name: failure3.name,
8385
- code: failure3.code,
8386
- expected: failure3.expected,
8387
- actual: failure3.actual,
8388
- hint: failure3.hint,
8389
- detail: failure3.detail,
8390
- message: failure3.message
8989
+ name: failure5.name,
8990
+ code: failure5.code,
8991
+ expected: failure5.expected,
8992
+ actual: failure5.actual,
8993
+ hint: failure5.hint,
8994
+ detail: failure5.detail,
8995
+ message: failure5.message
8391
8996
  };
8392
8997
  }
8393
8998
  return { message: String(value) };
8394
8999
  }
8395
9000
  function hydrateFailure(value) {
8396
9001
  if (value === null || typeof value !== "object") return value;
8397
- const failure3 = value;
8398
- if (failure3.name === "AssetError" && typeof failure3.code === "string" && typeof failure3.expected === "string" && typeof failure3.hint === "string") {
9002
+ const failure5 = value;
9003
+ if (failure5.name === "AssetError" && typeof failure5.code === "string" && typeof failure5.expected === "string" && typeof failure5.hint === "string") {
8399
9004
  const args = {
8400
- code: failure3.code,
8401
- expected: failure3.expected,
8402
- hint: failure3.hint
9005
+ code: failure5.code,
9006
+ expected: failure5.expected,
9007
+ hint: failure5.hint
8403
9008
  };
8404
- return failure3.detail === void 0 ? new AssetError(args) : new AssetError({
9009
+ return failure5.detail === void 0 ? new AssetError(args) : new AssetError({
8405
9010
  ...args,
8406
- detail: failure3.detail
9011
+ detail: failure5.detail
8407
9012
  });
8408
9013
  }
8409
- if (failure3.name === "ImportError" && typeof failure3.code === "string" && typeof failure3.expected === "string" && typeof failure3.hint === "string" && failure3.detail !== void 0) {
9014
+ if (failure5.name === "ImportError" && typeof failure5.code === "string" && typeof failure5.expected === "string" && typeof failure5.hint === "string" && failure5.detail !== void 0) {
8410
9015
  return new ImportError({
8411
- code: failure3.code,
8412
- expected: failure3.expected,
8413
- hint: failure3.hint,
8414
- detail: failure3.detail,
8415
- ...typeof failure3.actual === "string" ? { actual: failure3.actual } : {}
9016
+ code: failure5.code,
9017
+ expected: failure5.expected,
9018
+ hint: failure5.hint,
9019
+ detail: failure5.detail,
9020
+ ...typeof failure5.actual === "string" ? { actual: failure5.actual } : {}
8416
9021
  });
8417
9022
  }
8418
- if (typeof failure3.message === "string") {
8419
- const error = new Error(failure3.message);
8420
- if (typeof failure3.name === "string") error.name = failure3.name;
9023
+ if (typeof failure5.message === "string") {
9024
+ const error = new Error(failure5.message);
9025
+ if (typeof failure5.name === "string") error.name = failure5.name;
8421
9026
  return error;
8422
9027
  }
8423
9028
  return value;
8424
9029
  }
8425
9030
  var WorkerScriptablePackExecutor = class {
9031
+ supportsParameterizedContext = true;
8426
9032
  worker;
8427
9033
  compileRoot;
8428
9034
  nextBuildId = 0;
@@ -8450,7 +9056,7 @@ var WorkerScriptablePackExecutor = class {
8450
9056
  resolveLoad({
8451
9057
  default: value.definition === void 0 ? void 0 : {
8452
9058
  ...value.definition,
8453
- build: (reader) => this.runBuild(reader)
9059
+ build: (readByGuid, context) => this.runBuild({ readByGuid }, context)
8454
9060
  }
8455
9061
  });
8456
9062
  } else if (value.kind === "load-threw") {
@@ -8482,7 +9088,7 @@ var WorkerScriptablePackExecutor = class {
8482
9088
  })();
8483
9089
  return this.disposal;
8484
9090
  }
8485
- runBuild(reader) {
9091
+ runBuild(reader, context) {
8486
9092
  const worker = this.worker;
8487
9093
  if (worker === void 0) return Promise.reject(new Error("ScriptablePack worker is closed"));
8488
9094
  if (this.build !== void 0)
@@ -8490,7 +9096,7 @@ var WorkerScriptablePackExecutor = class {
8490
9096
  const id = this.nextBuildId++;
8491
9097
  return new Promise((resolveBuild, rejectBuild) => {
8492
9098
  this.build = { id, reader, resolve: resolveBuild, reject: rejectBuild };
8493
- worker.postMessage({ kind: "build", buildId: id });
9099
+ worker.postMessage({ kind: "build", buildId: id, context });
8494
9100
  });
8495
9101
  }
8496
9102
  onMessage(message) {
@@ -8523,11 +9129,11 @@ function scriptablePackWorkerUrl() {
8523
9129
  const bundledWorker = new URL("./scriptable-pack-worker.mjs", import.meta.url);
8524
9130
  return existsSync(fileURLToPath(bundledWorker)) ? bundledWorker : new URL("../dist/scriptable-pack-worker.mjs", import.meta.url);
8525
9131
  }
8526
- function loadFailure(sourcePath, reason, phase, diagnostic, timeoutMs) {
8527
- return err({
8528
- code: "pack-source-load-failed",
8529
- expected: "a trusted synchronous module with a valid default ScriptablePack export",
8530
- hint: "repair the module load or initialization failure, then inspect Meta again",
9132
+ function parameterizedLoadFailure(sourcePath, reason, phase, diagnostic, timeoutMs) {
9133
+ return {
9134
+ code: "pack-parameter-invalid",
9135
+ expected: "a trusted Pack v2 authoring module with a valid default export",
9136
+ hint: "repair the source module or its build timeout, then inspect the Pack again",
8531
9137
  detail: {
8532
9138
  sourcePath,
8533
9139
  reason,
@@ -8535,70 +9141,71 @@ function loadFailure(sourcePath, reason, phase, diagnostic, timeoutMs) {
8535
9141
  diagnostic,
8536
9142
  ...timeoutMs === void 0 ? {} : { timeoutMs }
8537
9143
  }
8538
- });
8539
- }
8540
- function buildTimeoutFailure(sourcePath, timeoutMs) {
8541
- return {
8542
- code: "pack-source-load-failed",
8543
- expected: "a ScriptablePack build to settle within the configured build timeout",
8544
- hint: "repair the authored build, then retry through a fresh ScriptablePack generation",
8545
- detail: {
8546
- sourcePath,
8547
- reason: "timeout",
8548
- phase: "build",
8549
- timeoutMs,
8550
- diagnostic: `ScriptablePack build exceeded ${timeoutMs}ms`
8551
- }
8552
9144
  };
8553
9145
  }
8554
- async function loadScriptablePack(sourcePath, options = {}) {
9146
+ async function loadParameterizedScriptablePack(sourcePath, options = {}) {
8555
9147
  const timeoutMs = options.timeoutMs ?? 15e3;
8556
9148
  const buildTimeoutMs = options.buildTimeoutMs ?? 5e3;
8557
9149
  const executor = options.executor ?? new WorkerScriptablePackExecutor();
8558
9150
  let disposeReason;
8559
9151
  let timeout;
8560
9152
  try {
8561
- const timeoutSignal = /* @__PURE__ */ Symbol("scriptable-pack-module-timeout");
9153
+ const timeoutSignal = /* @__PURE__ */ Symbol("parameterized-pack-module-timeout");
8562
9154
  const loaded = await Promise.race([
8563
9155
  executor.load(sourcePath),
8564
- new Promise((resolve3) => {
8565
- timeout = setTimeout(() => resolve3(timeoutSignal), timeoutMs);
9156
+ new Promise((resolve4) => {
9157
+ timeout = setTimeout(() => resolve4(timeoutSignal), timeoutMs);
8566
9158
  })
8567
9159
  ]);
8568
9160
  if (loaded === timeoutSignal) {
8569
9161
  disposeReason = "timeout";
8570
- return loadFailure(
8571
- sourcePath,
8572
- "timeout",
8573
- "module-load",
8574
- `ScriptablePack module initialization exceeded ${timeoutMs}ms`,
8575
- timeoutMs
9162
+ return err(
9163
+ parameterizedLoadFailure(
9164
+ sourcePath,
9165
+ "timeout",
9166
+ "module-load",
9167
+ `Pack module initialization exceeded ${timeoutMs}ms`,
9168
+ timeoutMs
9169
+ )
8576
9170
  );
8577
9171
  }
8578
9172
  const moduleValue = loaded !== null && typeof loaded === "object" && "default" in loaded ? loaded.default : void 0;
8579
- const validated = validateScriptablePackDefinition(moduleValue, sourcePath);
9173
+ const validated = validatePackDefinition(moduleValue, sourcePath);
8580
9174
  if (!validated.ok) {
8581
9175
  disposeReason = "failure";
8582
9176
  return validated;
8583
9177
  }
8584
9178
  if (options.metadataOnly === true) {
8585
9179
  disposeReason = "complete";
8586
- return validated;
9180
+ return ok(validated.value);
8587
9181
  }
8588
9182
  const definition = validated.value;
9183
+ const build = definition.build;
8589
9184
  return ok({
8590
9185
  ...definition,
8591
- build: async (reader) => {
9186
+ build: async (context) => {
8592
9187
  let buildTimedOut = false;
8593
9188
  let buildTimeout;
8594
- const timeoutResult = err(buildTimeoutFailure(sourcePath, buildTimeoutMs));
9189
+ const timeoutResult = err(
9190
+ parameterizedLoadFailure(
9191
+ sourcePath,
9192
+ "timeout",
9193
+ "build",
9194
+ `ScriptablePack build exceeded ${buildTimeoutMs}ms`,
9195
+ buildTimeoutMs
9196
+ )
9197
+ );
8595
9198
  try {
9199
+ const invocation = "supportsParameterizedContext" in executor && executor.supportsParameterizedContext === true ? build(context.readByGuid, {
9200
+ packageId: [...context.packageId],
9201
+ ..."values" in context && context.values !== void 0 ? { values: context.values } : {}
9202
+ }) : definition.build(context);
8596
9203
  const built = await Promise.race([
8597
- Promise.resolve(definition.build(reader)),
8598
- new Promise((resolve3) => {
9204
+ Promise.resolve(invocation),
9205
+ new Promise((resolve4) => {
8599
9206
  buildTimeout = setTimeout(() => {
8600
9207
  buildTimedOut = true;
8601
- resolve3(timeoutResult);
9208
+ resolve4(timeoutResult);
8602
9209
  }, buildTimeoutMs);
8603
9210
  })
8604
9211
  ]);
@@ -8617,9 +9224,15 @@ async function loadScriptablePack(sourcePath, options = {}) {
8617
9224
  }
8618
9225
  });
8619
9226
  } catch (error) {
8620
- const diagnostic = error instanceof Error ? error.message : String(error);
8621
9227
  disposeReason = "failure";
8622
- return loadFailure(sourcePath, "module-load", "module-load", diagnostic);
9228
+ return err(
9229
+ parameterizedLoadFailure(
9230
+ sourcePath,
9231
+ "module-load",
9232
+ "module-load",
9233
+ error instanceof Error ? error.message : String(error)
9234
+ )
9235
+ );
8623
9236
  } finally {
8624
9237
  if (timeout !== void 0) clearTimeout(timeout);
8625
9238
  if (disposeReason !== void 0) await executor.dispose?.(disposeReason);
@@ -8633,11 +9246,15 @@ function ok4(value) {
8633
9246
  function packErr(error) {
8634
9247
  return { ok: false, error };
8635
9248
  }
9249
+ function record(value) {
9250
+ return value !== null && typeof value === "object" && !Array.isArray(value);
9251
+ }
8636
9252
  var STANDARD_SCRIPTABLE_PACK_SCAN_OPTIONS = Object.freeze(
8637
9253
  {}
8638
9254
  );
8639
9255
  var BLACKLIST = /* @__PURE__ */ new Set([
8640
9256
  "node_modules",
9257
+ "__tests__",
8641
9258
  ".forgeax-harness",
8642
9259
  ".forgeax",
8643
9260
  ".git",
@@ -8740,10 +9357,87 @@ async function scanValidated(roots, opts = {}, capture) {
8740
9357
  const scriptablePaths = rawPaths.filter((p) => p.endsWith(".pack.ts"));
8741
9358
  const guidToPath = /* @__PURE__ */ new Map();
8742
9359
  const packRefs = /* @__PURE__ */ new Map();
9360
+ const packageIdToPath = /* @__PURE__ */ new Map();
9361
+ const packageKind = /* @__PURE__ */ new Map();
9362
+ const instanceParents = [];
9363
+ function registerPackage(packageId2, path, kind) {
9364
+ if (packageId2 === void 0) return void 0;
9365
+ const normalized = packageId2.toLowerCase();
9366
+ const existing = packageIdToPath.get(normalized);
9367
+ if (existing !== void 0) {
9368
+ return makePackError("pack-guid-collision", {
9369
+ paths: [existing, path],
9370
+ guid: normalized
9371
+ });
9372
+ }
9373
+ packageIdToPath.set(normalized, path);
9374
+ packageKind.set(normalized, kind);
9375
+ return void 0;
9376
+ }
8743
9377
  for (const packPath of packPaths) {
8744
9378
  const loaded = await readValidatedJson(packPath, "pack-malformed-pack", validatePack);
8745
9379
  if (!loaded.ok) return loaded;
8746
9380
  const { raw, parsed } = loaded.value;
9381
+ if (record(parsed) && parsed.schemaVersion === "3.0.0") {
9382
+ const authoring = parseParameterizedPackJson(parsed);
9383
+ if (!authoring.ok) {
9384
+ return packErr(
9385
+ makePackError("pack-malformed-pack", {
9386
+ path: packPath,
9387
+ ajvErrors: [{ instancePath: "", message: authoring.error.code }]
9388
+ })
9389
+ );
9390
+ }
9391
+ const packageId2 = authoring.value.packageId;
9392
+ const packageCollision2 = registerPackage(
9393
+ PackageId.format(packageId2),
9394
+ packPath,
9395
+ authoring.value.format
9396
+ );
9397
+ if (packageCollision2 !== void 0) return packErr(packageCollision2);
9398
+ if (authoring.value.format === "instance") {
9399
+ instanceParents.push({
9400
+ path: packPath,
9401
+ packageId: PackageId.format(authoring.value.packageId),
9402
+ parent: PackageId.format(authoring.value.parent)
9403
+ });
9404
+ } else {
9405
+ const projected = projectDirectPackJson(authoring.value);
9406
+ if (!projected.ok) {
9407
+ return packErr(
9408
+ makePackError("pack-malformed-pack", {
9409
+ path: packPath,
9410
+ ajvErrors: [{ instancePath: "/assets", message: projected.error.code }]
9411
+ })
9412
+ );
9413
+ }
9414
+ for (const asset of projected.value.assets) {
9415
+ const normalizedGuid = asset.guid.toLowerCase();
9416
+ const existing = guidToPath.get(normalizedGuid);
9417
+ if (existing !== void 0) {
9418
+ return packErr(
9419
+ makePackError("pack-guid-collision", {
9420
+ paths: [existing, packPath],
9421
+ guid: normalizedGuid
9422
+ })
9423
+ );
9424
+ }
9425
+ guidToPath.set(normalizedGuid, packPath);
9426
+ packRefs.set(normalizedGuid, [
9427
+ ...asset.refs.map((ref) => ref.toLowerCase()),
9428
+ ...extractMountSourceGuids(asset)
9429
+ ]);
9430
+ }
9431
+ }
9432
+ capture?.declarations.set(packPath, {
9433
+ format: "pack.json",
9434
+ sourcePath: packPath,
9435
+ sourceRevision: `sha256:${createHash("sha256").update(raw).digest("hex")}`,
9436
+ sourceText: raw,
9437
+ value: parsed
9438
+ });
9439
+ continue;
9440
+ }
8747
9441
  const packageContract = validateProducerContract(parsed);
8748
9442
  if (!packageContract.ok) {
8749
9443
  return packErr(
@@ -8754,6 +9448,8 @@ async function scanValidated(roots, opts = {}, capture) {
8754
9448
  );
8755
9449
  }
8756
9450
  const packObj = parsed;
9451
+ const packageCollision = registerPackage(packObj.packageId, packPath, "legacy");
9452
+ if (packageCollision !== void 0) return packErr(packageCollision);
8757
9453
  for (const asset of packObj.assets) {
8758
9454
  if (asset.kind === "particle-effect" && asset.execution === "direct" && (asset.refs.length > 0 || Object.keys(asset.artifacts ?? {}).length > 0)) {
8759
9455
  return packErr(
@@ -8844,7 +9540,6 @@ async function scanValidated(roots, opts = {}, capture) {
8844
9540
  value: packObj
8845
9541
  });
8846
9542
  }
8847
- const { paths: assetPaths } = loadAssetConfig(process.cwd());
8848
9543
  for (const metaPath of metaPaths) {
8849
9544
  const loaded = await readValidatedJson(metaPath, "pack-malformed-meta", validateMeta);
8850
9545
  if (!loaded.ok) return loaded;
@@ -8859,6 +9554,8 @@ async function scanValidated(roots, opts = {}, capture) {
8859
9554
  );
8860
9555
  }
8861
9556
  const metaObj = parsed;
9557
+ const packageCollision = registerPackage(metaObj.packageId, metaPath, "legacy");
9558
+ if (packageCollision !== void 0) return packErr(packageCollision);
8862
9559
  capture?.declarations.set(metaPath, {
8863
9560
  format: "meta.json",
8864
9561
  sourcePath: metaPath,
@@ -8898,11 +9595,7 @@ async function scanValidated(roots, opts = {}, capture) {
8898
9595
  );
8899
9596
  }
8900
9597
  }
8901
- const sourceResolved = resolveAssetSource(metaPath, metaObj.source, assetPaths);
8902
- if (!sourceResolved.ok) {
8903
- return packErr(sourceResolved.error);
8904
- }
8905
- const expectedSourcePath = sourceResolved.value;
9598
+ const expectedSourcePath = resolveAssetSource(metaPath, metaObj.source);
8906
9599
  try {
8907
9600
  await stat(expectedSourcePath);
8908
9601
  } catch {
@@ -8922,23 +9615,24 @@ async function scanValidated(roots, opts = {}, capture) {
8922
9615
  return packErr(
8923
9616
  makePackError("pack-malformed-meta", {
8924
9617
  path: sourcePath,
8925
- ajvErrors: [{ instancePath: "", message: "ScriptablePack source read failed" }]
9618
+ ajvErrors: [{ instancePath: "", message: "Pack source read failed" }]
8926
9619
  })
8927
9620
  );
8928
9621
  }
8929
- const loaded = await loadScriptablePack(sourcePath, {
9622
+ const loaderOptions = {
8930
9623
  ...opts.scriptablePack ?? {},
8931
9624
  ...capture === void 0 ? { metadataOnly: true } : {}
8932
- });
9625
+ };
9626
+ const loaded = await loadParameterizedScriptablePack(sourcePath, loaderOptions);
8933
9627
  if (!loaded.ok) {
8934
- const diagnostic = "diagnostic" in loaded.error.detail ? loaded.error.detail.diagnostic : void 0;
9628
+ const diagnostic = loaded.error.detail.diagnostic;
8935
9629
  return packErr(
8936
9630
  makePackError("pack-malformed-meta", {
8937
9631
  path: sourcePath,
8938
9632
  ajvErrors: [
8939
9633
  {
8940
9634
  instancePath: "",
8941
- message: diagnostic === void 0 ? loaded.error.code : `${loaded.error.code}: ${diagnostic}`
9635
+ message: typeof diagnostic === "string" ? `${loaded.error.code}: ${diagnostic}` : loaded.error.code
8942
9636
  }
8943
9637
  ]
8944
9638
  })
@@ -8951,41 +9645,81 @@ async function scanValidated(roots, opts = {}, capture) {
8951
9645
  return packErr(
8952
9646
  makePackError("pack-malformed-meta", {
8953
9647
  path: sourcePath,
8954
- ajvErrors: [{ instancePath: "", message: "ScriptablePack source closure read failed" }]
8955
- })
8956
- );
8957
- }
8958
- const meta = projectScriptablePackMeta(loaded.value, sourcePath);
8959
- const topology = validateProducerOutputs(meta.subAssets);
8960
- if (!topology.ok) {
8961
- return packErr(
8962
- makePackError("pack-malformed-meta", {
8963
- path: sourcePath,
8964
- ajvErrors: [{ instancePath: "/subAssets", message: topology.error.code }]
9648
+ ajvErrors: [{ instancePath: "", message: "Pack source closure read failed" }]
8965
9649
  })
8966
9650
  );
8967
9651
  }
8968
- const declaration = {
9652
+ const packageCollision = registerPackage(
9653
+ PackageId.format(loaded.value.packageId),
9654
+ sourcePath,
9655
+ "parameterized"
9656
+ );
9657
+ if (packageCollision !== void 0) return packErr(packageCollision);
9658
+ const meta = projectParameterizedPackMeta(loaded.value, sourcePath);
9659
+ capture?.declarations.set(sourcePath, {
9660
+ format: "pack.ts-parameterized",
8969
9661
  sourcePath,
8970
9662
  sourceRevision: `sha256:${createHash("sha256").update(source).digest("hex")}`,
8971
- meta,
9663
+ value: meta,
8972
9664
  definition: loaded.value,
8973
9665
  sourceClosure
8974
- };
8975
- capture?.declarations.set(sourcePath, {
8976
- format: "pack.ts",
8977
- ...declaration,
8978
- value: meta
8979
9666
  });
8980
- for (const sub of meta.subAssets) {
8981
- const guid = sub.guid.toLowerCase();
8982
- const existing = guidToPath.get(guid);
8983
- if (existing !== void 0) {
9667
+ }
9668
+ const parentByPackageId = new Map(
9669
+ instanceParents.map((instance) => [instance.packageId.toLowerCase(), instance])
9670
+ );
9671
+ for (const instance of instanceParents) {
9672
+ const parentId = instance.parent.toLowerCase();
9673
+ const parentPath = packageIdToPath.get(parentId);
9674
+ if (parentPath === void 0) {
9675
+ return packErr(
9676
+ makePackError("pack-malformed-pack", {
9677
+ path: instance.path,
9678
+ reason: "pack-parent-not-found",
9679
+ ajvErrors: [
9680
+ {
9681
+ instancePath: "/parent",
9682
+ message: `parent packageId ${instance.parent} was not found`
9683
+ }
9684
+ ]
9685
+ })
9686
+ );
9687
+ }
9688
+ const kind = packageKind.get(parentId);
9689
+ if (kind !== "parameterized" && kind !== "instance") {
9690
+ return packErr(
9691
+ makePackError("pack-malformed-pack", {
9692
+ path: instance.path,
9693
+ reason: "pack-parent-not-parameterized",
9694
+ ajvErrors: [
9695
+ {
9696
+ instancePath: "/parent",
9697
+ message: `parent ${instance.parent} is not a parameterized Pack`
9698
+ }
9699
+ ]
9700
+ })
9701
+ );
9702
+ }
9703
+ }
9704
+ for (const instance of instanceParents) {
9705
+ const chain = /* @__PURE__ */ new Set();
9706
+ let current = instance.packageId.toLowerCase();
9707
+ while (true) {
9708
+ if (chain.has(current)) {
8984
9709
  return packErr(
8985
- makePackError("pack-guid-collision", { paths: [existing, sourcePath], guid })
9710
+ makePackError("pack-malformed-pack", {
9711
+ path: instance.path,
9712
+ reason: "pack-parent-cycle",
9713
+ ajvErrors: [
9714
+ { instancePath: "/parent", message: `parent chain repeats packageId ${current}` }
9715
+ ]
9716
+ })
8986
9717
  );
8987
9718
  }
8988
- guidToPath.set(guid, sourcePath);
9719
+ chain.add(current);
9720
+ const next = parentByPackageId.get(current);
9721
+ if (next === void 0) break;
9722
+ current = next.parent.toLowerCase();
8989
9723
  }
8990
9724
  }
8991
9725
  const visited = /* @__PURE__ */ new Set();
@@ -9029,6 +9763,38 @@ async function scanInventory(roots, opts = {}) {
9029
9763
  for (const sourcePath of scanned.value) {
9030
9764
  const declaration = declarations.get(sourcePath);
9031
9765
  if (declaration?.format !== "pack.json") continue;
9766
+ if (declaration.value.schemaVersion === "3.0.0") {
9767
+ const parsed = parseParameterizedPackJson(declaration.value);
9768
+ if (!parsed.ok) {
9769
+ return packErr(
9770
+ makePackError("pack-malformed-pack", {
9771
+ path: sourcePath,
9772
+ ajvErrors: [{ instancePath: "", message: parsed.error.code }]
9773
+ })
9774
+ );
9775
+ }
9776
+ if (parsed.value.format !== "direct") continue;
9777
+ const projected = projectDirectPackJson(parsed.value);
9778
+ if (!projected.ok) {
9779
+ return packErr(
9780
+ makePackError("pack-malformed-pack", {
9781
+ path: sourcePath,
9782
+ ajvErrors: [{ instancePath: "/assets", message: projected.error.code }]
9783
+ })
9784
+ );
9785
+ }
9786
+ for (const [index, asset] of projected.value.assets.entries()) {
9787
+ inventory.push({
9788
+ guid: asset.guid,
9789
+ kind: asset.kind,
9790
+ sourcePath,
9791
+ sourceRevision: declaration.sourceRevision,
9792
+ sourceKey: asset.sourceKey,
9793
+ sourceIndex: index
9794
+ });
9795
+ }
9796
+ continue;
9797
+ }
9032
9798
  for (const [index, asset] of declaration.value.assets.entries()) {
9033
9799
  inventory.push({
9034
9800
  guid: asset.guid,
@@ -9070,28 +9836,18 @@ function unsupportedPolicyError(path, importer, policy) {
9070
9836
  subjects: [path]
9071
9837
  };
9072
9838
  }
9073
- function projectMeta(rawPath, cwd, base, assetPaths, importerPolicy, sourceDeclaration, sourceIdentityFor) {
9839
+ function projectMeta(rawPath, cwd, base, importerPolicy, sourceDeclaration, sourceIdentityFor) {
9074
9840
  const meta = sourceDeclaration.value;
9075
9841
  const policy = importerPolicy(meta.importer);
9076
9842
  if (policy.disposition !== "publish") {
9077
9843
  return { entries: [], error: unsupportedPolicyError(rawPath, meta.importer, policy) };
9078
9844
  }
9079
- const resolved = resolveAssetSource(rawPath, meta.source, assetPaths);
9080
- if (!resolved.ok) {
9081
- return {
9082
- entries: [],
9083
- error: {
9084
- code: "catalog-meta-schema-invalid",
9085
- path: rawPath,
9086
- message: `source resolution failed: ${resolved.error.code} - ${resolved.error.hint}`
9087
- }
9088
- };
9089
- }
9090
- const sourcePath = catalogSourcePathFor(cwd, resolved.value, sourceIdentityFor);
9845
+ const resolved = resolveAssetSource(rawPath, meta.source);
9846
+ const sourcePath = catalogSourcePathFor(cwd, resolved, sourceIdentityFor);
9091
9847
  const packageUrl2 = `${base}/__forgeax-ddc/${meta.subAssets[0]?.guid?.toLowerCase() ?? "pack"}.pack.json`;
9092
9848
  const declaration = {
9093
9849
  importer: meta.importer,
9094
- source: resolved.value,
9850
+ source: resolved,
9095
9851
  sourceRevision: sourceDeclaration.sourceRevision,
9096
9852
  ...meta.packageId === void 0 ? {} : { packageId: meta.packageId },
9097
9853
  ...meta.provenance === void 0 ? {} : { provenance: meta.provenance },
@@ -9125,23 +9881,13 @@ function projectMeta(rawPath, cwd, base, assetPaths, importerPolicy, sourceDecla
9125
9881
  sourcePath,
9126
9882
  packageUrl2,
9127
9883
  meta.subAssets,
9128
- (output) => deriveAssetName(resolved.value, meta.subAssets.length, output.name)
9884
+ (output) => deriveAssetName(resolved, meta.subAssets.length, output.name)
9129
9885
  )
9130
9886
  };
9131
9887
  }
9132
- function projectSource(path, cwd, base, assetPaths, importerPolicy, visibility, sourceDeclaration, declarations, sourceIdentityFor) {
9133
- if (sourceDeclaration.format === "pack.ts") {
9134
- const meta = { ...sourceDeclaration.meta, source: sourcePathFor(cwd, path) };
9135
- const anchorGuid = meta.subAssets.map((asset) => asset.guid.toLowerCase()).sort()[0];
9136
- return anchorGuid === void 0 ? { entries: [] } : {
9137
- entries: projectExternalCatalogEntries(
9138
- meta,
9139
- catalogSourcePathFor(cwd, path, sourceIdentityFor),
9140
- `${base}/__forgeax-ddc/${anchorGuid}.pack.json`,
9141
- meta.subAssets,
9142
- (output) => deriveAssetName(path, meta.subAssets.length, output.name)
9143
- )
9144
- };
9888
+ function projectSource(path, cwd, base, importerPolicy, visibility, sourceDeclaration, declarations, sourceIdentityFor) {
9889
+ if (sourceDeclaration.format === "pack.ts-parameterized") {
9890
+ return { entries: [] };
9145
9891
  }
9146
9892
  if (sourceDeclaration.format === "meta.json") {
9147
9893
  const policy = importerPolicy(sourceDeclaration.value.importer);
@@ -9156,7 +9902,6 @@ function projectSource(path, cwd, base, assetPaths, importerPolicy, visibility,
9156
9902
  path,
9157
9903
  cwd,
9158
9904
  base,
9159
- assetPaths,
9160
9905
  importerPolicy,
9161
9906
  sourceDeclaration,
9162
9907
  sourceIdentityFor
@@ -9166,6 +9911,57 @@ function projectSource(path, cwd, base, assetPaths, importerPolicy, visibility,
9166
9911
  }
9167
9912
  const parsed = sourceDeclaration.value;
9168
9913
  const sourcePath = catalogSourcePathFor(cwd, path, sourceIdentityFor);
9914
+ if (parsed.schemaVersion === "3.0.0") {
9915
+ const authoring = parseParameterizedPackJson(parsed);
9916
+ if (!authoring.ok) {
9917
+ return {
9918
+ entries: [],
9919
+ error: {
9920
+ code: "catalog-meta-schema-invalid",
9921
+ path,
9922
+ message: `v3 Pack parse failed: ${authoring.error.code}`,
9923
+ expected: "a valid direct or instance v3 Pack document",
9924
+ actual: authoring.error.code,
9925
+ hint: authoring.error.hint,
9926
+ subjects: [path]
9927
+ }
9928
+ };
9929
+ }
9930
+ if (authoring.value.format !== "direct") return { entries: [] };
9931
+ const projected = projectDirectPackJson(authoring.value);
9932
+ if (!projected.ok) {
9933
+ return {
9934
+ entries: [],
9935
+ error: {
9936
+ code: "catalog-meta-schema-invalid",
9937
+ path,
9938
+ message: `v3 Pack projection failed: ${projected.error.code}`,
9939
+ expected: "direct v3 assets to derive stable AssetGuids",
9940
+ actual: projected.error.code,
9941
+ hint: projected.error.hint,
9942
+ subjects: [path]
9943
+ }
9944
+ };
9945
+ }
9946
+ const provenance2 = { provider: "pack", version: "3.0.0" };
9947
+ return {
9948
+ entries: projectPackageCatalog(
9949
+ projected.value.assets.map((asset, sourceIndex) => ({
9950
+ guid: asset.guid,
9951
+ kind: asset.kind,
9952
+ sourcePath,
9953
+ sourceIndex,
9954
+ sourceKey: asset.sourceKey,
9955
+ refs: asset.refs,
9956
+ execution: "direct",
9957
+ packageId: projected.value.packageId,
9958
+ provenance: provenance2,
9959
+ name: deriveAssetName(path, projected.value.assets.length, asset.name)
9960
+ })),
9961
+ `${base}/${sourcePath}`
9962
+ )
9963
+ };
9964
+ }
9169
9965
  const provenance = parsed.provenance ?? { provider: "pack", version: parsed.schemaVersion };
9170
9966
  return {
9171
9967
  entries: projectPackageCatalog(
@@ -9216,7 +10012,6 @@ async function buildCatalogProjection(roots, options) {
9216
10012
  return empty("degraded", [error]);
9217
10013
  }
9218
10014
  const { paths, declarations: sourceDeclarations } = scanned.value;
9219
- const { paths: assetPaths } = loadAssetConfig(cwd);
9220
10015
  const entries = [];
9221
10016
  const errors = [];
9222
10017
  const declarations = /* @__PURE__ */ new Map();
@@ -9227,7 +10022,6 @@ async function buildCatalogProjection(roots, options) {
9227
10022
  path,
9228
10023
  cwd,
9229
10024
  base,
9230
- assetPaths,
9231
10025
  options.importerPolicy,
9232
10026
  options.visibility,
9233
10027
  sourceDeclaration,
@@ -9389,13 +10183,13 @@ function topologyGroups(rows) {
9389
10183
  }
9390
10184
  return groups;
9391
10185
  }
9392
- function revisionDiagnostic(code, expected, actual2, hint, rootId) {
10186
+ function revisionDiagnostic(code, expected, actual, hint, rootId) {
9393
10187
  return {
9394
10188
  code,
9395
10189
  severity: "blocking",
9396
10190
  authority: "catalog",
9397
10191
  expected,
9398
- actual: actual2,
10192
+ actual,
9399
10193
  hint,
9400
10194
  ...rootId === void 0 ? {} : { subject: { type: "package", id: rootId } }
9401
10195
  };
@@ -9521,7 +10315,9 @@ function textureValues(values, textureFields) {
9521
10315
  }
9522
10316
  function collectMaterialCookRefs(material) {
9523
10317
  const textureFields = material.parameters === void 0 ? new Set(MATERIAL_TEXTURE_SLOTS) : new Set(
9524
- material.parameters.filter((parameter) => parameter.type === "texture").map((parameter) => parameter.name)
10318
+ material.parameters.filter(
10319
+ (parameter) => parameter.type === "texture" || parameter.type === "texture_cube"
10320
+ ).map((parameter) => parameter.name)
9525
10321
  );
9526
10322
  const textures = textureValues(material.values, textureFields);
9527
10323
  return {
@@ -9555,20 +10351,20 @@ function jsonValue(value) {
9555
10351
  }
9556
10352
  return value;
9557
10353
  }
9558
- function serializeCookedMaterialRecord(record) {
9559
- return JSON.stringify(jsonValue(record));
10354
+ function serializeCookedMaterialRecord(record2) {
10355
+ return JSON.stringify(jsonValue(record2));
9560
10356
  }
9561
10357
  function serializeMaterialCookReceipt(receipt) {
9562
10358
  return JSON.stringify(jsonValue({ ...receipt, sourceClosure: unique(receipt.sourceClosure) }));
9563
10359
  }
9564
- function invalid2(field, actual2) {
10360
+ function invalid(field, actual) {
9565
10361
  return err({
9566
10362
  code: "material-cook-record-invalid",
9567
10363
  expected: "a complete material-cook/3 record with layered identity and provenance",
9568
10364
  hint: "re-cook the material and publish its record, artifact, references, and receipt together",
9569
10365
  detail: {
9570
10366
  field,
9571
- ...actual2 === void 0 ? {} : { actual: actual2 },
10367
+ ...actual === void 0 ? {} : { actual },
9572
10368
  action: "inspect the named field and recook the material generation"
9573
10369
  }
9574
10370
  });
@@ -9589,25 +10385,25 @@ function isGeneration(value) {
9589
10385
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
9590
10386
  }
9591
10387
  function validateIdentity(value) {
9592
- if (value === null || typeof value !== "object") return invalid2("receipt.identity", value);
10388
+ if (value === null || typeof value !== "object") return invalid("receipt.identity", value);
9593
10389
  const candidate = value;
9594
10390
  for (const field of IDENTITY_FIELDS) {
9595
10391
  if (typeof candidate[field] !== "string" || candidate[field].length === 0) {
9596
- return invalid2(`receipt.identity.${field}`, candidate[field]);
10392
+ return invalid(`receipt.identity.${field}`, candidate[field]);
9597
10393
  }
9598
10394
  }
9599
10395
  if (candidate.wasm === null || typeof candidate.wasm !== "object") {
9600
- return invalid2("receipt.identity.wasm", candidate.wasm);
10396
+ return invalid("receipt.identity.wasm", candidate.wasm);
9601
10397
  }
9602
10398
  const wasm = candidate.wasm;
9603
10399
  for (const field of ["sourceContentKey", "artifactSha256", "glueSha256"]) {
9604
10400
  if (typeof wasm[field] !== "string" || wasm[field].length === 0) {
9605
- return invalid2(`receipt.identity.wasm.${field}`, wasm[field]);
10401
+ return invalid(`receipt.identity.wasm.${field}`, wasm[field]);
9606
10402
  }
9607
10403
  }
9608
10404
  for (const field of ["valueGeneration", "dependencyGeneration", "cookGeneration"]) {
9609
10405
  if (!isGeneration(candidate[field]))
9610
- return invalid2(`receipt.identity.${field}`, candidate[field]);
10406
+ return invalid(`receipt.identity.${field}`, candidate[field]);
9611
10407
  }
9612
10408
  return ok({
9613
10409
  ...candidate,
@@ -9615,38 +10411,38 @@ function validateIdentity(value) {
9615
10411
  });
9616
10412
  }
9617
10413
  function validateMaterialCookReceipt(value, expected = {}) {
9618
- if (value === null || typeof value !== "object") return invalid2("receipt");
10414
+ if (value === null || typeof value !== "object") return invalid("receipt");
9619
10415
  const candidate = value;
9620
10416
  if (candidate.schemaVersion !== "material-cook/3")
9621
- return invalid2("receipt.schemaVersion", candidate.schemaVersion);
10417
+ return invalid("receipt.schemaVersion", candidate.schemaVersion);
9622
10418
  const identityResult = validateIdentity(candidate.identity);
9623
10419
  if (!identityResult.ok) return identityResult;
9624
10420
  const identity = identityResult.value;
9625
10421
  if (candidate.derivedInterface === null || typeof candidate.derivedInterface !== "object") {
9626
- return invalid2("receipt.derivedInterface", candidate.derivedInterface);
10422
+ return invalid("receipt.derivedInterface", candidate.derivedInterface);
9627
10423
  }
9628
10424
  const derivedInterface = candidate.derivedInterface;
9629
10425
  if (derivedInterface.layoutIdentity !== identity.layoutIdentity) {
9630
- return invalid2("receipt.derivedInterface.layoutIdentity", derivedInterface.layoutIdentity);
10426
+ return invalid("receipt.derivedInterface.layoutIdentity", derivedInterface.layoutIdentity);
9631
10427
  }
9632
10428
  for (const field of ["sourceClosure", "profile", "compilerVersion"]) {
9633
10429
  const fieldValue = candidate[field];
9634
10430
  if (field === "sourceClosure" && !Array.isArray(fieldValue) || field !== "sourceClosure" && typeof fieldValue !== "string") {
9635
- return invalid2(`receipt.${field}`, fieldValue);
10431
+ return invalid(`receipt.${field}`, fieldValue);
9636
10432
  }
9637
10433
  }
9638
10434
  const sourceClosure = candidate.sourceClosure;
9639
10435
  if (Array.isArray(sourceClosure) && sourceClosure.some((path) => typeof path !== "string")) {
9640
- return invalid2("receipt.sourceClosure", sourceClosure);
10436
+ return invalid("receipt.sourceClosure", sourceClosure);
9641
10437
  }
9642
10438
  if (expected.layoutIdentity !== void 0 && identity.layoutIdentity !== expected.layoutIdentity) {
9643
- return invalid2("receipt.identity.layoutIdentity", identity.layoutIdentity);
10439
+ return invalid("receipt.identity.layoutIdentity", identity.layoutIdentity);
9644
10440
  }
9645
10441
  if (expected.artifactDigest !== void 0 && identity.artifactDigest !== expected.artifactDigest) {
9646
- return invalid2("receipt.identity.artifactDigest", identity.artifactDigest);
10442
+ return invalid("receipt.identity.artifactDigest", identity.artifactDigest);
9647
10443
  }
9648
10444
  if (expected.inputDigest !== void 0 && identity.cookIdentity !== expected.inputDigest) {
9649
- return invalid2("receipt.identity.cookIdentity", identity.cookIdentity);
10445
+ return invalid("receipt.identity.cookIdentity", identity.cookIdentity);
9650
10446
  }
9651
10447
  return ok({
9652
10448
  schemaVersion: "material-cook/3",
@@ -9658,49 +10454,49 @@ function validateMaterialCookReceipt(value, expected = {}) {
9658
10454
  });
9659
10455
  }
9660
10456
  function validateCookedMaterialRecord(value) {
9661
- if (value === null || typeof value !== "object") return invalid2("record");
10457
+ if (value === null || typeof value !== "object") return invalid("record");
9662
10458
  const candidate = value;
9663
10459
  if (candidate.schemaVersion !== "material-cook/3")
9664
- return invalid2("schemaVersion", candidate.schemaVersion);
9665
- if (typeof candidate.guid !== "string" || !candidate.guid) return invalid2("guid");
10460
+ return invalid("schemaVersion", candidate.schemaVersion);
10461
+ if (typeof candidate.guid !== "string" || !candidate.guid) return invalid("guid");
9666
10462
  if (candidate.materialGuid !== void 0 && typeof candidate.materialGuid !== "string")
9667
- return invalid2("materialGuid");
10463
+ return invalid("materialGuid");
9668
10464
  if (candidate.publicationGeneration !== void 0 && !isGeneration(candidate.publicationGeneration))
9669
- return invalid2("publicationGeneration", candidate.publicationGeneration);
10465
+ return invalid("publicationGeneration", candidate.publicationGeneration);
9670
10466
  if (candidate.specializationKey !== void 0 && typeof candidate.specializationKey !== "string")
9671
- return invalid2("specializationKey");
10467
+ return invalid("specializationKey");
9672
10468
  if (candidate.artifactDigest !== void 0 && typeof candidate.artifactDigest !== "string")
9673
- return invalid2("artifactDigest");
10469
+ return invalid("artifactDigest");
9674
10470
  if (candidate.sourceClosure !== void 0 && (!Array.isArray(candidate.sourceClosure) || candidate.sourceClosure.some((path) => typeof path !== "string")))
9675
- return invalid2("sourceClosure");
10471
+ return invalid("sourceClosure");
9676
10472
  if (candidate.parameterContract !== void 0) {
9677
10473
  if (candidate.parameterContract === null || typeof candidate.parameterContract !== "object")
9678
- return invalid2("parameterContract");
10474
+ return invalid("parameterContract");
9679
10475
  const parameterContract = candidate.parameterContract;
9680
10476
  if (!Array.isArray(parameterContract.parameters))
9681
- return invalid2("parameterContract.parameters");
10477
+ return invalid("parameterContract.parameters");
9682
10478
  if (parameterContract.values === null || typeof parameterContract.values !== "object" || Array.isArray(parameterContract.values))
9683
- return invalid2("parameterContract.values");
10479
+ return invalid("parameterContract.values");
9684
10480
  }
9685
10481
  if (candidate.resolved === null || typeof candidate.resolved !== "object")
9686
- return invalid2("resolved");
9687
- if (candidate.refs === null || typeof candidate.refs !== "object") return invalid2("refs");
10482
+ return invalid("resolved");
10483
+ if (candidate.refs === null || typeof candidate.refs !== "object") return invalid("refs");
9688
10484
  if (candidate.artifact === null || typeof candidate.artifact !== "object")
9689
- return invalid2("artifact");
10485
+ return invalid("artifact");
9690
10486
  if (candidate.receipt === null || typeof candidate.receipt !== "object")
9691
- return invalid2("receipt");
10487
+ return invalid("receipt");
9692
10488
  const artifact = candidate.artifact;
9693
10489
  if (typeof artifact.digest !== "string" || !Array.isArray(artifact.bytes) && !ArrayBuffer.isView(artifact.bytes)) {
9694
- return invalid2("artifact");
10490
+ return invalid("artifact");
9695
10491
  }
9696
10492
  const receiptResult = validateMaterialCookReceipt(candidate.receipt, {
9697
10493
  artifactDigest: artifact.digest
9698
10494
  });
9699
10495
  if (!receiptResult.ok) return receiptResult;
9700
10496
  if (candidate.artifactDigest !== void 0 && candidate.artifactDigest !== artifact.digest)
9701
- return invalid2("artifactDigest", candidate.artifactDigest);
10497
+ return invalid("artifactDigest", candidate.artifactDigest);
9702
10498
  if (candidate.publicationGeneration !== void 0 && receiptResult.value.identity.cookGeneration !== candidate.publicationGeneration)
9703
- return invalid2("receipt.identity.cookGeneration", receiptResult.value.identity.cookGeneration);
10499
+ return invalid("receipt.identity.cookGeneration", receiptResult.value.identity.cookGeneration);
9704
10500
  const normalized = {
9705
10501
  ...candidate,
9706
10502
  artifact: {
@@ -9711,13 +10507,13 @@ function validateCookedMaterialRecord(value) {
9711
10507
  };
9712
10508
  return ok(normalized);
9713
10509
  }
9714
- function projectCookedMaterialRecord(record) {
10510
+ function projectCookedMaterialRecord(record2) {
9715
10511
  return {
9716
- resolved: record.resolved,
9717
- refs: record.refs,
9718
- artifact: record.artifact,
9719
- receipt: record.receipt,
9720
- schemaVersion: record.schemaVersion
10512
+ resolved: record2.resolved,
10513
+ refs: record2.refs,
10514
+ artifact: record2.artifact,
10515
+ receipt: record2.receipt,
10516
+ schemaVersion: record2.schemaVersion
9721
10517
  };
9722
10518
  }
9723
10519
  function packageEvidence(input) {
@@ -9760,12 +10556,150 @@ function packageVerification(input) {
9760
10556
  };
9761
10557
  }
9762
10558
 
10559
+ // src/inventory/binding.ts
10560
+ function projectAssetRefs(inventory) {
10561
+ return inventory.declarations.map(
10562
+ (row) => ({
10563
+ guid: row.guid,
10564
+ sourceKey: row.sourceKey,
10565
+ kind: row.kind
10566
+ })
10567
+ );
10568
+ }
10569
+ function projectSceneEntityRefs(inventory) {
10570
+ return inventory.declarations.flatMap(
10571
+ (row) => row.sceneBindings === void 0 ? [] : row.sceneBindings.map((bindingKey) => ({
10572
+ sceneSourceKey: row.sourceKey,
10573
+ bindingKey
10574
+ }))
10575
+ );
10576
+ }
10577
+ function failure2(code, expected, hint, detail) {
10578
+ return err({ code, expected, hint, detail });
10579
+ }
10580
+ function validateAuthorInventory(value) {
10581
+ if (typeof value !== "object" || value === null || !Array.isArray(value.declarations)) {
10582
+ return failure2(
10583
+ "inventory-source-key-missing",
10584
+ "an author inventory with declaration rows",
10585
+ "declare each author asset in the source inventory",
10586
+ {}
10587
+ );
10588
+ }
10589
+ const guids = /* @__PURE__ */ new Set();
10590
+ const sourceKeys = /* @__PURE__ */ new Set();
10591
+ const declarations = [];
10592
+ const rows = value.declarations;
10593
+ for (const raw of rows) {
10594
+ if (typeof raw !== "object" || raw === null) {
10595
+ return failure2(
10596
+ "inventory-source-key-missing",
10597
+ "each declaration has a non-empty sourceKey",
10598
+ "add sourceKey to the author declaration before projection",
10599
+ {}
10600
+ );
10601
+ }
10602
+ const row = raw;
10603
+ const guid = typeof row.guid === "string" ? row.guid : void 0;
10604
+ const sourceKey2 = typeof row.sourceKey === "string" ? row.sourceKey : void 0;
10605
+ if (guid === void 0 || sourceKey2 === void 0 || sourceKey2.trim().length === 0 || sourceKey2 !== sourceKey2.trim()) {
10606
+ return failure2(
10607
+ "inventory-source-key-missing",
10608
+ "each declaration has a non-empty sourceKey",
10609
+ "add sourceKey to the author declaration before projection",
10610
+ {
10611
+ ...guid === void 0 ? {} : { guid },
10612
+ ...sourceKey2 === void 0 ? {} : { sourceKey: sourceKey2 }
10613
+ }
10614
+ );
10615
+ }
10616
+ const normalizedGuid = guid.toLowerCase();
10617
+ if (guids.has(normalizedGuid)) {
10618
+ return failure2(
10619
+ "inventory-guid-duplicate",
10620
+ "every GUID identifies exactly one author declaration",
10621
+ "remove or rename the duplicate GUID before publishing the inventory",
10622
+ { guid, sourceKey: sourceKey2 }
10623
+ );
10624
+ }
10625
+ if (sourceKeys.has(sourceKey2)) {
10626
+ return failure2(
10627
+ "inventory-source-key-duplicate",
10628
+ "every sourceKey identifies exactly one author declaration",
10629
+ "rename the duplicate semantic sourceKey before publishing the inventory",
10630
+ { guid, sourceKey: sourceKey2 }
10631
+ );
10632
+ }
10633
+ if (row.kind === "scene") {
10634
+ const payload = row.payload;
10635
+ const payloadRecord = typeof payload === "object" && payload !== null ? payload : void 0;
10636
+ const entities = payloadRecord !== void 0 && Array.isArray(payloadRecord.entities) ? payloadRecord.entities : [];
10637
+ const bindings = /* @__PURE__ */ new Set();
10638
+ for (const entity of entities) {
10639
+ const bindingKey = typeof entity === "object" && entity !== null && typeof entity.bindingKey === "string" ? entity.bindingKey : void 0;
10640
+ if (bindingKey !== void 0 && bindingKey.length === 0) {
10641
+ return failure2(
10642
+ "inventory-scene-binding-missing",
10643
+ "each declared scene bindingKey is non-empty",
10644
+ "remove the empty bindingKey or declare a stable key in the scene producer",
10645
+ { sourceKey: sourceKey2 }
10646
+ );
10647
+ }
10648
+ if (bindingKey === void 0) continue;
10649
+ if (bindings.has(bindingKey)) {
10650
+ return failure2(
10651
+ "inventory-scene-binding-duplicate",
10652
+ "bindingKey values are unique within one scene",
10653
+ "rename the duplicate bindingKey in the scene producer",
10654
+ { sourceKey: sourceKey2 }
10655
+ );
10656
+ }
10657
+ bindings.add(bindingKey);
10658
+ }
10659
+ }
10660
+ guids.add(normalizedGuid);
10661
+ sourceKeys.add(sourceKey2);
10662
+ const sceneBindings = row.kind === "scene" && typeof row.payload === "object" && row.payload !== null ? row.payload.entities?.flatMap(
10663
+ (entity) => {
10664
+ if (typeof entity !== "object" || entity === null) return [];
10665
+ const key = entity.bindingKey;
10666
+ return typeof key === "string" && key.length > 0 ? [key] : [];
10667
+ }
10668
+ ) : void 0;
10669
+ declarations.push({
10670
+ guid,
10671
+ sourceKey: sourceKey2,
10672
+ kind: typeof row.kind === "string" ? row.kind : "unknown",
10673
+ payload: typeof row.payload === "object" && row.payload !== null ? row.payload : {},
10674
+ refs: Array.isArray(row.refs) ? row.refs.filter((ref) => typeof ref === "string") : [],
10675
+ ...sceneBindings === void 0 ? {} : { sceneBindings }
10676
+ });
10677
+ }
10678
+ return ok({ declarations });
10679
+ }
10680
+
10681
+ // src/inventory/sync.ts
10682
+ function inventoryDigest(inventory) {
10683
+ return inventory.declarations.map(
10684
+ (row) => `${row.guid}\0${row.sourceKey}\0${row.kind}\0${row.sceneBindings?.join("\0") ?? ""}`
10685
+ ).sort().join("\n");
10686
+ }
10687
+ function syncAuthorInventory(inventory) {
10688
+ return {
10689
+ declarations: inventory.declarations.map((row) => ({
10690
+ ...row,
10691
+ refs: [...row.refs],
10692
+ ...row.sceneBindings === void 0 ? {} : { sceneBindings: [...row.sceneBindings] }
10693
+ }))
10694
+ };
10695
+ }
10696
+
9763
10697
  // src/mesh-bin-contract.ts
9764
10698
  var MESH_BIN_VERSION = 4;
9765
10699
  var MESH_BIN_PROJECTION_VERSION = 1;
9766
10700
  var MESH_BIN_HEADER_V4_BYTES = 80;
9767
10701
  var MESH_BIN_DIGEST_BYTES = 32;
9768
- function failure(code, sourceKey2, expected, actual2) {
10702
+ function failure3(code, sourceKey2, expected, actual) {
9769
10703
  return {
9770
10704
  ok: false,
9771
10705
  error: {
@@ -9773,7 +10707,7 @@ function failure(code, sourceKey2, expected, actual2) {
9773
10707
  subject: "mesh-bin",
9774
10708
  sourceKey: sourceKey2,
9775
10709
  expected,
9776
- actual: actual2,
10710
+ actual,
9777
10711
  recovery: "re-cook the source with its Meta sidecar through the build-time importer"
9778
10712
  }
9779
10713
  };
@@ -9805,7 +10739,7 @@ function writeMeshBinHeader(header, out) {
9805
10739
  }
9806
10740
  function decodeMeshBinHeader(bytes, sourceKey2 = "<unknown mesh source>") {
9807
10741
  if (bytes.byteLength < MESH_BIN_HEADER_V4_BYTES) {
9808
- return failure(
10742
+ return failure3(
9809
10743
  "mesh-bin-header-truncated",
9810
10744
  sourceKey2,
9811
10745
  `mesh-bin v4 header (${MESH_BIN_HEADER_V4_BYTES} bytes)`,
@@ -9815,7 +10749,7 @@ function decodeMeshBinHeader(bytes, sourceKey2 = "<unknown mesh source>") {
9815
10749
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
9816
10750
  const version = view.getUint32(0, true);
9817
10751
  if (version !== MESH_BIN_VERSION) {
9818
- return failure("mesh-bin-version-unsupported", sourceKey2, "mesh-bin v4", `version ${version}`);
10752
+ return failure3("mesh-bin-version-unsupported", sourceKey2, "mesh-bin v4", `version ${version}`);
9819
10753
  }
9820
10754
  const projectionVersion = view.getUint32(4, true);
9821
10755
  const mask = view.getUint32(8, true);
@@ -9828,7 +10762,7 @@ function decodeMeshBinHeader(bytes, sourceKey2 = "<unknown mesh source>") {
9828
10762
  const jsonBytes = view.getUint32(36, true);
9829
10763
  const digest3 = readDigest(bytes.subarray(48, 48 + MESH_BIN_DIGEST_BYTES));
9830
10764
  if (projectionVersion !== MESH_BIN_PROJECTION_VERSION || mask === 0 || stride === 0 || !Number.isSafeInteger(vertexCount) || !Number.isSafeInteger(vertexBytes) || !Number.isSafeInteger(indexCount) || !Number.isSafeInteger(indexBytes) || !Number.isSafeInteger(jsonBytes) || indexCount === 0 && indexWidth !== 0 || indexCount > 0 && indexWidth !== 2 && indexWidth !== 4 || indexBytes !== indexCount * indexWidth || vertexBytes !== vertexCount * stride || digest3.length === 0) {
9831
- return failure(
10765
+ return failure3(
9832
10766
  "mesh-bin-header-invalid",
9833
10767
  sourceKey2,
9834
10768
  "safe v4 projection, stride, cardinality, and payload byte lengths",
@@ -9852,24 +10786,7 @@ function decodeMeshBinHeader(bytes, sourceKey2 = "<unknown mesh source>") {
9852
10786
  }
9853
10787
  };
9854
10788
  }
9855
- function upgradeLegacyAuthoredPack(pack) {
9856
- if (pack.schemaVersion !== "1.0.0" || pack.assets === void 0) return pack;
9857
- return {
9858
- ...pack,
9859
- schemaVersion: "2.0.0",
9860
- assets: pack.assets.map((asset) => {
9861
- const fontRefs = asset.kind === "font" ? [asset.payload.atlasGuid, asset.payload.samplerGuid].filter(
9862
- (guid) => typeof guid === "string"
9863
- ) : [];
9864
- return {
9865
- ...asset,
9866
- refs: asset.refs ?? fontRefs,
9867
- artifacts: asset.artifacts ?? {}
9868
- };
9869
- })
9870
- };
9871
- }
9872
- function failure2(code, expected, hint, detail) {
10789
+ function failure4(code, expected, hint, detail) {
9873
10790
  return { ok: false, error: { code, expected, hint, detail } };
9874
10791
  }
9875
10792
  function sorted(value) {
@@ -9968,7 +10885,7 @@ function transportArtifacts(assets, artifactPath) {
9968
10885
  }
9969
10886
  async function finalizePackageProduct(product, policy) {
9970
10887
  if (product.sourceRevision.trim().length === 0) {
9971
- return failure2(
10888
+ return failure4(
9972
10889
  "missing-source-revision",
9973
10890
  "a non-empty source revision",
9974
10891
  "rebuild the inventory and provide the producer source revision",
@@ -9979,7 +10896,7 @@ async function finalizePackageProduct(product, policy) {
9979
10896
  for (const asset of product.assets) {
9980
10897
  const guid = asset.guid.toLowerCase();
9981
10898
  if (seen.has(guid)) {
9982
- return failure2(
10899
+ return failure4(
9983
10900
  "duplicate-guid",
9984
10901
  "one terminal product row per GUID",
9985
10902
  "repair the inventory or producer output before finalization",
@@ -9989,7 +10906,7 @@ async function finalizePackageProduct(product, policy) {
9989
10906
  seen.add(guid);
9990
10907
  for (const [key, artifact] of Object.entries(asset.artifacts)) {
9991
10908
  if (!safePath(key) || !(artifact.bytes instanceof Uint8Array)) {
9992
- return failure2(
10909
+ return failure4(
9993
10910
  "invalid-artifact",
9994
10911
  "asset-local artifact keys and byte bodies",
9995
10912
  "repair the producer artifact before finalization",
@@ -10000,7 +10917,7 @@ async function finalizePackageProduct(product, policy) {
10000
10917
  }
10001
10918
  for (const guid of seen) {
10002
10919
  if (!product.receipts.some((receipt) => receipt.guid.toLowerCase() === guid)) {
10003
- return failure2(
10920
+ return failure4(
10004
10921
  "missing-receipt",
10005
10922
  "one receipt for every terminal product GUID",
10006
10923
  "preserve the producer receipt when handing the product to engine-pack",
@@ -10016,7 +10933,7 @@ async function finalizePackageProduct(product, policy) {
10016
10933
  for (const [key, artifact] of Object.entries(asset.artifacts)) {
10017
10934
  const path = policy.artifactPath(asset.guid, key);
10018
10935
  if (!safePath(path)) {
10019
- return failure2(
10936
+ return failure4(
10020
10937
  "invalid-artifact",
10021
10938
  "package-relative artifact output paths",
10022
10939
  "repair the finalizer artifact path policy",
@@ -10027,7 +10944,7 @@ async function finalizePackageProduct(product, policy) {
10027
10944
  }
10028
10945
  }
10029
10946
  if (!safePath(policy.packagePath)) {
10030
- return failure2(
10947
+ return failure4(
10031
10948
  "invalid-artifact",
10032
10949
  "a package-relative package path",
10033
10950
  "repair the finalizer package path policy",
@@ -10070,6 +10987,7 @@ async function finalizePackageTransportSource(input, policy) {
10070
10987
  packageUrl: finalized.value.packageUrl,
10071
10988
  artifacts: transportArtifacts(product.assets, policy.artifactPath),
10072
10989
  digest: finalized.value.digest,
10990
+ receipts: finalized.value.receipts,
10073
10991
  sourceRevision,
10074
10992
  semantic: JSON.stringify(sorted(finalized.value.pack))
10075
10993
  };
@@ -10092,6 +11010,1601 @@ function revisionStable(value) {
10092
11010
  function packageTransportRevision(value) {
10093
11011
  return createHash("sha256").update(JSON.stringify(revisionStable(value))).digest("hex");
10094
11012
  }
11013
+ function isRecord3(value) {
11014
+ return value !== null && typeof value === "object" && !Array.isArray(value);
11015
+ }
11016
+ function jsonValue2(value) {
11017
+ if (value instanceof Uint8Array) return AssetGuid.format(value);
11018
+ if (Array.isArray(value)) return value.map((item) => jsonValue2(item));
11019
+ if (isRecord3(value)) {
11020
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, jsonValue2(item)]));
11021
+ }
11022
+ return value;
11023
+ }
11024
+ function revisionOf(source) {
11025
+ return `sha256:${createHash("sha256").update(source).digest("hex")}`;
11026
+ }
11027
+ function makeError(code, expected, hint, detail = {}, actual) {
11028
+ return {
11029
+ code,
11030
+ expected,
11031
+ hint,
11032
+ ...actual === void 0 ? {} : { actual },
11033
+ detail
11034
+ };
11035
+ }
11036
+ function pathError(code, operation, expected, hint, detail = {}, actual) {
11037
+ return makeError(code, expected, hint, { requestId: operation.requestId, ...detail }, actual);
11038
+ }
11039
+ function confinedPath(gameRoot, candidate, operation) {
11040
+ if (typeof candidate !== "string" || candidate.length === 0 || isAbsolute(candidate)) {
11041
+ return err(
11042
+ pathError(
11043
+ "pack-source-path-invalid",
11044
+ operation,
11045
+ "a non-empty game-root-relative .pack.ts or .pack.json path",
11046
+ "pass a relative source locator inside the selected game root",
11047
+ { sourcePath: candidate },
11048
+ typeof candidate === "string" ? candidate : void 0
11049
+ )
11050
+ );
11051
+ }
11052
+ const root = resolve(gameRoot);
11053
+ const absolute = resolve(root, candidate);
11054
+ const relativePath = relative(root, absolute).split(sep).join("/");
11055
+ if (relativePath.length === 0 || relativePath === ".." || relativePath.startsWith("../") || !relativePath.endsWith(".pack.ts") && !relativePath.endsWith(".pack.json")) {
11056
+ return err(
11057
+ pathError(
11058
+ "pack-source-path-invalid",
11059
+ operation,
11060
+ "a game-root-relative .pack.ts or .pack.json path",
11061
+ "choose a Pack source path inside the selected game root",
11062
+ { sourcePath: candidate },
11063
+ candidate
11064
+ )
11065
+ );
11066
+ }
11067
+ return ok({ absolute, relative: relativePath });
11068
+ }
11069
+ async function atomicWrite(path, source) {
11070
+ await mkdir(dirname(path), { recursive: true });
11071
+ const temporary = `${path}.${process.pid}.${Date.now()}.tmp`;
11072
+ try {
11073
+ await writeFile(temporary, source, { encoding: "utf8", flag: "wx" });
11074
+ await rename(temporary, path);
11075
+ } catch (cause) {
11076
+ await rm(temporary, { force: true });
11077
+ throw cause;
11078
+ }
11079
+ }
11080
+ function packageKey(packageId2) {
11081
+ return PackageId.format(packageId2).toLowerCase();
11082
+ }
11083
+ function parsePackageId2(value) {
11084
+ if (typeof value !== "string") return void 0;
11085
+ const parsed = PackageId.parse(value);
11086
+ return parsed.ok ? parsed.value : void 0;
11087
+ }
11088
+ function serializedParameters(definition) {
11089
+ if (!("parameters" in definition)) return void 0;
11090
+ return definition.parameters.map((parameter) => ({
11091
+ name: parameter.name,
11092
+ type: parameter.type,
11093
+ default: jsonValue2(parameter.default),
11094
+ ...parameter.minimum === void 0 ? {} : { minimum: parameter.minimum },
11095
+ ...parameter.maximum === void 0 ? {} : { maximum: parameter.maximum },
11096
+ ...parameter.values === void 0 ? {} : { values: parameter.values },
11097
+ ...parameter.kind === void 0 ? {} : { kind: parameter.kind }
11098
+ }));
11099
+ }
11100
+ function sourceResult(subject, operation) {
11101
+ const base = {
11102
+ operation: operation.operation,
11103
+ requestId: operation.requestId,
11104
+ sourcePath: subject.relativePath,
11105
+ packageId: PackageId.format(subject.packageId),
11106
+ format: subject.format === "source" ? "pack.ts" : subject.format
11107
+ };
11108
+ if (subject.format === "source") {
11109
+ const parameters = serializedParameters(subject.definition);
11110
+ return {
11111
+ ...base,
11112
+ ...parameters === void 0 ? {} : { parameters },
11113
+ ...parameters === void 0 ? {} : {
11114
+ capabilities: [
11115
+ "asset-source.create-instance",
11116
+ "asset-source.rebuild",
11117
+ "asset-source.cold-cook"
11118
+ ]
11119
+ }
11120
+ };
11121
+ }
11122
+ if (subject.format === "instance") {
11123
+ return {
11124
+ ...base,
11125
+ parentPackageId: PackageId.format(subject.parent),
11126
+ values: jsonValue2(subject.values)
11127
+ };
11128
+ }
11129
+ return {
11130
+ ...base,
11131
+ assets: subject.assets.map((asset) => ({
11132
+ sourceKey: asset.sourceKey,
11133
+ guid: asset.guid,
11134
+ kind: asset.kind,
11135
+ ...asset.name === void 0 ? {} : { name: asset.name },
11136
+ refs: asset.refs
11137
+ }))
11138
+ };
11139
+ }
11140
+ function scanFailure(operation, cause) {
11141
+ const value = cause !== null && typeof cause === "object" ? cause : {};
11142
+ const detail = value.detail !== null && typeof value.detail === "object" ? value.detail : {};
11143
+ const reason = isRecord3(detail) && typeof detail.reason === "string" ? detail.reason : void 0;
11144
+ const code = reason === "pack-parent-not-found" || reason === "pack-parent-cycle" || reason === "pack-parent-not-parameterized" ? reason : value.code === "pack-guid-collision" ? "pack-guid-collision" : "pack-parameter-invalid";
11145
+ return makeError(
11146
+ code,
11147
+ typeof value.expected === "string" ? value.expected : "a valid Pack Source Index",
11148
+ typeof value.hint === "string" ? value.hint : "repair the first source or package diagnostic, then retry",
11149
+ { requestId: operation.requestId, cause: value.detail ?? String(cause) }
11150
+ );
11151
+ }
11152
+ function scanRoots(options) {
11153
+ return options.scanRoots === void 0 || options.scanRoots.length === 0 ? [resolve(options.gameRoot)] : options.scanRoots.map((root) => resolve(options.gameRoot, root));
11154
+ }
11155
+ async function readMaterialized(options, operation) {
11156
+ let rawRows;
11157
+ try {
11158
+ rawRows = await options.materialized?.() ?? [];
11159
+ } catch (cause) {
11160
+ return err(
11161
+ makeError(
11162
+ "pack-parameter-invalid",
11163
+ "the materialized projection callback to return current-generation rows",
11164
+ "repair the build-owner projection callback, then retry the Pack operation",
11165
+ {
11166
+ requestId: operation.requestId,
11167
+ cause: cause instanceof Error ? cause.message : String(cause)
11168
+ }
11169
+ )
11170
+ );
11171
+ }
11172
+ if (!Array.isArray(rawRows)) {
11173
+ return err(
11174
+ makeError(
11175
+ "pack-parameter-invalid",
11176
+ "the materialized projection callback to return an array",
11177
+ "repair the build-owner projection callback, then retry the Pack operation",
11178
+ { requestId: operation.requestId, actual: typeof rawRows }
11179
+ )
11180
+ );
11181
+ }
11182
+ const materialized = [];
11183
+ const pairOwners = /* @__PURE__ */ new Map();
11184
+ const guidOwners = /* @__PURE__ */ new Map();
11185
+ for (const rawRow of rawRows) {
11186
+ if (!isRecord3(rawRow)) {
11187
+ return err(
11188
+ makeError(
11189
+ "pack-parameter-invalid",
11190
+ "materialized rows to be objects",
11191
+ "repair the current generation projection before querying Pack authoring",
11192
+ { requestId: operation.requestId, actual: typeof rawRow }
11193
+ )
11194
+ );
11195
+ }
11196
+ const row = rawRow;
11197
+ const parsedPackageId = parsePackageId2(row.packageId);
11198
+ if (parsedPackageId === void 0) {
11199
+ return err(
11200
+ makeError(
11201
+ "pack-package-id-invalid",
11202
+ "materialized rows to contain a valid UUID packageId",
11203
+ "repair the current generation projection before querying Pack authoring",
11204
+ { requestId: operation.requestId, packageId: row.packageId, sourceKey: row.sourceKey }
11205
+ )
11206
+ );
11207
+ }
11208
+ if (!isValidPackSourceKey(row.sourceKey)) {
11209
+ return err(
11210
+ makeError(
11211
+ "pack-source-key-invalid",
11212
+ "materialized rows to contain a valid sourceKey",
11213
+ "repair the current generation projection before querying Pack authoring",
11214
+ { requestId: operation.requestId, packageId: row.packageId, sourceKey: row.sourceKey }
11215
+ )
11216
+ );
11217
+ }
11218
+ if (typeof row.guid !== "string" || !isValidAssetGuidString(row.guid) || typeof row.kind !== "string" || row.kind.length === 0) {
11219
+ return err(
11220
+ makeError(
11221
+ "pack-parameter-invalid",
11222
+ "materialized rows to contain a valid GUID and non-empty kind",
11223
+ "repair the current generation projection before querying Pack authoring",
11224
+ { requestId: operation.requestId, packageId: row.packageId, sourceKey: row.sourceKey }
11225
+ )
11226
+ );
11227
+ }
11228
+ if (row.refs !== void 0 && (!Array.isArray(row.refs) || row.refs.some((ref) => typeof ref !== "string" || !isValidAssetGuidString(ref)))) {
11229
+ return err(
11230
+ makeError(
11231
+ "pack-parameter-invalid",
11232
+ "materialized refs to contain UUID AssetGuid strings when present",
11233
+ "repair the current generation projection before querying Pack authoring",
11234
+ { requestId: operation.requestId, packageId: row.packageId, sourceKey: row.sourceKey }
11235
+ )
11236
+ );
11237
+ }
11238
+ if (row.sourcePath !== void 0 && typeof row.sourcePath !== "string") {
11239
+ return err(
11240
+ makeError(
11241
+ "pack-parameter-invalid",
11242
+ "materialized sourcePath to be a string when present",
11243
+ "repair the current generation projection before querying Pack authoring",
11244
+ { requestId: operation.requestId, sourcePath: row.sourcePath }
11245
+ )
11246
+ );
11247
+ }
11248
+ const packageId2 = PackageId.format(parsedPackageId).toLowerCase();
11249
+ const expectedGuid = AssetGuid.format(
11250
+ AssetGuid.derive(parsedPackageId, row.sourceKey)
11251
+ ).toLowerCase();
11252
+ const guid = row.guid.toLowerCase();
11253
+ if (guid !== expectedGuid) {
11254
+ return err(
11255
+ makeError(
11256
+ "pack-guid-collision",
11257
+ "materialized GUID to equal UUIDv5(packageId, sourceKey)",
11258
+ "rebuild the current generation from the stable packageId and sourceKey pair",
11259
+ {
11260
+ requestId: operation.requestId,
11261
+ packageId: packageId2,
11262
+ sourceKey: row.sourceKey,
11263
+ expectedGuid,
11264
+ observedGuid: row.guid
11265
+ }
11266
+ )
11267
+ );
11268
+ }
11269
+ const pair = `${packageId2}/${row.sourceKey}`;
11270
+ const pairOwner = pairOwners.get(pair);
11271
+ if (pairOwner !== void 0) {
11272
+ return err(
11273
+ makeError(
11274
+ "pack-guid-collision",
11275
+ "one materialized row per packageId/sourceKey pair",
11276
+ "remove the stale duplicate row from the current generation projection",
11277
+ { requestId: operation.requestId, pair, paths: [pairOwner, row.sourcePath] }
11278
+ )
11279
+ );
11280
+ }
11281
+ pairOwners.set(pair, row.sourcePath ?? pair);
11282
+ const guidOwner = guidOwners.get(guid);
11283
+ if (guidOwner !== void 0) {
11284
+ return err(
11285
+ makeError(
11286
+ "pack-guid-collision",
11287
+ "one materialized owner per derived AssetGuid",
11288
+ "repair the current generation projection so each derived GUID has one owner",
11289
+ { requestId: operation.requestId, guid, paths: [guidOwner, row.sourcePath] }
11290
+ )
11291
+ );
11292
+ }
11293
+ guidOwners.set(guid, row.sourcePath ?? pair);
11294
+ materialized.push({
11295
+ packageId: packageId2,
11296
+ sourceKey: row.sourceKey,
11297
+ guid,
11298
+ kind: row.kind,
11299
+ ...row.sourcePath === void 0 ? {} : { sourcePath: row.sourcePath },
11300
+ ...row.refs === void 0 ? {} : { refs: Object.freeze([...row.refs]) },
11301
+ ready: row.ready === true && row.artifactsReady !== false
11302
+ });
11303
+ }
11304
+ materialized.sort(
11305
+ (left, right) => `${left.packageId}/${left.sourceKey}`.localeCompare(`${right.packageId}/${right.sourceKey}`)
11306
+ );
11307
+ return ok(materialized);
11308
+ }
11309
+ async function createSnapshot(options, operation) {
11310
+ const scanned = options.inventory === void 0 ? await scanInventory(scanRoots(options)) : await options.inventory();
11311
+ if (!scanned.ok) return err(scanFailure(operation, scanned.error));
11312
+ const subjects = /* @__PURE__ */ new Map();
11313
+ const directAssets = [];
11314
+ for (const [sourcePath, declaration] of scanned.value.declarations) {
11315
+ const relativePath = relative(resolve(options.gameRoot), sourcePath).split(sep).join("/");
11316
+ if (declaration.format === "pack.ts-parameterized") {
11317
+ subjects.set(packageKey(declaration.definition.packageId), {
11318
+ format: "source",
11319
+ packageId: declaration.definition.packageId,
11320
+ sourcePath,
11321
+ relativePath,
11322
+ definition: declaration.definition
11323
+ });
11324
+ continue;
11325
+ }
11326
+ if (declaration.format !== "pack.json" || declaration.value.schemaVersion !== "3.0.0") continue;
11327
+ const parsed = parseParameterizedPackJson(declaration.value);
11328
+ if (!parsed.ok) return err(parsed.error);
11329
+ if (parsed.value.format === "direct") {
11330
+ const projected = projectDirectPackJson(parsed.value);
11331
+ if (!projected.ok) return err(projected.error);
11332
+ const subject = {
11333
+ format: "direct",
11334
+ packageId: parsed.value.packageId,
11335
+ sourcePath,
11336
+ relativePath,
11337
+ assets: projected.value.assets
11338
+ };
11339
+ subjects.set(packageKey(subject.packageId), subject);
11340
+ directAssets.push(
11341
+ ...projected.value.assets.map((asset) => ({
11342
+ packageId: projected.value.packageId.toLowerCase(),
11343
+ sourceKey: asset.sourceKey,
11344
+ guid: asset.guid.toLowerCase(),
11345
+ kind: asset.kind,
11346
+ sourcePath: relativePath,
11347
+ ...asset.name === void 0 ? {} : { name: asset.name },
11348
+ refs: asset.refs
11349
+ }))
11350
+ );
11351
+ } else {
11352
+ subjects.set(packageKey(parsed.value.packageId), {
11353
+ format: "instance",
11354
+ packageId: parsed.value.packageId,
11355
+ parent: parsed.value.parent,
11356
+ values: parsed.value.values,
11357
+ sourcePath,
11358
+ relativePath
11359
+ });
11360
+ }
11361
+ }
11362
+ const materializedResult = await readMaterialized(options, operation);
11363
+ if (!materializedResult.ok) return materializedResult;
11364
+ const materialized = materializedResult.value;
11365
+ const guidOwners = /* @__PURE__ */ new Map();
11366
+ const directPairs = /* @__PURE__ */ new Set();
11367
+ for (const entry of scanned.value.inventory) {
11368
+ const declaration = scanned.value.declarations.get(entry.sourcePath);
11369
+ if (declaration?.format === "pack.json" && declaration.value.schemaVersion === "3.0.0") {
11370
+ continue;
11371
+ }
11372
+ guidOwners.set(entry.guid.toLowerCase(), entry.sourcePath);
11373
+ }
11374
+ for (const asset of directAssets) {
11375
+ directPairs.add(`${asset.packageId}/${asset.sourceKey}`);
11376
+ const existing = guidOwners.get(asset.guid);
11377
+ if (existing !== void 0) {
11378
+ return err(
11379
+ makeError(
11380
+ "pack-guid-collision",
11381
+ "one current-generation owner per AssetGuid",
11382
+ "repair the direct Pack or external source projection before querying Pack authoring",
11383
+ {
11384
+ requestId: operation.requestId,
11385
+ guid: asset.guid,
11386
+ paths: [existing, asset.sourcePath]
11387
+ }
11388
+ )
11389
+ );
11390
+ }
11391
+ guidOwners.set(asset.guid, asset.sourcePath ?? asset.packageId);
11392
+ }
11393
+ for (const asset of materialized) {
11394
+ const existing = guidOwners.get(asset.guid);
11395
+ const pair = `${asset.packageId}/${asset.sourceKey}`;
11396
+ if (existing !== void 0 && !directPairs.has(pair)) {
11397
+ return err(
11398
+ makeError(
11399
+ "pack-guid-collision",
11400
+ "one current-generation owner per AssetGuid",
11401
+ "remove the stale materialized row or repair the direct/source owner before querying Pack authoring",
11402
+ {
11403
+ requestId: operation.requestId,
11404
+ guid: asset.guid,
11405
+ paths: [existing, asset.sourcePath]
11406
+ }
11407
+ )
11408
+ );
11409
+ }
11410
+ guidOwners.set(asset.guid, asset.sourcePath ?? asset.packageId);
11411
+ }
11412
+ const knownGuids = /* @__PURE__ */ new Set([
11413
+ ...scanned.value.inventory.map((entry) => entry.guid.toLowerCase()),
11414
+ ...directAssets.map((asset) => asset.guid),
11415
+ ...materialized.map((asset) => asset.guid)
11416
+ ]);
11417
+ return ok({ inventory: scanned.value, subjects, directAssets, materialized, knownGuids });
11418
+ }
11419
+ function parentSubject(snapshot, packageId2) {
11420
+ const subject = snapshot.subjects.get(packageKey(packageId2));
11421
+ if (subject === void 0) return void 0;
11422
+ if (subject.format === "source") {
11423
+ return {
11424
+ format: "source",
11425
+ packageId: subject.packageId,
11426
+ parameters: "parameters" in subject.definition ? subject.definition.parameters : []
11427
+ };
11428
+ }
11429
+ if (subject.format === "instance") {
11430
+ return {
11431
+ format: "instance",
11432
+ packageId: subject.packageId,
11433
+ parent: subject.parent,
11434
+ values: subject.values
11435
+ };
11436
+ }
11437
+ return { format: "direct", packageId: subject.packageId };
11438
+ }
11439
+ async function resolveInstance(snapshot, subject) {
11440
+ return resolvePackParameterInheritance(
11441
+ {
11442
+ format: "instance",
11443
+ packageId: subject.packageId,
11444
+ parent: subject.parent,
11445
+ values: subject.values
11446
+ },
11447
+ async (packageId2) => parentSubject(snapshot, packageId2)
11448
+ );
11449
+ }
11450
+ function subjectFor(gameRoot, snapshot, operation) {
11451
+ if (operation.sourcePath !== void 0) {
11452
+ const confined = confinedPath(gameRoot, operation.sourcePath, operation);
11453
+ if (!confined.ok) return confined;
11454
+ const expected = confined.value.relative;
11455
+ const source = [...snapshot.subjects.values()].find(
11456
+ (candidate) => candidate.relativePath === expected
11457
+ );
11458
+ if (source !== void 0) {
11459
+ const requestedPackageId = operation.packageId === void 0 ? void 0 : PackageId.parse(operation.packageId);
11460
+ if (requestedPackageId?.ok === false) {
11461
+ return err(
11462
+ makeError(
11463
+ "pack-package-id-invalid",
11464
+ "a UUID packageId",
11465
+ "repair packageId before querying the Pack subject",
11466
+ { requestId: operation.requestId, packageId: operation.packageId },
11467
+ operation.packageId
11468
+ )
11469
+ );
11470
+ }
11471
+ if (requestedPackageId?.ok === true && packageKey(requestedPackageId.value) !== packageKey(source.packageId)) {
11472
+ return err(
11473
+ makeError(
11474
+ "pack-parameter-invalid",
11475
+ "sourcePath and packageId to identify the same Pack subject",
11476
+ "remove the conflicting locator or use the packageId returned by asset.list",
11477
+ {
11478
+ requestId: operation.requestId,
11479
+ sourcePath: expected,
11480
+ packageId: operation.packageId,
11481
+ observedPackageId: PackageId.format(source.packageId)
11482
+ }
11483
+ )
11484
+ );
11485
+ }
11486
+ return ok(source);
11487
+ }
11488
+ return err(
11489
+ pathError(
11490
+ "pack-source-not-found",
11491
+ operation,
11492
+ "sourcePath to identify a current Pack subject",
11493
+ "inspect the current Source Index and choose an existing source path",
11494
+ { sourcePath: expected },
11495
+ expected
11496
+ )
11497
+ );
11498
+ }
11499
+ if (operation.packageId !== void 0) {
11500
+ const parsed = PackageId.parse(operation.packageId);
11501
+ if (!parsed.ok) {
11502
+ return err(
11503
+ makeError(
11504
+ "pack-package-id-invalid",
11505
+ "a UUID packageId",
11506
+ "repair packageId before querying the Pack",
11507
+ { requestId: operation.requestId, packageId: operation.packageId },
11508
+ operation.packageId
11509
+ )
11510
+ );
11511
+ }
11512
+ const subject = snapshot.subjects.get(packageKey(parsed.value));
11513
+ if (subject !== void 0) return ok(subject);
11514
+ return err(
11515
+ makeError(
11516
+ "pack-source-not-found",
11517
+ "packageId to exist in the current Source Index",
11518
+ "inspect the current source index and choose an existing packageId",
11519
+ { requestId: operation.requestId, packageId: operation.packageId }
11520
+ )
11521
+ );
11522
+ }
11523
+ if (operation.subject !== void 0 && !isValidAssetGuidString(operation.subject)) {
11524
+ const confined = confinedPath(gameRoot, operation.subject, operation);
11525
+ if (!confined.ok) return confined;
11526
+ const source = [...snapshot.subjects.values()].find(
11527
+ (candidate) => candidate.relativePath === confined.value.relative
11528
+ );
11529
+ if (source !== void 0) return ok(source);
11530
+ }
11531
+ if (operation.subject !== void 0 && isValidAssetGuidString(operation.subject)) {
11532
+ const guid = operation.subject.toLowerCase();
11533
+ const packageSubject = snapshot.subjects.get(guid);
11534
+ if (packageSubject !== void 0) return ok(packageSubject);
11535
+ const asset = [...snapshot.directAssets, ...snapshot.materialized].find(
11536
+ (candidate) => candidate.guid === guid
11537
+ );
11538
+ if (asset !== void 0) {
11539
+ const subject = snapshot.subjects.get(asset.packageId);
11540
+ if (subject !== void 0) return ok(subject);
11541
+ }
11542
+ }
11543
+ return err(
11544
+ makeError(
11545
+ "pack-source-not-found",
11546
+ "a packageId, source path, or known AssetGuid subject",
11547
+ "run asset.list or asset.inspect to obtain a current subject locator",
11548
+ { requestId: operation.requestId, subject: operation.subject }
11549
+ )
11550
+ );
11551
+ }
11552
+ async function inspectAsset(gameRoot, snapshot, operation) {
11553
+ const selected = subjectFor(gameRoot, snapshot, operation);
11554
+ if (!selected.ok) return selected;
11555
+ const subject = selected.value;
11556
+ if (operation.sourceKey !== void 0) {
11557
+ if (!isValidPackSourceKey(operation.sourceKey)) {
11558
+ return err(
11559
+ makeError(
11560
+ "pack-source-key-invalid",
11561
+ "a stable lower-case sourceKey",
11562
+ "repair sourceKey before inspecting the output identity",
11563
+ { requestId: operation.requestId, sourceKey: operation.sourceKey },
11564
+ operation.sourceKey
11565
+ )
11566
+ );
11567
+ }
11568
+ if (subject.format === "direct") {
11569
+ const asset = subject.assets.find((candidate) => candidate.sourceKey === operation.sourceKey);
11570
+ if (asset === void 0) {
11571
+ return err(
11572
+ makeError(
11573
+ "pack-output-not-materialized",
11574
+ "sourceKey to exist in the direct Pack",
11575
+ "inspect the current source and choose an existing sourceKey",
11576
+ {
11577
+ requestId: operation.requestId,
11578
+ sourceKey: operation.sourceKey,
11579
+ packageId: PackageId.format(subject.packageId)
11580
+ }
11581
+ )
11582
+ );
11583
+ }
11584
+ const materialized = snapshot.materialized.find(
11585
+ (candidate) => candidate.packageId === packageKey(subject.packageId) && candidate.sourceKey === operation.sourceKey
11586
+ );
11587
+ return ok({
11588
+ ...sourceResult(subject, operation),
11589
+ sourceKey: asset.sourceKey,
11590
+ guid: asset.guid,
11591
+ kind: asset.kind,
11592
+ status: materialized === void 0 ? "identity" : materialized.ready ? "ready" : "present"
11593
+ });
11594
+ }
11595
+ try {
11596
+ const guid = AssetGuid.format(AssetGuid.derive(subject.packageId, operation.sourceKey));
11597
+ const materialized = [...snapshot.materialized].find(
11598
+ (candidate) => candidate.packageId === packageKey(subject.packageId) && candidate.sourceKey === operation.sourceKey
11599
+ );
11600
+ return ok({
11601
+ ...sourceResult(subject, operation),
11602
+ sourceKey: operation.sourceKey,
11603
+ guid,
11604
+ ...materialized === void 0 ? { status: "identity" } : {
11605
+ status: materialized.ready ? "ready" : "present",
11606
+ kind: materialized.kind
11607
+ }
11608
+ });
11609
+ } catch (cause) {
11610
+ return err(
11611
+ makeError(
11612
+ "pack-source-key-invalid",
11613
+ "a valid lower-case sourceKey",
11614
+ "repair sourceKey before inspecting the output identity",
11615
+ { requestId: operation.requestId, sourceKey: operation.sourceKey, cause: String(cause) },
11616
+ operation.sourceKey
11617
+ )
11618
+ );
11619
+ }
11620
+ }
11621
+ if (subject.format === "instance") {
11622
+ const resolved = await resolveInstance(snapshot, subject);
11623
+ if (!resolved.ok) return resolved;
11624
+ return ok({
11625
+ ...sourceResult(subject, operation),
11626
+ parameters: resolved.value.parameters.map((parameter) => ({
11627
+ name: parameter.name,
11628
+ type: parameter.type,
11629
+ default: jsonValue2(parameter.default),
11630
+ ...parameter.minimum === void 0 ? {} : { minimum: parameter.minimum },
11631
+ ...parameter.maximum === void 0 ? {} : { maximum: parameter.maximum },
11632
+ ...parameter.values === void 0 ? {} : { values: parameter.values },
11633
+ ...parameter.kind === void 0 ? {} : { kind: parameter.kind }
11634
+ })),
11635
+ effectiveValues: jsonValue2(resolved.value.values),
11636
+ parentChain: resolved.value.parentChain
11637
+ });
11638
+ }
11639
+ return ok(sourceResult(subject, operation));
11640
+ }
11641
+ function listAssets(snapshot, operation) {
11642
+ const assets = snapshotAssets(snapshot).map((asset) => ({
11643
+ packageId: asset.packageId,
11644
+ sourceKey: asset.sourceKey,
11645
+ guid: asset.guid,
11646
+ kind: asset.kind,
11647
+ ...asset.name === void 0 ? {} : { name: asset.name },
11648
+ ...asset.sourcePath === void 0 ? {} : { sourcePath: asset.sourcePath },
11649
+ status: !("ready" in asset) ? "identity" : asset.ready ? "ready" : "present"
11650
+ })).sort(
11651
+ (left, right) => `${left.packageId}/${left.sourceKey}`.localeCompare(`${right.packageId}/${right.sourceKey}`)
11652
+ );
11653
+ const sources = [...snapshot.subjects.values()].map((subject) => {
11654
+ if (subject.format === "source") {
11655
+ const parameters = serializedParameters(subject.definition);
11656
+ return {
11657
+ packageId: PackageId.format(subject.packageId),
11658
+ sourcePath: subject.relativePath,
11659
+ format: "pack.ts",
11660
+ ...parameters === void 0 ? {} : { parameters }
11661
+ };
11662
+ }
11663
+ if (subject.format === "instance") {
11664
+ return {
11665
+ packageId: PackageId.format(subject.packageId),
11666
+ sourcePath: subject.relativePath,
11667
+ format: "instance",
11668
+ parentPackageId: PackageId.format(subject.parent),
11669
+ values: jsonValue2(subject.values)
11670
+ };
11671
+ }
11672
+ return {
11673
+ packageId: PackageId.format(subject.packageId),
11674
+ sourcePath: subject.relativePath,
11675
+ format: "direct"
11676
+ };
11677
+ }).sort(
11678
+ (left, right) => `${left.packageId}/${left.sourcePath}`.localeCompare(
11679
+ `${right.packageId}/${right.sourcePath}`
11680
+ )
11681
+ );
11682
+ return {
11683
+ operation: operation.operation,
11684
+ requestId: operation.requestId,
11685
+ assets,
11686
+ sources,
11687
+ snapshot: { sourceCount: snapshot.subjects.size, assetCount: assets.length }
11688
+ };
11689
+ }
11690
+ function snapshotAssets(snapshot) {
11691
+ const assets = /* @__PURE__ */ new Map();
11692
+ for (const asset of snapshot.directAssets) {
11693
+ assets.set(`${asset.packageId}/${asset.sourceKey}`, asset);
11694
+ }
11695
+ for (const asset of snapshot.materialized) {
11696
+ assets.set(`${asset.packageId}/${asset.sourceKey}`, asset);
11697
+ }
11698
+ return [...assets.values()];
11699
+ }
11700
+ function assetForPair(snapshot, packageId2, sourceKey2) {
11701
+ const key = `${packageKey(packageId2)}/${sourceKey2}`;
11702
+ return snapshotAssets(snapshot).find((asset) => `${asset.packageId}/${asset.sourceKey}` === key);
11703
+ }
11704
+ async function resolveAsset(gameRoot, snapshot, operation) {
11705
+ if (operation.require !== void 0 && operation.require !== "identity" && operation.require !== "present" && operation.require !== "ready") {
11706
+ return err(
11707
+ makeError(
11708
+ "pack-parameter-invalid",
11709
+ "require to be 'identity', 'present', or 'ready'",
11710
+ "choose one of the three Pack identity proof levels",
11711
+ { requestId: operation.requestId, require: operation.require }
11712
+ )
11713
+ );
11714
+ }
11715
+ let packageId2;
11716
+ let sourceKey2 = operation.sourceKey;
11717
+ let resolvedOutputGuid;
11718
+ if (operation.packageId !== void 0) {
11719
+ const parsed = PackageId.parse(operation.packageId);
11720
+ if (!parsed.ok) {
11721
+ return err(
11722
+ makeError(
11723
+ "pack-package-id-invalid",
11724
+ "a UUID packageId",
11725
+ "repair packageId before resolving a sourceKey",
11726
+ { requestId: operation.requestId },
11727
+ operation.packageId
11728
+ )
11729
+ );
11730
+ }
11731
+ packageId2 = parsed.value;
11732
+ }
11733
+ if (operation.subject !== void 0 && isValidAssetGuidString(operation.subject)) {
11734
+ const found = [...snapshot.directAssets, ...snapshot.materialized].find(
11735
+ (asset2) => asset2.guid === operation.subject?.toLowerCase()
11736
+ );
11737
+ if (found !== void 0) {
11738
+ const parsed = PackageId.parse(found.packageId);
11739
+ if (parsed.ok) {
11740
+ if (packageId2 !== void 0 && packageKey(packageId2) !== packageKey(parsed.value)) {
11741
+ return err(
11742
+ makeError(
11743
+ "pack-parameter-invalid",
11744
+ "packageId and AssetGuid to identify the same Pack subject",
11745
+ "remove the conflicting locator or resolve the pair from one source",
11746
+ {
11747
+ requestId: operation.requestId,
11748
+ packageId: PackageId.format(packageId2),
11749
+ observedPackageId: found.packageId,
11750
+ guid: operation.subject
11751
+ }
11752
+ )
11753
+ );
11754
+ }
11755
+ if (sourceKey2 !== void 0 && sourceKey2 !== found.sourceKey) {
11756
+ return err(
11757
+ makeError(
11758
+ "pack-parameter-invalid",
11759
+ "sourceKey and AssetGuid to identify the same Pack output",
11760
+ "remove the conflicting locator or use the sourceKey returned by asset.list",
11761
+ {
11762
+ requestId: operation.requestId,
11763
+ sourceKey: sourceKey2,
11764
+ observedSourceKey: found.sourceKey,
11765
+ guid: operation.subject
11766
+ }
11767
+ )
11768
+ );
11769
+ }
11770
+ packageId2 = parsed.value;
11771
+ sourceKey2 ??= found.sourceKey;
11772
+ resolvedOutputGuid = found.guid;
11773
+ }
11774
+ }
11775
+ }
11776
+ if (packageId2 === void 0 && operation.subject !== void 0 && isValidAssetGuidString(operation.subject)) {
11777
+ const parsed = PackageId.parse(operation.subject);
11778
+ if (parsed.ok) packageId2 = parsed.value;
11779
+ }
11780
+ const sourceLocatorCandidate = operation.sourcePath ?? (operation.subject !== void 0 && !isValidAssetGuidString(operation.subject) ? operation.subject : void 0);
11781
+ let sourceLocator;
11782
+ if (sourceLocatorCandidate !== void 0) {
11783
+ const confined = confinedPath(gameRoot, sourceLocatorCandidate, operation);
11784
+ if (!confined.ok) return confined;
11785
+ sourceLocator = confined.value.relative;
11786
+ }
11787
+ if (sourceLocator !== void 0) {
11788
+ const source = [...snapshot.subjects.values()].find(
11789
+ (candidate) => candidate.relativePath === sourceLocator
11790
+ );
11791
+ if (source === void 0) {
11792
+ return err(
11793
+ pathError(
11794
+ "pack-source-not-found",
11795
+ operation,
11796
+ "sourcePath to identify a current Pack subject",
11797
+ "inspect the current Source Index and choose an existing source path",
11798
+ { sourcePath: sourceLocator },
11799
+ sourceLocator
11800
+ )
11801
+ );
11802
+ }
11803
+ if (packageId2 !== void 0 && packageKey(packageId2) !== packageKey(source.packageId)) {
11804
+ return err(
11805
+ makeError(
11806
+ "pack-parameter-invalid",
11807
+ "sourcePath and packageId to identify the same Pack subject",
11808
+ "remove the conflicting locator or use the packageId returned by asset.list",
11809
+ {
11810
+ requestId: operation.requestId,
11811
+ sourcePath: sourceLocator,
11812
+ packageId: PackageId.format(packageId2),
11813
+ observedPackageId: PackageId.format(source.packageId)
11814
+ }
11815
+ )
11816
+ );
11817
+ }
11818
+ packageId2 ??= source.packageId;
11819
+ }
11820
+ if (packageId2 === void 0 || sourceKey2 === void 0) {
11821
+ return err(
11822
+ makeError(
11823
+ "pack-parameter-invalid",
11824
+ "packageId and sourceKey (or a known output GUID)",
11825
+ "pass packageId + sourceKey, a known AssetGuid, or a source path plus sourceKey",
11826
+ { requestId: operation.requestId, packageId: operation.packageId, sourceKey: sourceKey2 }
11827
+ )
11828
+ );
11829
+ }
11830
+ if (!isValidPackSourceKey(sourceKey2)) {
11831
+ return err(
11832
+ makeError(
11833
+ "pack-source-key-invalid",
11834
+ "a stable lower-case sourceKey",
11835
+ "repair sourceKey before deriving its AssetGuid",
11836
+ { requestId: operation.requestId, sourceKey: sourceKey2 },
11837
+ sourceKey2
11838
+ )
11839
+ );
11840
+ }
11841
+ let guid;
11842
+ try {
11843
+ guid = AssetGuid.derive(packageId2, sourceKey2);
11844
+ } catch (cause) {
11845
+ return err(
11846
+ makeError(
11847
+ "pack-source-key-invalid",
11848
+ "a valid packageId and sourceKey pair",
11849
+ "repair the identity pair before resolving",
11850
+ {
11851
+ requestId: operation.requestId,
11852
+ cause: cause instanceof Error ? cause.message : String(cause)
11853
+ }
11854
+ )
11855
+ );
11856
+ }
11857
+ const guidString = AssetGuid.format(guid);
11858
+ if (resolvedOutputGuid !== void 0 && guidString.toLowerCase() !== resolvedOutputGuid) {
11859
+ return err(
11860
+ makeError(
11861
+ "pack-guid-collision",
11862
+ "the known output GUID to equal UUIDv5(packageId, sourceKey)",
11863
+ "rebuild the current generation from the stable identity pair",
11864
+ {
11865
+ requestId: operation.requestId,
11866
+ packageId: PackageId.format(packageId2),
11867
+ sourceKey: sourceKey2,
11868
+ expectedGuid: guidString,
11869
+ observedGuid: resolvedOutputGuid
11870
+ }
11871
+ )
11872
+ );
11873
+ }
11874
+ const asset = assetForPair(snapshot, packageId2, sourceKey2);
11875
+ const materialized = snapshot.materialized.find(
11876
+ (candidate) => candidate.packageId === packageKey(packageId2) && candidate.sourceKey === sourceKey2
11877
+ );
11878
+ const requirement = operation.require ?? "identity";
11879
+ if (requirement === "present" && materialized === void 0) {
11880
+ return err(
11881
+ makeError(
11882
+ "pack-output-not-materialized",
11883
+ "the current build topology to contain this sourceKey",
11884
+ "run rebuild/cold-cook and retry after the current generation publishes the output",
11885
+ {
11886
+ requestId: operation.requestId,
11887
+ packageId: PackageId.format(packageId2),
11888
+ sourceKey: sourceKey2,
11889
+ guid: guidString
11890
+ }
11891
+ )
11892
+ );
11893
+ }
11894
+ if (requirement === "ready" && (materialized === void 0 || !materialized.ready)) {
11895
+ return err(
11896
+ makeError(
11897
+ "asset-not-ready",
11898
+ "current Pack evidence and artifacts for this derived GUID",
11899
+ "inspect producer evidence, then rebuild or cold-cook the same subject",
11900
+ {
11901
+ requestId: operation.requestId,
11902
+ packageId: PackageId.format(packageId2),
11903
+ sourceKey: sourceKey2,
11904
+ guid: guidString
11905
+ }
11906
+ )
11907
+ );
11908
+ }
11909
+ return ok({
11910
+ operation: operation.operation,
11911
+ requestId: operation.requestId,
11912
+ packageId: PackageId.format(packageId2),
11913
+ sourceKey: sourceKey2,
11914
+ guid: guidString,
11915
+ status: materialized === void 0 ? "identity" : materialized.ready ? "ready" : "present",
11916
+ ...asset === void 0 ? {} : {
11917
+ ...asset.sourcePath === void 0 ? {} : { sourcePath: asset.sourcePath },
11918
+ kind: asset.kind
11919
+ }
11920
+ });
11921
+ }
11922
+ async function verify(snapshot, operation) {
11923
+ for (const subject of snapshot.subjects.values()) {
11924
+ if (subject.format === "instance") {
11925
+ const resolved = await resolveInstance(snapshot, subject);
11926
+ if (!resolved.ok) return err(resolved.error);
11927
+ }
11928
+ if (subject.format === "direct") {
11929
+ for (const asset of subject.assets) {
11930
+ const missing = asset.refs.find((ref) => !snapshot.knownGuids.has(ref.toLowerCase()));
11931
+ if (missing !== void 0) {
11932
+ return err(
11933
+ makeError(
11934
+ "pack-output-reference-missing",
11935
+ "every direct Pack ref to resolve to a known AssetGuid",
11936
+ "repair the payload ref or add the owning external asset before verifying",
11937
+ {
11938
+ requestId: operation.requestId,
11939
+ packageId: PackageId.format(subject.packageId),
11940
+ sourceKey: asset.sourceKey,
11941
+ guid: missing
11942
+ }
11943
+ )
11944
+ );
11945
+ }
11946
+ }
11947
+ }
11948
+ }
11949
+ for (const asset of snapshotAssets(snapshot)) {
11950
+ const missing = asset.refs?.find((ref) => !snapshot.knownGuids.has(ref.toLowerCase()));
11951
+ if (missing !== void 0) {
11952
+ return err(
11953
+ makeError(
11954
+ "pack-output-reference-missing",
11955
+ "every materialized Pack ref to resolve to a known AssetGuid",
11956
+ "repair the published output reference or publish its owning asset before verifying",
11957
+ {
11958
+ requestId: operation.requestId,
11959
+ packageId: asset.packageId,
11960
+ sourceKey: asset.sourceKey,
11961
+ guid: missing
11962
+ }
11963
+ )
11964
+ );
11965
+ }
11966
+ }
11967
+ return ok({
11968
+ operation: operation.operation,
11969
+ requestId: operation.requestId,
11970
+ snapshot: {
11971
+ sourceCount: snapshot.subjects.size,
11972
+ assetCount: snapshotAssets(snapshot).length
11973
+ }
11974
+ });
11975
+ }
11976
+ function packageIdFromOperation(snapshot, operation) {
11977
+ const value = operation.packageId === void 0 ? ok(PackageId.random()) : PackageId.parse(operation.packageId);
11978
+ if (!value.ok) {
11979
+ return err(
11980
+ makeError(
11981
+ "pack-package-id-invalid",
11982
+ "a UUID packageId",
11983
+ "repair packageId or omit it so the gateway can mint one",
11984
+ { requestId: operation.requestId },
11985
+ operation.packageId
11986
+ )
11987
+ );
11988
+ }
11989
+ const normalized = packageKey(value.value);
11990
+ const alreadyMaterialized = snapshot.materialized.some((asset) => asset.packageId === normalized);
11991
+ return snapshot.subjects.has(normalized) || alreadyMaterialized ? err(
11992
+ makeError(
11993
+ "pack-package-id-collision",
11994
+ "a packageId unused by the current Source Index",
11995
+ "choose a new packageId or use clone without an explicit identity",
11996
+ { requestId: operation.requestId, packageId: PackageId.format(value.value) }
11997
+ )
11998
+ ) : ok(value.value);
11999
+ }
12000
+ async function readConfined(options, operation, sourcePath) {
12001
+ const path = confinedPath(options.gameRoot, sourcePath, operation);
12002
+ if (!path.ok) return path;
12003
+ try {
12004
+ const source = await readFile(path.value.absolute, "utf8");
12005
+ return ok({ ...path.value, source, revision: revisionOf(source) });
12006
+ } catch (cause) {
12007
+ const code = cause.code === "ENOENT" ? "pack-source-not-found" : "pack-source-write-failed";
12008
+ return err(
12009
+ pathError(
12010
+ code,
12011
+ operation,
12012
+ "the requested Pack source to be readable",
12013
+ "restore the source or choose a current locator",
12014
+ {
12015
+ sourcePath: path.value.relative,
12016
+ cause: cause instanceof Error ? cause.message : String(cause)
12017
+ },
12018
+ path.value.relative
12019
+ )
12020
+ );
12021
+ }
12022
+ }
12023
+ function revisionConflict(operation, path, actual) {
12024
+ return makeError(
12025
+ "pack-source-revision-conflict",
12026
+ "expectedRevision to match the current source bytes",
12027
+ "inspect the current source, reconcile the edit, and retry with a new requestId",
12028
+ {
12029
+ requestId: operation.requestId,
12030
+ sourcePath: path,
12031
+ expectedRevision: operation.expectedRevision,
12032
+ actualRevision: actual
12033
+ },
12034
+ actual
12035
+ );
12036
+ }
12037
+ function directJson(packageId2, assets) {
12038
+ return { schemaVersion: "3.0.0", packageId: PackageId.format(packageId2), assets };
12039
+ }
12040
+ function parameterizedScaffold(packageId2, parameters) {
12041
+ const parameterSource = parameters === void 0 ? "" : "\n parameters: " + JSON.stringify(
12042
+ parameters.map((parameter) => jsonValue2(parameter)),
12043
+ null,
12044
+ 2
12045
+ ).replaceAll("\n", "\n ") + ",";
12046
+ return "import { definePack, definePackageId } from '@forgeax/engine-pack/source';\nimport { ok } from '@forgeax/engine-types';\n\nconst packageId = definePackageId(" + JSON.stringify(PackageId.format(packageId2)) + ");\n\nexport default definePack({\n schemaVersion: '2.0.0',\n packageId," + parameterSource + "\n build: () => ok({}),\n});\n";
12047
+ }
12048
+ function replacePackageIdLiteral(source, packageId2) {
12049
+ const pattern = /(definePackageId\s*\(\s*)(['"])([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})(\2\s*\))/;
12050
+ if (!pattern.test(source)) return void 0;
12051
+ return source.replace(pattern, `$1${JSON.stringify(packageId2)}$4`);
12052
+ }
12053
+ async function inspectAfterWrite(operation, path, source, packageId2, extra = {}) {
12054
+ return ok({
12055
+ operation: operation.operation,
12056
+ requestId: operation.requestId,
12057
+ sourcePath: path.relative,
12058
+ targetPath: path.relative,
12059
+ packageId: PackageId.format(packageId2),
12060
+ revision: revisionOf(source),
12061
+ ...extra
12062
+ });
12063
+ }
12064
+ async function executeRaw(options, operation) {
12065
+ if (operation.operation === "asset.list" || operation.operation === "asset.inspect") {
12066
+ const snapshot2 = await createSnapshot(options, operation);
12067
+ if (!snapshot2.ok) return snapshot2;
12068
+ return operation.operation === "asset.list" ? ok(listAssets(snapshot2.value, operation)) : await inspectAsset(options.gameRoot, snapshot2.value, operation);
12069
+ }
12070
+ const snapshot = await createSnapshot(options, operation);
12071
+ if (!snapshot.ok) return snapshot;
12072
+ if (operation.operation === "asset.resolve")
12073
+ return resolveAsset(options.gameRoot, snapshot.value, operation);
12074
+ if (operation.operation === "asset.verify") return verify(snapshot.value, operation);
12075
+ if (operation.operation === "asset-source.create") {
12076
+ const path = confinedPath(
12077
+ options.gameRoot,
12078
+ operation.targetPath ?? operation.sourcePath,
12079
+ operation
12080
+ );
12081
+ if (!path.ok) return path;
12082
+ try {
12083
+ await stat(path.value.absolute);
12084
+ return err(
12085
+ pathError(
12086
+ "pack-source-revision-conflict",
12087
+ operation,
12088
+ "an unused target source path",
12089
+ "inspect the existing source or choose another target path",
12090
+ { targetPath: path.value.relative },
12091
+ path.value.relative
12092
+ )
12093
+ );
12094
+ } catch (cause) {
12095
+ if (cause.code !== "ENOENT") {
12096
+ return err(
12097
+ pathError(
12098
+ "pack-source-write-failed",
12099
+ operation,
12100
+ "the target path to be available for atomic creation",
12101
+ "repair the filesystem and retry",
12102
+ { targetPath: path.value.relative, cause: String(cause) }
12103
+ )
12104
+ );
12105
+ }
12106
+ }
12107
+ const identity = packageIdFromOperation(snapshot.value, operation);
12108
+ if (!identity.ok) return identity;
12109
+ const format = operation.format ?? (path.value.relative.endsWith(".pack.json") ? "pack.json" : "pack.ts");
12110
+ if (format === "pack.json" && !path.value.relative.endsWith(".pack.json") || format === "pack.ts" && !path.value.relative.endsWith(".pack.ts")) {
12111
+ return err(
12112
+ makeError(
12113
+ "pack-source-path-invalid",
12114
+ "format and target extension to agree",
12115
+ "use a .pack.ts target for source creation or a .pack.json target for direct data",
12116
+ { requestId: operation.requestId, format, targetPath: path.value.relative }
12117
+ )
12118
+ );
12119
+ }
12120
+ let source;
12121
+ let extra = { format };
12122
+ if (format === "pack.json") {
12123
+ if (operation.parameters !== void 0 || operation.parent !== void 0 || operation.parentPackageId !== void 0 || operation.values !== void 0) {
12124
+ return err(
12125
+ makeError(
12126
+ "pack-parameter-invalid",
12127
+ "direct create to contain assets only",
12128
+ "use pack.ts for a parameterized source or create-instance for parent+values",
12129
+ { requestId: operation.requestId }
12130
+ )
12131
+ );
12132
+ }
12133
+ const assets = operation.initialAssets ?? {};
12134
+ const parsed = parseParameterizedPackJson(directJson(identity.value, assets));
12135
+ if (!parsed.ok) return err(parsed.error);
12136
+ if (parsed.value.format !== "direct") {
12137
+ return err(
12138
+ makeError(
12139
+ "pack-parameter-invalid",
12140
+ "direct create to produce a direct v3 Pack document",
12141
+ "use create-instance for parent + values instead of direct assets",
12142
+ { requestId: operation.requestId }
12143
+ )
12144
+ );
12145
+ }
12146
+ source = `${JSON.stringify(directJson(identity.value, parsed.value.assets), null, 2)}
12147
+ `;
12148
+ } else {
12149
+ if (operation.initialAssets !== void 0 && Object.keys(operation.initialAssets).length > 0) {
12150
+ return err(
12151
+ makeError(
12152
+ "pack-parameter-invalid",
12153
+ "a pack.ts source to define its outputs in build()",
12154
+ "use pack.json for direct assets or omit initialAssets when creating a pack.ts source",
12155
+ { requestId: operation.requestId }
12156
+ )
12157
+ );
12158
+ }
12159
+ if (operation.parent !== void 0 || operation.parentPackageId !== void 0 || operation.values !== void 0) {
12160
+ return err(
12161
+ makeError(
12162
+ "pack-parameter-invalid",
12163
+ "a new pack.ts source without instance-only parent/value fields",
12164
+ "use asset-source.create-instance for parent + values",
12165
+ { requestId: operation.requestId }
12166
+ )
12167
+ );
12168
+ }
12169
+ let parameters;
12170
+ if (operation.parameters !== void 0) {
12171
+ const validated = validatePackDefinition({
12172
+ schemaVersion: "2.0.0",
12173
+ packageId: identity.value,
12174
+ parameters: operation.parameters,
12175
+ build: () => ok({})
12176
+ });
12177
+ if (!validated.ok) return err(validated.error);
12178
+ if (!("parameters" in validated.value)) {
12179
+ return err(
12180
+ makeError(
12181
+ "pack-parameter-invalid",
12182
+ "parameters to be non-empty when supplied",
12183
+ "omit parameters for a zero-parameter Pack",
12184
+ { requestId: operation.requestId }
12185
+ )
12186
+ );
12187
+ }
12188
+ parameters = validated.value.parameters;
12189
+ }
12190
+ source = parameterizedScaffold(identity.value, parameters);
12191
+ const serialized = parameters === void 0 ? void 0 : serializedParameters({
12192
+ schemaVersion: "2.0.0",
12193
+ packageId: identity.value,
12194
+ parameters,
12195
+ build: () => ok({})
12196
+ });
12197
+ extra = {
12198
+ format,
12199
+ ...serialized === void 0 ? {} : { parameters: serialized }
12200
+ };
12201
+ }
12202
+ try {
12203
+ await atomicWrite(path.value.absolute, source);
12204
+ } catch (cause) {
12205
+ return err(
12206
+ pathError(
12207
+ "pack-source-write-failed",
12208
+ operation,
12209
+ "the new Pack source to be written atomically",
12210
+ "repair filesystem permissions or disk capacity, then retry with a new requestId",
12211
+ {
12212
+ targetPath: path.value.relative,
12213
+ cause: cause instanceof Error ? cause.message : String(cause)
12214
+ }
12215
+ )
12216
+ );
12217
+ }
12218
+ return inspectAfterWrite(operation, path.value, source, identity.value, extra);
12219
+ }
12220
+ if (operation.operation === "asset-source.clone") {
12221
+ const read = await readConfined(options, operation, operation.sourcePath);
12222
+ if (!read.ok) return read;
12223
+ if (operation.expectedRevision !== void 0 && operation.expectedRevision !== read.value.revision) {
12224
+ return err(revisionConflict(operation, read.value.relative, read.value.revision));
12225
+ }
12226
+ const target = confinedPath(options.gameRoot, operation.targetPath, operation);
12227
+ if (!target.ok) return target;
12228
+ const sourceIsTypeScript = read.value.relative.endsWith(".pack.ts");
12229
+ const targetIsTypeScript = target.value.relative.endsWith(".pack.ts");
12230
+ if (sourceIsTypeScript !== targetIsTypeScript) {
12231
+ return err(
12232
+ makeError(
12233
+ "pack-source-path-invalid",
12234
+ "clone source and target to use the same .pack.ts or .pack.json format",
12235
+ "preserve the source extension when cloning a Pack subject",
12236
+ {
12237
+ requestId: operation.requestId,
12238
+ sourcePath: read.value.relative,
12239
+ targetPath: target.value.relative
12240
+ }
12241
+ )
12242
+ );
12243
+ }
12244
+ try {
12245
+ await stat(target.value.absolute);
12246
+ return err(
12247
+ pathError(
12248
+ "pack-source-revision-conflict",
12249
+ operation,
12250
+ "an unused clone target path",
12251
+ "inspect the target or choose another path",
12252
+ { targetPath: target.value.relative },
12253
+ target.value.relative
12254
+ )
12255
+ );
12256
+ } catch (cause) {
12257
+ if (cause.code !== "ENOENT") {
12258
+ return err(
12259
+ pathError(
12260
+ "pack-source-write-failed",
12261
+ operation,
12262
+ "the clone target to be writable",
12263
+ "repair filesystem permissions and retry",
12264
+ { targetPath: target.value.relative, cause: String(cause) }
12265
+ )
12266
+ );
12267
+ }
12268
+ }
12269
+ const identity = packageIdFromOperation(snapshot.value, operation);
12270
+ if (!identity.ok) return identity;
12271
+ let source = read.value.source;
12272
+ if (read.value.relative.endsWith(".pack.json")) {
12273
+ let parsedValue;
12274
+ try {
12275
+ parsedValue = JSON.parse(source);
12276
+ } catch {
12277
+ return err(
12278
+ makeError(
12279
+ "pack-parameter-invalid",
12280
+ "a valid v3 pack.json document",
12281
+ "repair the source before cloning it",
12282
+ { requestId: operation.requestId, sourcePath: read.value.relative }
12283
+ )
12284
+ );
12285
+ }
12286
+ const parsed = parseParameterizedPackJson(parsedValue);
12287
+ if (!parsed.ok) return err(parsed.error);
12288
+ source = `${JSON.stringify(
12289
+ parsed.value.format === "direct" ? directJson(identity.value, parsed.value.assets) : {
12290
+ schemaVersion: "3.0.0",
12291
+ packageId: PackageId.format(identity.value),
12292
+ parent: PackageId.format(parsed.value.parent),
12293
+ values: parsed.value.values
12294
+ },
12295
+ null,
12296
+ 2
12297
+ )}
12298
+ `;
12299
+ } else {
12300
+ const replaced = replacePackageIdLiteral(source, PackageId.format(identity.value));
12301
+ if (replaced === void 0) {
12302
+ return err(
12303
+ makeError(
12304
+ "pack-source-mutation-unsupported",
12305
+ "a constrained definePackageId(UUID) literal in the source",
12306
+ "clone a generated Pack source or create a new Pack; arbitrary TypeScript AST rewriting is unsupported",
12307
+ { requestId: operation.requestId, sourcePath: read.value.relative }
12308
+ )
12309
+ );
12310
+ }
12311
+ source = replaced;
12312
+ }
12313
+ try {
12314
+ await atomicWrite(target.value.absolute, source);
12315
+ } catch (cause) {
12316
+ return err(
12317
+ pathError(
12318
+ "pack-source-write-failed",
12319
+ operation,
12320
+ "the cloned Pack source to be written atomically",
12321
+ "repair filesystem permissions or disk capacity, then retry",
12322
+ {
12323
+ targetPath: target.value.relative,
12324
+ cause: cause instanceof Error ? cause.message : String(cause)
12325
+ }
12326
+ )
12327
+ );
12328
+ }
12329
+ return inspectAfterWrite(operation, target.value, source, identity.value, {
12330
+ format: target.value.relative.endsWith(".pack.json") ? "pack.json" : "pack.ts"
12331
+ });
12332
+ }
12333
+ if (operation.operation === "asset-source.create-instance") {
12334
+ const target = confinedPath(
12335
+ options.gameRoot,
12336
+ operation.targetPath ?? operation.sourcePath,
12337
+ operation
12338
+ );
12339
+ if (!target.ok) return target;
12340
+ const parentText = operation.parentPackageId ?? operation.parent;
12341
+ if (parentText === void 0) {
12342
+ return err(
12343
+ makeError(
12344
+ "pack-parent-not-found",
12345
+ "a parent packageId for the new instance",
12346
+ "pass parentPackageId or parent and point it at a parameterized source",
12347
+ { requestId: operation.requestId }
12348
+ )
12349
+ );
12350
+ }
12351
+ const parentId = PackageId.parse(parentText);
12352
+ if (!parentId.ok) {
12353
+ return err(
12354
+ makeError(
12355
+ "pack-package-id-invalid",
12356
+ "a UUID parent packageId",
12357
+ "repair the parent package identity",
12358
+ { requestId: operation.requestId },
12359
+ parentText
12360
+ )
12361
+ );
12362
+ }
12363
+ const parent = snapshot.value.subjects.get(packageKey(parentId.value));
12364
+ if (parent === void 0) {
12365
+ return err(
12366
+ makeError(
12367
+ "pack-parent-not-found",
12368
+ "the parent packageId to exist in the current Source Index",
12369
+ "inspect the Source Index and choose a parameterized parent",
12370
+ { requestId: operation.requestId, parent: parentText }
12371
+ )
12372
+ );
12373
+ }
12374
+ if (parent.format === "source" && !("parameters" in parent.definition)) {
12375
+ return err(
12376
+ makeError(
12377
+ "pack-parent-not-parameterized",
12378
+ "a non-empty parameterized Pack source as the parent",
12379
+ "use asset-source.clone for a zero-parameter Pack",
12380
+ { requestId: operation.requestId, parent: parentText }
12381
+ )
12382
+ );
12383
+ }
12384
+ if (parent.format === "direct") {
12385
+ return err(
12386
+ makeError(
12387
+ "pack-parent-not-parameterized",
12388
+ "a parameterized Pack source or instance as the parent",
12389
+ "direct Packs cannot be instance parents",
12390
+ { requestId: operation.requestId, parent: parentText }
12391
+ )
12392
+ );
12393
+ }
12394
+ try {
12395
+ await stat(target.value.absolute);
12396
+ return err(
12397
+ pathError(
12398
+ "pack-source-revision-conflict",
12399
+ operation,
12400
+ "an unused instance target path",
12401
+ "inspect the existing instance or choose another path",
12402
+ { targetPath: target.value.relative },
12403
+ target.value.relative
12404
+ )
12405
+ );
12406
+ } catch (cause) {
12407
+ if (cause.code !== "ENOENT") {
12408
+ return err(
12409
+ pathError(
12410
+ "pack-source-write-failed",
12411
+ operation,
12412
+ "the instance target to be writable",
12413
+ "repair filesystem permissions and retry",
12414
+ { targetPath: target.value.relative, cause: String(cause) }
12415
+ )
12416
+ );
12417
+ }
12418
+ }
12419
+ const identity = packageIdFromOperation(snapshot.value, operation);
12420
+ if (!identity.ok) return identity;
12421
+ const values = operation.values ?? {};
12422
+ const serializedValues = jsonValue2(values);
12423
+ const candidate = {
12424
+ format: "instance",
12425
+ packageId: identity.value,
12426
+ parent: parentId.value,
12427
+ values,
12428
+ sourcePath: target.value.absolute,
12429
+ relativePath: target.value.relative
12430
+ };
12431
+ const withCandidate = {
12432
+ ...snapshot.value,
12433
+ subjects: new Map([...snapshot.value.subjects, [packageKey(identity.value), candidate]])
12434
+ };
12435
+ const resolved = await resolveInstance(withCandidate, candidate);
12436
+ if (!resolved.ok) return err(resolved.error);
12437
+ const source = `${JSON.stringify(
12438
+ {
12439
+ schemaVersion: "3.0.0",
12440
+ packageId: PackageId.format(identity.value),
12441
+ parent: PackageId.format(parentId.value),
12442
+ values: serializedValues
12443
+ },
12444
+ null,
12445
+ 2
12446
+ )}
12447
+ `;
12448
+ try {
12449
+ await atomicWrite(target.value.absolute, source);
12450
+ } catch (cause) {
12451
+ return err(
12452
+ pathError(
12453
+ "pack-source-write-failed",
12454
+ operation,
12455
+ "the new instance to be written atomically",
12456
+ "repair filesystem permissions or disk capacity, then retry",
12457
+ {
12458
+ targetPath: target.value.relative,
12459
+ cause: cause instanceof Error ? cause.message : String(cause)
12460
+ }
12461
+ )
12462
+ );
12463
+ }
12464
+ return inspectAfterWrite(operation, target.value, source, identity.value, {
12465
+ format: "instance",
12466
+ parentPackageId: PackageId.format(parentId.value),
12467
+ values: serializedValues,
12468
+ effectiveValues: jsonValue2(resolved.value.values),
12469
+ parentChain: resolved.value.parentChain
12470
+ });
12471
+ }
12472
+ if (operation.operation === "asset-source.apply-values") {
12473
+ const read = await readConfined(options, operation, operation.sourcePath);
12474
+ if (!read.ok) return read;
12475
+ if (operation.expectedRevision === void 0 || operation.expectedRevision !== read.value.revision) {
12476
+ return err(revisionConflict(operation, read.value.relative, read.value.revision));
12477
+ }
12478
+ let json;
12479
+ try {
12480
+ json = JSON.parse(read.value.source);
12481
+ } catch {
12482
+ return err(
12483
+ makeError(
12484
+ "pack-parameter-invalid",
12485
+ "a valid v3 instance JSON document",
12486
+ "repair the source before applying values",
12487
+ { requestId: operation.requestId, sourcePath: read.value.relative }
12488
+ )
12489
+ );
12490
+ }
12491
+ const parsed = parseParameterizedPackJson(json);
12492
+ if (!parsed.ok) return err(parsed.error);
12493
+ if (parsed.value.format !== "instance") {
12494
+ return err(
12495
+ makeError(
12496
+ "pack-parameter-invalid",
12497
+ "an instance pack.json subject",
12498
+ "apply-values only edits parent+values instances",
12499
+ { requestId: operation.requestId, sourcePath: read.value.relative }
12500
+ )
12501
+ );
12502
+ }
12503
+ const values = operation.values ?? {};
12504
+ const serializedValues = jsonValue2(values);
12505
+ const candidate = {
12506
+ format: "instance",
12507
+ packageId: parsed.value.packageId,
12508
+ parent: parsed.value.parent,
12509
+ values,
12510
+ sourcePath: read.value.absolute,
12511
+ relativePath: read.value.relative
12512
+ };
12513
+ const withCandidate = {
12514
+ ...snapshot.value,
12515
+ subjects: new Map([...snapshot.value.subjects, [packageKey(candidate.packageId), candidate]])
12516
+ };
12517
+ const resolved = await resolveInstance(withCandidate, candidate);
12518
+ if (!resolved.ok) return err(resolved.error);
12519
+ const source = `${JSON.stringify(
12520
+ {
12521
+ schemaVersion: "3.0.0",
12522
+ packageId: PackageId.format(parsed.value.packageId),
12523
+ parent: PackageId.format(parsed.value.parent),
12524
+ values: serializedValues
12525
+ },
12526
+ null,
12527
+ 2
12528
+ )}
12529
+ `;
12530
+ try {
12531
+ await atomicWrite(read.value.absolute, source);
12532
+ } catch (cause) {
12533
+ return err(
12534
+ pathError(
12535
+ "pack-source-write-failed",
12536
+ operation,
12537
+ "the updated instance to be written atomically",
12538
+ "repair filesystem permissions or disk capacity, then retry with a new requestId",
12539
+ {
12540
+ sourcePath: read.value.relative,
12541
+ cause: cause instanceof Error ? cause.message : String(cause)
12542
+ }
12543
+ )
12544
+ );
12545
+ }
12546
+ return inspectAfterWrite(operation, read.value, source, parsed.value.packageId, {
12547
+ format: "instance",
12548
+ parentPackageId: PackageId.format(parsed.value.parent),
12549
+ values: serializedValues,
12550
+ effectiveValues: jsonValue2(resolved.value.values),
12551
+ parentChain: resolved.value.parentChain
12552
+ });
12553
+ }
12554
+ if (operation.operation === "asset-source.rebuild" || operation.operation === "asset-source.cold-cook") {
12555
+ const read = await readConfined(options, operation, operation.sourcePath);
12556
+ if (!read.ok) return read;
12557
+ if (operation.expectedRevision === void 0 || operation.expectedRevision !== read.value.revision) {
12558
+ return err(revisionConflict(operation, read.value.relative, read.value.revision));
12559
+ }
12560
+ const rebuilt = await options.rebuild?.(
12561
+ read.value.relative,
12562
+ operation.operation === "asset-source.rebuild" ? "rebuild" : "cold-cook"
12563
+ );
12564
+ if (rebuilt === void 0) {
12565
+ return err(
12566
+ makeError(
12567
+ "pack-source-mutation-unsupported",
12568
+ "a build-owner callback for rebuild or cold-cook",
12569
+ "connect the gateway to the project build owner before requesting a mutation",
12570
+ { requestId: operation.requestId, sourcePath: read.value.relative }
12571
+ )
12572
+ );
12573
+ }
12574
+ if (!rebuilt.ok) return err(rebuilt.error);
12575
+ const refreshed = await readConfined(options, operation, operation.sourcePath);
12576
+ if (!refreshed.ok) return refreshed;
12577
+ const refreshedSnapshot = await createSnapshot(options, operation);
12578
+ if (!refreshedSnapshot.ok) return refreshedSnapshot;
12579
+ const selected = subjectFor(options.gameRoot, refreshedSnapshot.value, operation);
12580
+ if (!selected.ok) return selected;
12581
+ return inspectAfterWrite(
12582
+ operation,
12583
+ refreshed.value,
12584
+ refreshed.value.source,
12585
+ selected.value.packageId,
12586
+ {
12587
+ format: refreshed.value.relative.endsWith(".pack.json") ? "pack.json" : "pack.ts"
12588
+ }
12589
+ );
12590
+ }
12591
+ return err(
12592
+ makeError(
12593
+ "pack-parameter-invalid",
12594
+ "a supported parameterized Pack operation",
12595
+ "use one of PARAMETERIZED_PACK_OPERATION_IDS",
12596
+ { requestId: operation.requestId, operation: operation.operation }
12597
+ )
12598
+ );
12599
+ }
12600
+ function createFileSystemParameterizedPackAuthoringPort(options) {
12601
+ return { execute: (operation) => executeRaw(options, operation) };
12602
+ }
12603
+ function createFileSystemParameterizedPackAuthoringGateway(options) {
12604
+ return createParameterizedPackAuthoringGateway(
12605
+ createFileSystemParameterizedPackAuthoringPort(options)
12606
+ );
12607
+ }
10095
12608
  function parsePackV2(value) {
10096
12609
  if (!validatePackV2(value)) {
10097
12610
  return err({
@@ -10107,8 +12620,8 @@ function stable(value) {
10107
12620
  if (value instanceof Uint8Array) return `bytes:${Buffer.from(value).toString("base64")}`;
10108
12621
  if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
10109
12622
  if (value !== null && typeof value === "object") {
10110
- const record = value;
10111
- return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stable(record[key])}`).join(",")}}`;
12623
+ const record2 = value;
12624
+ return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${stable(record2[key])}`).join(",")}}`;
10112
12625
  }
10113
12626
  return JSON.stringify(value) ?? "null";
10114
12627
  }
@@ -10212,6 +12725,6 @@ function createRuntimePackPublication(input) {
10212
12725
  };
10213
12726
  }
10214
12727
 
10215
- export { MESH_BIN_DIGEST_BYTES, MESH_BIN_HEADER_V4_BYTES, MESH_BIN_PROJECTION_VERSION, MESH_BIN_VERSION, SCRIPTABLE_PACK_ASSET_KINDS, SCRIPTABLE_PACK_CAPABILITY_MANIFEST, STANDARD_SCRIPTABLE_PACK_SCAN_OPTIONS, buildCatalogProjection, buildOfflineAssetEvidence, calculateCatalogDelta, calculateTopologyDiff, catalogProjectionFor, catalogSourcePathFor, collectMaterialCookRefs, createMaterialArtifactDigest, createRuntimePackPublication, currentProjectionFor, decodeMeshBinHeader, diffTopology, finalizePackageProduct, finalizePackageTransportSource, findReservedAssetKindConflict, isScriptablePackAssetKind, loadAssetConfig, metaPathForGuid, packageTransportRevision, packageVerification, parsePackV2, projectCookedMaterialRecord, projectCookedPackageEntry, projectExternalCatalogEntries, projectPackageCatalog, projectRuntimeCatalogRow, projectScriptablePackMeta, projectScriptablePackSceneComponents, resolveAssetSource, scanInventory, serializeCookedMaterialRecord, serializeMaterialCookReceipt, upgradeLegacyAuthoredPack, validateArtifactPath, validateCookedMaterialRecord, validateMaterialCookReceipt, validateMeta, validatePack, validatePackV2, validateProducerContract, validateProducerOutputs, validateScriptablePackDefinition, writeMeshBinHeader };
12728
+ export { AssetGuid, MESH_BIN_DIGEST_BYTES, MESH_BIN_HEADER_V4_BYTES, MESH_BIN_PROJECTION_VERSION, MESH_BIN_VERSION, PACK_PARAMETER_TYPES, PACK_SOURCE_KEY_RE, PARAMETERIZED_PACK_OPERATION_DESCRIPTORS, PARAMETERIZED_PACK_OPERATION_IDS, PackageId, SCRIPTABLE_PACK_ASSET_KINDS, STANDARD_SCRIPTABLE_PACK_SCAN_OPTIONS, buildCatalogProjection, buildOfflineAssetEvidence, calculateCatalogDelta, calculateTopologyDiff, catalogProjectionFor, catalogSourcePathFor, collectMaterialCookRefs, createFileSystemParameterizedPackAuthoringGateway, createFileSystemParameterizedPackAuthoringPort, createMaterialArtifactDigest, createParameterizedPackAuthoringGateway, createRuntimePackPublication, currentProjectionFor, decodeMeshBinHeader, definePack, definePackageId, diffTopology, finalizePackageProduct, finalizePackageTransportSource, findReservedAssetKindConflict, inventoryDigest, isParameterizedPack, isScriptablePackAssetKind, isValidAssetGuidString, isValidPackSourceKey, metaPathForGuid, packageTransportRevision, packageVerification, parsePackAuthoringJson, parsePackJson, parsePackV2, parseParameterizedPackJson, projectAssetRefs, projectCookedMaterialRecord, projectCookedPackageEntry, projectDirectPackJson, projectExternalCatalogEntries, projectPackageCatalog, projectParameterizedPackMeta, projectRuntimeCatalogRow, projectSceneEntityRefs, projectScriptablePackSceneComponents, resolveAssetSource, resolvePackInheritance, resolvePackParameterInheritance, resolvePackParameterValues, scanInventory, serializeCookedMaterialRecord, serializeMaterialCookReceipt, syncAuthorInventory, validateArtifactPath, validateAuthorInventory, validateCookedMaterialRecord, validateMaterialCookReceipt, validateMeta, validatePack, validatePackDefinition, validatePackV2, validateProducerContract, validateProducerOutputs, writeMeshBinHeader };
10216
12729
  //# sourceMappingURL=build.mjs.map
10217
12730
  //# sourceMappingURL=build.mjs.map