@plasius/gpu-model-core 0.2.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
@@ -25,6 +25,8 @@ __export(index_exports, {
25
25
  GPU_MODEL_DOCUMENT_LIMITS: () => GPU_MODEL_DOCUMENT_LIMITS,
26
26
  GPU_MODEL_DOCUMENT_SCHEMA_VERSION: () => GPU_MODEL_DOCUMENT_SCHEMA_VERSION,
27
27
  GPU_MODEL_REPAIR_RULES: () => GPU_MODEL_REPAIR_RULES,
28
+ GPU_MODEL_RESOURCE_GRAPH_LIMITS: () => GPU_MODEL_RESOURCE_GRAPH_LIMITS,
29
+ GPU_MODEL_RESOURCE_GRAPH_SCHEMA_VERSION: () => GPU_MODEL_RESOURCE_GRAPH_SCHEMA_VERSION,
28
30
  GPU_MODEL_RESOURCE_VERIFICATION_LIMITS: () => GPU_MODEL_RESOURCE_VERIFICATION_LIMITS,
29
31
  GPU_MODEL_STATIC_DEMO_FEATURE_FLAG: () => GPU_MODEL_STATIC_DEMO_FEATURE_FLAG,
30
32
  GPU_MODEL_STATIC_DEMO_LIMITS: () => GPU_MODEL_STATIC_DEMO_LIMITS,
@@ -35,15 +37,23 @@ __export(index_exports, {
35
37
  GPU_MODEL_STATIC_DEMO_PROFILE_VERSION: () => GPU_MODEL_STATIC_DEMO_PROFILE_VERSION,
36
38
  GpuModelDiagnosticsError: () => GpuModelDiagnosticsError,
37
39
  GpuModelDocumentError: () => GpuModelDocumentError,
40
+ GpuModelResourceGraphError: () => GpuModelResourceGraphError,
41
+ MODEL_REGISTRY_LIMITS: () => MODEL_REGISTRY_LIMITS,
42
+ ModelRegistryError: () => ModelRegistryError,
38
43
  assertGpuModelStaticDemoNormalization: () => assertGpuModelStaticDemoNormalization,
39
44
  canCreateGpuModelDocument: () => canCreateGpuModelDocument,
40
45
  createAndVerifyGpuModelDocument: () => createAndVerifyGpuModelDocument,
41
46
  createGpuModelDocument: () => createGpuModelDocument,
47
+ createGpuModelResourceGraph: () => createGpuModelResourceGraph,
42
48
  createGpuModelStaticDemoCompilerInput: () => createGpuModelStaticDemoCompilerInput,
49
+ createModelConversionRegistry: () => createModelConversionRegistry,
50
+ createModelResultBase: () => createModelResultBase,
43
51
  encodeGpuModelDocumentCanonical: () => encodeGpuModelDocumentCanonical,
44
52
  evaluateGpuModelDiagnostics: () => evaluateGpuModelDiagnostics,
53
+ gpuModelResourceKey: () => gpuModelResourceKey,
45
54
  hashGpuModelDocumentCanonical: () => hashGpuModelDocumentCanonical,
46
55
  isGpuModelDocument: () => isGpuModelDocument,
56
+ isGpuModelResourceGraph: () => isGpuModelResourceGraph,
47
57
  packageBootstrap: () => packageBootstrap,
48
58
  packageName: () => packageName,
49
59
  verifyGpuModelResource: () => verifyGpuModelResource
@@ -224,9 +234,9 @@ function readRecord(value, path, allowedKeys) {
224
234
  }
225
235
  return value;
226
236
  }
227
- function requireKey(record2, key, path) {
228
- if (!Object.hasOwn(record2, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
229
- return record2[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];
230
240
  }
231
241
  function readArray(value, path, maximum, minimum = 0) {
232
242
  if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
@@ -305,9 +315,9 @@ function readNumberTuple(value, path, length, minimum, maximum) {
305
315
  const input = readArray(value, path, length, length);
306
316
  return Object.freeze(input.map((entry, index) => readFinite(entry, `${path}[${index}]`, minimum, maximum)));
307
317
  }
308
- function readOptional(record2, key, parser, path) {
309
- if (!Object.hasOwn(record2, key)) return void 0;
310
- return parser(record2[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}`);
311
321
  }
312
322
  function assignOptional(target, key, value) {
313
323
  if (value !== void 0) Object.assign(target, { [key]: value });
@@ -329,17 +339,17 @@ function readMetadataValue(value, path, depth, budget) {
329
339
  return Object.freeze(input.map((entry, index) => readMetadataValue(entry, `${path}[${index}]`, depth + 1, budget)));
330
340
  }
331
341
  if (typeof value !== "object" || value === null) return fail("invalid-type", path, "metadata value is not JSON-compatible");
332
- const record2 = value;
333
- const prototype = Object.getPrototypeOf(record2);
342
+ const record4 = value;
343
+ const prototype = Object.getPrototypeOf(record4);
334
344
  if (prototype !== Object.prototype && prototype !== null) return fail("invalid-type", path, "metadata objects must be plain objects");
335
- const keys = Reflect.ownKeys(record2);
345
+ const keys = Reflect.ownKeys(record4);
336
346
  if (keys.length > GPU_MODEL_DOCUMENT_LIMITS.metadataObjectKeys) return fail("limit-exceeded", path, "metadata object has too many keys");
337
347
  const output = {};
338
348
  for (const key of keys) {
339
349
  if (typeof key !== "string") return fail("unknown-key", path, "metadata symbol keys are not permitted");
340
350
  if (PROHIBITED_KEYS.has(key)) return fail("unknown-key", `${path}.${key}`, "prohibited metadata key");
341
351
  readString(key, `${path}.[key]`, GPU_MODEL_DOCUMENT_LIMITS.metadataKeyLength, METADATA_KEY);
342
- const descriptor = Object.getOwnPropertyDescriptor(record2, key);
352
+ const descriptor = Object.getOwnPropertyDescriptor(record4, key);
343
353
  if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) return fail("invalid-type", `${path}.${key}`, "metadata properties must be enumerable data properties");
344
354
  output[key] = readMetadataValue(descriptor.value, `${path}.${key}`, depth + 1, budget);
345
355
  }
@@ -352,9 +362,9 @@ function readMetadata(value, path, budget) {
352
362
  }
353
363
  function parseCoordinateSystem(value) {
354
364
  const path = "$.coordinateSystem";
355
- const record2 = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
365
+ const record4 = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
356
366
  for (const [key, expected] of Object.entries(CANONICAL_GPU_MODEL_COORDINATE_SYSTEM)) {
357
- if (requireKey(record2, 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");
358
368
  }
359
369
  return CANONICAL_GPU_MODEL_COORDINATE_SYSTEM;
360
370
  }
@@ -366,19 +376,19 @@ function parseMatrix(value, path, requireAffine = true) {
366
376
  return matrix;
367
377
  }
368
378
  function parseBounds(value, path) {
369
- const record2 = readRecord(value, path, ["min", "max"]);
370
- const min = readNumberTuple(requireKey(record2, "min", path), `${path}.min`, 3);
371
- const max = readNumberTuple(requireKey(record2, "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);
372
382
  for (let index = 0; index < 3; index += 1) {
373
383
  if (min[index] > max[index]) return fail("invalid-value", path, "bounds min cannot exceed max");
374
384
  }
375
385
  return { min, max };
376
386
  }
377
387
  function parseTransformTrs(value, path) {
378
- const record2 = readRecord(value, path, ["translation", "rotation", "scale"]);
379
- const translation = readNumberTuple(requireKey(record2, "translation", path), `${path}.translation`, 3);
380
- const rotation = readNumberTuple(requireKey(record2, "rotation", path), `${path}.rotation`, 4);
381
- const scale = readNumberTuple(requireKey(record2, "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);
382
392
  const magnitude = Math.hypot(...rotation);
383
393
  if (!almostEqual(magnitude, 1, 1e-4)) return fail("invalid-value", `${path}.rotation`, "rotation quaternion must be normalized");
384
394
  return { translation, rotation, scale };
@@ -400,25 +410,25 @@ function intrinsicBlobArrayBuffer(blob) {
400
410
  return Reflect.apply(blobArrayBuffer, blob, []);
401
411
  }
402
412
  function parseResourceHeader(value, path) {
403
- const record2 = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
404
- const kind = readEnum(requireKey(record2, "kind", path), `${path}.kind`, ["buffer", "image"]);
405
- const mimeType = readString(requireKey(record2, "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();
406
416
  if (kind === "image" ? !IMAGE_MIME_TYPES.has(mimeType) : !BUFFER_MIME_TYPES.has(mimeType)) {
407
417
  return fail("invalid-value", `${path}.mimeType`, `unsupported ${kind} MIME type`);
408
418
  }
409
- const payload = requireKey(record2, "payload", path);
419
+ const payload = requireKey(record4, "payload", path);
410
420
  if (!(payload instanceof Blob)) return fail("invalid-type", `${path}.payload`, "expected an immutable Blob payload");
411
421
  if (Object.getPrototypeOf(payload) !== Blob.prototype || Reflect.ownKeys(payload).some((key) => typeof key === "string")) {
412
422
  return fail("invalid-type", `${path}.payload`, "expected a native Blob without caller-defined properties");
413
423
  }
414
- const byteLength = readSafeInteger(requireKey(record2, "byteLength", path), `${path}.byteLength`, 1);
424
+ const byteLength = readSafeInteger(requireKey(record4, "byteLength", path), `${path}.byteLength`, 1);
415
425
  if (intrinsicBlobSize(payload) !== byteLength) return fail("payload-length-mismatch", `${path}.payload`, "payload byte length does not match byteLength");
416
426
  const payloadType = intrinsicBlobType(payload);
417
427
  if (payloadType && payloadType.toLowerCase() !== mimeType) return fail("invalid-value", `${path}.payload`, "payload MIME type does not match mimeType");
418
428
  return {
419
- id: readId(requireKey(record2, "id", path), `${path}.id`),
429
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
420
430
  kind,
421
- contentHash: readSha256(requireKey(record2, "contentHash", path), `${path}.contentHash`),
431
+ contentHash: readSha256(requireKey(record4, "contentHash", path), `${path}.contentHash`),
422
432
  byteLength,
423
433
  mimeType,
424
434
  payload
@@ -427,11 +437,11 @@ function parseResourceHeader(value, path) {
427
437
  function resolveVerificationLimits(options) {
428
438
  const overrides = options?.limits;
429
439
  if (overrides === void 0) return GPU_MODEL_RESOURCE_VERIFICATION_LIMITS;
430
- const record2 = 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));
431
441
  const output = { ...GPU_MODEL_RESOURCE_VERIFICATION_LIMITS };
432
442
  for (const [key, ceiling] of Object.entries(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS)) {
433
- if (!Object.hasOwn(record2, key)) continue;
434
- const value = readSafeInteger(record2[key], `$.verification.limits.${key}`, 1, ceiling);
443
+ if (!Object.hasOwn(record4, key)) continue;
444
+ const value = readSafeInteger(record4[key], `$.verification.limits.${key}`, 1, ceiling);
435
445
  Object.assign(output, { [key]: value });
436
446
  }
437
447
  if (output.maxImageBytes > output.maxResourceBytes) output.maxImageBytes = output.maxResourceBytes;
@@ -543,13 +553,13 @@ function createVerificationDeadline(timeoutMs, callerSignal, path) {
543
553
  };
544
554
  }
545
555
  function parseInspection(value, path) {
546
- const record2 = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
556
+ const record4 = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
547
557
  const output = {
548
- valid: readBoolean(requireKey(record2, "valid", path), `${path}.valid`),
549
- detectedMimeType: readString(requireKey(record2, "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()
550
560
  };
551
- assignOptional(output, "width", readOptional(record2, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
552
- assignOptional(output, "height", readOptional(record2, "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));
553
563
  return output;
554
564
  }
555
565
  function bytesStartWith(bytes, signature) {
@@ -664,62 +674,62 @@ async function verifyGpuModelResource(value, port, options) {
664
674
  }
665
675
  }
666
676
  function parseNode(value, path, budget) {
667
- const record2 = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
668
- const children = Object.freeze(readArray(requireKey(record2, "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}]`)));
669
679
  consumeBudget(budget, "sceneEdges", children.length, GPU_MODEL_DOCUMENT_LIMITS.sceneEdges, path, "scene edge");
670
680
  ensureUnique(children, `${path}.children`, "duplicate child node");
671
681
  const output = {
672
- id: readId(requireKey(record2, "id", path), `${path}.id`),
682
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
673
683
  children,
674
- localMatrix: parseMatrix(requireKey(record2, "localMatrix", path), `${path}.localMatrix`)
684
+ localMatrix: parseMatrix(requireKey(record4, "localMatrix", path), `${path}.localMatrix`)
675
685
  };
676
- assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
677
- assignOptional(output, "meshId", readOptional(record2, "meshId", readId, path));
678
- assignOptional(output, "skinId", readOptional(record2, "skinId", readId, path));
679
- assignOptional(output, "metadata", readOptional(record2, "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));
680
690
  return output;
681
691
  }
682
692
  function parseAccessor(value, path, budget) {
683
- const record2 = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
684
- const componentType = readEnum(requireKey(record2, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
685
- const elementType = readEnum(requireKey(record2, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
686
- const normalized = readOptional(record2, "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);
687
697
  if (normalized === true && componentType === "f32") return fail("invalid-value", `${path}.normalized`, "floating-point accessors cannot be normalized");
688
698
  if (elementType.startsWith("mat") && componentType !== "f32") return fail("invalid-value", `${path}.componentType`, "matrix accessors must use f32 components");
689
699
  const expectedComponents = ELEMENT_COMPONENTS[elementType];
690
- const min = readOptional(record2, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
691
- const max = readOptional(record2, "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);
692
702
  if (min === void 0 !== (max === void 0)) return fail("invalid-value", path, "accessor min and max must be supplied together");
693
703
  if (min && max) {
694
- const [componentMinimum, componentMaximum, integer] = COMPONENT_RANGES[componentType];
704
+ const [componentMinimum, componentMaximum, integer2] = COMPONENT_RANGES[componentType];
695
705
  for (let index = 0; index < expectedComponents; index += 1) {
696
706
  const minimum = min[index];
697
707
  const maximum = max[index];
698
708
  if (minimum > maximum) return fail("invalid-value", path, "accessor min cannot exceed max");
699
- 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))) {
700
710
  return fail("invalid-value", path, "accessor bounds are outside the component representation");
701
711
  }
702
712
  }
703
713
  }
704
- const count = readSafeInteger(requireKey(record2, "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);
705
715
  consumeBudget(budget, "accessorElements", count * expectedComponents, GPU_MODEL_DOCUMENT_LIMITS.accessorElements, path, "accessor element component");
706
716
  const output = {
707
- id: readId(requireKey(record2, "id", path), `${path}.id`),
708
- resourceId: readId(requireKey(record2, "resourceId", path), `${path}.resourceId`),
709
- byteOffset: readSafeInteger(requireKey(record2, "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`),
710
720
  count,
711
721
  componentType,
712
722
  elementType
713
723
  };
714
- assignOptional(output, "byteStride", readOptional(record2, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
724
+ assignOptional(output, "byteStride", readOptional(record4, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
715
725
  assignOptional(output, "normalized", normalized);
716
726
  assignOptional(output, "min", min);
717
727
  assignOptional(output, "max", max);
718
728
  return output;
719
729
  }
720
730
  function parsePrimitive(value, path, budget) {
721
- const record2 = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
722
- const attributesInput = readArray(requireKey(record2, "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);
723
733
  consumeBudget(budget, "primitiveAttributes", attributesInput.length, GPU_MODEL_DOCUMENT_LIMITS.primitiveAttributes, path, "primitive attribute");
724
734
  const attributes = Object.freeze(attributesInput.map((entry, index) => {
725
735
  const entryPath = `${path}.attributes[${index}]`;
@@ -732,53 +742,53 @@ function parsePrimitive(value, path, budget) {
732
742
  ensureUnique(attributes.map(({ semantic }) => semantic), `${path}.attributes`, "duplicate attribute semantic");
733
743
  if (!attributes.some(({ semantic }) => semantic === "POSITION")) return fail("invalid-value", `${path}.attributes`, "primitive requires POSITION");
734
744
  const output = {
735
- id: readId(requireKey(record2, "id", path), `${path}.id`),
736
- topology: readEnum(requireKey(record2, "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"]),
737
747
  attributes
738
748
  };
739
- assignOptional(output, "indicesAccessorId", readOptional(record2, "indicesAccessorId", readId, path));
740
- assignOptional(output, "materialId", readOptional(record2, "materialId", readId, path));
741
- assignOptional(output, "metadata", readOptional(record2, "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));
742
752
  return output;
743
753
  }
744
754
  function parseMesh(value, path, budget) {
745
- const record2 = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
746
- const input = readArray(requireKey(record2, "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);
747
757
  consumeBudget(budget, "primitives", input.length, GPU_MODEL_DOCUMENT_LIMITS.primitives, path, "primitive");
748
758
  const output = {
749
- id: readId(requireKey(record2, "id", path), `${path}.id`),
759
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
750
760
  primitives: Object.freeze(input.map((entry, index) => parsePrimitive(entry, `${path}.primitives[${index}]`, budget)))
751
761
  };
752
- assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
753
- assignOptional(output, "metadata", readOptional(record2, "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));
754
764
  return output;
755
765
  }
756
766
  function parseTextureTransform(value, path) {
757
- const record2 = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
758
- if (Reflect.ownKeys(record2).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");
759
769
  const output = {};
760
- assignOptional(output, "offset", readOptional(record2, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
761
- assignOptional(output, "rotationRadians", readOptional(record2, "rotationRadians", readFinite, path));
762
- assignOptional(output, "scale", readOptional(record2, "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));
763
773
  return output;
764
774
  }
765
775
  function parseTextureBinding(value, path) {
766
- const record2 = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
776
+ const record4 = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
767
777
  const output = {
768
- textureId: readId(requireKey(record2, "textureId", path), `${path}.textureId`),
769
- colorSpace: readEnum(requireKey(record2, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
770
- intendedUsage: readEnum(requireKey(record2, "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)
771
781
  };
772
- assignOptional(output, "samplerId", readOptional(record2, "samplerId", readId, path));
773
- assignOptional(output, "texCoordSet", readOptional(record2, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
774
- assignOptional(output, "transform", readOptional(record2, "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));
775
785
  return output;
776
786
  }
777
787
  function parseMaterialTextures(value, path) {
778
788
  const slots = Object.keys(MATERIAL_TEXTURE_SLOTS);
779
- const record2 = readRecord(value, path, slots);
789
+ const record4 = readRecord(value, path, slots);
780
790
  const output = {};
781
- for (const slot of slots) assignOptional(output, slot, readOptional(record2, slot, parseTextureBinding, path));
791
+ for (const slot of slots) assignOptional(output, slot, readOptional(record4, slot, parseTextureBinding, path));
782
792
  return output;
783
793
  }
784
794
  function parseMaterial(value, path, budget) {
@@ -804,138 +814,138 @@ function parseMaterial(value, path, budget) {
804
814
  "extensions",
805
815
  "sourceMetadata"
806
816
  ];
807
- const record2 = readRecord(value, path, keys);
808
- const alphaMode = readEnum(requireKey(record2, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
809
- const alphaCutoff = readOptional(record2, "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);
810
820
  if (alphaMode === "mask" && alphaCutoff === void 0) return fail("invalid-value", `${path}.alphaCutoff`, "mask materials require alphaCutoff");
811
821
  if (alphaMode !== "mask" && alphaCutoff !== void 0) return fail("invalid-value", `${path}.alphaCutoff`, "alphaCutoff is only valid for mask materials");
812
822
  const output = {
813
- id: readId(requireKey(record2, "id", path), `${path}.id`),
814
- workflow: readEnum(requireKey(record2, "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"]),
815
825
  alphaMode,
816
- baseColorFactor: readNumberTuple(requireKey(record2, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
817
- textures: parseMaterialTextures(requireKey(record2, "textures", path), `${path}.textures`),
818
- extensions: readMetadata(requireKey(record2, "extensions", path), `${path}.extensions`, budget),
819
- sourceMetadata: readMetadata(requireKey(record2, "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)
820
830
  };
821
- assignOptional(output, "name", readOptional(record2, "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));
822
832
  assignOptional(output, "alphaCutoff", alphaCutoff);
823
- assignOptional(output, "doubleSided", readOptional(record2, "doubleSided", readBoolean, path));
833
+ assignOptional(output, "doubleSided", readOptional(record4, "doubleSided", readBoolean, path));
824
834
  for (const key of ["metallicFactor", "roughnessFactor", "transmissionFactor", "clearcoatFactor", "clearcoatRoughnessFactor", "sheenRoughnessFactor", "occlusionStrength"]) {
825
- assignOptional(output, key, readOptional(record2, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
835
+ assignOptional(output, key, readOptional(record4, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
826
836
  }
827
- assignOptional(output, "normalScale", readOptional(record2, "normalScale", readFinite, path));
828
- assignOptional(output, "emissiveFactor", readOptional(record2, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
829
- assignOptional(output, "sheenColorFactor", readOptional(record2, "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));
830
840
  return output;
831
841
  }
832
842
  function parseTextureSampler(value, path) {
833
- const record2 = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
834
- const output = { id: readId(requireKey(record2, "id", path), `${path}.id`) };
835
- assignOptional(output, "wrapS", readOptional(record2, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
836
- assignOptional(output, "wrapT", readOptional(record2, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
837
- assignOptional(output, "minFilter", readOptional(record2, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
838
- assignOptional(output, "magFilter", readOptional(record2, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
839
- assignOptional(output, "mipFilter", readOptional(record2, "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));
840
850
  return output;
841
851
  }
842
852
  function parseTexture(value, path, budget) {
843
- const record2 = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
844
- const usages = Object.freeze(readArray(requireKey(record2, "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)));
845
855
  ensureUnique(usages, `${path}.intendedUsage`, "duplicate intended texture usage");
846
856
  const output = {
847
- id: readId(requireKey(record2, "id", path), `${path}.id`),
848
- contentHash: readSha256(requireKey(record2, "contentHash", path), `${path}.contentHash`),
849
- imageResourceId: readId(requireKey(record2, "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`),
850
860
  intendedUsage: usages,
851
- colorSpace: readEnum(requireKey(record2, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
852
- exportEmbeddingPolicy: readEnum(requireKey(record2, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
853
- sourceMetadata: readMetadata(requireKey(record2, "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)
854
864
  };
855
- assignOptional(output, "fileName", readOptional(record2, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
856
- assignOptional(output, "relativePath", readOptional(record2, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
857
- assignOptional(output, "mimeType", readOptional(record2, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
858
- assignOptional(output, "byteLength", readOptional(record2, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
859
- assignOptional(output, "sampler", readOptional(record2, "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));
860
870
  return output;
861
871
  }
862
872
  function parseSkeleton(value, path, budget) {
863
- const record2 = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
864
- const jointIds = Object.freeze(readArray(requireKey(record2, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
865
- const rootJointIds = Object.freeze(readArray(requireKey(record2, "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}]`)));
866
876
  consumeBudget(budget, "skinJoints", jointIds.length + rootJointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "rig joint");
867
877
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skeleton joint");
868
878
  ensureUnique(rootJointIds, `${path}.rootJointIds`, "duplicate root joint");
869
879
  const output = {
870
- id: readId(requireKey(record2, "id", path), `${path}.id`),
880
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
871
881
  jointIds,
872
882
  rootJointIds,
873
- sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
883
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
874
884
  };
875
- assignOptional(output, "name", readOptional(record2, "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));
876
886
  return output;
877
887
  }
878
888
  function parseJoint(value, path, budget) {
879
- const record2 = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
889
+ const record4 = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
880
890
  const output = {
881
- id: readId(requireKey(record2, "id", path), `${path}.id`),
882
- nodeId: readId(requireKey(record2, "nodeId", path), `${path}.nodeId`),
883
- restLocalTransform: parseTransformTrs(requireKey(record2, "restLocalTransform", path), `${path}.restLocalTransform`),
884
- sourceMetadata: readMetadata(requireKey(record2, "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)
885
895
  };
886
- assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
887
- assignOptional(output, "parentJointId", readOptional(record2, "parentJointId", readId, path));
888
- assignOptional(output, "inverseBindMatrix", readOptional(record2, "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));
889
899
  return output;
890
900
  }
891
- function parseWeightRows(value, path, rows, columns, integer, budget) {
901
+ function parseWeightRows(value, path, rows, columns, integer2, budget) {
892
902
  const input = readArray(value, path, rows, rows);
893
903
  consumeBudget(budget, "weightVertices", input.length, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, path, "weight vertex");
894
904
  consumeBudget(budget, "weightInfluences", input.length * columns, GPU_MODEL_DOCUMENT_LIMITS.weightInfluences, path, "weight influence");
895
905
  return Object.freeze(input.map((row, rowIndex) => {
896
- const parsed = readNumberTuple(row, `${path}[${rowIndex}]`, columns, 0, integer ? Number.MAX_SAFE_INTEGER : 1);
897
- 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");
898
908
  return parsed;
899
909
  }));
900
910
  }
901
911
  function parseVertexWeightSet(value, path, budget) {
902
- const record2 = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
903
- const influences = readSafeInteger(requireKey(record2, "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);
904
914
  if (influences % 4 !== 0) return fail("invalid-value", `${path}.influencesPerVertex`, "influencesPerVertex must be a multiple of four");
905
- const jointRows = readArray(requireKey(record2, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
906
- const weightRows = readArray(requireKey(record2, "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);
907
917
  if (jointRows.length !== weightRows.length) return fail("invalid-value", path, "joint and weight row counts must match");
908
918
  return {
909
- primitiveId: readId(requireKey(record2, "primitiveId", path), `${path}.primitiveId`),
919
+ primitiveId: readId(requireKey(record4, "primitiveId", path), `${path}.primitiveId`),
910
920
  influencesPerVertex: influences,
911
921
  joints: parseWeightRows(jointRows, `${path}.joints`, jointRows.length, influences, true, budget),
912
922
  weights: parseWeightRows(weightRows, `${path}.weights`, weightRows.length, influences, false, budget)
913
923
  };
914
924
  }
915
925
  function parseSkin(value, path, budget) {
916
- const record2 = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
917
- const jointIds = Object.freeze(readArray(requireKey(record2, "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}]`)));
918
928
  consumeBudget(budget, "skinJoints", jointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "skin joint");
919
929
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skin joint");
920
- const inverseInput = readArray(requireKey(record2, "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);
921
931
  if (inverseInput.length !== jointIds.length) return fail("invalid-value", `${path}.inverseBindMatrices`, "one inverse bind matrix is required per skin joint");
922
- const weightInput = readArray(requireKey(record2, "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);
923
933
  const weightSets = Object.freeze(weightInput.map((entry, index) => parseVertexWeightSet(entry, `${path}.weightSets[${index}]`, budget)));
924
934
  ensureUnique(weightSets.map(({ primitiveId }) => primitiveId), `${path}.weightSets`, "duplicate primitive weight set");
925
935
  return {
926
- id: readId(requireKey(record2, "id", path), `${path}.id`),
927
- skeletonId: readId(requireKey(record2, "skeletonId", path), `${path}.skeletonId`),
936
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
937
+ skeletonId: readId(requireKey(record4, "skeletonId", path), `${path}.skeletonId`),
928
938
  jointIds,
929
939
  inverseBindMatrices: Object.freeze(inverseInput.map((entry, index) => parseMatrix(entry, `${path}.inverseBindMatrices[${index}]`))),
930
940
  weightSets,
931
- sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
941
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
932
942
  };
933
943
  }
934
944
  function parseBlendShapeDelta(value, path) {
935
- const record2 = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
936
- const output = { primitiveId: readId(requireKey(record2, "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`) };
937
947
  for (const key of ["positionAccessorId", "normalAccessorId", "tangentAccessorId"]) {
938
- assignOptional(output, key, readOptional(record2, key, readId, path));
948
+ assignOptional(output, key, readOptional(record4, key, readId, path));
939
949
  }
940
950
  if (!output.positionAccessorId && !output.normalAccessorId && !output.tangentAccessorId) {
941
951
  return fail("invalid-value", path, "blend-shape delta requires at least one accessor");
@@ -943,8 +953,8 @@ function parseBlendShapeDelta(value, path) {
943
953
  return output;
944
954
  }
945
955
  function parseBlendShape(value, path, budget) {
946
- const record2 = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
947
- const deltaInput = readArray(requireKey(record2, "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);
948
958
  consumeBudget(
949
959
  budget,
950
960
  "blendShapeDeltas",
@@ -956,96 +966,96 @@ function parseBlendShape(value, path, budget) {
956
966
  const deltas = Object.freeze(deltaInput.map((entry, index) => parseBlendShapeDelta(entry, `${path}.deltas[${index}]`)));
957
967
  ensureUnique(deltas.map(({ primitiveId }) => primitiveId), `${path}.deltas`, "duplicate blend-shape primitive delta");
958
968
  const output = {
959
- id: readId(requireKey(record2, "id", path), `${path}.id`),
960
- meshId: readId(requireKey(record2, "meshId", path), `${path}.meshId`),
969
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
970
+ meshId: readId(requireKey(record4, "meshId", path), `${path}.meshId`),
961
971
  deltas,
962
- sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
972
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
963
973
  };
964
- assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
965
- assignOptional(output, "defaultWeight", readOptional(record2, "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));
966
976
  return output;
967
977
  }
968
978
  function parseAnimationSampler(value, path, budget) {
969
- const record2 = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
970
- const timesInput = readArray(requireKey(record2, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
971
- const valuesInput = readArray(requireKey(record2, "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);
972
982
  consumeBudget(budget, "animationValues", timesInput.length + valuesInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationValues, path, "animation value");
973
983
  const times = Object.freeze(timesInput.map((entry, index) => readFinite(entry, `${path}.inputTimesSeconds[${index}]`, 0)));
974
984
  for (let index = 1; index < times.length; index += 1) {
975
985
  if (times[index] <= times[index - 1]) return fail("invalid-value", `${path}.inputTimesSeconds`, "animation input times must be strictly increasing");
976
986
  }
977
987
  return {
978
- id: readId(requireKey(record2, "id", path), `${path}.id`),
979
- inputAccessorId: readId(requireKey(record2, "inputAccessorId", path), `${path}.inputAccessorId`),
980
- outputAccessorId: readId(requireKey(record2, "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`),
981
991
  inputTimesSeconds: times,
982
992
  outputValues: Object.freeze(valuesInput.map((entry, index) => readFinite(entry, `${path}.outputValues[${index}]`))),
983
- interpolation: readEnum(requireKey(record2, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
984
- sourceMetadata: readMetadata(requireKey(record2, "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)
985
995
  };
986
996
  }
987
997
  function parseAnimationChannel(value, path, budget) {
988
- const record2 = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
998
+ const record4 = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
989
999
  return {
990
- id: readId(requireKey(record2, "id", path), `${path}.id`),
991
- samplerId: readId(requireKey(record2, "samplerId", path), `${path}.samplerId`),
992
- targetType: readEnum(requireKey(record2, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
993
- targetId: readId(requireKey(record2, "targetId", path), `${path}.targetId`),
994
- sourceMetadata: readMetadata(requireKey(record2, "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)
995
1005
  };
996
1006
  }
997
1007
  function parseAnimation(value, path, budget) {
998
- const record2 = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
999
- const samplerInput = readArray(requireKey(record2, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
1000
- const channelInput = readArray(requireKey(record2, "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);
1001
1011
  consumeBudget(budget, "animationSamplers", samplerInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, path, "animation sampler");
1002
1012
  consumeBudget(budget, "animationChannels", channelInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, path, "animation channel");
1003
1013
  const output = {
1004
- id: readId(requireKey(record2, "id", path), `${path}.id`),
1005
- durationSeconds: readFinite(requireKey(record2, "durationSeconds", path), `${path}.durationSeconds`, 0),
1014
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
1015
+ durationSeconds: readFinite(requireKey(record4, "durationSeconds", path), `${path}.durationSeconds`, 0),
1006
1016
  samplers: Object.freeze(samplerInput.map((entry, index) => parseAnimationSampler(entry, `${path}.samplers[${index}]`, budget))),
1007
1017
  channels: Object.freeze(channelInput.map((entry, index) => parseAnimationChannel(entry, `${path}.channels[${index}]`, budget))),
1008
- sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1018
+ sourceMetadata: readMetadata(requireKey(record4, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1009
1019
  };
1010
- assignOptional(output, "name", readOptional(record2, "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));
1011
1021
  return output;
1012
1022
  }
1013
1023
  function parseAnalyticGeometry(value, path, budget) {
1014
- const record2 = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
1015
- if (requireKey(record2, "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");
1016
1026
  const output = {
1017
- id: readId(requireKey(record2, "id", path), `${path}.id`),
1027
+ id: readId(requireKey(record4, "id", path), `${path}.id`),
1018
1028
  kind: "brep-placeholder",
1019
- sourceResourceId: readId(requireKey(record2, "sourceResourceId", path), `${path}.sourceResourceId`),
1020
- sourceUnit: readEnum(requireKey(record2, "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"])
1021
1031
  };
1022
- assignOptional(output, "tessellatedMeshId", readOptional(record2, "tessellatedMeshId", readId, path));
1023
- assignOptional(output, "metadata", readOptional(record2, "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));
1024
1034
  return output;
1025
1035
  }
1026
1036
  function parseProvenance(value, budget) {
1027
1037
  const path = "$.provenance";
1028
- const record2 = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
1038
+ const record4 = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
1029
1039
  const output = {
1030
- sourceFormat: readString(requireKey(record2, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
1031
- sourceContentHash: readSha256(requireKey(record2, "sourceContentHash", path), `${path}.sourceContentHash`),
1032
- converterId: readString(requireKey(record2, "converterId", path), `${path}.converterId`, 128, TOKEN),
1033
- converterVersion: readString(requireKey(record2, "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)
1034
1044
  };
1035
- assignOptional(output, "provider", readOptional(record2, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
1036
- assignOptional(output, "sourceIdentifier", readOptional(record2, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
1037
- assignOptional(output, "metadata", readOptional(record2, "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));
1038
1048
  return output;
1039
1049
  }
1040
1050
  function parseDiagnostic(value, path, budget) {
1041
- const record2 = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
1051
+ const record4 = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
1042
1052
  const output = {
1043
- severity: readEnum(requireKey(record2, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
1044
- code: readString(requireKey(record2, "code", path), `${path}.code`, 128, TOKEN),
1045
- message: readString(requireKey(record2, "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)
1046
1056
  };
1047
- assignOptional(output, "path", readOptional(record2, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
1048
- assignOptional(output, "metadata", readOptional(record2, "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));
1049
1059
  return output;
1050
1060
  }
1051
1061
  function ensureUnique(values, path, detail = "duplicate identity") {
@@ -1686,11 +1696,11 @@ var DOCUMENT_KEYS = Object.freeze([
1686
1696
  ]);
1687
1697
  function parseGpuModelDocument(value) {
1688
1698
  const path = "$";
1689
- const record2 = readRecord(value, path, DOCUMENT_KEYS);
1690
- const schemaVersion = requireKey(record2, "schemaVersion", path);
1699
+ const record4 = readRecord(value, path, DOCUMENT_KEYS);
1700
+ const schemaVersion = requireKey(record4, "schemaVersion", path);
1691
1701
  if (schemaVersion !== GPU_MODEL_DOCUMENT_SCHEMA_VERSION) return fail("invalid-value", "$.schemaVersion", "unsupported schema version");
1692
1702
  const parseCollection = (key, maximum, parser, minimum = 0) => {
1693
- const input = readArray(requireKey(record2, key, path), `$.${key}`, maximum, minimum);
1703
+ const input = readArray(requireKey(record4, key, path), `$.${key}`, maximum, minimum);
1694
1704
  return Object.freeze(input.map((entry, index) => parser(entry, `$.${key}[${index}]`)));
1695
1705
  };
1696
1706
  const budget = {
@@ -1711,10 +1721,10 @@ function parseGpuModelDocument(value) {
1711
1721
  geometryWorldAccessorInstances: 0,
1712
1722
  geometryWorldVertices: 0
1713
1723
  };
1714
- const roots = Object.freeze(readArray(requireKey(record2, "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}]`)));
1715
1725
  const document = {
1716
1726
  schemaVersion,
1717
- coordinateSystem: parseCoordinateSystem(requireKey(record2, "coordinateSystem", path)),
1727
+ coordinateSystem: parseCoordinateSystem(requireKey(record4, "coordinateSystem", path)),
1718
1728
  roots,
1719
1729
  nodes: parseCollection("nodes", GPU_MODEL_DOCUMENT_LIMITS.nodes, (entry, entryPath) => parseNode(entry, entryPath, budget), 1),
1720
1730
  resources: parseCollection("resources", GPU_MODEL_DOCUMENT_LIMITS.resources, parseVerifiedResource, 1),
@@ -1728,10 +1738,10 @@ function parseGpuModelDocument(value) {
1728
1738
  blendShapes: parseCollection("blendShapes", GPU_MODEL_DOCUMENT_LIMITS.blendShapes, (entry, entryPath) => parseBlendShape(entry, entryPath, budget)),
1729
1739
  animations: parseCollection("animations", GPU_MODEL_DOCUMENT_LIMITS.animations, (entry, entryPath) => parseAnimation(entry, entryPath, budget)),
1730
1740
  analyticGeometry: parseCollection("analyticGeometry", GPU_MODEL_DOCUMENT_LIMITS.analyticGeometry, (entry, entryPath) => parseAnalyticGeometry(entry, entryPath, budget)),
1731
- bounds: parseBounds(requireKey(record2, "bounds", path), "$.bounds"),
1732
- provenance: parseProvenance(requireKey(record2, "provenance", path), budget),
1741
+ bounds: parseBounds(requireKey(record4, "bounds", path), "$.bounds"),
1742
+ provenance: parseProvenance(requireKey(record4, "provenance", path), budget),
1733
1743
  diagnostics: parseCollection("diagnostics", GPU_MODEL_DOCUMENT_LIMITS.diagnostics, (entry, entryPath) => parseDiagnostic(entry, entryPath, budget)),
1734
- metadata: readMetadata(requireKey(record2, "metadata", path), "$.metadata", budget)
1744
+ metadata: readMetadata(requireKey(record4, "metadata", path), "$.metadata", budget)
1735
1745
  };
1736
1746
  if (document.meshes.length === 0) return fail("invalid-value", "$.meshes", "v1 document bounds require at least one canonical mesh");
1737
1747
  validateReferences(document, budget);
@@ -1768,8 +1778,8 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1768
1778
  value.resources.forEach((resource, index) => assertVerifiedResourceEvidence(resource, limits, `$.resources[${index}]`));
1769
1779
  return value;
1770
1780
  }
1771
- const record2 = readRecord(value, "$", DOCUMENT_KEYS);
1772
- const resourceInput = readArray(requireKey(record2, "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);
1773
1783
  const parsed = resourceInput.map((entry, index) => {
1774
1784
  if (typeof entry === "object" && entry !== null && verifiedResources.has(entry) && verifiedResourceBytes.has(entry)) return entry;
1775
1785
  return parseResourceHeader(entry, `$.resources[${index}]`);
@@ -1782,7 +1792,7 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1782
1792
  }
1783
1793
  const candidate = {};
1784
1794
  for (const key of DOCUMENT_KEYS) {
1785
- if (Object.hasOwn(record2, key)) candidate[key] = record2[key];
1795
+ if (Object.hasOwn(record4, key)) candidate[key] = record4[key];
1786
1796
  }
1787
1797
  candidate.resources = verified;
1788
1798
  return createGpuModelDocument(candidate);
@@ -1847,11 +1857,11 @@ async function hashGpuModelDocumentCanonical(document) {
1847
1857
  return Array.from(new Uint8Array(digest), (value) => value.toString(16).padStart(2, "0")).join("");
1848
1858
  }
1849
1859
  function resolveStaticDemoLimits(options) {
1850
- const record2 = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1851
- const enabled = readBoolean(requireKey(record2, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1860
+ const record4 = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1861
+ const enabled = readBoolean(requireKey(record4, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1852
1862
  if (!enabled) return fail("feature-disabled", "$.options.pvoxModelsEnabled", `${GPU_MODEL_STATIC_DEMO_FEATURE_FLAG} is disabled`);
1853
- if (!Object.hasOwn(record2, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1854
- const overrides = readRecord(record2.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));
1855
1865
  const limits = { ...GPU_MODEL_STATIC_DEMO_LIMITS };
1856
1866
  for (const [key, ceiling] of Object.entries(GPU_MODEL_STATIC_DEMO_LIMITS)) {
1857
1867
  if (Object.hasOwn(overrides, key)) limits[key] = readSafeInteger(overrides[key], `$.options.limits.${key}`, 1, ceiling);
@@ -2220,15 +2230,15 @@ function snapshot(value, depth, budget, ancestors, memberLimit = GPU_MODEL_DIAGN
2220
2230
  return value;
2221
2231
  }
2222
2232
  if (typeof value !== "object" || ancestors.has(value)) fail2();
2223
- const array = Array.isArray(value);
2233
+ const array2 = Array.isArray(value);
2224
2234
  const prototype = Object.getPrototypeOf(value);
2225
- if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) fail2();
2235
+ if (array2 ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) fail2();
2226
2236
  const keys = Reflect.ownKeys(value);
2227
2237
  const maxMembers = memberLimit;
2228
- if (keys.length > maxMembers + (array ? 1 : 0)) fail2();
2238
+ if (keys.length > maxMembers + (array2 ? 1 : 0)) fail2();
2229
2239
  ancestors.add(value);
2230
2240
  charge(budget, 2);
2231
- if (array) {
2241
+ if (array2) {
2232
2242
  const length = Object.getOwnPropertyDescriptor(value, "length").value;
2233
2243
  if (length > maxMembers || keys.length !== length + 1) fail2();
2234
2244
  const result2 = [];
@@ -2313,7 +2323,7 @@ function evaluateGpuModelDiagnostics(value) {
2313
2323
  const diagnostics = [];
2314
2324
  const repairs = [];
2315
2325
  const losses = [];
2316
- let accepted = true;
2326
+ let accepted2 = true;
2317
2327
  for (const issue of issues) {
2318
2328
  const base = { code: issue.code, path: issue.path, message: issue.message };
2319
2329
  if (issue.kind === "violation") {
@@ -2324,20 +2334,565 @@ function evaluateGpuModelDiagnostics(value) {
2324
2334
  repairs.push(Object.freeze({ ruleId: rule.id, originalValue: issue.originalValue, action: rule.action, severity: rule.severity, affectedPath: issue.path, confidence: rule.confidence }));
2325
2335
  diagnostics.push(Object.freeze({ ...base, kind: "repair", severity: rule.severity, ruleId: rule.id }));
2326
2336
  } else {
2327
- accepted = false;
2337
+ accepted2 = false;
2328
2338
  diagnostics.push(Object.freeze({ ...base, kind: "blocking-error", severity: "error", ruleId: issue.ruleId }));
2329
2339
  }
2330
2340
  } else if (issue.kind === "unsupported") {
2331
- accepted = false;
2341
+ accepted2 = false;
2332
2342
  diagnostics.push(Object.freeze({ ...issue, severity: "error" }));
2333
2343
  } else if (issue.kind === "loss") {
2334
- if (!allowLoss) accepted = false;
2344
+ if (!allowLoss) accepted2 = false;
2335
2345
  const loss = Object.freeze({ ...issue, severity: "warning" });
2336
2346
  losses.push(loss);
2337
2347
  diagnostics.push(loss);
2338
2348
  } else diagnostics.push(Object.freeze({ ...issue, severity: "warning" }));
2339
2349
  }
2340
- 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 } : {} });
2351
+ }
2352
+
2353
+ // src/resource-graph.ts
2354
+ var GPU_MODEL_RESOURCE_GRAPH_LIMITS = Object.freeze({
2355
+ nodes: 65536,
2356
+ edges: 262144,
2357
+ packages: 256,
2358
+ pathLength: 1024
2359
+ });
2360
+ var GPU_MODEL_RESOURCE_GRAPH_SCHEMA_VERSION = "plasius.gpu-model-resource-graph/1";
2361
+ var KINDS = ["resource", "accessor", "texture", "material", "skeleton", "skin", "animation", "provenance", "package"];
2362
+ var GpuModelResourceGraphError = class extends TypeError {
2363
+ constructor(code) {
2364
+ super(`Invalid GPU model resource graph: ${code}`);
2365
+ this.code = code;
2366
+ this.name = "GpuModelResourceGraphError";
2367
+ }
2368
+ code;
2369
+ };
2370
+ function fail3(code = "invalid-value") {
2371
+ throw new GpuModelResourceGraphError(code);
2372
+ }
2373
+ function identifier(value) {
2374
+ if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value)) fail3();
2375
+ return value;
2376
+ }
2377
+ function choice(value, choices) {
2378
+ if (typeof value !== "string" || !choices.includes(value)) fail3();
2379
+ return value;
2380
+ }
2381
+ function gpuModelResourceKey(kind, id) {
2382
+ return JSON.stringify([choice(kind, KINDS), identifier(id)]);
2383
+ }
2384
+ function record2(value, keys) {
2385
+ if (typeof value !== "object" || value === null || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail3();
2386
+ const names = Reflect.ownKeys(value);
2387
+ if (names.length !== keys.length || names.some((key) => typeof key !== "string" || !keys.includes(key))) fail3();
2388
+ const result = /* @__PURE__ */ Object.create(null);
2389
+ for (const key of keys) {
2390
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2391
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail3();
2392
+ result[key] = descriptor.value;
2393
+ }
2394
+ return result;
2395
+ }
2396
+ function array(value, maximum) {
2397
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) fail3();
2398
+ if (value.length > maximum) fail3("limit-exceeded");
2399
+ if (Reflect.ownKeys(value).length !== value.length + 1) fail3();
2400
+ const output = [];
2401
+ for (let index = 0; index < value.length; index++) {
2402
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
2403
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail3();
2404
+ output.push(descriptor.value);
2405
+ }
2406
+ return output;
2407
+ }
2408
+ function relativePath(value) {
2409
+ if (typeof value !== "string" || value.length > GPU_MODEL_RESOURCE_GRAPH_LIMITS.pathLength || !/^[A-Za-z0-9._/-]+$/u.test(value)) fail3();
2410
+ for (const part of value.split("/")) {
2411
+ if (!part || part.length > 255 || part === "." || part === ".." || part.endsWith(".") || /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\.|$)/iu.test(part)) fail3();
2412
+ }
2413
+ return value;
2414
+ }
2415
+ function parseLocation(value) {
2416
+ if (typeof value !== "object" || value === null) fail3();
2417
+ const kind = Object.getOwnPropertyDescriptor(value, "kind");
2418
+ if (!kind || !("value" in kind)) fail3();
2419
+ if (kind.value === "embedded") {
2420
+ record2(value, ["kind"]);
2421
+ return Object.freeze({ kind: "embedded" });
2422
+ }
2423
+ const location = record2(value, ["kind", "relativePath"]);
2424
+ if (location.kind !== "external") fail3();
2425
+ return Object.freeze({ kind: "external", relativePath: relativePath(location.relativePath) });
2426
+ }
2427
+ var graphs = /* @__PURE__ */ new WeakSet();
2428
+ function isGpuModelResourceGraph(value) {
2429
+ return typeof value === "object" && value !== null && graphs.has(value);
2430
+ }
2431
+ var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
2432
+ function createGpuModelResourceGraph(document, input) {
2433
+ try {
2434
+ return buildGraph(document, input);
2435
+ } catch (error) {
2436
+ if (error instanceof GpuModelResourceGraphError) throw error;
2437
+ fail3();
2438
+ }
2439
+ }
2440
+ function buildGraph(document, input) {
2441
+ if (!isGpuModelDocument(document)) fail3("unverified-document");
2442
+ const raw = record2(input, ["resources", "packages"]);
2443
+ const resourceInputs = array(raw.resources, GPU_MODEL_RESOURCE_GRAPH_LIMITS.nodes);
2444
+ const packageInputs = array(raw.packages, GPU_MODEL_RESOURCE_GRAPH_LIMITS.packages);
2445
+ const nodes = /* @__PURE__ */ new Map();
2446
+ let edgeCount = 0;
2447
+ const add = (kind, id, value, dependencies) => {
2448
+ const key = gpuModelResourceKey(kind, id);
2449
+ if (nodes.has(key)) fail3("duplicate-reference");
2450
+ if (nodes.size >= GPU_MODEL_RESOURCE_GRAPH_LIMITS.nodes) fail3("limit-exceeded");
2451
+ edgeCount += dependencies.length;
2452
+ if (edgeCount > GPU_MODEL_RESOURCE_GRAPH_LIMITS.edges) fail3("limit-exceeded");
2453
+ nodes.set(key, Object.freeze({ kind, id, key, value, dependencies: Object.freeze([...new Set(dependencies)].sort(compare)) }));
2454
+ };
2455
+ const resources = new Map(document.resources.map((resource) => [resource.id, resource]));
2456
+ const paths = /* @__PURE__ */ new Set();
2457
+ for (const entry of resourceInputs) {
2458
+ const binding = record2(entry, ["resourceId", "location", "exportEmbeddingPolicy"]);
2459
+ const id = identifier(binding.resourceId);
2460
+ const resource = resources.get(id);
2461
+ if (!resource) fail3("missing-reference");
2462
+ const location = parseLocation(binding.location);
2463
+ const exportEmbeddingPolicy = choice(binding.exportEmbeddingPolicy, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]);
2464
+ if (location.kind === "embedded" && exportEmbeddingPolicy === "forbid-embedding") fail3();
2465
+ if (location.kind === "external") {
2466
+ const path = location.relativePath.toLowerCase();
2467
+ if (paths.has(path)) fail3("duplicate-reference");
2468
+ paths.add(path);
2469
+ }
2470
+ add("resource", id, Object.freeze({ resource, location, exportEmbeddingPolicy }), []);
2471
+ }
2472
+ for (const path of paths) {
2473
+ for (let index = path.indexOf("/"); index !== -1; index = path.indexOf("/", index + 1)) {
2474
+ if (paths.has(path.slice(0, index))) fail3("duplicate-reference");
2475
+ }
2476
+ }
2477
+ if (nodes.size !== resources.size) fail3("missing-reference");
2478
+ addDocumentNodes(document, add);
2479
+ for (const entry of packageInputs) {
2480
+ const pkg = record2(entry, ["id", "members"]);
2481
+ const id = identifier(pkg.id);
2482
+ const members = [];
2483
+ const dependencies = /* @__PURE__ */ new Set();
2484
+ for (const entry2 of array(pkg.members, GPU_MODEL_RESOURCE_GRAPH_LIMITS.edges - edgeCount)) {
2485
+ const reference = record2(entry2, ["kind", "id"]);
2486
+ const member = Object.freeze({ kind: choice(reference.kind, KINDS), id: identifier(reference.id) });
2487
+ const key = gpuModelResourceKey(member.kind, member.id);
2488
+ if (dependencies.has(key)) fail3("duplicate-reference");
2489
+ dependencies.add(key);
2490
+ members.push(member);
2491
+ }
2492
+ members.sort((a, b) => compare(gpuModelResourceKey(a.kind, a.id), gpuModelResourceKey(b.kind, b.id)));
2493
+ add("package", id, Object.freeze({ id, members: Object.freeze(members) }), [...dependencies]);
2494
+ }
2495
+ validateDag(nodes);
2496
+ const result = Object.freeze({ schemaVersion: GPU_MODEL_RESOURCE_GRAPH_SCHEMA_VERSION, nodes: Object.freeze([...nodes.values()].sort((a, b) => compare(a.key, b.key))) });
2497
+ graphs.add(result);
2498
+ return result;
2499
+ }
2500
+ function addDocumentNodes(document, add) {
2501
+ const key = gpuModelResourceKey;
2502
+ for (const value of document.accessors) add("accessor", value.id, value, [key("resource", value.resourceId)]);
2503
+ for (const value of document.textures) add("texture", value.id, value, [key("resource", value.imageResourceId)]);
2504
+ for (const value of document.materials) add("material", value.id, value, Object.values(value.textures).map((binding) => key("texture", binding.textureId)));
2505
+ const joints = new Map(document.joints.map((joint) => [joint.id, joint]));
2506
+ for (const skeleton of document.skeletons) add("skeleton", skeleton.id, Object.freeze({ skeleton, joints: Object.freeze(skeleton.jointIds.map((id) => joints.get(id))) }), []);
2507
+ const primitives = new Map(document.meshes.flatMap((mesh) => mesh.primitives.map((primitive) => [primitive.id, primitive])));
2508
+ for (const value of document.skins) {
2509
+ const dependencies = [key("skeleton", value.skeletonId)];
2510
+ for (const weightSet of value.weightSets) {
2511
+ for (const attribute of primitives.get(weightSet.primitiveId).attributes) {
2512
+ if (/^(JOINTS|WEIGHTS)_/u.test(attribute.semantic)) dependencies.push(key("accessor", attribute.accessorId));
2513
+ }
2514
+ }
2515
+ add("skin", value.id, value, dependencies);
2516
+ }
2517
+ for (const value of document.animations) add("animation", value.id, value, value.samplers.flatMap((sampler) => [key("accessor", sampler.inputAccessorId), key("accessor", sampler.outputAccessorId)]));
2518
+ add("provenance", "source", document.provenance, []);
2519
+ }
2520
+ function validateDag(nodes) {
2521
+ const remaining = /* @__PURE__ */ new Map();
2522
+ const dependents = /* @__PURE__ */ new Map();
2523
+ const queue = [];
2524
+ for (const node of nodes.values()) {
2525
+ remaining.set(node.key, node.dependencies.length);
2526
+ if (!node.dependencies.length) queue.push(node.key);
2527
+ for (const dependency of node.dependencies) {
2528
+ if (!nodes.has(dependency)) fail3("missing-reference");
2529
+ const users = dependents.get(dependency) ?? [];
2530
+ users.push(node.key);
2531
+ dependents.set(dependency, users);
2532
+ }
2533
+ }
2534
+ for (let index = 0; index < queue.length; index++) {
2535
+ for (const dependent of dependents.get(queue[index]) ?? []) {
2536
+ const count = remaining.get(dependent) - 1;
2537
+ remaining.set(dependent, count);
2538
+ if (!count) queue.push(dependent);
2539
+ }
2540
+ }
2541
+ if (queue.length !== nodes.size) fail3("graph-cycle");
2542
+ }
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);
2341
2896
  }
2342
2897
 
2343
2898
  // src/index.ts
@@ -2354,6 +2909,8 @@ var packageBootstrap = Object.freeze({
2354
2909
  GPU_MODEL_DOCUMENT_LIMITS,
2355
2910
  GPU_MODEL_DOCUMENT_SCHEMA_VERSION,
2356
2911
  GPU_MODEL_REPAIR_RULES,
2912
+ GPU_MODEL_RESOURCE_GRAPH_LIMITS,
2913
+ GPU_MODEL_RESOURCE_GRAPH_SCHEMA_VERSION,
2357
2914
  GPU_MODEL_RESOURCE_VERIFICATION_LIMITS,
2358
2915
  GPU_MODEL_STATIC_DEMO_FEATURE_FLAG,
2359
2916
  GPU_MODEL_STATIC_DEMO_LIMITS,
@@ -2364,15 +2921,23 @@ var packageBootstrap = Object.freeze({
2364
2921
  GPU_MODEL_STATIC_DEMO_PROFILE_VERSION,
2365
2922
  GpuModelDiagnosticsError,
2366
2923
  GpuModelDocumentError,
2924
+ GpuModelResourceGraphError,
2925
+ MODEL_REGISTRY_LIMITS,
2926
+ ModelRegistryError,
2367
2927
  assertGpuModelStaticDemoNormalization,
2368
2928
  canCreateGpuModelDocument,
2369
2929
  createAndVerifyGpuModelDocument,
2370
2930
  createGpuModelDocument,
2931
+ createGpuModelResourceGraph,
2371
2932
  createGpuModelStaticDemoCompilerInput,
2933
+ createModelConversionRegistry,
2934
+ createModelResultBase,
2372
2935
  encodeGpuModelDocumentCanonical,
2373
2936
  evaluateGpuModelDiagnostics,
2937
+ gpuModelResourceKey,
2374
2938
  hashGpuModelDocumentCanonical,
2375
2939
  isGpuModelDocument,
2940
+ isGpuModelResourceGraph,
2376
2941
  packageBootstrap,
2377
2942
  packageName,
2378
2943
  verifyGpuModelResource