@plasius/gpu-model-core 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -172,9 +172,9 @@ function readRecord(value, path, allowedKeys) {
172
172
  }
173
173
  return value;
174
174
  }
175
- function requireKey(record3, key, path) {
176
- if (!Object.hasOwn(record3, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
177
- return record3[key];
175
+ function requireKey(record4, key, path) {
176
+ if (!Object.hasOwn(record4, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
177
+ return record4[key];
178
178
  }
179
179
  function readArray(value, path, maximum, minimum = 0) {
180
180
  if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
@@ -253,9 +253,9 @@ function readNumberTuple(value, path, length, minimum, maximum) {
253
253
  const input = readArray(value, path, length, length);
254
254
  return Object.freeze(input.map((entry, index) => readFinite(entry, `${path}[${index}]`, minimum, maximum)));
255
255
  }
256
- function readOptional(record3, key, parser, path) {
257
- if (!Object.hasOwn(record3, key)) return void 0;
258
- return parser(record3[key], `${path}.${key}`);
256
+ function readOptional(record4, key, parser, path) {
257
+ if (!Object.hasOwn(record4, key)) return void 0;
258
+ return parser(record4[key], `${path}.${key}`);
259
259
  }
260
260
  function assignOptional(target, key, value) {
261
261
  if (value !== void 0) Object.assign(target, { [key]: value });
@@ -277,17 +277,17 @@ function readMetadataValue(value, path, depth, budget) {
277
277
  return Object.freeze(input.map((entry, index) => readMetadataValue(entry, `${path}[${index}]`, depth + 1, budget)));
278
278
  }
279
279
  if (typeof value !== "object" || value === null) return fail("invalid-type", path, "metadata value is not JSON-compatible");
280
- const record3 = value;
281
- const prototype = Object.getPrototypeOf(record3);
280
+ const record4 = value;
281
+ const prototype = Object.getPrototypeOf(record4);
282
282
  if (prototype !== Object.prototype && prototype !== null) return fail("invalid-type", path, "metadata objects must be plain objects");
283
- const keys = Reflect.ownKeys(record3);
283
+ const keys = Reflect.ownKeys(record4);
284
284
  if (keys.length > GPU_MODEL_DOCUMENT_LIMITS.metadataObjectKeys) return fail("limit-exceeded", path, "metadata object has too many keys");
285
285
  const output = {};
286
286
  for (const key of keys) {
287
287
  if (typeof key !== "string") return fail("unknown-key", path, "metadata symbol keys are not permitted");
288
288
  if (PROHIBITED_KEYS.has(key)) return fail("unknown-key", `${path}.${key}`, "prohibited metadata key");
289
289
  readString(key, `${path}.[key]`, GPU_MODEL_DOCUMENT_LIMITS.metadataKeyLength, METADATA_KEY);
290
- const descriptor = Object.getOwnPropertyDescriptor(record3, key);
290
+ const descriptor = Object.getOwnPropertyDescriptor(record4, key);
291
291
  if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) return fail("invalid-type", `${path}.${key}`, "metadata properties must be enumerable data properties");
292
292
  output[key] = readMetadataValue(descriptor.value, `${path}.${key}`, depth + 1, budget);
293
293
  }
@@ -300,9 +300,9 @@ function readMetadata(value, path, budget) {
300
300
  }
301
301
  function parseCoordinateSystem(value) {
302
302
  const path = "$.coordinateSystem";
303
- const record3 = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
303
+ const record4 = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
304
304
  for (const [key, expected] of Object.entries(CANONICAL_GPU_MODEL_COORDINATE_SYSTEM)) {
305
- if (requireKey(record3, key, path) !== expected) return fail("invalid-value", `${path}.${key}`, "coordinate system is not canonical");
305
+ if (requireKey(record4, key, path) !== expected) return fail("invalid-value", `${path}.${key}`, "coordinate system is not canonical");
306
306
  }
307
307
  return CANONICAL_GPU_MODEL_COORDINATE_SYSTEM;
308
308
  }
@@ -314,19 +314,19 @@ function parseMatrix(value, path, requireAffine = true) {
314
314
  return matrix;
315
315
  }
316
316
  function parseBounds(value, path) {
317
- const record3 = readRecord(value, path, ["min", "max"]);
318
- const min = readNumberTuple(requireKey(record3, "min", path), `${path}.min`, 3);
319
- const max = readNumberTuple(requireKey(record3, "max", path), `${path}.max`, 3);
317
+ const record4 = readRecord(value, path, ["min", "max"]);
318
+ const min = readNumberTuple(requireKey(record4, "min", path), `${path}.min`, 3);
319
+ const max = readNumberTuple(requireKey(record4, "max", path), `${path}.max`, 3);
320
320
  for (let index = 0; index < 3; index += 1) {
321
321
  if (min[index] > max[index]) return fail("invalid-value", path, "bounds min cannot exceed max");
322
322
  }
323
323
  return { min, max };
324
324
  }
325
325
  function parseTransformTrs(value, path) {
326
- const record3 = readRecord(value, path, ["translation", "rotation", "scale"]);
327
- const translation = readNumberTuple(requireKey(record3, "translation", path), `${path}.translation`, 3);
328
- const rotation = readNumberTuple(requireKey(record3, "rotation", path), `${path}.rotation`, 4);
329
- const scale = readNumberTuple(requireKey(record3, "scale", path), `${path}.scale`, 3);
326
+ const record4 = readRecord(value, path, ["translation", "rotation", "scale"]);
327
+ const translation = readNumberTuple(requireKey(record4, "translation", path), `${path}.translation`, 3);
328
+ const rotation = readNumberTuple(requireKey(record4, "rotation", path), `${path}.rotation`, 4);
329
+ const scale = readNumberTuple(requireKey(record4, "scale", path), `${path}.scale`, 3);
330
330
  const magnitude = Math.hypot(...rotation);
331
331
  if (!almostEqual(magnitude, 1, 1e-4)) return fail("invalid-value", `${path}.rotation`, "rotation quaternion must be normalized");
332
332
  return { translation, rotation, scale };
@@ -348,25 +348,25 @@ function intrinsicBlobArrayBuffer(blob) {
348
348
  return Reflect.apply(blobArrayBuffer, blob, []);
349
349
  }
350
350
  function parseResourceHeader(value, path) {
351
- const record3 = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
352
- const kind = readEnum(requireKey(record3, "kind", path), `${path}.kind`, ["buffer", "image"]);
353
- const mimeType = readString(requireKey(record3, "mimeType", path), `${path}.mimeType`, 128, MIME_TYPE).toLowerCase();
351
+ const record4 = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
352
+ const kind = readEnum(requireKey(record4, "kind", path), `${path}.kind`, ["buffer", "image"]);
353
+ const mimeType = readString(requireKey(record4, "mimeType", path), `${path}.mimeType`, 128, MIME_TYPE).toLowerCase();
354
354
  if (kind === "image" ? !IMAGE_MIME_TYPES.has(mimeType) : !BUFFER_MIME_TYPES.has(mimeType)) {
355
355
  return fail("invalid-value", `${path}.mimeType`, `unsupported ${kind} MIME type`);
356
356
  }
357
- const payload = requireKey(record3, "payload", path);
357
+ const payload = requireKey(record4, "payload", path);
358
358
  if (!(payload instanceof Blob)) return fail("invalid-type", `${path}.payload`, "expected an immutable Blob payload");
359
359
  if (Object.getPrototypeOf(payload) !== Blob.prototype || Reflect.ownKeys(payload).some((key) => typeof key === "string")) {
360
360
  return fail("invalid-type", `${path}.payload`, "expected a native Blob without caller-defined properties");
361
361
  }
362
- const byteLength = readSafeInteger(requireKey(record3, "byteLength", path), `${path}.byteLength`, 1);
362
+ const byteLength = readSafeInteger(requireKey(record4, "byteLength", path), `${path}.byteLength`, 1);
363
363
  if (intrinsicBlobSize(payload) !== byteLength) return fail("payload-length-mismatch", `${path}.payload`, "payload byte length does not match byteLength");
364
364
  const payloadType = intrinsicBlobType(payload);
365
365
  if (payloadType && payloadType.toLowerCase() !== mimeType) return fail("invalid-value", `${path}.payload`, "payload MIME type does not match mimeType");
366
366
  return {
367
- id: readId(requireKey(record3, "id", path), `${path}.id`),
367
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
368
368
  kind,
369
- contentHash: readSha256(requireKey(record3, "contentHash", path), `${path}.contentHash`),
369
+ contentHash: readSha256(requireKey(record4, "contentHash", path), `${path}.contentHash`),
370
370
  byteLength,
371
371
  mimeType,
372
372
  payload
@@ -375,11 +375,11 @@ function parseResourceHeader(value, path) {
375
375
  function resolveVerificationLimits(options) {
376
376
  const overrides = options?.limits;
377
377
  if (overrides === void 0) return GPU_MODEL_RESOURCE_VERIFICATION_LIMITS;
378
- const record3 = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
378
+ const record4 = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
379
379
  const output = { ...GPU_MODEL_RESOURCE_VERIFICATION_LIMITS };
380
380
  for (const [key, ceiling] of Object.entries(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS)) {
381
- if (!Object.hasOwn(record3, key)) continue;
382
- const value = readSafeInteger(record3[key], `$.verification.limits.${key}`, 1, ceiling);
381
+ if (!Object.hasOwn(record4, key)) continue;
382
+ const value = readSafeInteger(record4[key], `$.verification.limits.${key}`, 1, ceiling);
383
383
  Object.assign(output, { [key]: value });
384
384
  }
385
385
  if (output.maxImageBytes > output.maxResourceBytes) output.maxImageBytes = output.maxResourceBytes;
@@ -491,13 +491,13 @@ function createVerificationDeadline(timeoutMs, callerSignal, path) {
491
491
  };
492
492
  }
493
493
  function parseInspection(value, path) {
494
- const record3 = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
494
+ const record4 = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
495
495
  const output = {
496
- valid: readBoolean(requireKey(record3, "valid", path), `${path}.valid`),
497
- detectedMimeType: readString(requireKey(record3, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
496
+ valid: readBoolean(requireKey(record4, "valid", path), `${path}.valid`),
497
+ detectedMimeType: readString(requireKey(record4, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
498
498
  };
499
- assignOptional(output, "width", readOptional(record3, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
500
- assignOptional(output, "height", readOptional(record3, "height", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
499
+ assignOptional(output, "width", readOptional(record4, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
500
+ assignOptional(output, "height", readOptional(record4, "height", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
501
501
  return output;
502
502
  }
503
503
  function bytesStartWith(bytes, signature) {
@@ -612,62 +612,62 @@ async function verifyGpuModelResource(value, port, options) {
612
612
  }
613
613
  }
614
614
  function parseNode(value, path, budget) {
615
- const record3 = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
616
- const children = Object.freeze(readArray(requireKey(record3, "children", path), `${path}.children`, GPU_MODEL_DOCUMENT_LIMITS.nodes).map((entry, index) => readId(entry, `${path}.children[${index}]`)));
615
+ const record4 = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
616
+ const children = Object.freeze(readArray(requireKey(record4, "children", path), `${path}.children`, GPU_MODEL_DOCUMENT_LIMITS.nodes).map((entry, index) => readId(entry, `${path}.children[${index}]`)));
617
617
  consumeBudget(budget, "sceneEdges", children.length, GPU_MODEL_DOCUMENT_LIMITS.sceneEdges, path, "scene edge");
618
618
  ensureUnique(children, `${path}.children`, "duplicate child node");
619
619
  const output = {
620
- id: readId(requireKey(record3, "id", path), `${path}.id`),
620
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
621
621
  children,
622
- localMatrix: parseMatrix(requireKey(record3, "localMatrix", path), `${path}.localMatrix`)
622
+ localMatrix: parseMatrix(requireKey(record4, "localMatrix", path), `${path}.localMatrix`)
623
623
  };
624
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
625
- assignOptional(output, "meshId", readOptional(record3, "meshId", readId, path));
626
- assignOptional(output, "skinId", readOptional(record3, "skinId", readId, path));
627
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
624
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
625
+ assignOptional(output, "meshId", readOptional(record4, "meshId", readId, path));
626
+ assignOptional(output, "skinId", readOptional(record4, "skinId", readId, path));
627
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
628
628
  return output;
629
629
  }
630
630
  function parseAccessor(value, path, budget) {
631
- const record3 = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
632
- const componentType = readEnum(requireKey(record3, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
633
- const elementType = readEnum(requireKey(record3, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
634
- const normalized = readOptional(record3, "normalized", readBoolean, path);
631
+ const record4 = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
632
+ const componentType = readEnum(requireKey(record4, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
633
+ const elementType = readEnum(requireKey(record4, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
634
+ const normalized = readOptional(record4, "normalized", readBoolean, path);
635
635
  if (normalized === true && componentType === "f32") return fail("invalid-value", `${path}.normalized`, "floating-point accessors cannot be normalized");
636
636
  if (elementType.startsWith("mat") && componentType !== "f32") return fail("invalid-value", `${path}.componentType`, "matrix accessors must use f32 components");
637
637
  const expectedComponents = ELEMENT_COMPONENTS[elementType];
638
- const min = readOptional(record3, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
639
- const max = readOptional(record3, "max", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
638
+ const min = readOptional(record4, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
639
+ const max = readOptional(record4, "max", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
640
640
  if (min === void 0 !== (max === void 0)) return fail("invalid-value", path, "accessor min and max must be supplied together");
641
641
  if (min && max) {
642
- const [componentMinimum, componentMaximum, integer] = COMPONENT_RANGES[componentType];
642
+ const [componentMinimum, componentMaximum, integer2] = COMPONENT_RANGES[componentType];
643
643
  for (let index = 0; index < expectedComponents; index += 1) {
644
644
  const minimum = min[index];
645
645
  const maximum = max[index];
646
646
  if (minimum > maximum) return fail("invalid-value", path, "accessor min cannot exceed max");
647
- if (minimum < componentMinimum || maximum > componentMaximum || integer && (!Number.isInteger(minimum) || !Number.isInteger(maximum))) {
647
+ if (minimum < componentMinimum || maximum > componentMaximum || integer2 && (!Number.isInteger(minimum) || !Number.isInteger(maximum))) {
648
648
  return fail("invalid-value", path, "accessor bounds are outside the component representation");
649
649
  }
650
650
  }
651
651
  }
652
- const count = readSafeInteger(requireKey(record3, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
652
+ const count = readSafeInteger(requireKey(record4, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
653
653
  consumeBudget(budget, "accessorElements", count * expectedComponents, GPU_MODEL_DOCUMENT_LIMITS.accessorElements, path, "accessor element component");
654
654
  const output = {
655
- id: readId(requireKey(record3, "id", path), `${path}.id`),
656
- resourceId: readId(requireKey(record3, "resourceId", path), `${path}.resourceId`),
657
- byteOffset: readSafeInteger(requireKey(record3, "byteOffset", path), `${path}.byteOffset`),
655
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
656
+ resourceId: readId(requireKey(record4, "resourceId", path), `${path}.resourceId`),
657
+ byteOffset: readSafeInteger(requireKey(record4, "byteOffset", path), `${path}.byteOffset`),
658
658
  count,
659
659
  componentType,
660
660
  elementType
661
661
  };
662
- assignOptional(output, "byteStride", readOptional(record3, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
662
+ assignOptional(output, "byteStride", readOptional(record4, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
663
663
  assignOptional(output, "normalized", normalized);
664
664
  assignOptional(output, "min", min);
665
665
  assignOptional(output, "max", max);
666
666
  return output;
667
667
  }
668
668
  function parsePrimitive(value, path, budget) {
669
- const record3 = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
670
- const attributesInput = readArray(requireKey(record3, "attributes", path), `${path}.attributes`, 64, 1);
669
+ const record4 = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
670
+ const attributesInput = readArray(requireKey(record4, "attributes", path), `${path}.attributes`, 64, 1);
671
671
  consumeBudget(budget, "primitiveAttributes", attributesInput.length, GPU_MODEL_DOCUMENT_LIMITS.primitiveAttributes, path, "primitive attribute");
672
672
  const attributes = Object.freeze(attributesInput.map((entry, index) => {
673
673
  const entryPath = `${path}.attributes[${index}]`;
@@ -680,53 +680,53 @@ function parsePrimitive(value, path, budget) {
680
680
  ensureUnique(attributes.map(({ semantic }) => semantic), `${path}.attributes`, "duplicate attribute semantic");
681
681
  if (!attributes.some(({ semantic }) => semantic === "POSITION")) return fail("invalid-value", `${path}.attributes`, "primitive requires POSITION");
682
682
  const output = {
683
- id: readId(requireKey(record3, "id", path), `${path}.id`),
684
- topology: readEnum(requireKey(record3, "topology", path), `${path}.topology`, ["points", "lines", "line-strip", "triangles", "triangle-strip"]),
683
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
684
+ topology: readEnum(requireKey(record4, "topology", path), `${path}.topology`, ["points", "lines", "line-strip", "triangles", "triangle-strip"]),
685
685
  attributes
686
686
  };
687
- assignOptional(output, "indicesAccessorId", readOptional(record3, "indicesAccessorId", readId, path));
688
- assignOptional(output, "materialId", readOptional(record3, "materialId", readId, path));
689
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
687
+ assignOptional(output, "indicesAccessorId", readOptional(record4, "indicesAccessorId", readId, path));
688
+ assignOptional(output, "materialId", readOptional(record4, "materialId", readId, path));
689
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
690
690
  return output;
691
691
  }
692
692
  function parseMesh(value, path, budget) {
693
- const record3 = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
694
- const input = readArray(requireKey(record3, "primitives", path), `${path}.primitives`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
693
+ const record4 = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
694
+ const input = readArray(requireKey(record4, "primitives", path), `${path}.primitives`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
695
695
  consumeBudget(budget, "primitives", input.length, GPU_MODEL_DOCUMENT_LIMITS.primitives, path, "primitive");
696
696
  const output = {
697
- id: readId(requireKey(record3, "id", path), `${path}.id`),
697
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
698
698
  primitives: Object.freeze(input.map((entry, index) => parsePrimitive(entry, `${path}.primitives[${index}]`, budget)))
699
699
  };
700
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
701
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
700
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
701
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
702
702
  return output;
703
703
  }
704
704
  function parseTextureTransform(value, path) {
705
- const record3 = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
706
- if (Reflect.ownKeys(record3).length === 0) return fail("invalid-value", path, "texture transform cannot be empty");
705
+ const record4 = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
706
+ if (Reflect.ownKeys(record4).length === 0) return fail("invalid-value", path, "texture transform cannot be empty");
707
707
  const output = {};
708
- assignOptional(output, "offset", readOptional(record3, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
709
- assignOptional(output, "rotationRadians", readOptional(record3, "rotationRadians", readFinite, path));
710
- assignOptional(output, "scale", readOptional(record3, "scale", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
708
+ assignOptional(output, "offset", readOptional(record4, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
709
+ assignOptional(output, "rotationRadians", readOptional(record4, "rotationRadians", readFinite, path));
710
+ assignOptional(output, "scale", readOptional(record4, "scale", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
711
711
  return output;
712
712
  }
713
713
  function parseTextureBinding(value, path) {
714
- const record3 = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
714
+ const record4 = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
715
715
  const output = {
716
- textureId: readId(requireKey(record3, "textureId", path), `${path}.textureId`),
717
- colorSpace: readEnum(requireKey(record3, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
718
- intendedUsage: readEnum(requireKey(record3, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES)
716
+ textureId: readId(requireKey(record4, "textureId", path), `${path}.textureId`),
717
+ colorSpace: readEnum(requireKey(record4, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
718
+ intendedUsage: readEnum(requireKey(record4, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES)
719
719
  };
720
- assignOptional(output, "samplerId", readOptional(record3, "samplerId", readId, path));
721
- assignOptional(output, "texCoordSet", readOptional(record3, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
722
- assignOptional(output, "transform", readOptional(record3, "transform", parseTextureTransform, path));
720
+ assignOptional(output, "samplerId", readOptional(record4, "samplerId", readId, path));
721
+ assignOptional(output, "texCoordSet", readOptional(record4, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
722
+ assignOptional(output, "transform", readOptional(record4, "transform", parseTextureTransform, path));
723
723
  return output;
724
724
  }
725
725
  function parseMaterialTextures(value, path) {
726
726
  const slots = Object.keys(MATERIAL_TEXTURE_SLOTS);
727
- const record3 = readRecord(value, path, slots);
727
+ const record4 = readRecord(value, path, slots);
728
728
  const output = {};
729
- for (const slot of slots) assignOptional(output, slot, readOptional(record3, slot, parseTextureBinding, path));
729
+ for (const slot of slots) assignOptional(output, slot, readOptional(record4, slot, parseTextureBinding, path));
730
730
  return output;
731
731
  }
732
732
  function parseMaterial(value, path, budget) {
@@ -752,138 +752,138 @@ function parseMaterial(value, path, budget) {
752
752
  "extensions",
753
753
  "sourceMetadata"
754
754
  ];
755
- const record3 = readRecord(value, path, keys);
756
- const alphaMode = readEnum(requireKey(record3, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
757
- const alphaCutoff = readOptional(record3, "alphaCutoff", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path);
755
+ const record4 = readRecord(value, path, keys);
756
+ const alphaMode = readEnum(requireKey(record4, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
757
+ const alphaCutoff = readOptional(record4, "alphaCutoff", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path);
758
758
  if (alphaMode === "mask" && alphaCutoff === void 0) return fail("invalid-value", `${path}.alphaCutoff`, "mask materials require alphaCutoff");
759
759
  if (alphaMode !== "mask" && alphaCutoff !== void 0) return fail("invalid-value", `${path}.alphaCutoff`, "alphaCutoff is only valid for mask materials");
760
760
  const output = {
761
- id: readId(requireKey(record3, "id", path), `${path}.id`),
762
- workflow: readEnum(requireKey(record3, "workflow", path), `${path}.workflow`, ["metallic-roughness", "specular-glossiness", "unlit", "custom"]),
761
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
762
+ workflow: readEnum(requireKey(record4, "workflow", path), `${path}.workflow`, ["metallic-roughness", "specular-glossiness", "unlit", "custom"]),
763
763
  alphaMode,
764
- baseColorFactor: readNumberTuple(requireKey(record3, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
765
- textures: parseMaterialTextures(requireKey(record3, "textures", path), `${path}.textures`),
766
- extensions: readMetadata(requireKey(record3, "extensions", path), `${path}.extensions`, budget),
767
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
764
+ baseColorFactor: readNumberTuple(requireKey(record4, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
765
+ textures: parseMaterialTextures(requireKey(record4, "textures", path), `${path}.textures`),
766
+ extensions: readMetadata(requireKey(record4, "extensions", path), `${path}.extensions`, budget),
767
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
768
768
  };
769
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
769
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
770
770
  assignOptional(output, "alphaCutoff", alphaCutoff);
771
- assignOptional(output, "doubleSided", readOptional(record3, "doubleSided", readBoolean, path));
771
+ assignOptional(output, "doubleSided", readOptional(record4, "doubleSided", readBoolean, path));
772
772
  for (const key of ["metallicFactor", "roughnessFactor", "transmissionFactor", "clearcoatFactor", "clearcoatRoughnessFactor", "sheenRoughnessFactor", "occlusionStrength"]) {
773
- assignOptional(output, key, readOptional(record3, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
773
+ assignOptional(output, key, readOptional(record4, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
774
774
  }
775
- assignOptional(output, "normalScale", readOptional(record3, "normalScale", readFinite, path));
776
- assignOptional(output, "emissiveFactor", readOptional(record3, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
777
- assignOptional(output, "sheenColorFactor", readOptional(record3, "sheenColorFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0, 1), path));
775
+ assignOptional(output, "normalScale", readOptional(record4, "normalScale", readFinite, path));
776
+ assignOptional(output, "emissiveFactor", readOptional(record4, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
777
+ assignOptional(output, "sheenColorFactor", readOptional(record4, "sheenColorFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0, 1), path));
778
778
  return output;
779
779
  }
780
780
  function parseTextureSampler(value, path) {
781
- const record3 = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
782
- const output = { id: readId(requireKey(record3, "id", path), `${path}.id`) };
783
- assignOptional(output, "wrapS", readOptional(record3, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
784
- assignOptional(output, "wrapT", readOptional(record3, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
785
- assignOptional(output, "minFilter", readOptional(record3, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
786
- assignOptional(output, "magFilter", readOptional(record3, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
787
- assignOptional(output, "mipFilter", readOptional(record3, "mipFilter", (entry, entryPath) => readEnum(entry, entryPath, ["nearest", "linear"]), path));
781
+ const record4 = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
782
+ const output = { id: readId(requireKey(record4, "id", path), `${path}.id`) };
783
+ assignOptional(output, "wrapS", readOptional(record4, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
784
+ assignOptional(output, "wrapT", readOptional(record4, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
785
+ assignOptional(output, "minFilter", readOptional(record4, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
786
+ assignOptional(output, "magFilter", readOptional(record4, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
787
+ assignOptional(output, "mipFilter", readOptional(record4, "mipFilter", (entry, entryPath) => readEnum(entry, entryPath, ["nearest", "linear"]), path));
788
788
  return output;
789
789
  }
790
790
  function parseTexture(value, path, budget) {
791
- const record3 = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
792
- const usages = Object.freeze(readArray(requireKey(record3, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES.length, 1).map((entry, index) => readEnum(entry, `${path}.intendedUsage[${index}]`, TEXTURE_USAGES)));
791
+ const record4 = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
792
+ const usages = Object.freeze(readArray(requireKey(record4, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES.length, 1).map((entry, index) => readEnum(entry, `${path}.intendedUsage[${index}]`, TEXTURE_USAGES)));
793
793
  ensureUnique(usages, `${path}.intendedUsage`, "duplicate intended texture usage");
794
794
  const output = {
795
- id: readId(requireKey(record3, "id", path), `${path}.id`),
796
- contentHash: readSha256(requireKey(record3, "contentHash", path), `${path}.contentHash`),
797
- imageResourceId: readId(requireKey(record3, "imageResourceId", path), `${path}.imageResourceId`),
795
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
796
+ contentHash: readSha256(requireKey(record4, "contentHash", path), `${path}.contentHash`),
797
+ imageResourceId: readId(requireKey(record4, "imageResourceId", path), `${path}.imageResourceId`),
798
798
  intendedUsage: usages,
799
- colorSpace: readEnum(requireKey(record3, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
800
- exportEmbeddingPolicy: readEnum(requireKey(record3, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
801
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
799
+ colorSpace: readEnum(requireKey(record4, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
800
+ exportEmbeddingPolicy: readEnum(requireKey(record4, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
801
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
802
802
  };
803
- assignOptional(output, "fileName", readOptional(record3, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
804
- assignOptional(output, "relativePath", readOptional(record3, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
805
- assignOptional(output, "mimeType", readOptional(record3, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
806
- assignOptional(output, "byteLength", readOptional(record3, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
807
- assignOptional(output, "sampler", readOptional(record3, "sampler", parseTextureSampler, path));
803
+ assignOptional(output, "fileName", readOptional(record4, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
804
+ assignOptional(output, "relativePath", readOptional(record4, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
805
+ assignOptional(output, "mimeType", readOptional(record4, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
806
+ assignOptional(output, "byteLength", readOptional(record4, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
807
+ assignOptional(output, "sampler", readOptional(record4, "sampler", parseTextureSampler, path));
808
808
  return output;
809
809
  }
810
810
  function parseSkeleton(value, path, budget) {
811
- const record3 = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
812
- const jointIds = Object.freeze(readArray(requireKey(record3, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
813
- const rootJointIds = Object.freeze(readArray(requireKey(record3, "rootJointIds", path), `${path}.rootJointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.rootJointIds[${index}]`)));
811
+ const record4 = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
812
+ const jointIds = Object.freeze(readArray(requireKey(record4, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
813
+ const rootJointIds = Object.freeze(readArray(requireKey(record4, "rootJointIds", path), `${path}.rootJointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.rootJointIds[${index}]`)));
814
814
  consumeBudget(budget, "skinJoints", jointIds.length + rootJointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "rig joint");
815
815
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skeleton joint");
816
816
  ensureUnique(rootJointIds, `${path}.rootJointIds`, "duplicate root joint");
817
817
  const output = {
818
- id: readId(requireKey(record3, "id", path), `${path}.id`),
818
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
819
819
  jointIds,
820
820
  rootJointIds,
821
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
821
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
822
822
  };
823
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
823
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
824
824
  return output;
825
825
  }
826
826
  function parseJoint(value, path, budget) {
827
- const record3 = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
827
+ const record4 = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
828
828
  const output = {
829
- id: readId(requireKey(record3, "id", path), `${path}.id`),
830
- nodeId: readId(requireKey(record3, "nodeId", path), `${path}.nodeId`),
831
- restLocalTransform: parseTransformTrs(requireKey(record3, "restLocalTransform", path), `${path}.restLocalTransform`),
832
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
829
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
830
+ nodeId: readId(requireKey(record4, "nodeId", path), `${path}.nodeId`),
831
+ restLocalTransform: parseTransformTrs(requireKey(record4, "restLocalTransform", path), `${path}.restLocalTransform`),
832
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
833
833
  };
834
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
835
- assignOptional(output, "parentJointId", readOptional(record3, "parentJointId", readId, path));
836
- assignOptional(output, "inverseBindMatrix", readOptional(record3, "inverseBindMatrix", parseMatrix, path));
834
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
835
+ assignOptional(output, "parentJointId", readOptional(record4, "parentJointId", readId, path));
836
+ assignOptional(output, "inverseBindMatrix", readOptional(record4, "inverseBindMatrix", parseMatrix, path));
837
837
  return output;
838
838
  }
839
- function parseWeightRows(value, path, rows, columns, integer, budget) {
839
+ function parseWeightRows(value, path, rows, columns, integer2, budget) {
840
840
  const input = readArray(value, path, rows, rows);
841
841
  consumeBudget(budget, "weightVertices", input.length, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, path, "weight vertex");
842
842
  consumeBudget(budget, "weightInfluences", input.length * columns, GPU_MODEL_DOCUMENT_LIMITS.weightInfluences, path, "weight influence");
843
843
  return Object.freeze(input.map((row, rowIndex) => {
844
- const parsed = readNumberTuple(row, `${path}[${rowIndex}]`, columns, 0, integer ? Number.MAX_SAFE_INTEGER : 1);
845
- if (integer && parsed.some((entry) => !Number.isSafeInteger(entry))) return fail("invalid-value", `${path}[${rowIndex}]`, "joint indices must be safe integers");
844
+ const parsed = readNumberTuple(row, `${path}[${rowIndex}]`, columns, 0, integer2 ? Number.MAX_SAFE_INTEGER : 1);
845
+ if (integer2 && parsed.some((entry) => !Number.isSafeInteger(entry))) return fail("invalid-value", `${path}[${rowIndex}]`, "joint indices must be safe integers");
846
846
  return parsed;
847
847
  }));
848
848
  }
849
849
  function parseVertexWeightSet(value, path, budget) {
850
- const record3 = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
851
- const influences = readSafeInteger(requireKey(record3, "influencesPerVertex", path), `${path}.influencesPerVertex`, 1, 32);
850
+ const record4 = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
851
+ const influences = readSafeInteger(requireKey(record4, "influencesPerVertex", path), `${path}.influencesPerVertex`, 1, 32);
852
852
  if (influences % 4 !== 0) return fail("invalid-value", `${path}.influencesPerVertex`, "influencesPerVertex must be a multiple of four");
853
- const jointRows = readArray(requireKey(record3, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
854
- const weightRows = readArray(requireKey(record3, "weights", path), `${path}.weights`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
853
+ const jointRows = readArray(requireKey(record4, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
854
+ const weightRows = readArray(requireKey(record4, "weights", path), `${path}.weights`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
855
855
  if (jointRows.length !== weightRows.length) return fail("invalid-value", path, "joint and weight row counts must match");
856
856
  return {
857
- primitiveId: readId(requireKey(record3, "primitiveId", path), `${path}.primitiveId`),
857
+ primitiveId: readId(requireKey(record4, "primitiveId", path), `${path}.primitiveId`),
858
858
  influencesPerVertex: influences,
859
859
  joints: parseWeightRows(jointRows, `${path}.joints`, jointRows.length, influences, true, budget),
860
860
  weights: parseWeightRows(weightRows, `${path}.weights`, weightRows.length, influences, false, budget)
861
861
  };
862
862
  }
863
863
  function parseSkin(value, path, budget) {
864
- const record3 = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
865
- const jointIds = Object.freeze(readArray(requireKey(record3, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
864
+ const record4 = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
865
+ const jointIds = Object.freeze(readArray(requireKey(record4, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
866
866
  consumeBudget(budget, "skinJoints", jointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "skin joint");
867
867
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skin joint");
868
- const inverseInput = readArray(requireKey(record3, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
868
+ const inverseInput = readArray(requireKey(record4, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
869
869
  if (inverseInput.length !== jointIds.length) return fail("invalid-value", `${path}.inverseBindMatrices`, "one inverse bind matrix is required per skin joint");
870
- const weightInput = readArray(requireKey(record3, "weightSets", path), `${path}.weightSets`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
870
+ const weightInput = readArray(requireKey(record4, "weightSets", path), `${path}.weightSets`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
871
871
  const weightSets = Object.freeze(weightInput.map((entry, index) => parseVertexWeightSet(entry, `${path}.weightSets[${index}]`, budget)));
872
872
  ensureUnique(weightSets.map(({ primitiveId }) => primitiveId), `${path}.weightSets`, "duplicate primitive weight set");
873
873
  return {
874
- id: readId(requireKey(record3, "id", path), `${path}.id`),
875
- skeletonId: readId(requireKey(record3, "skeletonId", path), `${path}.skeletonId`),
874
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
875
+ skeletonId: readId(requireKey(record4, "skeletonId", path), `${path}.skeletonId`),
876
876
  jointIds,
877
877
  inverseBindMatrices: Object.freeze(inverseInput.map((entry, index) => parseMatrix(entry, `${path}.inverseBindMatrices[${index}]`))),
878
878
  weightSets,
879
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
879
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
880
880
  };
881
881
  }
882
882
  function parseBlendShapeDelta(value, path) {
883
- const record3 = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
884
- const output = { primitiveId: readId(requireKey(record3, "primitiveId", path), `${path}.primitiveId`) };
883
+ const record4 = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
884
+ const output = { primitiveId: readId(requireKey(record4, "primitiveId", path), `${path}.primitiveId`) };
885
885
  for (const key of ["positionAccessorId", "normalAccessorId", "tangentAccessorId"]) {
886
- assignOptional(output, key, readOptional(record3, key, readId, path));
886
+ assignOptional(output, key, readOptional(record4, key, readId, path));
887
887
  }
888
888
  if (!output.positionAccessorId && !output.normalAccessorId && !output.tangentAccessorId) {
889
889
  return fail("invalid-value", path, "blend-shape delta requires at least one accessor");
@@ -891,8 +891,8 @@ function parseBlendShapeDelta(value, path) {
891
891
  return output;
892
892
  }
893
893
  function parseBlendShape(value, path, budget) {
894
- const record3 = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
895
- const deltaInput = readArray(requireKey(record3, "deltas", path), `${path}.deltas`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
894
+ const record4 = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
895
+ const deltaInput = readArray(requireKey(record4, "deltas", path), `${path}.deltas`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
896
896
  consumeBudget(
897
897
  budget,
898
898
  "blendShapeDeltas",
@@ -904,96 +904,96 @@ function parseBlendShape(value, path, budget) {
904
904
  const deltas = Object.freeze(deltaInput.map((entry, index) => parseBlendShapeDelta(entry, `${path}.deltas[${index}]`)));
905
905
  ensureUnique(deltas.map(({ primitiveId }) => primitiveId), `${path}.deltas`, "duplicate blend-shape primitive delta");
906
906
  const output = {
907
- id: readId(requireKey(record3, "id", path), `${path}.id`),
908
- meshId: readId(requireKey(record3, "meshId", path), `${path}.meshId`),
907
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
908
+ meshId: readId(requireKey(record4, "meshId", path), `${path}.meshId`),
909
909
  deltas,
910
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
910
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
911
911
  };
912
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
913
- assignOptional(output, "defaultWeight", readOptional(record3, "defaultWeight", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
912
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
913
+ assignOptional(output, "defaultWeight", readOptional(record4, "defaultWeight", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
914
914
  return output;
915
915
  }
916
916
  function parseAnimationSampler(value, path, budget) {
917
- const record3 = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
918
- const timesInput = readArray(requireKey(record3, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
919
- const valuesInput = readArray(requireKey(record3, "outputValues", path), `${path}.outputValues`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
917
+ const record4 = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
918
+ const timesInput = readArray(requireKey(record4, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
919
+ const valuesInput = readArray(requireKey(record4, "outputValues", path), `${path}.outputValues`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
920
920
  consumeBudget(budget, "animationValues", timesInput.length + valuesInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationValues, path, "animation value");
921
921
  const times = Object.freeze(timesInput.map((entry, index) => readFinite(entry, `${path}.inputTimesSeconds[${index}]`, 0)));
922
922
  for (let index = 1; index < times.length; index += 1) {
923
923
  if (times[index] <= times[index - 1]) return fail("invalid-value", `${path}.inputTimesSeconds`, "animation input times must be strictly increasing");
924
924
  }
925
925
  return {
926
- id: readId(requireKey(record3, "id", path), `${path}.id`),
927
- inputAccessorId: readId(requireKey(record3, "inputAccessorId", path), `${path}.inputAccessorId`),
928
- outputAccessorId: readId(requireKey(record3, "outputAccessorId", path), `${path}.outputAccessorId`),
926
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
927
+ inputAccessorId: readId(requireKey(record4, "inputAccessorId", path), `${path}.inputAccessorId`),
928
+ outputAccessorId: readId(requireKey(record4, "outputAccessorId", path), `${path}.outputAccessorId`),
929
929
  inputTimesSeconds: times,
930
930
  outputValues: Object.freeze(valuesInput.map((entry, index) => readFinite(entry, `${path}.outputValues[${index}]`))),
931
- interpolation: readEnum(requireKey(record3, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
932
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
931
+ interpolation: readEnum(requireKey(record4, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
932
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
933
933
  };
934
934
  }
935
935
  function parseAnimationChannel(value, path, budget) {
936
- const record3 = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
936
+ const record4 = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
937
937
  return {
938
- id: readId(requireKey(record3, "id", path), `${path}.id`),
939
- samplerId: readId(requireKey(record3, "samplerId", path), `${path}.samplerId`),
940
- targetType: readEnum(requireKey(record3, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
941
- targetId: readId(requireKey(record3, "targetId", path), `${path}.targetId`),
942
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
938
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
939
+ samplerId: readId(requireKey(record4, "samplerId", path), `${path}.samplerId`),
940
+ targetType: readEnum(requireKey(record4, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
941
+ targetId: readId(requireKey(record4, "targetId", path), `${path}.targetId`),
942
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
943
943
  };
944
944
  }
945
945
  function parseAnimation(value, path, budget) {
946
- const record3 = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
947
- const samplerInput = readArray(requireKey(record3, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
948
- const channelInput = readArray(requireKey(record3, "channels", path), `${path}.channels`, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, 1);
946
+ const record4 = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
947
+ const samplerInput = readArray(requireKey(record4, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
948
+ const channelInput = readArray(requireKey(record4, "channels", path), `${path}.channels`, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, 1);
949
949
  consumeBudget(budget, "animationSamplers", samplerInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, path, "animation sampler");
950
950
  consumeBudget(budget, "animationChannels", channelInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, path, "animation channel");
951
951
  const output = {
952
- id: readId(requireKey(record3, "id", path), `${path}.id`),
953
- durationSeconds: readFinite(requireKey(record3, "durationSeconds", path), `${path}.durationSeconds`, 0),
952
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
953
+ durationSeconds: readFinite(requireKey(record4, "durationSeconds", path), `${path}.durationSeconds`, 0),
954
954
  samplers: Object.freeze(samplerInput.map((entry, index) => parseAnimationSampler(entry, `${path}.samplers[${index}]`, budget))),
955
955
  channels: Object.freeze(channelInput.map((entry, index) => parseAnimationChannel(entry, `${path}.channels[${index}]`, budget))),
956
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
956
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
957
957
  };
958
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
958
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
959
959
  return output;
960
960
  }
961
961
  function parseAnalyticGeometry(value, path, budget) {
962
- const record3 = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
963
- if (requireKey(record3, "kind", path) !== "brep-placeholder") return fail("invalid-value", `${path}.kind`, "unsupported analytic geometry kind");
962
+ const record4 = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
963
+ if (requireKey(record4, "kind", path) !== "brep-placeholder") return fail("invalid-value", `${path}.kind`, "unsupported analytic geometry kind");
964
964
  const output = {
965
- id: readId(requireKey(record3, "id", path), `${path}.id`),
965
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
966
966
  kind: "brep-placeholder",
967
- sourceResourceId: readId(requireKey(record3, "sourceResourceId", path), `${path}.sourceResourceId`),
968
- sourceUnit: readEnum(requireKey(record3, "sourceUnit", path), `${path}.sourceUnit`, ["metre", "millimetre", "centimetre", "inch", "foot", "unitless"])
967
+ sourceResourceId: readId(requireKey(record4, "sourceResourceId", path), `${path}.sourceResourceId`),
968
+ sourceUnit: readEnum(requireKey(record4, "sourceUnit", path), `${path}.sourceUnit`, ["metre", "millimetre", "centimetre", "inch", "foot", "unitless"])
969
969
  };
970
- assignOptional(output, "tessellatedMeshId", readOptional(record3, "tessellatedMeshId", readId, path));
971
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
970
+ assignOptional(output, "tessellatedMeshId", readOptional(record4, "tessellatedMeshId", readId, path));
971
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
972
972
  return output;
973
973
  }
974
974
  function parseProvenance(value, budget) {
975
975
  const path = "$.provenance";
976
- const record3 = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
976
+ const record4 = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
977
977
  const output = {
978
- sourceFormat: readString(requireKey(record3, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
979
- sourceContentHash: readSha256(requireKey(record3, "sourceContentHash", path), `${path}.sourceContentHash`),
980
- converterId: readString(requireKey(record3, "converterId", path), `${path}.converterId`, 128, TOKEN),
981
- converterVersion: readString(requireKey(record3, "converterVersion", path), `${path}.converterVersion`, 128, TOKEN)
978
+ sourceFormat: readString(requireKey(record4, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
979
+ sourceContentHash: readSha256(requireKey(record4, "sourceContentHash", path), `${path}.sourceContentHash`),
980
+ converterId: readString(requireKey(record4, "converterId", path), `${path}.converterId`, 128, TOKEN),
981
+ converterVersion: readString(requireKey(record4, "converterVersion", path), `${path}.converterVersion`, 128, TOKEN)
982
982
  };
983
- assignOptional(output, "provider", readOptional(record3, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
984
- assignOptional(output, "sourceIdentifier", readOptional(record3, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
985
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
983
+ assignOptional(output, "provider", readOptional(record4, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
984
+ assignOptional(output, "sourceIdentifier", readOptional(record4, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
985
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
986
986
  return output;
987
987
  }
988
988
  function parseDiagnostic(value, path, budget) {
989
- const record3 = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
989
+ const record4 = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
990
990
  const output = {
991
- severity: readEnum(requireKey(record3, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
992
- code: readString(requireKey(record3, "code", path), `${path}.code`, 128, TOKEN),
993
- message: readString(requireKey(record3, "message", path), `${path}.message`, GPU_MODEL_DOCUMENT_LIMITS.diagnosticMessageLength)
991
+ severity: readEnum(requireKey(record4, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
992
+ code: readString(requireKey(record4, "code", path), `${path}.code`, 128, TOKEN),
993
+ message: readString(requireKey(record4, "message", path), `${path}.message`, GPU_MODEL_DOCUMENT_LIMITS.diagnosticMessageLength)
994
994
  };
995
- assignOptional(output, "path", readOptional(record3, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
996
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
995
+ assignOptional(output, "path", readOptional(record4, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
996
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
997
997
  return output;
998
998
  }
999
999
  function ensureUnique(values, path, detail = "duplicate identity") {
@@ -1634,11 +1634,11 @@ var DOCUMENT_KEYS = Object.freeze([
1634
1634
  ]);
1635
1635
  function parseGpuModelDocument(value) {
1636
1636
  const path = "$";
1637
- const record3 = readRecord(value, path, DOCUMENT_KEYS);
1638
- const schemaVersion = requireKey(record3, "schemaVersion", path);
1637
+ const record4 = readRecord(value, path, DOCUMENT_KEYS);
1638
+ const schemaVersion = requireKey(record4, "schemaVersion", path);
1639
1639
  if (schemaVersion !== GPU_MODEL_DOCUMENT_SCHEMA_VERSION) return fail("invalid-value", "$.schemaVersion", "unsupported schema version");
1640
1640
  const parseCollection = (key, maximum, parser, minimum = 0) => {
1641
- const input = readArray(requireKey(record3, key, path), `$.${key}`, maximum, minimum);
1641
+ const input = readArray(requireKey(record4, key, path), `$.${key}`, maximum, minimum);
1642
1642
  return Object.freeze(input.map((entry, index) => parser(entry, `$.${key}[${index}]`)));
1643
1643
  };
1644
1644
  const budget = {
@@ -1659,10 +1659,10 @@ function parseGpuModelDocument(value) {
1659
1659
  geometryWorldAccessorInstances: 0,
1660
1660
  geometryWorldVertices: 0
1661
1661
  };
1662
- const roots = Object.freeze(readArray(requireKey(record3, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1662
+ const roots = Object.freeze(readArray(requireKey(record4, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1663
1663
  const document = {
1664
1664
  schemaVersion,
1665
- coordinateSystem: parseCoordinateSystem(requireKey(record3, "coordinateSystem", path)),
1665
+ coordinateSystem: parseCoordinateSystem(requireKey(record4, "coordinateSystem", path)),
1666
1666
  roots,
1667
1667
  nodes: parseCollection("nodes", GPU_MODEL_DOCUMENT_LIMITS.nodes, (entry, entryPath) => parseNode(entry, entryPath, budget), 1),
1668
1668
  resources: parseCollection("resources", GPU_MODEL_DOCUMENT_LIMITS.resources, parseVerifiedResource, 1),
@@ -1676,10 +1676,10 @@ function parseGpuModelDocument(value) {
1676
1676
  blendShapes: parseCollection("blendShapes", GPU_MODEL_DOCUMENT_LIMITS.blendShapes, (entry, entryPath) => parseBlendShape(entry, entryPath, budget)),
1677
1677
  animations: parseCollection("animations", GPU_MODEL_DOCUMENT_LIMITS.animations, (entry, entryPath) => parseAnimation(entry, entryPath, budget)),
1678
1678
  analyticGeometry: parseCollection("analyticGeometry", GPU_MODEL_DOCUMENT_LIMITS.analyticGeometry, (entry, entryPath) => parseAnalyticGeometry(entry, entryPath, budget)),
1679
- bounds: parseBounds(requireKey(record3, "bounds", path), "$.bounds"),
1680
- provenance: parseProvenance(requireKey(record3, "provenance", path), budget),
1679
+ bounds: parseBounds(requireKey(record4, "bounds", path), "$.bounds"),
1680
+ provenance: parseProvenance(requireKey(record4, "provenance", path), budget),
1681
1681
  diagnostics: parseCollection("diagnostics", GPU_MODEL_DOCUMENT_LIMITS.diagnostics, (entry, entryPath) => parseDiagnostic(entry, entryPath, budget)),
1682
- metadata: readMetadata(requireKey(record3, "metadata", path), "$.metadata", budget)
1682
+ metadata: readMetadata(requireKey(record4, "metadata", path), "$.metadata", budget)
1683
1683
  };
1684
1684
  if (document.meshes.length === 0) return fail("invalid-value", "$.meshes", "v1 document bounds require at least one canonical mesh");
1685
1685
  validateReferences(document, budget);
@@ -1716,8 +1716,8 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1716
1716
  value.resources.forEach((resource, index) => assertVerifiedResourceEvidence(resource, limits, `$.resources[${index}]`));
1717
1717
  return value;
1718
1718
  }
1719
- const record3 = readRecord(value, "$", DOCUMENT_KEYS);
1720
- const resourceInput = readArray(requireKey(record3, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1719
+ const record4 = readRecord(value, "$", DOCUMENT_KEYS);
1720
+ const resourceInput = readArray(requireKey(record4, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1721
1721
  const parsed = resourceInput.map((entry, index) => {
1722
1722
  if (typeof entry === "object" && entry !== null && verifiedResources.has(entry) && verifiedResourceBytes.has(entry)) return entry;
1723
1723
  return parseResourceHeader(entry, `$.resources[${index}]`);
@@ -1730,7 +1730,7 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1730
1730
  }
1731
1731
  const candidate = {};
1732
1732
  for (const key of DOCUMENT_KEYS) {
1733
- if (Object.hasOwn(record3, key)) candidate[key] = record3[key];
1733
+ if (Object.hasOwn(record4, key)) candidate[key] = record4[key];
1734
1734
  }
1735
1735
  candidate.resources = verified;
1736
1736
  return createGpuModelDocument(candidate);
@@ -1795,11 +1795,11 @@ async function hashGpuModelDocumentCanonical(document) {
1795
1795
  return Array.from(new Uint8Array(digest), (value) => value.toString(16).padStart(2, "0")).join("");
1796
1796
  }
1797
1797
  function resolveStaticDemoLimits(options) {
1798
- const record3 = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1799
- const enabled = readBoolean(requireKey(record3, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1798
+ const record4 = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1799
+ const enabled = readBoolean(requireKey(record4, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1800
1800
  if (!enabled) return fail("feature-disabled", "$.options.pvoxModelsEnabled", `${GPU_MODEL_STATIC_DEMO_FEATURE_FLAG} is disabled`);
1801
- if (!Object.hasOwn(record3, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1802
- const overrides = readRecord(record3.limits, "$.options.limits", Object.keys(GPU_MODEL_STATIC_DEMO_LIMITS));
1801
+ if (!Object.hasOwn(record4, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1802
+ const overrides = readRecord(record4.limits, "$.options.limits", Object.keys(GPU_MODEL_STATIC_DEMO_LIMITS));
1803
1803
  const limits = { ...GPU_MODEL_STATIC_DEMO_LIMITS };
1804
1804
  for (const [key, ceiling] of Object.entries(GPU_MODEL_STATIC_DEMO_LIMITS)) {
1805
1805
  if (Object.hasOwn(overrides, key)) limits[key] = readSafeInteger(overrides[key], `$.options.limits.${key}`, 1, ceiling);
@@ -2261,7 +2261,7 @@ function evaluateGpuModelDiagnostics(value) {
2261
2261
  const diagnostics = [];
2262
2262
  const repairs = [];
2263
2263
  const losses = [];
2264
- let accepted = true;
2264
+ let accepted2 = true;
2265
2265
  for (const issue of issues) {
2266
2266
  const base = { code: issue.code, path: issue.path, message: issue.message };
2267
2267
  if (issue.kind === "violation") {
@@ -2272,20 +2272,20 @@ function evaluateGpuModelDiagnostics(value) {
2272
2272
  repairs.push(Object.freeze({ ruleId: rule.id, originalValue: issue.originalValue, action: rule.action, severity: rule.severity, affectedPath: issue.path, confidence: rule.confidence }));
2273
2273
  diagnostics.push(Object.freeze({ ...base, kind: "repair", severity: rule.severity, ruleId: rule.id }));
2274
2274
  } else {
2275
- accepted = false;
2275
+ accepted2 = false;
2276
2276
  diagnostics.push(Object.freeze({ ...base, kind: "blocking-error", severity: "error", ruleId: issue.ruleId }));
2277
2277
  }
2278
2278
  } else if (issue.kind === "unsupported") {
2279
- accepted = false;
2279
+ accepted2 = false;
2280
2280
  diagnostics.push(Object.freeze({ ...issue, severity: "error" }));
2281
2281
  } else if (issue.kind === "loss") {
2282
- if (!allowLoss) accepted = false;
2282
+ if (!allowLoss) accepted2 = false;
2283
2283
  const loss = Object.freeze({ ...issue, severity: "warning" });
2284
2284
  losses.push(loss);
2285
2285
  diagnostics.push(loss);
2286
2286
  } else diagnostics.push(Object.freeze({ ...issue, severity: "warning" }));
2287
2287
  }
2288
- return Object.freeze({ mode, standard, accepted, diagnostics: Object.freeze(diagnostics), repairs: Object.freeze(repairs), losses: Object.freeze(losses), ...mode === "forensic" ? { rawSource: args.rawSource } : {} });
2288
+ return Object.freeze({ mode, standard, accepted: accepted2, diagnostics: Object.freeze(diagnostics), repairs: Object.freeze(repairs), losses: Object.freeze(losses), ...mode === "forensic" ? { rawSource: args.rawSource } : {} });
2289
2289
  }
2290
2290
 
2291
2291
  // src/resource-graph.ts
@@ -2479,6 +2479,360 @@ function validateDag(nodes) {
2479
2479
  if (queue.length !== nodes.size) fail3("graph-cycle");
2480
2480
  }
2481
2481
 
2482
+ // src/adapter-registry.ts
2483
+ var MODEL_REGISTRY_LIMITS = Object.freeze({ adapters: 128, concurrent: 8, timeoutMs: 3e4, maxTimeoutMs: 3e5, string: 4096, mapEntries: 64, manifestEntries: 4096, memoryBytes: 64 * 1024 * 1024 });
2484
+ var ModelRegistryError = class extends TypeError {
2485
+ constructor(code) {
2486
+ super(`GPU model registry: ${code}`);
2487
+ this.code = code;
2488
+ this.name = "ModelRegistryError";
2489
+ }
2490
+ code;
2491
+ };
2492
+ function fail4(code = "invalid-input") {
2493
+ throw new ModelRegistryError(code);
2494
+ }
2495
+ var sourceKinds = ["file-path", "url", "blob-storage-url", "blob", "array-buffer", "uint8-array", "stream"];
2496
+ var targetKinds = ["file-path", "blob-storage-url", "blob", "array-buffer", "uint8-array", "stream", "package"];
2497
+ var bases = /* @__PURE__ */ new WeakMap();
2498
+ function createModelResultBase(input) {
2499
+ const report = evaluateGpuModelDiagnostics(input);
2500
+ const value = Object.freeze({ report, diagnostics: report.diagnostics, warnings: Object.freeze(report.diagnostics.filter((d) => d.severity === "warning")), repairs: report.repairs, lossReports: report.losses });
2501
+ bases.set(report, value);
2502
+ return value;
2503
+ }
2504
+ function record3(value, allowed) {
2505
+ if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail4();
2506
+ const keys = Reflect.ownKeys(value);
2507
+ if (keys.length > MODEL_REGISTRY_LIMITS.mapEntries) fail4();
2508
+ const output = /* @__PURE__ */ Object.create(null);
2509
+ for (const key of keys) {
2510
+ if (typeof key !== "string" || allowed && !allowed.includes(key)) fail4();
2511
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2512
+ if (!("value" in descriptor)) fail4();
2513
+ output[key] = descriptor.value;
2514
+ }
2515
+ return output;
2516
+ }
2517
+ function text2(value) {
2518
+ if (typeof value !== "string" || !value.length || value.length > MODEL_REGISTRY_LIMITS.string || Array.from(value).some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127)) fail4();
2519
+ return value;
2520
+ }
2521
+ function format(value) {
2522
+ if (typeof value !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(value)) fail4();
2523
+ return value;
2524
+ }
2525
+ function enumeration(value, values) {
2526
+ if (!values.includes(value)) fail4();
2527
+ return value;
2528
+ }
2529
+ function boolean2(value) {
2530
+ if (typeof value !== "boolean") fail4();
2531
+ return value;
2532
+ }
2533
+ function integer(value, max, min = 0) {
2534
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max) fail4();
2535
+ return value;
2536
+ }
2537
+ function list(value, max) {
2538
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > max || Reflect.ownKeys(value).length !== value.length + 1) fail4();
2539
+ return Array.from({ length: value.length }, (_, i) => {
2540
+ const d = Object.getOwnPropertyDescriptor(value, String(i));
2541
+ if (!d || !("value" in d)) fail4();
2542
+ return d.value;
2543
+ });
2544
+ }
2545
+ function strings(value) {
2546
+ const input = record3(value);
2547
+ const out = /* @__PURE__ */ Object.create(null);
2548
+ for (const [key, entry] of Object.entries(input)) out[text2(key)] = text2(entry);
2549
+ return Object.freeze(out);
2550
+ }
2551
+ function metadata(value) {
2552
+ const v = record3(value, ["formatId", "displayName", "supportedSourceKinds", "supportedTargetKinds", "supportsAnimations", "supportsSkins", "supportsAnalyticGeometry"]);
2553
+ const kinds = (input, allowed) => {
2554
+ const values = list(input, allowed.length).map((k) => enumeration(k, allowed));
2555
+ if (new Set(values).size !== values.length) fail4();
2556
+ return Object.freeze(values);
2557
+ };
2558
+ return Object.freeze({ formatId: format(v.formatId), displayName: text2(v.displayName), supportedSourceKinds: kinds(v.supportedSourceKinds, sourceKinds), supportedTargetKinds: kinds(v.supportedTargetKinds, targetKinds), supportsAnimations: boolean2(v.supportsAnimations), supportsSkins: boolean2(v.supportsSkins), supportsAnalyticGeometry: boolean2(v.supportsAnalyticGeometry) });
2559
+ }
2560
+ function snapshotIO(input, source) {
2561
+ const v = record3(input);
2562
+ const kind = enumeration(v.kind, source ? sourceKinds : targetKinds);
2563
+ const common = source ? ["kind", "fileNameHint", "mimeTypeHint", "provenance"] : ["kind"];
2564
+ const specific = source ? {
2565
+ "file-path": ["path"],
2566
+ url: ["url", "headers"],
2567
+ "blob-storage-url": ["url", "credentialMode"],
2568
+ blob: ["blob"],
2569
+ "array-buffer": ["bytes"],
2570
+ "uint8-array": ["bytes"],
2571
+ stream: ["stream", "byteLengthHint"]
2572
+ } : {
2573
+ "file-path": ["path", "overwrite"],
2574
+ "blob-storage-url": ["url", "overwrite", "contentType", "metadata"],
2575
+ blob: ["mimeType", "fileName"],
2576
+ "array-buffer": [],
2577
+ "uint8-array": [],
2578
+ stream: ["stream", "contentType"],
2579
+ package: ["packageFormat", "destinationHint", "includeSourcePayload"]
2580
+ };
2581
+ if (Object.keys(v).some((k) => ![...common, ...specific[kind]].includes(k))) fail4();
2582
+ const out = { kind };
2583
+ for (const key of ["fileNameHint", "mimeTypeHint", "mimeType", "fileName", "contentType", "destinationHint"]) if (v[key] !== void 0) out[key] = text2(v[key]);
2584
+ for (const key of ["overwrite", "includeSourcePayload"]) if (v[key] !== void 0) out[key] = boolean2(v[key]);
2585
+ for (const key of ["headers", "metadata"]) if (v[key] !== void 0) out[key] = strings(v[key]);
2586
+ if (v.provenance !== void 0) out.provenance = strings(record3(v.provenance, ["sourceId", "sourceSystem", "acquiredAt", "licenseHint"]));
2587
+ if (v.credentialMode !== void 0) out.credentialMode = enumeration(v.credentialMode, ["anonymous", "signed", "runtime-resolved"]);
2588
+ if (v.byteLengthHint !== void 0) out.byteLengthHint = integer(v.byteLengthHint, Number.MAX_SAFE_INTEGER);
2589
+ if (kind === "file-path") out.path = text2(v.path);
2590
+ if (kind === "url" || kind === "blob-storage-url") {
2591
+ const url = text2(v.url);
2592
+ let parsed;
2593
+ try {
2594
+ parsed = new URL(url);
2595
+ } catch {
2596
+ return fail4();
2597
+ }
2598
+ if (!["https:", "http:"].includes(parsed.protocol) || parsed.username || parsed.password) fail4();
2599
+ out.url = url;
2600
+ }
2601
+ if (source && kind === "blob") {
2602
+ if (!(v.blob instanceof Blob)) fail4();
2603
+ integer(v.blob.size, MODEL_REGISTRY_LIMITS.memoryBytes);
2604
+ out.blob = v.blob;
2605
+ }
2606
+ if (source && (kind === "array-buffer" || kind === "uint8-array")) {
2607
+ if (kind === "array-buffer") {
2608
+ if (!(v.bytes instanceof ArrayBuffer)) fail4();
2609
+ integer(v.bytes.byteLength, MODEL_REGISTRY_LIMITS.memoryBytes);
2610
+ out.bytes = v.bytes.slice(0);
2611
+ } else {
2612
+ if (!(v.bytes instanceof Uint8Array) || !(v.bytes.buffer instanceof ArrayBuffer)) fail4();
2613
+ integer(v.bytes.byteLength, MODEL_REGISTRY_LIMITS.memoryBytes);
2614
+ out.bytes = new Uint8Array(v.bytes);
2615
+ }
2616
+ }
2617
+ if (kind === "stream") {
2618
+ const stream = v.stream;
2619
+ if (!stream || typeof stream !== "object") fail4();
2620
+ if (source ? !(stream instanceof ReadableStream) && typeof stream[Symbol.asyncIterator] !== "function" : !(stream instanceof WritableStream) && !("write" in stream && typeof stream.write === "function" && typeof stream.end === "function")) fail4();
2621
+ out.stream = stream;
2622
+ }
2623
+ if (kind === "package") out.packageFormat = enumeration(v.packageFormat, ["zip", "tar", "directory-manifest"]);
2624
+ return Object.freeze(out);
2625
+ }
2626
+ function manifest(input) {
2627
+ const v = record3(input, ["format", "entries"]);
2628
+ const ids = /* @__PURE__ */ new Set();
2629
+ const paths = /* @__PURE__ */ new Set();
2630
+ const entries = list(v.entries, MODEL_REGISTRY_LIMITS.manifestEntries).map((entry) => {
2631
+ const e = record3(entry, ["id", "role", "path", "contentType"]);
2632
+ const id = text2(e.id);
2633
+ const path = text2(e.path);
2634
+ if (ids.has(id) || paths.has(path.toLowerCase()) || path.split("/").some((s) => !/^[a-zA-Z0-9_][a-zA-Z0-9_.-]*$/.test(s) || /[.]$/.test(s) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:[.]|$)/i.test(s))) fail4();
2635
+ ids.add(id);
2636
+ paths.add(path.toLowerCase());
2637
+ return Object.freeze({ id, path, role: enumeration(e.role, ["mesh", "material", "texture", "animation", "auxiliary"]), ...e.contentType !== void 0 ? { contentType: text2(e.contentType) } : {} });
2638
+ });
2639
+ return Object.freeze({ format: enumeration(v.format, ["zip", "tar", "directory-manifest", "inline"]), entries: Object.freeze(entries) });
2640
+ }
2641
+ function reportBase(value, context) {
2642
+ const base = bases.get(value.report);
2643
+ if (!base || base.report.mode !== context.options.mode || ["diagnostics", "warnings", "repairs", "lossReports"].some((k) => value[k] !== base[k])) fail4("invalid-result");
2644
+ return base;
2645
+ }
2646
+ function accepted(report, context) {
2647
+ return report.accepted && (context.options.allowLoss || report.losses.length === 0);
2648
+ }
2649
+ function ledger(stages, ok, payload = {}) {
2650
+ return Object.freeze({ ...payload, accepted: ok, stages: Object.freeze([...stages]), diagnostics: Object.freeze(stages.flatMap((s) => s.report.diagnostics)), warnings: Object.freeze(stages.flatMap((s) => s.report.diagnostics.filter((d) => d.severity === "warning"))), repairs: Object.freeze(stages.flatMap((s) => s.report.repairs)), lossReports: Object.freeze(stages.flatMap((s) => s.report.losses)) });
2651
+ }
2652
+ function createModelConversionRegistry(adapters, limits = {}) {
2653
+ const registry = /* @__PURE__ */ new Map();
2654
+ const maxConcurrent = integer(record3(limits, ["maxConcurrent"]).maxConcurrent ?? MODEL_REGISTRY_LIMITS.concurrent, MODEL_REGISTRY_LIMITS.concurrent, 1);
2655
+ for (const input of list(adapters, MODEL_REGISTRY_LIMITS.adapters)) {
2656
+ let a;
2657
+ try {
2658
+ const v = record3(input, ["metadata", "inspect", "load", "export", "validate"]);
2659
+ const m = metadata(v.metadata);
2660
+ for (const name of ["inspect", "load", "export", "validate"]) if (typeof v[name] !== "function") fail4();
2661
+ const raw = input;
2662
+ a = Object.freeze({ metadata: m, inspect: raw.inspect.bind(raw), load: raw.load.bind(raw), export: raw.export.bind(raw), validate: raw.validate.bind(raw) });
2663
+ } catch {
2664
+ return fail4("invalid-adapter");
2665
+ }
2666
+ if (registry.has(a.metadata.formatId)) fail4("duplicate-format");
2667
+ registry.set(a.metadata.formatId, a);
2668
+ }
2669
+ let active = 0;
2670
+ function lookup(id) {
2671
+ const a = registry.get(format(id));
2672
+ if (!a) fail4("unsupported-format");
2673
+ return a;
2674
+ }
2675
+ function sourceFor(a, input) {
2676
+ const source = snapshotIO(input, true);
2677
+ if (!a.metadata.supportedSourceKinds.includes(source.kind)) fail4("unsupported-source");
2678
+ return source;
2679
+ }
2680
+ function targetFor(a, input) {
2681
+ const target = snapshotIO(input, false);
2682
+ if (!a.metadata.supportedTargetKinds.includes(target.kind)) fail4("unsupported-target");
2683
+ return target;
2684
+ }
2685
+ function check(context) {
2686
+ if (context.options.signal.aborted) fail4("aborted");
2687
+ if (Date.now() >= context.deadline) fail4("deadline-exceeded");
2688
+ }
2689
+ async function run(options, work) {
2690
+ const v = record3(options ?? {}, ["mode", "allowLoss", "signal", "timeoutMs"]);
2691
+ const mode = enumeration(v.mode ?? "strict", ["strict", "tolerant", "forensic"]);
2692
+ const allowLoss = v.allowLoss === void 0 ? false : boolean2(v.allowLoss);
2693
+ const timeoutMs = integer(v.timeoutMs ?? MODEL_REGISTRY_LIMITS.timeoutMs, MODEL_REGISTRY_LIMITS.maxTimeoutMs, 1);
2694
+ if (v.signal !== void 0 && !(v.signal instanceof AbortSignal)) fail4();
2695
+ const parent = v.signal;
2696
+ if (parent?.aborted) fail4("aborted");
2697
+ if (active >= maxConcurrent) fail4("capacity-exceeded");
2698
+ const controller = new AbortController();
2699
+ const context = { options: Object.freeze({ mode, allowLoss, timeoutMs, signal: controller.signal }), deadline: Date.now() + timeoutMs };
2700
+ active++;
2701
+ let timer;
2702
+ let abort;
2703
+ const pending = Promise.resolve().then(() => {
2704
+ check(context);
2705
+ return work(context);
2706
+ }).finally(() => {
2707
+ active--;
2708
+ });
2709
+ const stopped = new Promise((_, reject) => {
2710
+ abort = () => {
2711
+ controller.abort();
2712
+ reject(new ModelRegistryError("aborted"));
2713
+ };
2714
+ parent?.addEventListener("abort", abort, { once: true });
2715
+ timer = setTimeout(() => {
2716
+ controller.abort();
2717
+ reject(new ModelRegistryError("deadline-exceeded"));
2718
+ }, timeoutMs);
2719
+ });
2720
+ try {
2721
+ return await Promise.race([pending, stopped]);
2722
+ } catch (error) {
2723
+ if (error instanceof ModelRegistryError) throw error;
2724
+ return fail4();
2725
+ } finally {
2726
+ clearTimeout(timer);
2727
+ if (abort) parent?.removeEventListener("abort", abort);
2728
+ }
2729
+ }
2730
+ async function invoke(a, operation, context, call, stages) {
2731
+ check(context);
2732
+ let raw;
2733
+ try {
2734
+ raw = await call();
2735
+ } catch {
2736
+ return fail4("adapter-failed");
2737
+ }
2738
+ check(context);
2739
+ try {
2740
+ const v = record3(raw, ["report", "diagnostics", "warnings", "repairs", "lossReports", ...{ inspect: ["detectedFormat", "capabilities"], load: ["document", "resourcePackage"], validate: ["valid"], export: ["target", "resourcePackage", "output"] }[operation]]);
2741
+ const base = reportBase(v, context);
2742
+ const result = { ...v, ...base };
2743
+ if (v.resourcePackage !== void 0) result.resourcePackage = manifest(v.resourcePackage);
2744
+ stages.push(Object.freeze({ formatId: a.metadata.formatId, operation, report: base.report }));
2745
+ return result;
2746
+ } catch {
2747
+ return fail4("invalid-result");
2748
+ }
2749
+ }
2750
+ async function load(a, source, c, stages) {
2751
+ const v = await invoke(a, "load", c, () => a.load(source, c.options), stages);
2752
+ if (v.document !== void 0 && !isGpuModelDocument(v.document)) fail4("invalid-result");
2753
+ if (accepted(v.report, c) && !isGpuModelDocument(v.document)) fail4("invalid-result");
2754
+ return Object.freeze(v);
2755
+ }
2756
+ async function validate(a, input, c, stages) {
2757
+ const v = await invoke(a, "validate", c, () => a.validate(input, c.options), stages);
2758
+ if (typeof v.valid !== "boolean") fail4("invalid-result");
2759
+ return v.valid && accepted(v.report, c);
2760
+ }
2761
+ async function exportDocument(a, document, target, c, stages) {
2762
+ const issues = [];
2763
+ for (const [supported, present, path] of [[a.metadata.supportsAnimations, document.animations.length, "/animations"], [a.metadata.supportsSkins, document.skins.length + document.skeletons.length + document.joints.length, "/skins"], [a.metadata.supportsAnalyticGeometry, document.analyticGeometry.length, "/analyticGeometry"]]) {
2764
+ if (!supported && present) issues.push({ kind: "loss", code: "TARGET_CAPABILITY_LOSS", path, message: "Target cannot preserve canonical semantics", effect: "dropped" });
2765
+ }
2766
+ const report = evaluateGpuModelDiagnostics({ standard: "gltf-glb", mode: c.options.mode, allowLoss: c.options.allowLoss, issues, ...c.options.mode === "forensic" ? { rawSource: null } : {} });
2767
+ stages.push(Object.freeze({ formatId: a.metadata.formatId, operation: "capabilities", report }));
2768
+ if (!accepted(report, c) || !await validate(a, document, c, stages)) return ledger(stages, false, { document });
2769
+ const v = await invoke(a, "export", c, () => a.export(document, target, c.options), stages);
2770
+ if (v.target !== target.kind) fail4("invalid-result");
2771
+ try {
2772
+ if (v.output !== void 0) {
2773
+ const output = v.output;
2774
+ if (target.kind === "blob" && output instanceof Blob) {
2775
+ integer(output.size, MODEL_REGISTRY_LIMITS.memoryBytes);
2776
+ } else if (target.kind === "array-buffer" && output instanceof ArrayBuffer) {
2777
+ integer(output.byteLength, MODEL_REGISTRY_LIMITS.memoryBytes);
2778
+ v.output = output.slice(0);
2779
+ } else if (target.kind === "uint8-array" && output instanceof Uint8Array && output.buffer instanceof ArrayBuffer) {
2780
+ integer(output.byteLength, MODEL_REGISTRY_LIMITS.memoryBytes);
2781
+ v.output = new Uint8Array(output);
2782
+ } else fail4();
2783
+ }
2784
+ } catch {
2785
+ return fail4("invalid-result");
2786
+ }
2787
+ const exported = Object.freeze(v);
2788
+ return ledger(stages, accepted(exported.report, c), { document, exported, ...exported.resourcePackage ? { resourcePackage: exported.resourcePackage } : {} });
2789
+ }
2790
+ const api = {
2791
+ capabilities: Object.freeze([...registry.values()].map((a) => a.metadata)),
2792
+ inspect: (id, input, options) => run(options, async (c) => {
2793
+ const a = lookup(id);
2794
+ const source = sourceFor(a, input);
2795
+ const stages = [];
2796
+ const v = await invoke(a, "inspect", c, () => a.inspect(source, c.options), stages);
2797
+ try {
2798
+ if (v.detectedFormat !== void 0 && format(v.detectedFormat) !== a.metadata.formatId) fail4();
2799
+ if (JSON.stringify(metadata(v.capabilities)) !== JSON.stringify(a.metadata)) fail4();
2800
+ } catch {
2801
+ return fail4("invalid-result");
2802
+ }
2803
+ return ledger(stages, accepted(v.report, c), { inspection: Object.freeze({ ...v, capabilities: a.metadata }) });
2804
+ }),
2805
+ load: (id, input, options) => run(options, async (c) => {
2806
+ const a = lookup(id);
2807
+ const stages = [];
2808
+ const result = await load(a, sourceFor(a, input), c, stages);
2809
+ return ledger(stages, accepted(result.report, c), { ...result.document ? { document: result.document } : {}, ...result.resourcePackage ? { resourcePackage: result.resourcePackage } : {} });
2810
+ }),
2811
+ validate: (id, input, options) => run(options, async (c) => {
2812
+ const a = lookup(id);
2813
+ const stages = [];
2814
+ return ledger(stages, await validate(a, isGpuModelDocument(input) ? input : sourceFor(a, input), c, stages));
2815
+ }),
2816
+ export: (id, document, input, options) => run(options, async (c) => {
2817
+ const a = lookup(id);
2818
+ if (!isGpuModelDocument(document)) fail4();
2819
+ return exportDocument(a, document, targetFor(a, input), c, []);
2820
+ }),
2821
+ convert: (request, options) => run(options, async (c) => {
2822
+ const v = record3(request, ["sourceFormat", "targetFormat", "source", "target"]);
2823
+ const from = lookup(v.sourceFormat);
2824
+ const to = lookup(v.targetFormat);
2825
+ const source = sourceFor(from, v.source);
2826
+ const target = targetFor(to, v.target);
2827
+ const stages = [];
2828
+ const result = await load(from, source, c, stages);
2829
+ if (!accepted(result.report, c)) return ledger(stages, false, { ...result.resourcePackage ? { resourcePackage: result.resourcePackage } : {} });
2830
+ return exportDocument(to, result.document, target, c, stages);
2831
+ })
2832
+ };
2833
+ return Object.freeze(api);
2834
+ }
2835
+
2482
2836
  // src/index.ts
2483
2837
  var packageName = "@plasius/gpu-model-core";
2484
2838
  var packageBootstrap = Object.freeze({
@@ -2505,12 +2859,16 @@ export {
2505
2859
  GpuModelDiagnosticsError,
2506
2860
  GpuModelDocumentError,
2507
2861
  GpuModelResourceGraphError,
2862
+ MODEL_REGISTRY_LIMITS,
2863
+ ModelRegistryError,
2508
2864
  assertGpuModelStaticDemoNormalization,
2509
2865
  canCreateGpuModelDocument,
2510
2866
  createAndVerifyGpuModelDocument,
2511
2867
  createGpuModelDocument,
2512
2868
  createGpuModelResourceGraph,
2513
2869
  createGpuModelStaticDemoCompilerInput,
2870
+ createModelConversionRegistry,
2871
+ createModelResultBase,
2514
2872
  encodeGpuModelDocumentCanonical,
2515
2873
  evaluateGpuModelDiagnostics,
2516
2874
  gpuModelResourceKey,