@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.cjs CHANGED
@@ -38,12 +38,16 @@ __export(index_exports, {
38
38
  GpuModelDiagnosticsError: () => GpuModelDiagnosticsError,
39
39
  GpuModelDocumentError: () => GpuModelDocumentError,
40
40
  GpuModelResourceGraphError: () => GpuModelResourceGraphError,
41
+ MODEL_REGISTRY_LIMITS: () => MODEL_REGISTRY_LIMITS,
42
+ ModelRegistryError: () => ModelRegistryError,
41
43
  assertGpuModelStaticDemoNormalization: () => assertGpuModelStaticDemoNormalization,
42
44
  canCreateGpuModelDocument: () => canCreateGpuModelDocument,
43
45
  createAndVerifyGpuModelDocument: () => createAndVerifyGpuModelDocument,
44
46
  createGpuModelDocument: () => createGpuModelDocument,
45
47
  createGpuModelResourceGraph: () => createGpuModelResourceGraph,
46
48
  createGpuModelStaticDemoCompilerInput: () => createGpuModelStaticDemoCompilerInput,
49
+ createModelConversionRegistry: () => createModelConversionRegistry,
50
+ createModelResultBase: () => createModelResultBase,
47
51
  encodeGpuModelDocumentCanonical: () => encodeGpuModelDocumentCanonical,
48
52
  evaluateGpuModelDiagnostics: () => evaluateGpuModelDiagnostics,
49
53
  gpuModelResourceKey: () => gpuModelResourceKey,
@@ -230,9 +234,9 @@ function readRecord(value, path, allowedKeys) {
230
234
  }
231
235
  return value;
232
236
  }
233
- function requireKey(record3, key, path) {
234
- if (!Object.hasOwn(record3, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
235
- return record3[key];
237
+ function requireKey(record4, key, path) {
238
+ if (!Object.hasOwn(record4, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
239
+ return record4[key];
236
240
  }
237
241
  function readArray(value, path, maximum, minimum = 0) {
238
242
  if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
@@ -311,9 +315,9 @@ function readNumberTuple(value, path, length, minimum, maximum) {
311
315
  const input = readArray(value, path, length, length);
312
316
  return Object.freeze(input.map((entry, index) => readFinite(entry, `${path}[${index}]`, minimum, maximum)));
313
317
  }
314
- function readOptional(record3, key, parser, path) {
315
- if (!Object.hasOwn(record3, key)) return void 0;
316
- return parser(record3[key], `${path}.${key}`);
318
+ function readOptional(record4, key, parser, path) {
319
+ if (!Object.hasOwn(record4, key)) return void 0;
320
+ return parser(record4[key], `${path}.${key}`);
317
321
  }
318
322
  function assignOptional(target, key, value) {
319
323
  if (value !== void 0) Object.assign(target, { [key]: value });
@@ -335,17 +339,17 @@ function readMetadataValue(value, path, depth, budget) {
335
339
  return Object.freeze(input.map((entry, index) => readMetadataValue(entry, `${path}[${index}]`, depth + 1, budget)));
336
340
  }
337
341
  if (typeof value !== "object" || value === null) return fail("invalid-type", path, "metadata value is not JSON-compatible");
338
- const record3 = value;
339
- const prototype = Object.getPrototypeOf(record3);
342
+ const record4 = value;
343
+ const prototype = Object.getPrototypeOf(record4);
340
344
  if (prototype !== Object.prototype && prototype !== null) return fail("invalid-type", path, "metadata objects must be plain objects");
341
- const keys = Reflect.ownKeys(record3);
345
+ const keys = Reflect.ownKeys(record4);
342
346
  if (keys.length > GPU_MODEL_DOCUMENT_LIMITS.metadataObjectKeys) return fail("limit-exceeded", path, "metadata object has too many keys");
343
347
  const output = {};
344
348
  for (const key of keys) {
345
349
  if (typeof key !== "string") return fail("unknown-key", path, "metadata symbol keys are not permitted");
346
350
  if (PROHIBITED_KEYS.has(key)) return fail("unknown-key", `${path}.${key}`, "prohibited metadata key");
347
351
  readString(key, `${path}.[key]`, GPU_MODEL_DOCUMENT_LIMITS.metadataKeyLength, METADATA_KEY);
348
- const descriptor = Object.getOwnPropertyDescriptor(record3, key);
352
+ const descriptor = Object.getOwnPropertyDescriptor(record4, key);
349
353
  if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) return fail("invalid-type", `${path}.${key}`, "metadata properties must be enumerable data properties");
350
354
  output[key] = readMetadataValue(descriptor.value, `${path}.${key}`, depth + 1, budget);
351
355
  }
@@ -358,9 +362,9 @@ function readMetadata(value, path, budget) {
358
362
  }
359
363
  function parseCoordinateSystem(value) {
360
364
  const path = "$.coordinateSystem";
361
- const record3 = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
365
+ const record4 = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
362
366
  for (const [key, expected] of Object.entries(CANONICAL_GPU_MODEL_COORDINATE_SYSTEM)) {
363
- if (requireKey(record3, key, path) !== expected) return fail("invalid-value", `${path}.${key}`, "coordinate system is not canonical");
367
+ if (requireKey(record4, key, path) !== expected) return fail("invalid-value", `${path}.${key}`, "coordinate system is not canonical");
364
368
  }
365
369
  return CANONICAL_GPU_MODEL_COORDINATE_SYSTEM;
366
370
  }
@@ -372,19 +376,19 @@ function parseMatrix(value, path, requireAffine = true) {
372
376
  return matrix;
373
377
  }
374
378
  function parseBounds(value, path) {
375
- const record3 = readRecord(value, path, ["min", "max"]);
376
- const min = readNumberTuple(requireKey(record3, "min", path), `${path}.min`, 3);
377
- const max = readNumberTuple(requireKey(record3, "max", path), `${path}.max`, 3);
379
+ const record4 = readRecord(value, path, ["min", "max"]);
380
+ const min = readNumberTuple(requireKey(record4, "min", path), `${path}.min`, 3);
381
+ const max = readNumberTuple(requireKey(record4, "max", path), `${path}.max`, 3);
378
382
  for (let index = 0; index < 3; index += 1) {
379
383
  if (min[index] > max[index]) return fail("invalid-value", path, "bounds min cannot exceed max");
380
384
  }
381
385
  return { min, max };
382
386
  }
383
387
  function parseTransformTrs(value, path) {
384
- const record3 = readRecord(value, path, ["translation", "rotation", "scale"]);
385
- const translation = readNumberTuple(requireKey(record3, "translation", path), `${path}.translation`, 3);
386
- const rotation = readNumberTuple(requireKey(record3, "rotation", path), `${path}.rotation`, 4);
387
- const scale = readNumberTuple(requireKey(record3, "scale", path), `${path}.scale`, 3);
388
+ const record4 = readRecord(value, path, ["translation", "rotation", "scale"]);
389
+ const translation = readNumberTuple(requireKey(record4, "translation", path), `${path}.translation`, 3);
390
+ const rotation = readNumberTuple(requireKey(record4, "rotation", path), `${path}.rotation`, 4);
391
+ const scale = readNumberTuple(requireKey(record4, "scale", path), `${path}.scale`, 3);
388
392
  const magnitude = Math.hypot(...rotation);
389
393
  if (!almostEqual(magnitude, 1, 1e-4)) return fail("invalid-value", `${path}.rotation`, "rotation quaternion must be normalized");
390
394
  return { translation, rotation, scale };
@@ -406,25 +410,25 @@ function intrinsicBlobArrayBuffer(blob) {
406
410
  return Reflect.apply(blobArrayBuffer, blob, []);
407
411
  }
408
412
  function parseResourceHeader(value, path) {
409
- const record3 = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
410
- const kind = readEnum(requireKey(record3, "kind", path), `${path}.kind`, ["buffer", "image"]);
411
- const mimeType = readString(requireKey(record3, "mimeType", path), `${path}.mimeType`, 128, MIME_TYPE).toLowerCase();
413
+ const record4 = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
414
+ const kind = readEnum(requireKey(record4, "kind", path), `${path}.kind`, ["buffer", "image"]);
415
+ const mimeType = readString(requireKey(record4, "mimeType", path), `${path}.mimeType`, 128, MIME_TYPE).toLowerCase();
412
416
  if (kind === "image" ? !IMAGE_MIME_TYPES.has(mimeType) : !BUFFER_MIME_TYPES.has(mimeType)) {
413
417
  return fail("invalid-value", `${path}.mimeType`, `unsupported ${kind} MIME type`);
414
418
  }
415
- const payload = requireKey(record3, "payload", path);
419
+ const payload = requireKey(record4, "payload", path);
416
420
  if (!(payload instanceof Blob)) return fail("invalid-type", `${path}.payload`, "expected an immutable Blob payload");
417
421
  if (Object.getPrototypeOf(payload) !== Blob.prototype || Reflect.ownKeys(payload).some((key) => typeof key === "string")) {
418
422
  return fail("invalid-type", `${path}.payload`, "expected a native Blob without caller-defined properties");
419
423
  }
420
- const byteLength = readSafeInteger(requireKey(record3, "byteLength", path), `${path}.byteLength`, 1);
424
+ const byteLength = readSafeInteger(requireKey(record4, "byteLength", path), `${path}.byteLength`, 1);
421
425
  if (intrinsicBlobSize(payload) !== byteLength) return fail("payload-length-mismatch", `${path}.payload`, "payload byte length does not match byteLength");
422
426
  const payloadType = intrinsicBlobType(payload);
423
427
  if (payloadType && payloadType.toLowerCase() !== mimeType) return fail("invalid-value", `${path}.payload`, "payload MIME type does not match mimeType");
424
428
  return {
425
- id: readId(requireKey(record3, "id", path), `${path}.id`),
429
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
426
430
  kind,
427
- contentHash: readSha256(requireKey(record3, "contentHash", path), `${path}.contentHash`),
431
+ contentHash: readSha256(requireKey(record4, "contentHash", path), `${path}.contentHash`),
428
432
  byteLength,
429
433
  mimeType,
430
434
  payload
@@ -433,11 +437,11 @@ function parseResourceHeader(value, path) {
433
437
  function resolveVerificationLimits(options) {
434
438
  const overrides = options?.limits;
435
439
  if (overrides === void 0) return GPU_MODEL_RESOURCE_VERIFICATION_LIMITS;
436
- const record3 = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
440
+ const record4 = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
437
441
  const output = { ...GPU_MODEL_RESOURCE_VERIFICATION_LIMITS };
438
442
  for (const [key, ceiling] of Object.entries(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS)) {
439
- if (!Object.hasOwn(record3, key)) continue;
440
- const value = readSafeInteger(record3[key], `$.verification.limits.${key}`, 1, ceiling);
443
+ if (!Object.hasOwn(record4, key)) continue;
444
+ const value = readSafeInteger(record4[key], `$.verification.limits.${key}`, 1, ceiling);
441
445
  Object.assign(output, { [key]: value });
442
446
  }
443
447
  if (output.maxImageBytes > output.maxResourceBytes) output.maxImageBytes = output.maxResourceBytes;
@@ -549,13 +553,13 @@ function createVerificationDeadline(timeoutMs, callerSignal, path) {
549
553
  };
550
554
  }
551
555
  function parseInspection(value, path) {
552
- const record3 = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
556
+ const record4 = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
553
557
  const output = {
554
- valid: readBoolean(requireKey(record3, "valid", path), `${path}.valid`),
555
- detectedMimeType: readString(requireKey(record3, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
558
+ valid: readBoolean(requireKey(record4, "valid", path), `${path}.valid`),
559
+ detectedMimeType: readString(requireKey(record4, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
556
560
  };
557
- assignOptional(output, "width", readOptional(record3, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
558
- assignOptional(output, "height", readOptional(record3, "height", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
561
+ assignOptional(output, "width", readOptional(record4, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
562
+ assignOptional(output, "height", readOptional(record4, "height", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
559
563
  return output;
560
564
  }
561
565
  function bytesStartWith(bytes, signature) {
@@ -670,62 +674,62 @@ async function verifyGpuModelResource(value, port, options) {
670
674
  }
671
675
  }
672
676
  function parseNode(value, path, budget) {
673
- const record3 = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
674
- const children = Object.freeze(readArray(requireKey(record3, "children", path), `${path}.children`, GPU_MODEL_DOCUMENT_LIMITS.nodes).map((entry, index) => readId(entry, `${path}.children[${index}]`)));
677
+ const record4 = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
678
+ const children = Object.freeze(readArray(requireKey(record4, "children", path), `${path}.children`, GPU_MODEL_DOCUMENT_LIMITS.nodes).map((entry, index) => readId(entry, `${path}.children[${index}]`)));
675
679
  consumeBudget(budget, "sceneEdges", children.length, GPU_MODEL_DOCUMENT_LIMITS.sceneEdges, path, "scene edge");
676
680
  ensureUnique(children, `${path}.children`, "duplicate child node");
677
681
  const output = {
678
- id: readId(requireKey(record3, "id", path), `${path}.id`),
682
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
679
683
  children,
680
- localMatrix: parseMatrix(requireKey(record3, "localMatrix", path), `${path}.localMatrix`)
684
+ localMatrix: parseMatrix(requireKey(record4, "localMatrix", path), `${path}.localMatrix`)
681
685
  };
682
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
683
- assignOptional(output, "meshId", readOptional(record3, "meshId", readId, path));
684
- assignOptional(output, "skinId", readOptional(record3, "skinId", readId, path));
685
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
686
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
687
+ assignOptional(output, "meshId", readOptional(record4, "meshId", readId, path));
688
+ assignOptional(output, "skinId", readOptional(record4, "skinId", readId, path));
689
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
686
690
  return output;
687
691
  }
688
692
  function parseAccessor(value, path, budget) {
689
- const record3 = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
690
- const componentType = readEnum(requireKey(record3, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
691
- const elementType = readEnum(requireKey(record3, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
692
- const normalized = readOptional(record3, "normalized", readBoolean, path);
693
+ const record4 = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
694
+ const componentType = readEnum(requireKey(record4, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
695
+ const elementType = readEnum(requireKey(record4, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
696
+ const normalized = readOptional(record4, "normalized", readBoolean, path);
693
697
  if (normalized === true && componentType === "f32") return fail("invalid-value", `${path}.normalized`, "floating-point accessors cannot be normalized");
694
698
  if (elementType.startsWith("mat") && componentType !== "f32") return fail("invalid-value", `${path}.componentType`, "matrix accessors must use f32 components");
695
699
  const expectedComponents = ELEMENT_COMPONENTS[elementType];
696
- const min = readOptional(record3, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
697
- const max = readOptional(record3, "max", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
700
+ const min = readOptional(record4, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
701
+ const max = readOptional(record4, "max", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
698
702
  if (min === void 0 !== (max === void 0)) return fail("invalid-value", path, "accessor min and max must be supplied together");
699
703
  if (min && max) {
700
- const [componentMinimum, componentMaximum, integer] = COMPONENT_RANGES[componentType];
704
+ const [componentMinimum, componentMaximum, integer2] = COMPONENT_RANGES[componentType];
701
705
  for (let index = 0; index < expectedComponents; index += 1) {
702
706
  const minimum = min[index];
703
707
  const maximum = max[index];
704
708
  if (minimum > maximum) return fail("invalid-value", path, "accessor min cannot exceed max");
705
- if (minimum < componentMinimum || maximum > componentMaximum || integer && (!Number.isInteger(minimum) || !Number.isInteger(maximum))) {
709
+ if (minimum < componentMinimum || maximum > componentMaximum || integer2 && (!Number.isInteger(minimum) || !Number.isInteger(maximum))) {
706
710
  return fail("invalid-value", path, "accessor bounds are outside the component representation");
707
711
  }
708
712
  }
709
713
  }
710
- const count = readSafeInteger(requireKey(record3, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
714
+ const count = readSafeInteger(requireKey(record4, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
711
715
  consumeBudget(budget, "accessorElements", count * expectedComponents, GPU_MODEL_DOCUMENT_LIMITS.accessorElements, path, "accessor element component");
712
716
  const output = {
713
- id: readId(requireKey(record3, "id", path), `${path}.id`),
714
- resourceId: readId(requireKey(record3, "resourceId", path), `${path}.resourceId`),
715
- byteOffset: readSafeInteger(requireKey(record3, "byteOffset", path), `${path}.byteOffset`),
717
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
718
+ resourceId: readId(requireKey(record4, "resourceId", path), `${path}.resourceId`),
719
+ byteOffset: readSafeInteger(requireKey(record4, "byteOffset", path), `${path}.byteOffset`),
716
720
  count,
717
721
  componentType,
718
722
  elementType
719
723
  };
720
- assignOptional(output, "byteStride", readOptional(record3, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
724
+ assignOptional(output, "byteStride", readOptional(record4, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
721
725
  assignOptional(output, "normalized", normalized);
722
726
  assignOptional(output, "min", min);
723
727
  assignOptional(output, "max", max);
724
728
  return output;
725
729
  }
726
730
  function parsePrimitive(value, path, budget) {
727
- const record3 = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
728
- const attributesInput = readArray(requireKey(record3, "attributes", path), `${path}.attributes`, 64, 1);
731
+ const record4 = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
732
+ const attributesInput = readArray(requireKey(record4, "attributes", path), `${path}.attributes`, 64, 1);
729
733
  consumeBudget(budget, "primitiveAttributes", attributesInput.length, GPU_MODEL_DOCUMENT_LIMITS.primitiveAttributes, path, "primitive attribute");
730
734
  const attributes = Object.freeze(attributesInput.map((entry, index) => {
731
735
  const entryPath = `${path}.attributes[${index}]`;
@@ -738,53 +742,53 @@ function parsePrimitive(value, path, budget) {
738
742
  ensureUnique(attributes.map(({ semantic }) => semantic), `${path}.attributes`, "duplicate attribute semantic");
739
743
  if (!attributes.some(({ semantic }) => semantic === "POSITION")) return fail("invalid-value", `${path}.attributes`, "primitive requires POSITION");
740
744
  const output = {
741
- id: readId(requireKey(record3, "id", path), `${path}.id`),
742
- topology: readEnum(requireKey(record3, "topology", path), `${path}.topology`, ["points", "lines", "line-strip", "triangles", "triangle-strip"]),
745
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
746
+ topology: readEnum(requireKey(record4, "topology", path), `${path}.topology`, ["points", "lines", "line-strip", "triangles", "triangle-strip"]),
743
747
  attributes
744
748
  };
745
- assignOptional(output, "indicesAccessorId", readOptional(record3, "indicesAccessorId", readId, path));
746
- assignOptional(output, "materialId", readOptional(record3, "materialId", readId, path));
747
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
749
+ assignOptional(output, "indicesAccessorId", readOptional(record4, "indicesAccessorId", readId, path));
750
+ assignOptional(output, "materialId", readOptional(record4, "materialId", readId, path));
751
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
748
752
  return output;
749
753
  }
750
754
  function parseMesh(value, path, budget) {
751
- const record3 = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
752
- const input = readArray(requireKey(record3, "primitives", path), `${path}.primitives`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
755
+ const record4 = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
756
+ const input = readArray(requireKey(record4, "primitives", path), `${path}.primitives`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
753
757
  consumeBudget(budget, "primitives", input.length, GPU_MODEL_DOCUMENT_LIMITS.primitives, path, "primitive");
754
758
  const output = {
755
- id: readId(requireKey(record3, "id", path), `${path}.id`),
759
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
756
760
  primitives: Object.freeze(input.map((entry, index) => parsePrimitive(entry, `${path}.primitives[${index}]`, budget)))
757
761
  };
758
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
759
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
762
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
763
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
760
764
  return output;
761
765
  }
762
766
  function parseTextureTransform(value, path) {
763
- const record3 = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
764
- if (Reflect.ownKeys(record3).length === 0) return fail("invalid-value", path, "texture transform cannot be empty");
767
+ const record4 = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
768
+ if (Reflect.ownKeys(record4).length === 0) return fail("invalid-value", path, "texture transform cannot be empty");
765
769
  const output = {};
766
- assignOptional(output, "offset", readOptional(record3, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
767
- assignOptional(output, "rotationRadians", readOptional(record3, "rotationRadians", readFinite, path));
768
- assignOptional(output, "scale", readOptional(record3, "scale", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
770
+ assignOptional(output, "offset", readOptional(record4, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
771
+ assignOptional(output, "rotationRadians", readOptional(record4, "rotationRadians", readFinite, path));
772
+ assignOptional(output, "scale", readOptional(record4, "scale", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
769
773
  return output;
770
774
  }
771
775
  function parseTextureBinding(value, path) {
772
- const record3 = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
776
+ const record4 = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
773
777
  const output = {
774
- textureId: readId(requireKey(record3, "textureId", path), `${path}.textureId`),
775
- colorSpace: readEnum(requireKey(record3, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
776
- intendedUsage: readEnum(requireKey(record3, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES)
778
+ textureId: readId(requireKey(record4, "textureId", path), `${path}.textureId`),
779
+ colorSpace: readEnum(requireKey(record4, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
780
+ intendedUsage: readEnum(requireKey(record4, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES)
777
781
  };
778
- assignOptional(output, "samplerId", readOptional(record3, "samplerId", readId, path));
779
- assignOptional(output, "texCoordSet", readOptional(record3, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
780
- assignOptional(output, "transform", readOptional(record3, "transform", parseTextureTransform, path));
782
+ assignOptional(output, "samplerId", readOptional(record4, "samplerId", readId, path));
783
+ assignOptional(output, "texCoordSet", readOptional(record4, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
784
+ assignOptional(output, "transform", readOptional(record4, "transform", parseTextureTransform, path));
781
785
  return output;
782
786
  }
783
787
  function parseMaterialTextures(value, path) {
784
788
  const slots = Object.keys(MATERIAL_TEXTURE_SLOTS);
785
- const record3 = readRecord(value, path, slots);
789
+ const record4 = readRecord(value, path, slots);
786
790
  const output = {};
787
- for (const slot of slots) assignOptional(output, slot, readOptional(record3, slot, parseTextureBinding, path));
791
+ for (const slot of slots) assignOptional(output, slot, readOptional(record4, slot, parseTextureBinding, path));
788
792
  return output;
789
793
  }
790
794
  function parseMaterial(value, path, budget) {
@@ -810,138 +814,138 @@ function parseMaterial(value, path, budget) {
810
814
  "extensions",
811
815
  "sourceMetadata"
812
816
  ];
813
- const record3 = readRecord(value, path, keys);
814
- const alphaMode = readEnum(requireKey(record3, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
815
- const alphaCutoff = readOptional(record3, "alphaCutoff", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path);
817
+ const record4 = readRecord(value, path, keys);
818
+ const alphaMode = readEnum(requireKey(record4, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
819
+ const alphaCutoff = readOptional(record4, "alphaCutoff", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path);
816
820
  if (alphaMode === "mask" && alphaCutoff === void 0) return fail("invalid-value", `${path}.alphaCutoff`, "mask materials require alphaCutoff");
817
821
  if (alphaMode !== "mask" && alphaCutoff !== void 0) return fail("invalid-value", `${path}.alphaCutoff`, "alphaCutoff is only valid for mask materials");
818
822
  const output = {
819
- id: readId(requireKey(record3, "id", path), `${path}.id`),
820
- workflow: readEnum(requireKey(record3, "workflow", path), `${path}.workflow`, ["metallic-roughness", "specular-glossiness", "unlit", "custom"]),
823
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
824
+ workflow: readEnum(requireKey(record4, "workflow", path), `${path}.workflow`, ["metallic-roughness", "specular-glossiness", "unlit", "custom"]),
821
825
  alphaMode,
822
- baseColorFactor: readNumberTuple(requireKey(record3, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
823
- textures: parseMaterialTextures(requireKey(record3, "textures", path), `${path}.textures`),
824
- extensions: readMetadata(requireKey(record3, "extensions", path), `${path}.extensions`, budget),
825
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
826
+ baseColorFactor: readNumberTuple(requireKey(record4, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
827
+ textures: parseMaterialTextures(requireKey(record4, "textures", path), `${path}.textures`),
828
+ extensions: readMetadata(requireKey(record4, "extensions", path), `${path}.extensions`, budget),
829
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
826
830
  };
827
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
831
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
828
832
  assignOptional(output, "alphaCutoff", alphaCutoff);
829
- assignOptional(output, "doubleSided", readOptional(record3, "doubleSided", readBoolean, path));
833
+ assignOptional(output, "doubleSided", readOptional(record4, "doubleSided", readBoolean, path));
830
834
  for (const key of ["metallicFactor", "roughnessFactor", "transmissionFactor", "clearcoatFactor", "clearcoatRoughnessFactor", "sheenRoughnessFactor", "occlusionStrength"]) {
831
- assignOptional(output, key, readOptional(record3, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
835
+ assignOptional(output, key, readOptional(record4, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
832
836
  }
833
- assignOptional(output, "normalScale", readOptional(record3, "normalScale", readFinite, path));
834
- assignOptional(output, "emissiveFactor", readOptional(record3, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
835
- assignOptional(output, "sheenColorFactor", readOptional(record3, "sheenColorFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0, 1), path));
837
+ assignOptional(output, "normalScale", readOptional(record4, "normalScale", readFinite, path));
838
+ assignOptional(output, "emissiveFactor", readOptional(record4, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
839
+ assignOptional(output, "sheenColorFactor", readOptional(record4, "sheenColorFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0, 1), path));
836
840
  return output;
837
841
  }
838
842
  function parseTextureSampler(value, path) {
839
- const record3 = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
840
- const output = { id: readId(requireKey(record3, "id", path), `${path}.id`) };
841
- assignOptional(output, "wrapS", readOptional(record3, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
842
- assignOptional(output, "wrapT", readOptional(record3, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
843
- assignOptional(output, "minFilter", readOptional(record3, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
844
- assignOptional(output, "magFilter", readOptional(record3, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
845
- assignOptional(output, "mipFilter", readOptional(record3, "mipFilter", (entry, entryPath) => readEnum(entry, entryPath, ["nearest", "linear"]), path));
843
+ const record4 = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
844
+ const output = { id: readId(requireKey(record4, "id", path), `${path}.id`) };
845
+ assignOptional(output, "wrapS", readOptional(record4, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
846
+ assignOptional(output, "wrapT", readOptional(record4, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
847
+ assignOptional(output, "minFilter", readOptional(record4, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
848
+ assignOptional(output, "magFilter", readOptional(record4, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
849
+ assignOptional(output, "mipFilter", readOptional(record4, "mipFilter", (entry, entryPath) => readEnum(entry, entryPath, ["nearest", "linear"]), path));
846
850
  return output;
847
851
  }
848
852
  function parseTexture(value, path, budget) {
849
- const record3 = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
850
- 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)));
853
+ const record4 = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
854
+ 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)));
851
855
  ensureUnique(usages, `${path}.intendedUsage`, "duplicate intended texture usage");
852
856
  const output = {
853
- id: readId(requireKey(record3, "id", path), `${path}.id`),
854
- contentHash: readSha256(requireKey(record3, "contentHash", path), `${path}.contentHash`),
855
- imageResourceId: readId(requireKey(record3, "imageResourceId", path), `${path}.imageResourceId`),
857
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
858
+ contentHash: readSha256(requireKey(record4, "contentHash", path), `${path}.contentHash`),
859
+ imageResourceId: readId(requireKey(record4, "imageResourceId", path), `${path}.imageResourceId`),
856
860
  intendedUsage: usages,
857
- colorSpace: readEnum(requireKey(record3, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
858
- exportEmbeddingPolicy: readEnum(requireKey(record3, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
859
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
861
+ colorSpace: readEnum(requireKey(record4, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
862
+ exportEmbeddingPolicy: readEnum(requireKey(record4, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
863
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
860
864
  };
861
- assignOptional(output, "fileName", readOptional(record3, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
862
- assignOptional(output, "relativePath", readOptional(record3, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
863
- assignOptional(output, "mimeType", readOptional(record3, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
864
- assignOptional(output, "byteLength", readOptional(record3, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
865
- assignOptional(output, "sampler", readOptional(record3, "sampler", parseTextureSampler, path));
865
+ assignOptional(output, "fileName", readOptional(record4, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
866
+ assignOptional(output, "relativePath", readOptional(record4, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
867
+ assignOptional(output, "mimeType", readOptional(record4, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
868
+ assignOptional(output, "byteLength", readOptional(record4, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
869
+ assignOptional(output, "sampler", readOptional(record4, "sampler", parseTextureSampler, path));
866
870
  return output;
867
871
  }
868
872
  function parseSkeleton(value, path, budget) {
869
- const record3 = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
870
- 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}]`)));
871
- 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}]`)));
873
+ const record4 = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
874
+ 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}]`)));
875
+ 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}]`)));
872
876
  consumeBudget(budget, "skinJoints", jointIds.length + rootJointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "rig joint");
873
877
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skeleton joint");
874
878
  ensureUnique(rootJointIds, `${path}.rootJointIds`, "duplicate root joint");
875
879
  const output = {
876
- id: readId(requireKey(record3, "id", path), `${path}.id`),
880
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
877
881
  jointIds,
878
882
  rootJointIds,
879
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
883
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
880
884
  };
881
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
885
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
882
886
  return output;
883
887
  }
884
888
  function parseJoint(value, path, budget) {
885
- const record3 = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
889
+ const record4 = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
886
890
  const output = {
887
- id: readId(requireKey(record3, "id", path), `${path}.id`),
888
- nodeId: readId(requireKey(record3, "nodeId", path), `${path}.nodeId`),
889
- restLocalTransform: parseTransformTrs(requireKey(record3, "restLocalTransform", path), `${path}.restLocalTransform`),
890
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
891
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
892
+ nodeId: readId(requireKey(record4, "nodeId", path), `${path}.nodeId`),
893
+ restLocalTransform: parseTransformTrs(requireKey(record4, "restLocalTransform", path), `${path}.restLocalTransform`),
894
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
891
895
  };
892
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
893
- assignOptional(output, "parentJointId", readOptional(record3, "parentJointId", readId, path));
894
- assignOptional(output, "inverseBindMatrix", readOptional(record3, "inverseBindMatrix", parseMatrix, path));
896
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
897
+ assignOptional(output, "parentJointId", readOptional(record4, "parentJointId", readId, path));
898
+ assignOptional(output, "inverseBindMatrix", readOptional(record4, "inverseBindMatrix", parseMatrix, path));
895
899
  return output;
896
900
  }
897
- function parseWeightRows(value, path, rows, columns, integer, budget) {
901
+ function parseWeightRows(value, path, rows, columns, integer2, budget) {
898
902
  const input = readArray(value, path, rows, rows);
899
903
  consumeBudget(budget, "weightVertices", input.length, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, path, "weight vertex");
900
904
  consumeBudget(budget, "weightInfluences", input.length * columns, GPU_MODEL_DOCUMENT_LIMITS.weightInfluences, path, "weight influence");
901
905
  return Object.freeze(input.map((row, rowIndex) => {
902
- const parsed = readNumberTuple(row, `${path}[${rowIndex}]`, columns, 0, integer ? Number.MAX_SAFE_INTEGER : 1);
903
- if (integer && parsed.some((entry) => !Number.isSafeInteger(entry))) return fail("invalid-value", `${path}[${rowIndex}]`, "joint indices must be safe integers");
906
+ const parsed = readNumberTuple(row, `${path}[${rowIndex}]`, columns, 0, integer2 ? Number.MAX_SAFE_INTEGER : 1);
907
+ if (integer2 && parsed.some((entry) => !Number.isSafeInteger(entry))) return fail("invalid-value", `${path}[${rowIndex}]`, "joint indices must be safe integers");
904
908
  return parsed;
905
909
  }));
906
910
  }
907
911
  function parseVertexWeightSet(value, path, budget) {
908
- const record3 = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
909
- const influences = readSafeInteger(requireKey(record3, "influencesPerVertex", path), `${path}.influencesPerVertex`, 1, 32);
912
+ const record4 = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
913
+ const influences = readSafeInteger(requireKey(record4, "influencesPerVertex", path), `${path}.influencesPerVertex`, 1, 32);
910
914
  if (influences % 4 !== 0) return fail("invalid-value", `${path}.influencesPerVertex`, "influencesPerVertex must be a multiple of four");
911
- const jointRows = readArray(requireKey(record3, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
912
- const weightRows = readArray(requireKey(record3, "weights", path), `${path}.weights`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
915
+ const jointRows = readArray(requireKey(record4, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
916
+ const weightRows = readArray(requireKey(record4, "weights", path), `${path}.weights`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
913
917
  if (jointRows.length !== weightRows.length) return fail("invalid-value", path, "joint and weight row counts must match");
914
918
  return {
915
- primitiveId: readId(requireKey(record3, "primitiveId", path), `${path}.primitiveId`),
919
+ primitiveId: readId(requireKey(record4, "primitiveId", path), `${path}.primitiveId`),
916
920
  influencesPerVertex: influences,
917
921
  joints: parseWeightRows(jointRows, `${path}.joints`, jointRows.length, influences, true, budget),
918
922
  weights: parseWeightRows(weightRows, `${path}.weights`, weightRows.length, influences, false, budget)
919
923
  };
920
924
  }
921
925
  function parseSkin(value, path, budget) {
922
- const record3 = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
923
- 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}]`)));
926
+ const record4 = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
927
+ 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}]`)));
924
928
  consumeBudget(budget, "skinJoints", jointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "skin joint");
925
929
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skin joint");
926
- const inverseInput = readArray(requireKey(record3, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
930
+ const inverseInput = readArray(requireKey(record4, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
927
931
  if (inverseInput.length !== jointIds.length) return fail("invalid-value", `${path}.inverseBindMatrices`, "one inverse bind matrix is required per skin joint");
928
- const weightInput = readArray(requireKey(record3, "weightSets", path), `${path}.weightSets`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
932
+ const weightInput = readArray(requireKey(record4, "weightSets", path), `${path}.weightSets`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
929
933
  const weightSets = Object.freeze(weightInput.map((entry, index) => parseVertexWeightSet(entry, `${path}.weightSets[${index}]`, budget)));
930
934
  ensureUnique(weightSets.map(({ primitiveId }) => primitiveId), `${path}.weightSets`, "duplicate primitive weight set");
931
935
  return {
932
- id: readId(requireKey(record3, "id", path), `${path}.id`),
933
- skeletonId: readId(requireKey(record3, "skeletonId", path), `${path}.skeletonId`),
936
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
937
+ skeletonId: readId(requireKey(record4, "skeletonId", path), `${path}.skeletonId`),
934
938
  jointIds,
935
939
  inverseBindMatrices: Object.freeze(inverseInput.map((entry, index) => parseMatrix(entry, `${path}.inverseBindMatrices[${index}]`))),
936
940
  weightSets,
937
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
941
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
938
942
  };
939
943
  }
940
944
  function parseBlendShapeDelta(value, path) {
941
- const record3 = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
942
- const output = { primitiveId: readId(requireKey(record3, "primitiveId", path), `${path}.primitiveId`) };
945
+ const record4 = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
946
+ const output = { primitiveId: readId(requireKey(record4, "primitiveId", path), `${path}.primitiveId`) };
943
947
  for (const key of ["positionAccessorId", "normalAccessorId", "tangentAccessorId"]) {
944
- assignOptional(output, key, readOptional(record3, key, readId, path));
948
+ assignOptional(output, key, readOptional(record4, key, readId, path));
945
949
  }
946
950
  if (!output.positionAccessorId && !output.normalAccessorId && !output.tangentAccessorId) {
947
951
  return fail("invalid-value", path, "blend-shape delta requires at least one accessor");
@@ -949,8 +953,8 @@ function parseBlendShapeDelta(value, path) {
949
953
  return output;
950
954
  }
951
955
  function parseBlendShape(value, path, budget) {
952
- const record3 = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
953
- const deltaInput = readArray(requireKey(record3, "deltas", path), `${path}.deltas`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
956
+ const record4 = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
957
+ const deltaInput = readArray(requireKey(record4, "deltas", path), `${path}.deltas`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
954
958
  consumeBudget(
955
959
  budget,
956
960
  "blendShapeDeltas",
@@ -962,96 +966,96 @@ function parseBlendShape(value, path, budget) {
962
966
  const deltas = Object.freeze(deltaInput.map((entry, index) => parseBlendShapeDelta(entry, `${path}.deltas[${index}]`)));
963
967
  ensureUnique(deltas.map(({ primitiveId }) => primitiveId), `${path}.deltas`, "duplicate blend-shape primitive delta");
964
968
  const output = {
965
- id: readId(requireKey(record3, "id", path), `${path}.id`),
966
- meshId: readId(requireKey(record3, "meshId", path), `${path}.meshId`),
969
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
970
+ meshId: readId(requireKey(record4, "meshId", path), `${path}.meshId`),
967
971
  deltas,
968
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
972
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
969
973
  };
970
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
971
- assignOptional(output, "defaultWeight", readOptional(record3, "defaultWeight", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
974
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
975
+ assignOptional(output, "defaultWeight", readOptional(record4, "defaultWeight", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
972
976
  return output;
973
977
  }
974
978
  function parseAnimationSampler(value, path, budget) {
975
- const record3 = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
976
- const timesInput = readArray(requireKey(record3, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
977
- const valuesInput = readArray(requireKey(record3, "outputValues", path), `${path}.outputValues`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
979
+ const record4 = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
980
+ const timesInput = readArray(requireKey(record4, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
981
+ const valuesInput = readArray(requireKey(record4, "outputValues", path), `${path}.outputValues`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
978
982
  consumeBudget(budget, "animationValues", timesInput.length + valuesInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationValues, path, "animation value");
979
983
  const times = Object.freeze(timesInput.map((entry, index) => readFinite(entry, `${path}.inputTimesSeconds[${index}]`, 0)));
980
984
  for (let index = 1; index < times.length; index += 1) {
981
985
  if (times[index] <= times[index - 1]) return fail("invalid-value", `${path}.inputTimesSeconds`, "animation input times must be strictly increasing");
982
986
  }
983
987
  return {
984
- id: readId(requireKey(record3, "id", path), `${path}.id`),
985
- inputAccessorId: readId(requireKey(record3, "inputAccessorId", path), `${path}.inputAccessorId`),
986
- outputAccessorId: readId(requireKey(record3, "outputAccessorId", path), `${path}.outputAccessorId`),
988
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
989
+ inputAccessorId: readId(requireKey(record4, "inputAccessorId", path), `${path}.inputAccessorId`),
990
+ outputAccessorId: readId(requireKey(record4, "outputAccessorId", path), `${path}.outputAccessorId`),
987
991
  inputTimesSeconds: times,
988
992
  outputValues: Object.freeze(valuesInput.map((entry, index) => readFinite(entry, `${path}.outputValues[${index}]`))),
989
- interpolation: readEnum(requireKey(record3, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
990
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
993
+ interpolation: readEnum(requireKey(record4, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
994
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
991
995
  };
992
996
  }
993
997
  function parseAnimationChannel(value, path, budget) {
994
- const record3 = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
998
+ const record4 = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
995
999
  return {
996
- id: readId(requireKey(record3, "id", path), `${path}.id`),
997
- samplerId: readId(requireKey(record3, "samplerId", path), `${path}.samplerId`),
998
- targetType: readEnum(requireKey(record3, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
999
- targetId: readId(requireKey(record3, "targetId", path), `${path}.targetId`),
1000
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1000
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
1001
+ samplerId: readId(requireKey(record4, "samplerId", path), `${path}.samplerId`),
1002
+ targetType: readEnum(requireKey(record4, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
1003
+ targetId: readId(requireKey(record4, "targetId", path), `${path}.targetId`),
1004
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1001
1005
  };
1002
1006
  }
1003
1007
  function parseAnimation(value, path, budget) {
1004
- const record3 = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
1005
- const samplerInput = readArray(requireKey(record3, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
1006
- const channelInput = readArray(requireKey(record3, "channels", path), `${path}.channels`, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, 1);
1008
+ const record4 = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
1009
+ const samplerInput = readArray(requireKey(record4, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
1010
+ const channelInput = readArray(requireKey(record4, "channels", path), `${path}.channels`, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, 1);
1007
1011
  consumeBudget(budget, "animationSamplers", samplerInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, path, "animation sampler");
1008
1012
  consumeBudget(budget, "animationChannels", channelInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, path, "animation channel");
1009
1013
  const output = {
1010
- id: readId(requireKey(record3, "id", path), `${path}.id`),
1011
- durationSeconds: readFinite(requireKey(record3, "durationSeconds", path), `${path}.durationSeconds`, 0),
1014
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
1015
+ durationSeconds: readFinite(requireKey(record4, "durationSeconds", path), `${path}.durationSeconds`, 0),
1012
1016
  samplers: Object.freeze(samplerInput.map((entry, index) => parseAnimationSampler(entry, `${path}.samplers[${index}]`, budget))),
1013
1017
  channels: Object.freeze(channelInput.map((entry, index) => parseAnimationChannel(entry, `${path}.channels[${index}]`, budget))),
1014
- sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1018
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1015
1019
  };
1016
- assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
1020
+ assignOptional(output, "name", readOptional(record4, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
1017
1021
  return output;
1018
1022
  }
1019
1023
  function parseAnalyticGeometry(value, path, budget) {
1020
- const record3 = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
1021
- if (requireKey(record3, "kind", path) !== "brep-placeholder") return fail("invalid-value", `${path}.kind`, "unsupported analytic geometry kind");
1024
+ const record4 = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
1025
+ if (requireKey(record4, "kind", path) !== "brep-placeholder") return fail("invalid-value", `${path}.kind`, "unsupported analytic geometry kind");
1022
1026
  const output = {
1023
- id: readId(requireKey(record3, "id", path), `${path}.id`),
1027
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
1024
1028
  kind: "brep-placeholder",
1025
- sourceResourceId: readId(requireKey(record3, "sourceResourceId", path), `${path}.sourceResourceId`),
1026
- sourceUnit: readEnum(requireKey(record3, "sourceUnit", path), `${path}.sourceUnit`, ["metre", "millimetre", "centimetre", "inch", "foot", "unitless"])
1029
+ sourceResourceId: readId(requireKey(record4, "sourceResourceId", path), `${path}.sourceResourceId`),
1030
+ sourceUnit: readEnum(requireKey(record4, "sourceUnit", path), `${path}.sourceUnit`, ["metre", "millimetre", "centimetre", "inch", "foot", "unitless"])
1027
1031
  };
1028
- assignOptional(output, "tessellatedMeshId", readOptional(record3, "tessellatedMeshId", readId, path));
1029
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1032
+ assignOptional(output, "tessellatedMeshId", readOptional(record4, "tessellatedMeshId", readId, path));
1033
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1030
1034
  return output;
1031
1035
  }
1032
1036
  function parseProvenance(value, budget) {
1033
1037
  const path = "$.provenance";
1034
- const record3 = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
1038
+ const record4 = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
1035
1039
  const output = {
1036
- sourceFormat: readString(requireKey(record3, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
1037
- sourceContentHash: readSha256(requireKey(record3, "sourceContentHash", path), `${path}.sourceContentHash`),
1038
- converterId: readString(requireKey(record3, "converterId", path), `${path}.converterId`, 128, TOKEN),
1039
- converterVersion: readString(requireKey(record3, "converterVersion", path), `${path}.converterVersion`, 128, TOKEN)
1040
+ sourceFormat: readString(requireKey(record4, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
1041
+ sourceContentHash: readSha256(requireKey(record4, "sourceContentHash", path), `${path}.sourceContentHash`),
1042
+ converterId: readString(requireKey(record4, "converterId", path), `${path}.converterId`, 128, TOKEN),
1043
+ converterVersion: readString(requireKey(record4, "converterVersion", path), `${path}.converterVersion`, 128, TOKEN)
1040
1044
  };
1041
- assignOptional(output, "provider", readOptional(record3, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
1042
- assignOptional(output, "sourceIdentifier", readOptional(record3, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
1043
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1045
+ assignOptional(output, "provider", readOptional(record4, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
1046
+ assignOptional(output, "sourceIdentifier", readOptional(record4, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
1047
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1044
1048
  return output;
1045
1049
  }
1046
1050
  function parseDiagnostic(value, path, budget) {
1047
- const record3 = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
1051
+ const record4 = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
1048
1052
  const output = {
1049
- severity: readEnum(requireKey(record3, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
1050
- code: readString(requireKey(record3, "code", path), `${path}.code`, 128, TOKEN),
1051
- message: readString(requireKey(record3, "message", path), `${path}.message`, GPU_MODEL_DOCUMENT_LIMITS.diagnosticMessageLength)
1053
+ severity: readEnum(requireKey(record4, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
1054
+ code: readString(requireKey(record4, "code", path), `${path}.code`, 128, TOKEN),
1055
+ message: readString(requireKey(record4, "message", path), `${path}.message`, GPU_MODEL_DOCUMENT_LIMITS.diagnosticMessageLength)
1052
1056
  };
1053
- assignOptional(output, "path", readOptional(record3, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
1054
- assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1057
+ assignOptional(output, "path", readOptional(record4, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
1058
+ assignOptional(output, "metadata", readOptional(record4, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1055
1059
  return output;
1056
1060
  }
1057
1061
  function ensureUnique(values, path, detail = "duplicate identity") {
@@ -1692,11 +1696,11 @@ var DOCUMENT_KEYS = Object.freeze([
1692
1696
  ]);
1693
1697
  function parseGpuModelDocument(value) {
1694
1698
  const path = "$";
1695
- const record3 = readRecord(value, path, DOCUMENT_KEYS);
1696
- const schemaVersion = requireKey(record3, "schemaVersion", path);
1699
+ const record4 = readRecord(value, path, DOCUMENT_KEYS);
1700
+ const schemaVersion = requireKey(record4, "schemaVersion", path);
1697
1701
  if (schemaVersion !== GPU_MODEL_DOCUMENT_SCHEMA_VERSION) return fail("invalid-value", "$.schemaVersion", "unsupported schema version");
1698
1702
  const parseCollection = (key, maximum, parser, minimum = 0) => {
1699
- const input = readArray(requireKey(record3, key, path), `$.${key}`, maximum, minimum);
1703
+ const input = readArray(requireKey(record4, key, path), `$.${key}`, maximum, minimum);
1700
1704
  return Object.freeze(input.map((entry, index) => parser(entry, `$.${key}[${index}]`)));
1701
1705
  };
1702
1706
  const budget = {
@@ -1717,10 +1721,10 @@ function parseGpuModelDocument(value) {
1717
1721
  geometryWorldAccessorInstances: 0,
1718
1722
  geometryWorldVertices: 0
1719
1723
  };
1720
- const roots = Object.freeze(readArray(requireKey(record3, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1724
+ const roots = Object.freeze(readArray(requireKey(record4, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1721
1725
  const document = {
1722
1726
  schemaVersion,
1723
- coordinateSystem: parseCoordinateSystem(requireKey(record3, "coordinateSystem", path)),
1727
+ coordinateSystem: parseCoordinateSystem(requireKey(record4, "coordinateSystem", path)),
1724
1728
  roots,
1725
1729
  nodes: parseCollection("nodes", GPU_MODEL_DOCUMENT_LIMITS.nodes, (entry, entryPath) => parseNode(entry, entryPath, budget), 1),
1726
1730
  resources: parseCollection("resources", GPU_MODEL_DOCUMENT_LIMITS.resources, parseVerifiedResource, 1),
@@ -1734,10 +1738,10 @@ function parseGpuModelDocument(value) {
1734
1738
  blendShapes: parseCollection("blendShapes", GPU_MODEL_DOCUMENT_LIMITS.blendShapes, (entry, entryPath) => parseBlendShape(entry, entryPath, budget)),
1735
1739
  animations: parseCollection("animations", GPU_MODEL_DOCUMENT_LIMITS.animations, (entry, entryPath) => parseAnimation(entry, entryPath, budget)),
1736
1740
  analyticGeometry: parseCollection("analyticGeometry", GPU_MODEL_DOCUMENT_LIMITS.analyticGeometry, (entry, entryPath) => parseAnalyticGeometry(entry, entryPath, budget)),
1737
- bounds: parseBounds(requireKey(record3, "bounds", path), "$.bounds"),
1738
- provenance: parseProvenance(requireKey(record3, "provenance", path), budget),
1741
+ bounds: parseBounds(requireKey(record4, "bounds", path), "$.bounds"),
1742
+ provenance: parseProvenance(requireKey(record4, "provenance", path), budget),
1739
1743
  diagnostics: parseCollection("diagnostics", GPU_MODEL_DOCUMENT_LIMITS.diagnostics, (entry, entryPath) => parseDiagnostic(entry, entryPath, budget)),
1740
- metadata: readMetadata(requireKey(record3, "metadata", path), "$.metadata", budget)
1744
+ metadata: readMetadata(requireKey(record4, "metadata", path), "$.metadata", budget)
1741
1745
  };
1742
1746
  if (document.meshes.length === 0) return fail("invalid-value", "$.meshes", "v1 document bounds require at least one canonical mesh");
1743
1747
  validateReferences(document, budget);
@@ -1774,8 +1778,8 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1774
1778
  value.resources.forEach((resource, index) => assertVerifiedResourceEvidence(resource, limits, `$.resources[${index}]`));
1775
1779
  return value;
1776
1780
  }
1777
- const record3 = readRecord(value, "$", DOCUMENT_KEYS);
1778
- const resourceInput = readArray(requireKey(record3, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1781
+ const record4 = readRecord(value, "$", DOCUMENT_KEYS);
1782
+ const resourceInput = readArray(requireKey(record4, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1779
1783
  const parsed = resourceInput.map((entry, index) => {
1780
1784
  if (typeof entry === "object" && entry !== null && verifiedResources.has(entry) && verifiedResourceBytes.has(entry)) return entry;
1781
1785
  return parseResourceHeader(entry, `$.resources[${index}]`);
@@ -1788,7 +1792,7 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1788
1792
  }
1789
1793
  const candidate = {};
1790
1794
  for (const key of DOCUMENT_KEYS) {
1791
- if (Object.hasOwn(record3, key)) candidate[key] = record3[key];
1795
+ if (Object.hasOwn(record4, key)) candidate[key] = record4[key];
1792
1796
  }
1793
1797
  candidate.resources = verified;
1794
1798
  return createGpuModelDocument(candidate);
@@ -1853,11 +1857,11 @@ async function hashGpuModelDocumentCanonical(document) {
1853
1857
  return Array.from(new Uint8Array(digest), (value) => value.toString(16).padStart(2, "0")).join("");
1854
1858
  }
1855
1859
  function resolveStaticDemoLimits(options) {
1856
- const record3 = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1857
- const enabled = readBoolean(requireKey(record3, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1860
+ const record4 = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1861
+ const enabled = readBoolean(requireKey(record4, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1858
1862
  if (!enabled) return fail("feature-disabled", "$.options.pvoxModelsEnabled", `${GPU_MODEL_STATIC_DEMO_FEATURE_FLAG} is disabled`);
1859
- if (!Object.hasOwn(record3, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1860
- const overrides = readRecord(record3.limits, "$.options.limits", Object.keys(GPU_MODEL_STATIC_DEMO_LIMITS));
1863
+ if (!Object.hasOwn(record4, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1864
+ const overrides = readRecord(record4.limits, "$.options.limits", Object.keys(GPU_MODEL_STATIC_DEMO_LIMITS));
1861
1865
  const limits = { ...GPU_MODEL_STATIC_DEMO_LIMITS };
1862
1866
  for (const [key, ceiling] of Object.entries(GPU_MODEL_STATIC_DEMO_LIMITS)) {
1863
1867
  if (Object.hasOwn(overrides, key)) limits[key] = readSafeInteger(overrides[key], `$.options.limits.${key}`, 1, ceiling);
@@ -2319,7 +2323,7 @@ function evaluateGpuModelDiagnostics(value) {
2319
2323
  const diagnostics = [];
2320
2324
  const repairs = [];
2321
2325
  const losses = [];
2322
- let accepted = true;
2326
+ let accepted2 = true;
2323
2327
  for (const issue of issues) {
2324
2328
  const base = { code: issue.code, path: issue.path, message: issue.message };
2325
2329
  if (issue.kind === "violation") {
@@ -2330,20 +2334,20 @@ function evaluateGpuModelDiagnostics(value) {
2330
2334
  repairs.push(Object.freeze({ ruleId: rule.id, originalValue: issue.originalValue, action: rule.action, severity: rule.severity, affectedPath: issue.path, confidence: rule.confidence }));
2331
2335
  diagnostics.push(Object.freeze({ ...base, kind: "repair", severity: rule.severity, ruleId: rule.id }));
2332
2336
  } else {
2333
- accepted = false;
2337
+ accepted2 = false;
2334
2338
  diagnostics.push(Object.freeze({ ...base, kind: "blocking-error", severity: "error", ruleId: issue.ruleId }));
2335
2339
  }
2336
2340
  } else if (issue.kind === "unsupported") {
2337
- accepted = false;
2341
+ accepted2 = false;
2338
2342
  diagnostics.push(Object.freeze({ ...issue, severity: "error" }));
2339
2343
  } else if (issue.kind === "loss") {
2340
- if (!allowLoss) accepted = false;
2344
+ if (!allowLoss) accepted2 = false;
2341
2345
  const loss = Object.freeze({ ...issue, severity: "warning" });
2342
2346
  losses.push(loss);
2343
2347
  diagnostics.push(loss);
2344
2348
  } else diagnostics.push(Object.freeze({ ...issue, severity: "warning" }));
2345
2349
  }
2346
- return Object.freeze({ mode, standard, accepted, diagnostics: Object.freeze(diagnostics), repairs: Object.freeze(repairs), losses: Object.freeze(losses), ...mode === "forensic" ? { rawSource: args.rawSource } : {} });
2350
+ return Object.freeze({ mode, standard, accepted: accepted2, diagnostics: Object.freeze(diagnostics), repairs: Object.freeze(repairs), losses: Object.freeze(losses), ...mode === "forensic" ? { rawSource: args.rawSource } : {} });
2347
2351
  }
2348
2352
 
2349
2353
  // src/resource-graph.ts
@@ -2537,6 +2541,360 @@ function validateDag(nodes) {
2537
2541
  if (queue.length !== nodes.size) fail3("graph-cycle");
2538
2542
  }
2539
2543
 
2544
+ // src/adapter-registry.ts
2545
+ var MODEL_REGISTRY_LIMITS = Object.freeze({ adapters: 128, concurrent: 8, timeoutMs: 3e4, maxTimeoutMs: 3e5, string: 4096, mapEntries: 64, manifestEntries: 4096, memoryBytes: 64 * 1024 * 1024 });
2546
+ var ModelRegistryError = class extends TypeError {
2547
+ constructor(code) {
2548
+ super(`GPU model registry: ${code}`);
2549
+ this.code = code;
2550
+ this.name = "ModelRegistryError";
2551
+ }
2552
+ code;
2553
+ };
2554
+ function fail4(code = "invalid-input") {
2555
+ throw new ModelRegistryError(code);
2556
+ }
2557
+ var sourceKinds = ["file-path", "url", "blob-storage-url", "blob", "array-buffer", "uint8-array", "stream"];
2558
+ var targetKinds = ["file-path", "blob-storage-url", "blob", "array-buffer", "uint8-array", "stream", "package"];
2559
+ var bases = /* @__PURE__ */ new WeakMap();
2560
+ function createModelResultBase(input) {
2561
+ const report = evaluateGpuModelDiagnostics(input);
2562
+ const value = Object.freeze({ report, diagnostics: report.diagnostics, warnings: Object.freeze(report.diagnostics.filter((d) => d.severity === "warning")), repairs: report.repairs, lossReports: report.losses });
2563
+ bases.set(report, value);
2564
+ return value;
2565
+ }
2566
+ function record3(value, allowed) {
2567
+ if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail4();
2568
+ const keys = Reflect.ownKeys(value);
2569
+ if (keys.length > MODEL_REGISTRY_LIMITS.mapEntries) fail4();
2570
+ const output = /* @__PURE__ */ Object.create(null);
2571
+ for (const key of keys) {
2572
+ if (typeof key !== "string" || allowed && !allowed.includes(key)) fail4();
2573
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2574
+ if (!("value" in descriptor)) fail4();
2575
+ output[key] = descriptor.value;
2576
+ }
2577
+ return output;
2578
+ }
2579
+ function text2(value) {
2580
+ 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();
2581
+ return value;
2582
+ }
2583
+ function format(value) {
2584
+ if (typeof value !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(value)) fail4();
2585
+ return value;
2586
+ }
2587
+ function enumeration(value, values) {
2588
+ if (!values.includes(value)) fail4();
2589
+ return value;
2590
+ }
2591
+ function boolean2(value) {
2592
+ if (typeof value !== "boolean") fail4();
2593
+ return value;
2594
+ }
2595
+ function integer(value, max, min = 0) {
2596
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max) fail4();
2597
+ return value;
2598
+ }
2599
+ function list(value, max) {
2600
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > max || Reflect.ownKeys(value).length !== value.length + 1) fail4();
2601
+ return Array.from({ length: value.length }, (_, i) => {
2602
+ const d = Object.getOwnPropertyDescriptor(value, String(i));
2603
+ if (!d || !("value" in d)) fail4();
2604
+ return d.value;
2605
+ });
2606
+ }
2607
+ function strings(value) {
2608
+ const input = record3(value);
2609
+ const out = /* @__PURE__ */ Object.create(null);
2610
+ for (const [key, entry] of Object.entries(input)) out[text2(key)] = text2(entry);
2611
+ return Object.freeze(out);
2612
+ }
2613
+ function metadata(value) {
2614
+ const v = record3(value, ["formatId", "displayName", "supportedSourceKinds", "supportedTargetKinds", "supportsAnimations", "supportsSkins", "supportsAnalyticGeometry"]);
2615
+ const kinds = (input, allowed) => {
2616
+ const values = list(input, allowed.length).map((k) => enumeration(k, allowed));
2617
+ if (new Set(values).size !== values.length) fail4();
2618
+ return Object.freeze(values);
2619
+ };
2620
+ 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) });
2621
+ }
2622
+ function snapshotIO(input, source) {
2623
+ const v = record3(input);
2624
+ const kind = enumeration(v.kind, source ? sourceKinds : targetKinds);
2625
+ const common = source ? ["kind", "fileNameHint", "mimeTypeHint", "provenance"] : ["kind"];
2626
+ const specific = source ? {
2627
+ "file-path": ["path"],
2628
+ url: ["url", "headers"],
2629
+ "blob-storage-url": ["url", "credentialMode"],
2630
+ blob: ["blob"],
2631
+ "array-buffer": ["bytes"],
2632
+ "uint8-array": ["bytes"],
2633
+ stream: ["stream", "byteLengthHint"]
2634
+ } : {
2635
+ "file-path": ["path", "overwrite"],
2636
+ "blob-storage-url": ["url", "overwrite", "contentType", "metadata"],
2637
+ blob: ["mimeType", "fileName"],
2638
+ "array-buffer": [],
2639
+ "uint8-array": [],
2640
+ stream: ["stream", "contentType"],
2641
+ package: ["packageFormat", "destinationHint", "includeSourcePayload"]
2642
+ };
2643
+ if (Object.keys(v).some((k) => ![...common, ...specific[kind]].includes(k))) fail4();
2644
+ const out = { kind };
2645
+ for (const key of ["fileNameHint", "mimeTypeHint", "mimeType", "fileName", "contentType", "destinationHint"]) if (v[key] !== void 0) out[key] = text2(v[key]);
2646
+ for (const key of ["overwrite", "includeSourcePayload"]) if (v[key] !== void 0) out[key] = boolean2(v[key]);
2647
+ for (const key of ["headers", "metadata"]) if (v[key] !== void 0) out[key] = strings(v[key]);
2648
+ if (v.provenance !== void 0) out.provenance = strings(record3(v.provenance, ["sourceId", "sourceSystem", "acquiredAt", "licenseHint"]));
2649
+ if (v.credentialMode !== void 0) out.credentialMode = enumeration(v.credentialMode, ["anonymous", "signed", "runtime-resolved"]);
2650
+ if (v.byteLengthHint !== void 0) out.byteLengthHint = integer(v.byteLengthHint, Number.MAX_SAFE_INTEGER);
2651
+ if (kind === "file-path") out.path = text2(v.path);
2652
+ if (kind === "url" || kind === "blob-storage-url") {
2653
+ const url = text2(v.url);
2654
+ let parsed;
2655
+ try {
2656
+ parsed = new URL(url);
2657
+ } catch {
2658
+ return fail4();
2659
+ }
2660
+ if (!["https:", "http:"].includes(parsed.protocol) || parsed.username || parsed.password) fail4();
2661
+ out.url = url;
2662
+ }
2663
+ if (source && kind === "blob") {
2664
+ if (!(v.blob instanceof Blob)) fail4();
2665
+ integer(v.blob.size, MODEL_REGISTRY_LIMITS.memoryBytes);
2666
+ out.blob = v.blob;
2667
+ }
2668
+ if (source && (kind === "array-buffer" || kind === "uint8-array")) {
2669
+ if (kind === "array-buffer") {
2670
+ if (!(v.bytes instanceof ArrayBuffer)) fail4();
2671
+ integer(v.bytes.byteLength, MODEL_REGISTRY_LIMITS.memoryBytes);
2672
+ out.bytes = v.bytes.slice(0);
2673
+ } else {
2674
+ if (!(v.bytes instanceof Uint8Array) || !(v.bytes.buffer instanceof ArrayBuffer)) fail4();
2675
+ integer(v.bytes.byteLength, MODEL_REGISTRY_LIMITS.memoryBytes);
2676
+ out.bytes = new Uint8Array(v.bytes);
2677
+ }
2678
+ }
2679
+ if (kind === "stream") {
2680
+ const stream = v.stream;
2681
+ if (!stream || typeof stream !== "object") fail4();
2682
+ 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();
2683
+ out.stream = stream;
2684
+ }
2685
+ if (kind === "package") out.packageFormat = enumeration(v.packageFormat, ["zip", "tar", "directory-manifest"]);
2686
+ return Object.freeze(out);
2687
+ }
2688
+ function manifest(input) {
2689
+ const v = record3(input, ["format", "entries"]);
2690
+ const ids = /* @__PURE__ */ new Set();
2691
+ const paths = /* @__PURE__ */ new Set();
2692
+ const entries = list(v.entries, MODEL_REGISTRY_LIMITS.manifestEntries).map((entry) => {
2693
+ const e = record3(entry, ["id", "role", "path", "contentType"]);
2694
+ const id = text2(e.id);
2695
+ const path = text2(e.path);
2696
+ 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();
2697
+ ids.add(id);
2698
+ paths.add(path.toLowerCase());
2699
+ return Object.freeze({ id, path, role: enumeration(e.role, ["mesh", "material", "texture", "animation", "auxiliary"]), ...e.contentType !== void 0 ? { contentType: text2(e.contentType) } : {} });
2700
+ });
2701
+ return Object.freeze({ format: enumeration(v.format, ["zip", "tar", "directory-manifest", "inline"]), entries: Object.freeze(entries) });
2702
+ }
2703
+ function reportBase(value, context) {
2704
+ const base = bases.get(value.report);
2705
+ if (!base || base.report.mode !== context.options.mode || ["diagnostics", "warnings", "repairs", "lossReports"].some((k) => value[k] !== base[k])) fail4("invalid-result");
2706
+ return base;
2707
+ }
2708
+ function accepted(report, context) {
2709
+ return report.accepted && (context.options.allowLoss || report.losses.length === 0);
2710
+ }
2711
+ function ledger(stages, ok, payload = {}) {
2712
+ 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)) });
2713
+ }
2714
+ function createModelConversionRegistry(adapters, limits = {}) {
2715
+ const registry = /* @__PURE__ */ new Map();
2716
+ const maxConcurrent = integer(record3(limits, ["maxConcurrent"]).maxConcurrent ?? MODEL_REGISTRY_LIMITS.concurrent, MODEL_REGISTRY_LIMITS.concurrent, 1);
2717
+ for (const input of list(adapters, MODEL_REGISTRY_LIMITS.adapters)) {
2718
+ let a;
2719
+ try {
2720
+ const v = record3(input, ["metadata", "inspect", "load", "export", "validate"]);
2721
+ const m = metadata(v.metadata);
2722
+ for (const name of ["inspect", "load", "export", "validate"]) if (typeof v[name] !== "function") fail4();
2723
+ const raw = input;
2724
+ 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) });
2725
+ } catch {
2726
+ return fail4("invalid-adapter");
2727
+ }
2728
+ if (registry.has(a.metadata.formatId)) fail4("duplicate-format");
2729
+ registry.set(a.metadata.formatId, a);
2730
+ }
2731
+ let active = 0;
2732
+ function lookup(id) {
2733
+ const a = registry.get(format(id));
2734
+ if (!a) fail4("unsupported-format");
2735
+ return a;
2736
+ }
2737
+ function sourceFor(a, input) {
2738
+ const source = snapshotIO(input, true);
2739
+ if (!a.metadata.supportedSourceKinds.includes(source.kind)) fail4("unsupported-source");
2740
+ return source;
2741
+ }
2742
+ function targetFor(a, input) {
2743
+ const target = snapshotIO(input, false);
2744
+ if (!a.metadata.supportedTargetKinds.includes(target.kind)) fail4("unsupported-target");
2745
+ return target;
2746
+ }
2747
+ function check(context) {
2748
+ if (context.options.signal.aborted) fail4("aborted");
2749
+ if (Date.now() >= context.deadline) fail4("deadline-exceeded");
2750
+ }
2751
+ async function run(options, work) {
2752
+ const v = record3(options ?? {}, ["mode", "allowLoss", "signal", "timeoutMs"]);
2753
+ const mode = enumeration(v.mode ?? "strict", ["strict", "tolerant", "forensic"]);
2754
+ const allowLoss = v.allowLoss === void 0 ? false : boolean2(v.allowLoss);
2755
+ const timeoutMs = integer(v.timeoutMs ?? MODEL_REGISTRY_LIMITS.timeoutMs, MODEL_REGISTRY_LIMITS.maxTimeoutMs, 1);
2756
+ if (v.signal !== void 0 && !(v.signal instanceof AbortSignal)) fail4();
2757
+ const parent = v.signal;
2758
+ if (parent?.aborted) fail4("aborted");
2759
+ if (active >= maxConcurrent) fail4("capacity-exceeded");
2760
+ const controller = new AbortController();
2761
+ const context = { options: Object.freeze({ mode, allowLoss, timeoutMs, signal: controller.signal }), deadline: Date.now() + timeoutMs };
2762
+ active++;
2763
+ let timer;
2764
+ let abort;
2765
+ const pending = Promise.resolve().then(() => {
2766
+ check(context);
2767
+ return work(context);
2768
+ }).finally(() => {
2769
+ active--;
2770
+ });
2771
+ const stopped = new Promise((_, reject) => {
2772
+ abort = () => {
2773
+ controller.abort();
2774
+ reject(new ModelRegistryError("aborted"));
2775
+ };
2776
+ parent?.addEventListener("abort", abort, { once: true });
2777
+ timer = setTimeout(() => {
2778
+ controller.abort();
2779
+ reject(new ModelRegistryError("deadline-exceeded"));
2780
+ }, timeoutMs);
2781
+ });
2782
+ try {
2783
+ return await Promise.race([pending, stopped]);
2784
+ } catch (error) {
2785
+ if (error instanceof ModelRegistryError) throw error;
2786
+ return fail4();
2787
+ } finally {
2788
+ clearTimeout(timer);
2789
+ if (abort) parent?.removeEventListener("abort", abort);
2790
+ }
2791
+ }
2792
+ async function invoke(a, operation, context, call, stages) {
2793
+ check(context);
2794
+ let raw;
2795
+ try {
2796
+ raw = await call();
2797
+ } catch {
2798
+ return fail4("adapter-failed");
2799
+ }
2800
+ check(context);
2801
+ try {
2802
+ const v = record3(raw, ["report", "diagnostics", "warnings", "repairs", "lossReports", ...{ inspect: ["detectedFormat", "capabilities"], load: ["document", "resourcePackage"], validate: ["valid"], export: ["target", "resourcePackage", "output"] }[operation]]);
2803
+ const base = reportBase(v, context);
2804
+ const result = { ...v, ...base };
2805
+ if (v.resourcePackage !== void 0) result.resourcePackage = manifest(v.resourcePackage);
2806
+ stages.push(Object.freeze({ formatId: a.metadata.formatId, operation, report: base.report }));
2807
+ return result;
2808
+ } catch {
2809
+ return fail4("invalid-result");
2810
+ }
2811
+ }
2812
+ async function load(a, source, c, stages) {
2813
+ const v = await invoke(a, "load", c, () => a.load(source, c.options), stages);
2814
+ if (v.document !== void 0 && !isGpuModelDocument(v.document)) fail4("invalid-result");
2815
+ if (accepted(v.report, c) && !isGpuModelDocument(v.document)) fail4("invalid-result");
2816
+ return Object.freeze(v);
2817
+ }
2818
+ async function validate(a, input, c, stages) {
2819
+ const v = await invoke(a, "validate", c, () => a.validate(input, c.options), stages);
2820
+ if (typeof v.valid !== "boolean") fail4("invalid-result");
2821
+ return v.valid && accepted(v.report, c);
2822
+ }
2823
+ async function exportDocument(a, document, target, c, stages) {
2824
+ const issues = [];
2825
+ 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"]]) {
2826
+ if (!supported && present) issues.push({ kind: "loss", code: "TARGET_CAPABILITY_LOSS", path, message: "Target cannot preserve canonical semantics", effect: "dropped" });
2827
+ }
2828
+ const report = evaluateGpuModelDiagnostics({ standard: "gltf-glb", mode: c.options.mode, allowLoss: c.options.allowLoss, issues, ...c.options.mode === "forensic" ? { rawSource: null } : {} });
2829
+ stages.push(Object.freeze({ formatId: a.metadata.formatId, operation: "capabilities", report }));
2830
+ if (!accepted(report, c) || !await validate(a, document, c, stages)) return ledger(stages, false, { document });
2831
+ const v = await invoke(a, "export", c, () => a.export(document, target, c.options), stages);
2832
+ if (v.target !== target.kind) fail4("invalid-result");
2833
+ try {
2834
+ if (v.output !== void 0) {
2835
+ const output = v.output;
2836
+ if (target.kind === "blob" && output instanceof Blob) {
2837
+ integer(output.size, MODEL_REGISTRY_LIMITS.memoryBytes);
2838
+ } else if (target.kind === "array-buffer" && output instanceof ArrayBuffer) {
2839
+ integer(output.byteLength, MODEL_REGISTRY_LIMITS.memoryBytes);
2840
+ v.output = output.slice(0);
2841
+ } else if (target.kind === "uint8-array" && output instanceof Uint8Array && output.buffer instanceof ArrayBuffer) {
2842
+ integer(output.byteLength, MODEL_REGISTRY_LIMITS.memoryBytes);
2843
+ v.output = new Uint8Array(output);
2844
+ } else fail4();
2845
+ }
2846
+ } catch {
2847
+ return fail4("invalid-result");
2848
+ }
2849
+ const exported = Object.freeze(v);
2850
+ return ledger(stages, accepted(exported.report, c), { document, exported, ...exported.resourcePackage ? { resourcePackage: exported.resourcePackage } : {} });
2851
+ }
2852
+ const api = {
2853
+ capabilities: Object.freeze([...registry.values()].map((a) => a.metadata)),
2854
+ inspect: (id, input, options) => run(options, async (c) => {
2855
+ const a = lookup(id);
2856
+ const source = sourceFor(a, input);
2857
+ const stages = [];
2858
+ const v = await invoke(a, "inspect", c, () => a.inspect(source, c.options), stages);
2859
+ try {
2860
+ if (v.detectedFormat !== void 0 && format(v.detectedFormat) !== a.metadata.formatId) fail4();
2861
+ if (JSON.stringify(metadata(v.capabilities)) !== JSON.stringify(a.metadata)) fail4();
2862
+ } catch {
2863
+ return fail4("invalid-result");
2864
+ }
2865
+ return ledger(stages, accepted(v.report, c), { inspection: Object.freeze({ ...v, capabilities: a.metadata }) });
2866
+ }),
2867
+ load: (id, input, options) => run(options, async (c) => {
2868
+ const a = lookup(id);
2869
+ const stages = [];
2870
+ const result = await load(a, sourceFor(a, input), c, stages);
2871
+ return ledger(stages, accepted(result.report, c), { ...result.document ? { document: result.document } : {}, ...result.resourcePackage ? { resourcePackage: result.resourcePackage } : {} });
2872
+ }),
2873
+ validate: (id, input, options) => run(options, async (c) => {
2874
+ const a = lookup(id);
2875
+ const stages = [];
2876
+ return ledger(stages, await validate(a, isGpuModelDocument(input) ? input : sourceFor(a, input), c, stages));
2877
+ }),
2878
+ export: (id, document, input, options) => run(options, async (c) => {
2879
+ const a = lookup(id);
2880
+ if (!isGpuModelDocument(document)) fail4();
2881
+ return exportDocument(a, document, targetFor(a, input), c, []);
2882
+ }),
2883
+ convert: (request, options) => run(options, async (c) => {
2884
+ const v = record3(request, ["sourceFormat", "targetFormat", "source", "target"]);
2885
+ const from = lookup(v.sourceFormat);
2886
+ const to = lookup(v.targetFormat);
2887
+ const source = sourceFor(from, v.source);
2888
+ const target = targetFor(to, v.target);
2889
+ const stages = [];
2890
+ const result = await load(from, source, c, stages);
2891
+ if (!accepted(result.report, c)) return ledger(stages, false, { ...result.resourcePackage ? { resourcePackage: result.resourcePackage } : {} });
2892
+ return exportDocument(to, result.document, target, c, stages);
2893
+ })
2894
+ };
2895
+ return Object.freeze(api);
2896
+ }
2897
+
2540
2898
  // src/index.ts
2541
2899
  var packageName = "@plasius/gpu-model-core";
2542
2900
  var packageBootstrap = Object.freeze({
@@ -2564,12 +2922,16 @@ var packageBootstrap = Object.freeze({
2564
2922
  GpuModelDiagnosticsError,
2565
2923
  GpuModelDocumentError,
2566
2924
  GpuModelResourceGraphError,
2925
+ MODEL_REGISTRY_LIMITS,
2926
+ ModelRegistryError,
2567
2927
  assertGpuModelStaticDemoNormalization,
2568
2928
  canCreateGpuModelDocument,
2569
2929
  createAndVerifyGpuModelDocument,
2570
2930
  createGpuModelDocument,
2571
2931
  createGpuModelResourceGraph,
2572
2932
  createGpuModelStaticDemoCompilerInput,
2933
+ createModelConversionRegistry,
2934
+ createModelResultBase,
2573
2935
  encodeGpuModelDocumentCanonical,
2574
2936
  evaluateGpuModelDiagnostics,
2575
2937
  gpuModelResourceKey,