@plasius/gpu-model-core 0.1.0 → 0.3.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
@@ -21,8 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CANONICAL_GPU_MODEL_COORDINATE_SYSTEM: () => CANONICAL_GPU_MODEL_COORDINATE_SYSTEM,
24
+ GPU_MODEL_DIAGNOSTIC_LIMITS: () => GPU_MODEL_DIAGNOSTIC_LIMITS,
24
25
  GPU_MODEL_DOCUMENT_LIMITS: () => GPU_MODEL_DOCUMENT_LIMITS,
25
26
  GPU_MODEL_DOCUMENT_SCHEMA_VERSION: () => GPU_MODEL_DOCUMENT_SCHEMA_VERSION,
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,
26
30
  GPU_MODEL_RESOURCE_VERIFICATION_LIMITS: () => GPU_MODEL_RESOURCE_VERIFICATION_LIMITS,
27
31
  GPU_MODEL_STATIC_DEMO_FEATURE_FLAG: () => GPU_MODEL_STATIC_DEMO_FEATURE_FLAG,
28
32
  GPU_MODEL_STATIC_DEMO_LIMITS: () => GPU_MODEL_STATIC_DEMO_LIMITS,
@@ -31,15 +35,21 @@ __export(index_exports, {
31
35
  GPU_MODEL_STATIC_DEMO_MAX_EXTENT_METRES: () => GPU_MODEL_STATIC_DEMO_MAX_EXTENT_METRES,
32
36
  GPU_MODEL_STATIC_DEMO_NORMALIZATION_CONTRACT: () => GPU_MODEL_STATIC_DEMO_NORMALIZATION_CONTRACT,
33
37
  GPU_MODEL_STATIC_DEMO_PROFILE_VERSION: () => GPU_MODEL_STATIC_DEMO_PROFILE_VERSION,
38
+ GpuModelDiagnosticsError: () => GpuModelDiagnosticsError,
34
39
  GpuModelDocumentError: () => GpuModelDocumentError,
40
+ GpuModelResourceGraphError: () => GpuModelResourceGraphError,
35
41
  assertGpuModelStaticDemoNormalization: () => assertGpuModelStaticDemoNormalization,
36
42
  canCreateGpuModelDocument: () => canCreateGpuModelDocument,
37
43
  createAndVerifyGpuModelDocument: () => createAndVerifyGpuModelDocument,
38
44
  createGpuModelDocument: () => createGpuModelDocument,
45
+ createGpuModelResourceGraph: () => createGpuModelResourceGraph,
39
46
  createGpuModelStaticDemoCompilerInput: () => createGpuModelStaticDemoCompilerInput,
40
47
  encodeGpuModelDocumentCanonical: () => encodeGpuModelDocumentCanonical,
48
+ evaluateGpuModelDiagnostics: () => evaluateGpuModelDiagnostics,
49
+ gpuModelResourceKey: () => gpuModelResourceKey,
41
50
  hashGpuModelDocumentCanonical: () => hashGpuModelDocumentCanonical,
42
51
  isGpuModelDocument: () => isGpuModelDocument,
52
+ isGpuModelResourceGraph: () => isGpuModelResourceGraph,
43
53
  packageBootstrap: () => packageBootstrap,
44
54
  packageName: () => packageName,
45
55
  verifyGpuModelResource: () => verifyGpuModelResource
@@ -220,9 +230,9 @@ function readRecord(value, path, allowedKeys) {
220
230
  }
221
231
  return value;
222
232
  }
223
- function requireKey(record, key, path) {
224
- if (!Object.hasOwn(record, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
225
- return record[key];
233
+ function requireKey(record3, key, path) {
234
+ if (!Object.hasOwn(record3, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
235
+ return record3[key];
226
236
  }
227
237
  function readArray(value, path, maximum, minimum = 0) {
228
238
  if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
@@ -301,9 +311,9 @@ function readNumberTuple(value, path, length, minimum, maximum) {
301
311
  const input = readArray(value, path, length, length);
302
312
  return Object.freeze(input.map((entry, index) => readFinite(entry, `${path}[${index}]`, minimum, maximum)));
303
313
  }
304
- function readOptional(record, key, parser, path) {
305
- if (!Object.hasOwn(record, key)) return void 0;
306
- return parser(record[key], `${path}.${key}`);
314
+ function readOptional(record3, key, parser, path) {
315
+ if (!Object.hasOwn(record3, key)) return void 0;
316
+ return parser(record3[key], `${path}.${key}`);
307
317
  }
308
318
  function assignOptional(target, key, value) {
309
319
  if (value !== void 0) Object.assign(target, { [key]: value });
@@ -325,17 +335,17 @@ function readMetadataValue(value, path, depth, budget) {
325
335
  return Object.freeze(input.map((entry, index) => readMetadataValue(entry, `${path}[${index}]`, depth + 1, budget)));
326
336
  }
327
337
  if (typeof value !== "object" || value === null) return fail("invalid-type", path, "metadata value is not JSON-compatible");
328
- const record = value;
329
- const prototype = Object.getPrototypeOf(record);
338
+ const record3 = value;
339
+ const prototype = Object.getPrototypeOf(record3);
330
340
  if (prototype !== Object.prototype && prototype !== null) return fail("invalid-type", path, "metadata objects must be plain objects");
331
- const keys = Reflect.ownKeys(record);
341
+ const keys = Reflect.ownKeys(record3);
332
342
  if (keys.length > GPU_MODEL_DOCUMENT_LIMITS.metadataObjectKeys) return fail("limit-exceeded", path, "metadata object has too many keys");
333
343
  const output = {};
334
344
  for (const key of keys) {
335
345
  if (typeof key !== "string") return fail("unknown-key", path, "metadata symbol keys are not permitted");
336
346
  if (PROHIBITED_KEYS.has(key)) return fail("unknown-key", `${path}.${key}`, "prohibited metadata key");
337
347
  readString(key, `${path}.[key]`, GPU_MODEL_DOCUMENT_LIMITS.metadataKeyLength, METADATA_KEY);
338
- const descriptor = Object.getOwnPropertyDescriptor(record, key);
348
+ const descriptor = Object.getOwnPropertyDescriptor(record3, key);
339
349
  if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) return fail("invalid-type", `${path}.${key}`, "metadata properties must be enumerable data properties");
340
350
  output[key] = readMetadataValue(descriptor.value, `${path}.${key}`, depth + 1, budget);
341
351
  }
@@ -348,9 +358,9 @@ function readMetadata(value, path, budget) {
348
358
  }
349
359
  function parseCoordinateSystem(value) {
350
360
  const path = "$.coordinateSystem";
351
- const record = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
361
+ const record3 = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
352
362
  for (const [key, expected] of Object.entries(CANONICAL_GPU_MODEL_COORDINATE_SYSTEM)) {
353
- if (requireKey(record, key, path) !== expected) return fail("invalid-value", `${path}.${key}`, "coordinate system is not canonical");
363
+ if (requireKey(record3, key, path) !== expected) return fail("invalid-value", `${path}.${key}`, "coordinate system is not canonical");
354
364
  }
355
365
  return CANONICAL_GPU_MODEL_COORDINATE_SYSTEM;
356
366
  }
@@ -362,19 +372,19 @@ function parseMatrix(value, path, requireAffine = true) {
362
372
  return matrix;
363
373
  }
364
374
  function parseBounds(value, path) {
365
- const record = readRecord(value, path, ["min", "max"]);
366
- const min = readNumberTuple(requireKey(record, "min", path), `${path}.min`, 3);
367
- const max = readNumberTuple(requireKey(record, "max", path), `${path}.max`, 3);
375
+ const record3 = readRecord(value, path, ["min", "max"]);
376
+ const min = readNumberTuple(requireKey(record3, "min", path), `${path}.min`, 3);
377
+ const max = readNumberTuple(requireKey(record3, "max", path), `${path}.max`, 3);
368
378
  for (let index = 0; index < 3; index += 1) {
369
379
  if (min[index] > max[index]) return fail("invalid-value", path, "bounds min cannot exceed max");
370
380
  }
371
381
  return { min, max };
372
382
  }
373
383
  function parseTransformTrs(value, path) {
374
- const record = readRecord(value, path, ["translation", "rotation", "scale"]);
375
- const translation = readNumberTuple(requireKey(record, "translation", path), `${path}.translation`, 3);
376
- const rotation = readNumberTuple(requireKey(record, "rotation", path), `${path}.rotation`, 4);
377
- const scale = readNumberTuple(requireKey(record, "scale", path), `${path}.scale`, 3);
384
+ const record3 = readRecord(value, path, ["translation", "rotation", "scale"]);
385
+ const translation = readNumberTuple(requireKey(record3, "translation", path), `${path}.translation`, 3);
386
+ const rotation = readNumberTuple(requireKey(record3, "rotation", path), `${path}.rotation`, 4);
387
+ const scale = readNumberTuple(requireKey(record3, "scale", path), `${path}.scale`, 3);
378
388
  const magnitude = Math.hypot(...rotation);
379
389
  if (!almostEqual(magnitude, 1, 1e-4)) return fail("invalid-value", `${path}.rotation`, "rotation quaternion must be normalized");
380
390
  return { translation, rotation, scale };
@@ -396,25 +406,25 @@ function intrinsicBlobArrayBuffer(blob) {
396
406
  return Reflect.apply(blobArrayBuffer, blob, []);
397
407
  }
398
408
  function parseResourceHeader(value, path) {
399
- const record = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
400
- const kind = readEnum(requireKey(record, "kind", path), `${path}.kind`, ["buffer", "image"]);
401
- const mimeType = readString(requireKey(record, "mimeType", path), `${path}.mimeType`, 128, MIME_TYPE).toLowerCase();
409
+ const record3 = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
410
+ const kind = readEnum(requireKey(record3, "kind", path), `${path}.kind`, ["buffer", "image"]);
411
+ const mimeType = readString(requireKey(record3, "mimeType", path), `${path}.mimeType`, 128, MIME_TYPE).toLowerCase();
402
412
  if (kind === "image" ? !IMAGE_MIME_TYPES.has(mimeType) : !BUFFER_MIME_TYPES.has(mimeType)) {
403
413
  return fail("invalid-value", `${path}.mimeType`, `unsupported ${kind} MIME type`);
404
414
  }
405
- const payload = requireKey(record, "payload", path);
415
+ const payload = requireKey(record3, "payload", path);
406
416
  if (!(payload instanceof Blob)) return fail("invalid-type", `${path}.payload`, "expected an immutable Blob payload");
407
417
  if (Object.getPrototypeOf(payload) !== Blob.prototype || Reflect.ownKeys(payload).some((key) => typeof key === "string")) {
408
418
  return fail("invalid-type", `${path}.payload`, "expected a native Blob without caller-defined properties");
409
419
  }
410
- const byteLength = readSafeInteger(requireKey(record, "byteLength", path), `${path}.byteLength`, 1);
420
+ const byteLength = readSafeInteger(requireKey(record3, "byteLength", path), `${path}.byteLength`, 1);
411
421
  if (intrinsicBlobSize(payload) !== byteLength) return fail("payload-length-mismatch", `${path}.payload`, "payload byte length does not match byteLength");
412
422
  const payloadType = intrinsicBlobType(payload);
413
423
  if (payloadType && payloadType.toLowerCase() !== mimeType) return fail("invalid-value", `${path}.payload`, "payload MIME type does not match mimeType");
414
424
  return {
415
- id: readId(requireKey(record, "id", path), `${path}.id`),
425
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
416
426
  kind,
417
- contentHash: readSha256(requireKey(record, "contentHash", path), `${path}.contentHash`),
427
+ contentHash: readSha256(requireKey(record3, "contentHash", path), `${path}.contentHash`),
418
428
  byteLength,
419
429
  mimeType,
420
430
  payload
@@ -423,11 +433,11 @@ function parseResourceHeader(value, path) {
423
433
  function resolveVerificationLimits(options) {
424
434
  const overrides = options?.limits;
425
435
  if (overrides === void 0) return GPU_MODEL_RESOURCE_VERIFICATION_LIMITS;
426
- const record = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
436
+ const record3 = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
427
437
  const output = { ...GPU_MODEL_RESOURCE_VERIFICATION_LIMITS };
428
438
  for (const [key, ceiling] of Object.entries(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS)) {
429
- if (!Object.hasOwn(record, key)) continue;
430
- const value = readSafeInteger(record[key], `$.verification.limits.${key}`, 1, ceiling);
439
+ if (!Object.hasOwn(record3, key)) continue;
440
+ const value = readSafeInteger(record3[key], `$.verification.limits.${key}`, 1, ceiling);
431
441
  Object.assign(output, { [key]: value });
432
442
  }
433
443
  if (output.maxImageBytes > output.maxResourceBytes) output.maxImageBytes = output.maxResourceBytes;
@@ -480,7 +490,7 @@ function verificationAbortError(signal, path) {
480
490
  function throwIfVerificationAborted(signal, path) {
481
491
  if (signal.aborted) throw verificationAbortError(signal, path);
482
492
  }
483
- function createBlobChunks(blob, chunkBytes, observed, signal, path, snapshot) {
493
+ function createBlobChunks(blob, chunkBytes, observed, signal, path, snapshot2) {
484
494
  return {
485
495
  async *[Symbol.asyncIterator]() {
486
496
  const size = intrinsicBlobSize(blob);
@@ -490,7 +500,7 @@ function createBlobChunks(blob, chunkBytes, observed, signal, path, snapshot) {
490
500
  const chunk = new Uint8Array(await intrinsicBlobArrayBuffer(intrinsicBlobSlice(blob, offset, end)));
491
501
  throwIfVerificationAborted(signal, path);
492
502
  observed.bytes += chunk.byteLength;
493
- if (snapshot) snapshot.set(chunk, offset);
503
+ if (snapshot2) snapshot2.set(chunk, offset);
494
504
  yield chunk;
495
505
  }
496
506
  throwIfVerificationAborted(signal, path);
@@ -539,13 +549,13 @@ function createVerificationDeadline(timeoutMs, callerSignal, path) {
539
549
  };
540
550
  }
541
551
  function parseInspection(value, path) {
542
- const record = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
552
+ const record3 = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
543
553
  const output = {
544
- valid: readBoolean(requireKey(record, "valid", path), `${path}.valid`),
545
- detectedMimeType: readString(requireKey(record, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
554
+ valid: readBoolean(requireKey(record3, "valid", path), `${path}.valid`),
555
+ detectedMimeType: readString(requireKey(record3, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
546
556
  };
547
- assignOptional(output, "width", readOptional(record, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
548
- assignOptional(output, "height", readOptional(record, "height", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
557
+ assignOptional(output, "width", readOptional(record3, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
558
+ assignOptional(output, "height", readOptional(record3, "height", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
549
559
  return output;
550
560
  }
551
561
  function bytesStartWith(bytes, signature) {
@@ -618,12 +628,12 @@ async function verifyParsedResource(resource, port, limits, options, path = "$.r
618
628
  } else if (inspection.width !== void 0 || inspection.height !== void 0) {
619
629
  return fail("resource-verification-failed", path, "buffer inspection must not report image dimensions");
620
630
  }
621
- const snapshot = new Uint8Array(resource.byteLength);
631
+ const snapshot2 = new Uint8Array(resource.byteLength);
622
632
  const digestObserved = { bytes: 0 };
623
633
  let digest;
624
634
  try {
625
635
  digest = await deadline.race(() => port.digestSha256(
626
- createBlobChunks(resource.payload, limits.chunkBytes, digestObserved, deadline.signal, path, snapshot),
636
+ createBlobChunks(resource.payload, limits.chunkBytes, digestObserved, deadline.signal, path, snapshot2),
627
637
  context
628
638
  ));
629
639
  } catch (error) {
@@ -636,13 +646,13 @@ async function verifyParsedResource(resource, port, limits, options, path = "$.r
636
646
  if (typeof digest !== "string" || !SHA_256.test(digest) || digest !== resource.contentHash) {
637
647
  return fail("resource-verification-failed", path, "resource digest does not match contentHash");
638
648
  }
639
- if (resource.kind === "image") validateImageMagic(resource, snapshot, inspection, path);
649
+ if (resource.kind === "image") validateImageMagic(resource, snapshot2, inspection, path);
640
650
  const verified = Object.freeze({
641
651
  ...resource,
642
- payload: new Blob([snapshot], { type: resource.mimeType })
652
+ payload: new Blob([snapshot2], { type: resource.mimeType })
643
653
  });
644
654
  verifiedResources.add(verified);
645
- verifiedResourceBytes.set(verified, snapshot);
655
+ verifiedResourceBytes.set(verified, snapshot2);
646
656
  verifiedResourceEvidence.set(verified, Object.freeze({ ...inspection }));
647
657
  return verified;
648
658
  } finally {
@@ -660,31 +670,31 @@ async function verifyGpuModelResource(value, port, options) {
660
670
  }
661
671
  }
662
672
  function parseNode(value, path, budget) {
663
- const record = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
664
- const children = Object.freeze(readArray(requireKey(record, "children", path), `${path}.children`, GPU_MODEL_DOCUMENT_LIMITS.nodes).map((entry, index) => readId(entry, `${path}.children[${index}]`)));
673
+ const record3 = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
674
+ const children = Object.freeze(readArray(requireKey(record3, "children", path), `${path}.children`, GPU_MODEL_DOCUMENT_LIMITS.nodes).map((entry, index) => readId(entry, `${path}.children[${index}]`)));
665
675
  consumeBudget(budget, "sceneEdges", children.length, GPU_MODEL_DOCUMENT_LIMITS.sceneEdges, path, "scene edge");
666
676
  ensureUnique(children, `${path}.children`, "duplicate child node");
667
677
  const output = {
668
- id: readId(requireKey(record, "id", path), `${path}.id`),
678
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
669
679
  children,
670
- localMatrix: parseMatrix(requireKey(record, "localMatrix", path), `${path}.localMatrix`)
680
+ localMatrix: parseMatrix(requireKey(record3, "localMatrix", path), `${path}.localMatrix`)
671
681
  };
672
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
673
- assignOptional(output, "meshId", readOptional(record, "meshId", readId, path));
674
- assignOptional(output, "skinId", readOptional(record, "skinId", readId, path));
675
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
682
+ assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
683
+ assignOptional(output, "meshId", readOptional(record3, "meshId", readId, path));
684
+ assignOptional(output, "skinId", readOptional(record3, "skinId", readId, path));
685
+ assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
676
686
  return output;
677
687
  }
678
688
  function parseAccessor(value, path, budget) {
679
- const record = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
680
- const componentType = readEnum(requireKey(record, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
681
- const elementType = readEnum(requireKey(record, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
682
- const normalized = readOptional(record, "normalized", readBoolean, path);
689
+ const record3 = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
690
+ const componentType = readEnum(requireKey(record3, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
691
+ const elementType = readEnum(requireKey(record3, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
692
+ const normalized = readOptional(record3, "normalized", readBoolean, path);
683
693
  if (normalized === true && componentType === "f32") return fail("invalid-value", `${path}.normalized`, "floating-point accessors cannot be normalized");
684
694
  if (elementType.startsWith("mat") && componentType !== "f32") return fail("invalid-value", `${path}.componentType`, "matrix accessors must use f32 components");
685
695
  const expectedComponents = ELEMENT_COMPONENTS[elementType];
686
- const min = readOptional(record, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
687
- const max = readOptional(record, "max", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
696
+ const min = readOptional(record3, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
697
+ const max = readOptional(record3, "max", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
688
698
  if (min === void 0 !== (max === void 0)) return fail("invalid-value", path, "accessor min and max must be supplied together");
689
699
  if (min && max) {
690
700
  const [componentMinimum, componentMaximum, integer] = COMPONENT_RANGES[componentType];
@@ -697,25 +707,25 @@ function parseAccessor(value, path, budget) {
697
707
  }
698
708
  }
699
709
  }
700
- const count = readSafeInteger(requireKey(record, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
710
+ const count = readSafeInteger(requireKey(record3, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
701
711
  consumeBudget(budget, "accessorElements", count * expectedComponents, GPU_MODEL_DOCUMENT_LIMITS.accessorElements, path, "accessor element component");
702
712
  const output = {
703
- id: readId(requireKey(record, "id", path), `${path}.id`),
704
- resourceId: readId(requireKey(record, "resourceId", path), `${path}.resourceId`),
705
- byteOffset: readSafeInteger(requireKey(record, "byteOffset", path), `${path}.byteOffset`),
713
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
714
+ resourceId: readId(requireKey(record3, "resourceId", path), `${path}.resourceId`),
715
+ byteOffset: readSafeInteger(requireKey(record3, "byteOffset", path), `${path}.byteOffset`),
706
716
  count,
707
717
  componentType,
708
718
  elementType
709
719
  };
710
- assignOptional(output, "byteStride", readOptional(record, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
720
+ assignOptional(output, "byteStride", readOptional(record3, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
711
721
  assignOptional(output, "normalized", normalized);
712
722
  assignOptional(output, "min", min);
713
723
  assignOptional(output, "max", max);
714
724
  return output;
715
725
  }
716
726
  function parsePrimitive(value, path, budget) {
717
- const record = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
718
- const attributesInput = readArray(requireKey(record, "attributes", path), `${path}.attributes`, 64, 1);
727
+ const record3 = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
728
+ const attributesInput = readArray(requireKey(record3, "attributes", path), `${path}.attributes`, 64, 1);
719
729
  consumeBudget(budget, "primitiveAttributes", attributesInput.length, GPU_MODEL_DOCUMENT_LIMITS.primitiveAttributes, path, "primitive attribute");
720
730
  const attributes = Object.freeze(attributesInput.map((entry, index) => {
721
731
  const entryPath = `${path}.attributes[${index}]`;
@@ -728,53 +738,53 @@ function parsePrimitive(value, path, budget) {
728
738
  ensureUnique(attributes.map(({ semantic }) => semantic), `${path}.attributes`, "duplicate attribute semantic");
729
739
  if (!attributes.some(({ semantic }) => semantic === "POSITION")) return fail("invalid-value", `${path}.attributes`, "primitive requires POSITION");
730
740
  const output = {
731
- id: readId(requireKey(record, "id", path), `${path}.id`),
732
- topology: readEnum(requireKey(record, "topology", path), `${path}.topology`, ["points", "lines", "line-strip", "triangles", "triangle-strip"]),
741
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
742
+ topology: readEnum(requireKey(record3, "topology", path), `${path}.topology`, ["points", "lines", "line-strip", "triangles", "triangle-strip"]),
733
743
  attributes
734
744
  };
735
- assignOptional(output, "indicesAccessorId", readOptional(record, "indicesAccessorId", readId, path));
736
- assignOptional(output, "materialId", readOptional(record, "materialId", readId, path));
737
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
745
+ assignOptional(output, "indicesAccessorId", readOptional(record3, "indicesAccessorId", readId, path));
746
+ assignOptional(output, "materialId", readOptional(record3, "materialId", readId, path));
747
+ assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
738
748
  return output;
739
749
  }
740
750
  function parseMesh(value, path, budget) {
741
- const record = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
742
- const input = readArray(requireKey(record, "primitives", path), `${path}.primitives`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
751
+ const record3 = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
752
+ const input = readArray(requireKey(record3, "primitives", path), `${path}.primitives`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
743
753
  consumeBudget(budget, "primitives", input.length, GPU_MODEL_DOCUMENT_LIMITS.primitives, path, "primitive");
744
754
  const output = {
745
- id: readId(requireKey(record, "id", path), `${path}.id`),
755
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
746
756
  primitives: Object.freeze(input.map((entry, index) => parsePrimitive(entry, `${path}.primitives[${index}]`, budget)))
747
757
  };
748
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
749
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
758
+ assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
759
+ assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
750
760
  return output;
751
761
  }
752
762
  function parseTextureTransform(value, path) {
753
- const record = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
754
- if (Reflect.ownKeys(record).length === 0) return fail("invalid-value", path, "texture transform cannot be empty");
763
+ const record3 = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
764
+ if (Reflect.ownKeys(record3).length === 0) return fail("invalid-value", path, "texture transform cannot be empty");
755
765
  const output = {};
756
- assignOptional(output, "offset", readOptional(record, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
757
- assignOptional(output, "rotationRadians", readOptional(record, "rotationRadians", readFinite, path));
758
- assignOptional(output, "scale", readOptional(record, "scale", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
766
+ assignOptional(output, "offset", readOptional(record3, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
767
+ assignOptional(output, "rotationRadians", readOptional(record3, "rotationRadians", readFinite, path));
768
+ assignOptional(output, "scale", readOptional(record3, "scale", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
759
769
  return output;
760
770
  }
761
771
  function parseTextureBinding(value, path) {
762
- const record = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
772
+ const record3 = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
763
773
  const output = {
764
- textureId: readId(requireKey(record, "textureId", path), `${path}.textureId`),
765
- colorSpace: readEnum(requireKey(record, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
766
- intendedUsage: readEnum(requireKey(record, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES)
774
+ textureId: readId(requireKey(record3, "textureId", path), `${path}.textureId`),
775
+ colorSpace: readEnum(requireKey(record3, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
776
+ intendedUsage: readEnum(requireKey(record3, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES)
767
777
  };
768
- assignOptional(output, "samplerId", readOptional(record, "samplerId", readId, path));
769
- assignOptional(output, "texCoordSet", readOptional(record, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
770
- assignOptional(output, "transform", readOptional(record, "transform", parseTextureTransform, path));
778
+ assignOptional(output, "samplerId", readOptional(record3, "samplerId", readId, path));
779
+ assignOptional(output, "texCoordSet", readOptional(record3, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
780
+ assignOptional(output, "transform", readOptional(record3, "transform", parseTextureTransform, path));
771
781
  return output;
772
782
  }
773
783
  function parseMaterialTextures(value, path) {
774
784
  const slots = Object.keys(MATERIAL_TEXTURE_SLOTS);
775
- const record = readRecord(value, path, slots);
785
+ const record3 = readRecord(value, path, slots);
776
786
  const output = {};
777
- for (const slot of slots) assignOptional(output, slot, readOptional(record, slot, parseTextureBinding, path));
787
+ for (const slot of slots) assignOptional(output, slot, readOptional(record3, slot, parseTextureBinding, path));
778
788
  return output;
779
789
  }
780
790
  function parseMaterial(value, path, budget) {
@@ -800,88 +810,88 @@ function parseMaterial(value, path, budget) {
800
810
  "extensions",
801
811
  "sourceMetadata"
802
812
  ];
803
- const record = readRecord(value, path, keys);
804
- const alphaMode = readEnum(requireKey(record, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
805
- const alphaCutoff = readOptional(record, "alphaCutoff", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path);
813
+ const record3 = readRecord(value, path, keys);
814
+ const alphaMode = readEnum(requireKey(record3, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
815
+ const alphaCutoff = readOptional(record3, "alphaCutoff", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path);
806
816
  if (alphaMode === "mask" && alphaCutoff === void 0) return fail("invalid-value", `${path}.alphaCutoff`, "mask materials require alphaCutoff");
807
817
  if (alphaMode !== "mask" && alphaCutoff !== void 0) return fail("invalid-value", `${path}.alphaCutoff`, "alphaCutoff is only valid for mask materials");
808
818
  const output = {
809
- id: readId(requireKey(record, "id", path), `${path}.id`),
810
- workflow: readEnum(requireKey(record, "workflow", path), `${path}.workflow`, ["metallic-roughness", "specular-glossiness", "unlit", "custom"]),
819
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
820
+ workflow: readEnum(requireKey(record3, "workflow", path), `${path}.workflow`, ["metallic-roughness", "specular-glossiness", "unlit", "custom"]),
811
821
  alphaMode,
812
- baseColorFactor: readNumberTuple(requireKey(record, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
813
- textures: parseMaterialTextures(requireKey(record, "textures", path), `${path}.textures`),
814
- extensions: readMetadata(requireKey(record, "extensions", path), `${path}.extensions`, budget),
815
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
822
+ baseColorFactor: readNumberTuple(requireKey(record3, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
823
+ textures: parseMaterialTextures(requireKey(record3, "textures", path), `${path}.textures`),
824
+ extensions: readMetadata(requireKey(record3, "extensions", path), `${path}.extensions`, budget),
825
+ sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
816
826
  };
817
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
827
+ assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
818
828
  assignOptional(output, "alphaCutoff", alphaCutoff);
819
- assignOptional(output, "doubleSided", readOptional(record, "doubleSided", readBoolean, path));
829
+ assignOptional(output, "doubleSided", readOptional(record3, "doubleSided", readBoolean, path));
820
830
  for (const key of ["metallicFactor", "roughnessFactor", "transmissionFactor", "clearcoatFactor", "clearcoatRoughnessFactor", "sheenRoughnessFactor", "occlusionStrength"]) {
821
- assignOptional(output, key, readOptional(record, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
831
+ assignOptional(output, key, readOptional(record3, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
822
832
  }
823
- assignOptional(output, "normalScale", readOptional(record, "normalScale", readFinite, path));
824
- assignOptional(output, "emissiveFactor", readOptional(record, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
825
- assignOptional(output, "sheenColorFactor", readOptional(record, "sheenColorFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0, 1), path));
833
+ assignOptional(output, "normalScale", readOptional(record3, "normalScale", readFinite, path));
834
+ assignOptional(output, "emissiveFactor", readOptional(record3, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
835
+ assignOptional(output, "sheenColorFactor", readOptional(record3, "sheenColorFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0, 1), path));
826
836
  return output;
827
837
  }
828
838
  function parseTextureSampler(value, path) {
829
- const record = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
830
- const output = { id: readId(requireKey(record, "id", path), `${path}.id`) };
831
- assignOptional(output, "wrapS", readOptional(record, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
832
- assignOptional(output, "wrapT", readOptional(record, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
833
- assignOptional(output, "minFilter", readOptional(record, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
834
- assignOptional(output, "magFilter", readOptional(record, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
835
- assignOptional(output, "mipFilter", readOptional(record, "mipFilter", (entry, entryPath) => readEnum(entry, entryPath, ["nearest", "linear"]), path));
839
+ const record3 = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
840
+ const output = { id: readId(requireKey(record3, "id", path), `${path}.id`) };
841
+ assignOptional(output, "wrapS", readOptional(record3, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
842
+ assignOptional(output, "wrapT", readOptional(record3, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
843
+ assignOptional(output, "minFilter", readOptional(record3, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
844
+ assignOptional(output, "magFilter", readOptional(record3, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
845
+ assignOptional(output, "mipFilter", readOptional(record3, "mipFilter", (entry, entryPath) => readEnum(entry, entryPath, ["nearest", "linear"]), path));
836
846
  return output;
837
847
  }
838
848
  function parseTexture(value, path, budget) {
839
- const record = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
840
- const usages = Object.freeze(readArray(requireKey(record, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES.length, 1).map((entry, index) => readEnum(entry, `${path}.intendedUsage[${index}]`, TEXTURE_USAGES)));
849
+ const record3 = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
850
+ const usages = Object.freeze(readArray(requireKey(record3, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES.length, 1).map((entry, index) => readEnum(entry, `${path}.intendedUsage[${index}]`, TEXTURE_USAGES)));
841
851
  ensureUnique(usages, `${path}.intendedUsage`, "duplicate intended texture usage");
842
852
  const output = {
843
- id: readId(requireKey(record, "id", path), `${path}.id`),
844
- contentHash: readSha256(requireKey(record, "contentHash", path), `${path}.contentHash`),
845
- imageResourceId: readId(requireKey(record, "imageResourceId", path), `${path}.imageResourceId`),
853
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
854
+ contentHash: readSha256(requireKey(record3, "contentHash", path), `${path}.contentHash`),
855
+ imageResourceId: readId(requireKey(record3, "imageResourceId", path), `${path}.imageResourceId`),
846
856
  intendedUsage: usages,
847
- colorSpace: readEnum(requireKey(record, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
848
- exportEmbeddingPolicy: readEnum(requireKey(record, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
849
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
857
+ colorSpace: readEnum(requireKey(record3, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
858
+ exportEmbeddingPolicy: readEnum(requireKey(record3, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
859
+ sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
850
860
  };
851
- assignOptional(output, "fileName", readOptional(record, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
852
- assignOptional(output, "relativePath", readOptional(record, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
853
- assignOptional(output, "mimeType", readOptional(record, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
854
- assignOptional(output, "byteLength", readOptional(record, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
855
- assignOptional(output, "sampler", readOptional(record, "sampler", parseTextureSampler, path));
861
+ assignOptional(output, "fileName", readOptional(record3, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
862
+ assignOptional(output, "relativePath", readOptional(record3, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
863
+ assignOptional(output, "mimeType", readOptional(record3, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
864
+ assignOptional(output, "byteLength", readOptional(record3, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
865
+ assignOptional(output, "sampler", readOptional(record3, "sampler", parseTextureSampler, path));
856
866
  return output;
857
867
  }
858
868
  function parseSkeleton(value, path, budget) {
859
- const record = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
860
- const jointIds = Object.freeze(readArray(requireKey(record, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
861
- const rootJointIds = Object.freeze(readArray(requireKey(record, "rootJointIds", path), `${path}.rootJointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.rootJointIds[${index}]`)));
869
+ const record3 = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
870
+ const jointIds = Object.freeze(readArray(requireKey(record3, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
871
+ const rootJointIds = Object.freeze(readArray(requireKey(record3, "rootJointIds", path), `${path}.rootJointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.rootJointIds[${index}]`)));
862
872
  consumeBudget(budget, "skinJoints", jointIds.length + rootJointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "rig joint");
863
873
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skeleton joint");
864
874
  ensureUnique(rootJointIds, `${path}.rootJointIds`, "duplicate root joint");
865
875
  const output = {
866
- id: readId(requireKey(record, "id", path), `${path}.id`),
876
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
867
877
  jointIds,
868
878
  rootJointIds,
869
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
879
+ sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
870
880
  };
871
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
881
+ assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
872
882
  return output;
873
883
  }
874
884
  function parseJoint(value, path, budget) {
875
- const record = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
885
+ const record3 = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
876
886
  const output = {
877
- id: readId(requireKey(record, "id", path), `${path}.id`),
878
- nodeId: readId(requireKey(record, "nodeId", path), `${path}.nodeId`),
879
- restLocalTransform: parseTransformTrs(requireKey(record, "restLocalTransform", path), `${path}.restLocalTransform`),
880
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
887
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
888
+ nodeId: readId(requireKey(record3, "nodeId", path), `${path}.nodeId`),
889
+ restLocalTransform: parseTransformTrs(requireKey(record3, "restLocalTransform", path), `${path}.restLocalTransform`),
890
+ sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
881
891
  };
882
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
883
- assignOptional(output, "parentJointId", readOptional(record, "parentJointId", readId, path));
884
- assignOptional(output, "inverseBindMatrix", readOptional(record, "inverseBindMatrix", parseMatrix, path));
892
+ assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
893
+ assignOptional(output, "parentJointId", readOptional(record3, "parentJointId", readId, path));
894
+ assignOptional(output, "inverseBindMatrix", readOptional(record3, "inverseBindMatrix", parseMatrix, path));
885
895
  return output;
886
896
  }
887
897
  function parseWeightRows(value, path, rows, columns, integer, budget) {
@@ -895,43 +905,43 @@ function parseWeightRows(value, path, rows, columns, integer, budget) {
895
905
  }));
896
906
  }
897
907
  function parseVertexWeightSet(value, path, budget) {
898
- const record = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
899
- const influences = readSafeInteger(requireKey(record, "influencesPerVertex", path), `${path}.influencesPerVertex`, 1, 32);
908
+ const record3 = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
909
+ const influences = readSafeInteger(requireKey(record3, "influencesPerVertex", path), `${path}.influencesPerVertex`, 1, 32);
900
910
  if (influences % 4 !== 0) return fail("invalid-value", `${path}.influencesPerVertex`, "influencesPerVertex must be a multiple of four");
901
- const jointRows = readArray(requireKey(record, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
902
- const weightRows = readArray(requireKey(record, "weights", path), `${path}.weights`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
911
+ const jointRows = readArray(requireKey(record3, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
912
+ const weightRows = readArray(requireKey(record3, "weights", path), `${path}.weights`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
903
913
  if (jointRows.length !== weightRows.length) return fail("invalid-value", path, "joint and weight row counts must match");
904
914
  return {
905
- primitiveId: readId(requireKey(record, "primitiveId", path), `${path}.primitiveId`),
915
+ primitiveId: readId(requireKey(record3, "primitiveId", path), `${path}.primitiveId`),
906
916
  influencesPerVertex: influences,
907
917
  joints: parseWeightRows(jointRows, `${path}.joints`, jointRows.length, influences, true, budget),
908
918
  weights: parseWeightRows(weightRows, `${path}.weights`, weightRows.length, influences, false, budget)
909
919
  };
910
920
  }
911
921
  function parseSkin(value, path, budget) {
912
- const record = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
913
- const jointIds = Object.freeze(readArray(requireKey(record, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
922
+ const record3 = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
923
+ const jointIds = Object.freeze(readArray(requireKey(record3, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
914
924
  consumeBudget(budget, "skinJoints", jointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "skin joint");
915
925
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skin joint");
916
- const inverseInput = readArray(requireKey(record, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
926
+ const inverseInput = readArray(requireKey(record3, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
917
927
  if (inverseInput.length !== jointIds.length) return fail("invalid-value", `${path}.inverseBindMatrices`, "one inverse bind matrix is required per skin joint");
918
- const weightInput = readArray(requireKey(record, "weightSets", path), `${path}.weightSets`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
928
+ const weightInput = readArray(requireKey(record3, "weightSets", path), `${path}.weightSets`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
919
929
  const weightSets = Object.freeze(weightInput.map((entry, index) => parseVertexWeightSet(entry, `${path}.weightSets[${index}]`, budget)));
920
930
  ensureUnique(weightSets.map(({ primitiveId }) => primitiveId), `${path}.weightSets`, "duplicate primitive weight set");
921
931
  return {
922
- id: readId(requireKey(record, "id", path), `${path}.id`),
923
- skeletonId: readId(requireKey(record, "skeletonId", path), `${path}.skeletonId`),
932
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
933
+ skeletonId: readId(requireKey(record3, "skeletonId", path), `${path}.skeletonId`),
924
934
  jointIds,
925
935
  inverseBindMatrices: Object.freeze(inverseInput.map((entry, index) => parseMatrix(entry, `${path}.inverseBindMatrices[${index}]`))),
926
936
  weightSets,
927
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
937
+ sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
928
938
  };
929
939
  }
930
940
  function parseBlendShapeDelta(value, path) {
931
- const record = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
932
- const output = { primitiveId: readId(requireKey(record, "primitiveId", path), `${path}.primitiveId`) };
941
+ const record3 = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
942
+ const output = { primitiveId: readId(requireKey(record3, "primitiveId", path), `${path}.primitiveId`) };
933
943
  for (const key of ["positionAccessorId", "normalAccessorId", "tangentAccessorId"]) {
934
- assignOptional(output, key, readOptional(record, key, readId, path));
944
+ assignOptional(output, key, readOptional(record3, key, readId, path));
935
945
  }
936
946
  if (!output.positionAccessorId && !output.normalAccessorId && !output.tangentAccessorId) {
937
947
  return fail("invalid-value", path, "blend-shape delta requires at least one accessor");
@@ -939,8 +949,8 @@ function parseBlendShapeDelta(value, path) {
939
949
  return output;
940
950
  }
941
951
  function parseBlendShape(value, path, budget) {
942
- const record = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
943
- const deltaInput = readArray(requireKey(record, "deltas", path), `${path}.deltas`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
952
+ const record3 = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
953
+ const deltaInput = readArray(requireKey(record3, "deltas", path), `${path}.deltas`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
944
954
  consumeBudget(
945
955
  budget,
946
956
  "blendShapeDeltas",
@@ -952,96 +962,96 @@ function parseBlendShape(value, path, budget) {
952
962
  const deltas = Object.freeze(deltaInput.map((entry, index) => parseBlendShapeDelta(entry, `${path}.deltas[${index}]`)));
953
963
  ensureUnique(deltas.map(({ primitiveId }) => primitiveId), `${path}.deltas`, "duplicate blend-shape primitive delta");
954
964
  const output = {
955
- id: readId(requireKey(record, "id", path), `${path}.id`),
956
- meshId: readId(requireKey(record, "meshId", path), `${path}.meshId`),
965
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
966
+ meshId: readId(requireKey(record3, "meshId", path), `${path}.meshId`),
957
967
  deltas,
958
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
968
+ sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
959
969
  };
960
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
961
- assignOptional(output, "defaultWeight", readOptional(record, "defaultWeight", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
970
+ assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
971
+ assignOptional(output, "defaultWeight", readOptional(record3, "defaultWeight", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
962
972
  return output;
963
973
  }
964
974
  function parseAnimationSampler(value, path, budget) {
965
- const record = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
966
- const timesInput = readArray(requireKey(record, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
967
- const valuesInput = readArray(requireKey(record, "outputValues", path), `${path}.outputValues`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
975
+ const record3 = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
976
+ const timesInput = readArray(requireKey(record3, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
977
+ const valuesInput = readArray(requireKey(record3, "outputValues", path), `${path}.outputValues`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
968
978
  consumeBudget(budget, "animationValues", timesInput.length + valuesInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationValues, path, "animation value");
969
979
  const times = Object.freeze(timesInput.map((entry, index) => readFinite(entry, `${path}.inputTimesSeconds[${index}]`, 0)));
970
980
  for (let index = 1; index < times.length; index += 1) {
971
981
  if (times[index] <= times[index - 1]) return fail("invalid-value", `${path}.inputTimesSeconds`, "animation input times must be strictly increasing");
972
982
  }
973
983
  return {
974
- id: readId(requireKey(record, "id", path), `${path}.id`),
975
- inputAccessorId: readId(requireKey(record, "inputAccessorId", path), `${path}.inputAccessorId`),
976
- outputAccessorId: readId(requireKey(record, "outputAccessorId", path), `${path}.outputAccessorId`),
984
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
985
+ inputAccessorId: readId(requireKey(record3, "inputAccessorId", path), `${path}.inputAccessorId`),
986
+ outputAccessorId: readId(requireKey(record3, "outputAccessorId", path), `${path}.outputAccessorId`),
977
987
  inputTimesSeconds: times,
978
988
  outputValues: Object.freeze(valuesInput.map((entry, index) => readFinite(entry, `${path}.outputValues[${index}]`))),
979
- interpolation: readEnum(requireKey(record, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
980
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
989
+ interpolation: readEnum(requireKey(record3, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
990
+ sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
981
991
  };
982
992
  }
983
993
  function parseAnimationChannel(value, path, budget) {
984
- const record = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
994
+ const record3 = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
985
995
  return {
986
- id: readId(requireKey(record, "id", path), `${path}.id`),
987
- samplerId: readId(requireKey(record, "samplerId", path), `${path}.samplerId`),
988
- targetType: readEnum(requireKey(record, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
989
- targetId: readId(requireKey(record, "targetId", path), `${path}.targetId`),
990
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
996
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
997
+ samplerId: readId(requireKey(record3, "samplerId", path), `${path}.samplerId`),
998
+ targetType: readEnum(requireKey(record3, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
999
+ targetId: readId(requireKey(record3, "targetId", path), `${path}.targetId`),
1000
+ sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
991
1001
  };
992
1002
  }
993
1003
  function parseAnimation(value, path, budget) {
994
- const record = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
995
- const samplerInput = readArray(requireKey(record, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
996
- const channelInput = readArray(requireKey(record, "channels", path), `${path}.channels`, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, 1);
1004
+ const record3 = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
1005
+ const samplerInput = readArray(requireKey(record3, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
1006
+ const channelInput = readArray(requireKey(record3, "channels", path), `${path}.channels`, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, 1);
997
1007
  consumeBudget(budget, "animationSamplers", samplerInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, path, "animation sampler");
998
1008
  consumeBudget(budget, "animationChannels", channelInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, path, "animation channel");
999
1009
  const output = {
1000
- id: readId(requireKey(record, "id", path), `${path}.id`),
1001
- durationSeconds: readFinite(requireKey(record, "durationSeconds", path), `${path}.durationSeconds`, 0),
1010
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
1011
+ durationSeconds: readFinite(requireKey(record3, "durationSeconds", path), `${path}.durationSeconds`, 0),
1002
1012
  samplers: Object.freeze(samplerInput.map((entry, index) => parseAnimationSampler(entry, `${path}.samplers[${index}]`, budget))),
1003
1013
  channels: Object.freeze(channelInput.map((entry, index) => parseAnimationChannel(entry, `${path}.channels[${index}]`, budget))),
1004
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1014
+ sourceMetadata: readMetadata(requireKey(record3, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1005
1015
  };
1006
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
1016
+ assignOptional(output, "name", readOptional(record3, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
1007
1017
  return output;
1008
1018
  }
1009
1019
  function parseAnalyticGeometry(value, path, budget) {
1010
- const record = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
1011
- if (requireKey(record, "kind", path) !== "brep-placeholder") return fail("invalid-value", `${path}.kind`, "unsupported analytic geometry kind");
1020
+ const record3 = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
1021
+ if (requireKey(record3, "kind", path) !== "brep-placeholder") return fail("invalid-value", `${path}.kind`, "unsupported analytic geometry kind");
1012
1022
  const output = {
1013
- id: readId(requireKey(record, "id", path), `${path}.id`),
1023
+ id: readId(requireKey(record3, "id", path), `${path}.id`),
1014
1024
  kind: "brep-placeholder",
1015
- sourceResourceId: readId(requireKey(record, "sourceResourceId", path), `${path}.sourceResourceId`),
1016
- sourceUnit: readEnum(requireKey(record, "sourceUnit", path), `${path}.sourceUnit`, ["metre", "millimetre", "centimetre", "inch", "foot", "unitless"])
1025
+ sourceResourceId: readId(requireKey(record3, "sourceResourceId", path), `${path}.sourceResourceId`),
1026
+ sourceUnit: readEnum(requireKey(record3, "sourceUnit", path), `${path}.sourceUnit`, ["metre", "millimetre", "centimetre", "inch", "foot", "unitless"])
1017
1027
  };
1018
- assignOptional(output, "tessellatedMeshId", readOptional(record, "tessellatedMeshId", readId, path));
1019
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1028
+ assignOptional(output, "tessellatedMeshId", readOptional(record3, "tessellatedMeshId", readId, path));
1029
+ assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1020
1030
  return output;
1021
1031
  }
1022
1032
  function parseProvenance(value, budget) {
1023
1033
  const path = "$.provenance";
1024
- const record = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
1034
+ const record3 = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
1025
1035
  const output = {
1026
- sourceFormat: readString(requireKey(record, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
1027
- sourceContentHash: readSha256(requireKey(record, "sourceContentHash", path), `${path}.sourceContentHash`),
1028
- converterId: readString(requireKey(record, "converterId", path), `${path}.converterId`, 128, TOKEN),
1029
- converterVersion: readString(requireKey(record, "converterVersion", path), `${path}.converterVersion`, 128, TOKEN)
1036
+ sourceFormat: readString(requireKey(record3, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
1037
+ sourceContentHash: readSha256(requireKey(record3, "sourceContentHash", path), `${path}.sourceContentHash`),
1038
+ converterId: readString(requireKey(record3, "converterId", path), `${path}.converterId`, 128, TOKEN),
1039
+ converterVersion: readString(requireKey(record3, "converterVersion", path), `${path}.converterVersion`, 128, TOKEN)
1030
1040
  };
1031
- assignOptional(output, "provider", readOptional(record, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
1032
- assignOptional(output, "sourceIdentifier", readOptional(record, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
1033
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1041
+ assignOptional(output, "provider", readOptional(record3, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
1042
+ assignOptional(output, "sourceIdentifier", readOptional(record3, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
1043
+ assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1034
1044
  return output;
1035
1045
  }
1036
1046
  function parseDiagnostic(value, path, budget) {
1037
- const record = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
1047
+ const record3 = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
1038
1048
  const output = {
1039
- severity: readEnum(requireKey(record, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
1040
- code: readString(requireKey(record, "code", path), `${path}.code`, 128, TOKEN),
1041
- message: readString(requireKey(record, "message", path), `${path}.message`, GPU_MODEL_DOCUMENT_LIMITS.diagnosticMessageLength)
1049
+ severity: readEnum(requireKey(record3, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
1050
+ code: readString(requireKey(record3, "code", path), `${path}.code`, 128, TOKEN),
1051
+ message: readString(requireKey(record3, "message", path), `${path}.message`, GPU_MODEL_DOCUMENT_LIMITS.diagnosticMessageLength)
1042
1052
  };
1043
- assignOptional(output, "path", readOptional(record, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
1044
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1053
+ assignOptional(output, "path", readOptional(record3, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
1054
+ assignOptional(output, "metadata", readOptional(record3, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1045
1055
  return output;
1046
1056
  }
1047
1057
  function ensureUnique(values, path, detail = "duplicate identity") {
@@ -1682,11 +1692,11 @@ var DOCUMENT_KEYS = Object.freeze([
1682
1692
  ]);
1683
1693
  function parseGpuModelDocument(value) {
1684
1694
  const path = "$";
1685
- const record = readRecord(value, path, DOCUMENT_KEYS);
1686
- const schemaVersion = requireKey(record, "schemaVersion", path);
1695
+ const record3 = readRecord(value, path, DOCUMENT_KEYS);
1696
+ const schemaVersion = requireKey(record3, "schemaVersion", path);
1687
1697
  if (schemaVersion !== GPU_MODEL_DOCUMENT_SCHEMA_VERSION) return fail("invalid-value", "$.schemaVersion", "unsupported schema version");
1688
1698
  const parseCollection = (key, maximum, parser, minimum = 0) => {
1689
- const input = readArray(requireKey(record, key, path), `$.${key}`, maximum, minimum);
1699
+ const input = readArray(requireKey(record3, key, path), `$.${key}`, maximum, minimum);
1690
1700
  return Object.freeze(input.map((entry, index) => parser(entry, `$.${key}[${index}]`)));
1691
1701
  };
1692
1702
  const budget = {
@@ -1707,10 +1717,10 @@ function parseGpuModelDocument(value) {
1707
1717
  geometryWorldAccessorInstances: 0,
1708
1718
  geometryWorldVertices: 0
1709
1719
  };
1710
- const roots = Object.freeze(readArray(requireKey(record, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1720
+ const roots = Object.freeze(readArray(requireKey(record3, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1711
1721
  const document = {
1712
1722
  schemaVersion,
1713
- coordinateSystem: parseCoordinateSystem(requireKey(record, "coordinateSystem", path)),
1723
+ coordinateSystem: parseCoordinateSystem(requireKey(record3, "coordinateSystem", path)),
1714
1724
  roots,
1715
1725
  nodes: parseCollection("nodes", GPU_MODEL_DOCUMENT_LIMITS.nodes, (entry, entryPath) => parseNode(entry, entryPath, budget), 1),
1716
1726
  resources: parseCollection("resources", GPU_MODEL_DOCUMENT_LIMITS.resources, parseVerifiedResource, 1),
@@ -1724,10 +1734,10 @@ function parseGpuModelDocument(value) {
1724
1734
  blendShapes: parseCollection("blendShapes", GPU_MODEL_DOCUMENT_LIMITS.blendShapes, (entry, entryPath) => parseBlendShape(entry, entryPath, budget)),
1725
1735
  animations: parseCollection("animations", GPU_MODEL_DOCUMENT_LIMITS.animations, (entry, entryPath) => parseAnimation(entry, entryPath, budget)),
1726
1736
  analyticGeometry: parseCollection("analyticGeometry", GPU_MODEL_DOCUMENT_LIMITS.analyticGeometry, (entry, entryPath) => parseAnalyticGeometry(entry, entryPath, budget)),
1727
- bounds: parseBounds(requireKey(record, "bounds", path), "$.bounds"),
1728
- provenance: parseProvenance(requireKey(record, "provenance", path), budget),
1737
+ bounds: parseBounds(requireKey(record3, "bounds", path), "$.bounds"),
1738
+ provenance: parseProvenance(requireKey(record3, "provenance", path), budget),
1729
1739
  diagnostics: parseCollection("diagnostics", GPU_MODEL_DOCUMENT_LIMITS.diagnostics, (entry, entryPath) => parseDiagnostic(entry, entryPath, budget)),
1730
- metadata: readMetadata(requireKey(record, "metadata", path), "$.metadata", budget)
1740
+ metadata: readMetadata(requireKey(record3, "metadata", path), "$.metadata", budget)
1731
1741
  };
1732
1742
  if (document.meshes.length === 0) return fail("invalid-value", "$.meshes", "v1 document bounds require at least one canonical mesh");
1733
1743
  validateReferences(document, budget);
@@ -1764,8 +1774,8 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1764
1774
  value.resources.forEach((resource, index) => assertVerifiedResourceEvidence(resource, limits, `$.resources[${index}]`));
1765
1775
  return value;
1766
1776
  }
1767
- const record = readRecord(value, "$", DOCUMENT_KEYS);
1768
- const resourceInput = readArray(requireKey(record, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1777
+ const record3 = readRecord(value, "$", DOCUMENT_KEYS);
1778
+ const resourceInput = readArray(requireKey(record3, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1769
1779
  const parsed = resourceInput.map((entry, index) => {
1770
1780
  if (typeof entry === "object" && entry !== null && verifiedResources.has(entry) && verifiedResourceBytes.has(entry)) return entry;
1771
1781
  return parseResourceHeader(entry, `$.resources[${index}]`);
@@ -1778,7 +1788,7 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1778
1788
  }
1779
1789
  const candidate = {};
1780
1790
  for (const key of DOCUMENT_KEYS) {
1781
- if (Object.hasOwn(record, key)) candidate[key] = record[key];
1791
+ if (Object.hasOwn(record3, key)) candidate[key] = record3[key];
1782
1792
  }
1783
1793
  candidate.resources = verified;
1784
1794
  return createGpuModelDocument(candidate);
@@ -1843,11 +1853,11 @@ async function hashGpuModelDocumentCanonical(document) {
1843
1853
  return Array.from(new Uint8Array(digest), (value) => value.toString(16).padStart(2, "0")).join("");
1844
1854
  }
1845
1855
  function resolveStaticDemoLimits(options) {
1846
- const record = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1847
- const enabled = readBoolean(requireKey(record, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1856
+ const record3 = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1857
+ const enabled = readBoolean(requireKey(record3, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1848
1858
  if (!enabled) return fail("feature-disabled", "$.options.pvoxModelsEnabled", `${GPU_MODEL_STATIC_DEMO_FEATURE_FLAG} is disabled`);
1849
- if (!Object.hasOwn(record, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1850
- const overrides = readRecord(record.limits, "$.options.limits", Object.keys(GPU_MODEL_STATIC_DEMO_LIMITS));
1859
+ if (!Object.hasOwn(record3, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1860
+ const overrides = readRecord(record3.limits, "$.options.limits", Object.keys(GPU_MODEL_STATIC_DEMO_LIMITS));
1851
1861
  const limits = { ...GPU_MODEL_STATIC_DEMO_LIMITS };
1852
1862
  for (const [key, ceiling] of Object.entries(GPU_MODEL_STATIC_DEMO_LIMITS)) {
1853
1863
  if (Object.hasOwn(overrides, key)) limits[key] = readSafeInteger(overrides[key], `$.options.limits.${key}`, 1, ceiling);
@@ -2082,6 +2092,451 @@ async function createGpuModelStaticDemoCompilerInput(document, options) {
2082
2092
  });
2083
2093
  }
2084
2094
 
2095
+ // src/diagnostics.ts
2096
+ var GPU_MODEL_REPAIR_RULES = Object.freeze([
2097
+ {
2098
+ "id": "missing-normals",
2099
+ "action": "Generate deterministic vertex normals from validated triangle geometry.",
2100
+ "severity": "warning",
2101
+ "confidence": "high",
2102
+ "standardIds": [
2103
+ "gltf-glb",
2104
+ "obj-mtl",
2105
+ "fbx",
2106
+ "cad-bim"
2107
+ ],
2108
+ "policy": "Generate deterministic vertex normals from validated triangle geometry and record the generation basis."
2109
+ },
2110
+ {
2111
+ "id": "bad-winding",
2112
+ "action": "Normalize winding to the adapter coordinate convention.",
2113
+ "severity": "warning",
2114
+ "confidence": "medium",
2115
+ "standardIds": [
2116
+ "gltf-glb",
2117
+ "obj-mtl",
2118
+ "fbx",
2119
+ "cad-bim"
2120
+ ],
2121
+ "policy": "Normalize winding to the adapter coordinate convention and record the inferred orientation."
2122
+ },
2123
+ {
2124
+ "id": "invalid-indices",
2125
+ "action": "Drop the invalid primitive.",
2126
+ "severity": "error",
2127
+ "confidence": "medium",
2128
+ "standardIds": [
2129
+ "gltf-glb",
2130
+ "obj-mtl",
2131
+ "fbx",
2132
+ "cad-bim"
2133
+ ],
2134
+ "policy": "Reject the invalid primitive or clamp only when the fixture and adapter policy explicitly prove a safe repair; retain the original index in the record."
2135
+ },
2136
+ {
2137
+ "id": "nan-transforms",
2138
+ "action": "Replace non-finite transform components with identity components.",
2139
+ "severity": "error",
2140
+ "confidence": "high",
2141
+ "standardIds": [
2142
+ "gltf-glb",
2143
+ "obj-mtl",
2144
+ "fbx",
2145
+ "cad-bim"
2146
+ ],
2147
+ "policy": "Replace the non-finite component with the identity transform component and record the source value and fallback."
2148
+ },
2149
+ {
2150
+ "id": "non-normalized-weights",
2151
+ "action": "Normalize finite non-negative skin weights.",
2152
+ "severity": "warning",
2153
+ "confidence": "high",
2154
+ "standardIds": [
2155
+ "gltf-glb",
2156
+ "fbx",
2157
+ "cad-bim"
2158
+ ],
2159
+ "policy": "Normalize finite, non-negative influences and record the pre-normalization weights and discarded influence policy."
2160
+ },
2161
+ {
2162
+ "id": "missing-texture-path",
2163
+ "action": "Leave the missing or unsafe texture slot unresolved.",
2164
+ "severity": "warning",
2165
+ "confidence": "low",
2166
+ "standardIds": [
2167
+ "gltf-glb",
2168
+ "obj-mtl",
2169
+ "fbx",
2170
+ "cad-bim"
2171
+ ],
2172
+ "policy": "Resolve only an explicitly bounded package-relative fallback; otherwise keep the material slot unresolved and emit a visible warning."
2173
+ },
2174
+ {
2175
+ "id": "malformed-material-reference",
2176
+ "action": "Attach the canonical default material.",
2177
+ "severity": "error",
2178
+ "confidence": "medium",
2179
+ "standardIds": [
2180
+ "gltf-glb",
2181
+ "obj-mtl",
2182
+ "fbx",
2183
+ "cad-bim"
2184
+ ],
2185
+ "policy": "Attach the canonical default material only when the target policy permits it and preserve the unresolved source reference."
2186
+ }
2187
+ ].map((rule) => Object.freeze({ ...rule, standardIds: Object.freeze(rule.standardIds) })));
2188
+ var GPU_MODEL_DIAGNOSTIC_LIMITS = Object.freeze({
2189
+ issues: 4096,
2190
+ values: 16384,
2191
+ depth: 12,
2192
+ members: 1024,
2193
+ string: 65536,
2194
+ bytes: 1048576
2195
+ });
2196
+ var GpuModelDiagnosticsError = class extends TypeError {
2197
+ constructor() {
2198
+ super("Invalid or over-budget GPU model diagnostics report");
2199
+ this.name = "GpuModelDiagnosticsError";
2200
+ }
2201
+ };
2202
+ function fail2() {
2203
+ throw new GpuModelDiagnosticsError();
2204
+ }
2205
+ function charge(budget, bytes) {
2206
+ budget.bytes += bytes;
2207
+ if (budget.bytes > GPU_MODEL_DIAGNOSTIC_LIMITS.bytes) fail2();
2208
+ }
2209
+ function textBytes(value) {
2210
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
2211
+ }
2212
+ function snapshot(value, depth, budget, ancestors, memberLimit = GPU_MODEL_DIAGNOSTIC_LIMITS.members) {
2213
+ if (++budget.values > GPU_MODEL_DIAGNOSTIC_LIMITS.values || depth > GPU_MODEL_DIAGNOSTIC_LIMITS.depth) fail2();
2214
+ if (value === null || typeof value === "boolean") {
2215
+ charge(budget, value === null ? 4 : value ? 4 : 5);
2216
+ return value;
2217
+ }
2218
+ if (typeof value === "number") {
2219
+ if (!Number.isFinite(value)) fail2();
2220
+ charge(budget, JSON.stringify(value).length);
2221
+ return Object.is(value, -0) ? 0 : value;
2222
+ }
2223
+ if (typeof value === "string") {
2224
+ if (value.length > GPU_MODEL_DIAGNOSTIC_LIMITS.string) fail2();
2225
+ charge(budget, textBytes(value));
2226
+ return value;
2227
+ }
2228
+ if (typeof value !== "object" || ancestors.has(value)) fail2();
2229
+ const array2 = Array.isArray(value);
2230
+ const prototype = Object.getPrototypeOf(value);
2231
+ if (array2 ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) fail2();
2232
+ const keys = Reflect.ownKeys(value);
2233
+ const maxMembers = memberLimit;
2234
+ if (keys.length > maxMembers + (array2 ? 1 : 0)) fail2();
2235
+ ancestors.add(value);
2236
+ charge(budget, 2);
2237
+ if (array2) {
2238
+ const length = Object.getOwnPropertyDescriptor(value, "length").value;
2239
+ if (length > maxMembers || keys.length !== length + 1) fail2();
2240
+ const result2 = [];
2241
+ for (let i = 0; i < length; i++) {
2242
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(i));
2243
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail2();
2244
+ if (i > 0) charge(budget, 1);
2245
+ result2.push(snapshot(descriptor.value, depth + 1, budget, ancestors));
2246
+ }
2247
+ ancestors.delete(value);
2248
+ return Object.freeze(result2);
2249
+ }
2250
+ const result = {};
2251
+ let count = 0;
2252
+ for (const key of keys) {
2253
+ if (typeof key !== "string" || key.length > 256 || ["__proto__", "constructor", "prototype"].includes(key)) fail2();
2254
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2255
+ if (!("value" in descriptor) || !descriptor.enumerable) fail2();
2256
+ if (count++ > 0) charge(budget, 1);
2257
+ charge(budget, textBytes(key) + 1);
2258
+ result[key] = snapshot(descriptor.value, depth + 1, budget, ancestors, depth === 0 && key === "issues" ? GPU_MODEL_DIAGNOSTIC_LIMITS.issues : GPU_MODEL_DIAGNOSTIC_LIMITS.members);
2259
+ }
2260
+ ancestors.delete(value);
2261
+ return Object.freeze(result);
2262
+ }
2263
+ function record(value) {
2264
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return fail2();
2265
+ return value;
2266
+ }
2267
+ function keysOnly(value, allowed) {
2268
+ if (Object.keys(value).some((key) => !allowed.includes(key))) fail2();
2269
+ }
2270
+ function enumValue(value, allowed) {
2271
+ if (typeof value !== "string" || !allowed.includes(value)) return fail2();
2272
+ return value;
2273
+ }
2274
+ function hasUnsafeText(value) {
2275
+ for (const character of value) {
2276
+ const point = character.codePointAt(0);
2277
+ if (point < 32 || point >= 127 && point <= 159 || point >= 8232 && point <= 8238 || point >= 8294 && point <= 8297 || point >= 55296 && point <= 57343) return true;
2278
+ }
2279
+ return false;
2280
+ }
2281
+ function text(value, max) {
2282
+ if (typeof value !== "string" || value.length === 0 || value.length > max || hasUnsafeText(value)) return fail2();
2283
+ return value;
2284
+ }
2285
+ function boolean(value) {
2286
+ if (typeof value !== "boolean") return fail2();
2287
+ return value;
2288
+ }
2289
+ function parseIssue(value) {
2290
+ const issue = record(value);
2291
+ const kind = enumValue(issue.kind, ["violation", "unsupported", "warning", "loss"]);
2292
+ const base = { code: text(issue.code, 128), path: text(issue.path, 1024), message: text(issue.message, 2048) };
2293
+ if (!base.path.startsWith("/")) fail2();
2294
+ const common = ["kind", "code", "path", "message"];
2295
+ if (kind === "violation") {
2296
+ keysOnly(issue, [...common, "ruleId", "originalValue", "repairApplied"]);
2297
+ if (!Object.hasOwn(issue, "originalValue")) fail2();
2298
+ return { ...base, kind, ruleId: text(issue.ruleId, 128), originalValue: issue.originalValue, repairApplied: boolean(issue.repairApplied) };
2299
+ }
2300
+ if (kind === "loss") {
2301
+ keysOnly(issue, [...common, "effect"]);
2302
+ return { ...base, kind, effect: enumValue(issue.effect, ["dropped", "approximated"]) };
2303
+ }
2304
+ keysOnly(issue, common);
2305
+ return { ...base, kind };
2306
+ }
2307
+ function evaluateGpuModelDiagnostics(value) {
2308
+ const budget = { values: 0, bytes: 0 };
2309
+ const args = record(snapshot(value, 0, budget, /* @__PURE__ */ new Set()));
2310
+ keysOnly(args, ["standard", "mode", "allowLoss", "issues", "rawSource", "maxEvidenceBytes"]);
2311
+ const standard = enumValue(args.standard, ["gltf-glb", "obj-mtl", "fbx", "cad-bim"]);
2312
+ const mode = args.mode === void 0 ? "strict" : enumValue(args.mode, ["strict", "tolerant", "forensic"]);
2313
+ const allowLoss = args.allowLoss === void 0 ? false : boolean(args.allowLoss);
2314
+ if (args.maxEvidenceBytes !== void 0 && (typeof args.maxEvidenceBytes !== "number" || !Number.isSafeInteger(args.maxEvidenceBytes) || args.maxEvidenceBytes < 1 || args.maxEvidenceBytes > GPU_MODEL_DIAGNOSTIC_LIMITS.bytes || budget.bytes > args.maxEvidenceBytes)) fail2();
2315
+ if (mode === "forensic" ? !Object.hasOwn(args, "rawSource") : Object.hasOwn(args, "rawSource")) fail2();
2316
+ if (!Array.isArray(args.issues) || args.issues.length > GPU_MODEL_DIAGNOSTIC_LIMITS.issues) fail2();
2317
+ const issues = args.issues.map(parseIssue);
2318
+ const lossPaths = new Set(issues.filter((issue) => issue.kind === "loss").map((issue) => issue.path));
2319
+ const diagnostics = [];
2320
+ const repairs = [];
2321
+ const losses = [];
2322
+ let accepted = true;
2323
+ for (const issue of issues) {
2324
+ const base = { code: issue.code, path: issue.path, message: issue.message };
2325
+ if (issue.kind === "violation") {
2326
+ const rule = GPU_MODEL_REPAIR_RULES.find((rule2) => rule2.id === issue.ruleId);
2327
+ const requiresLoss = ["invalid-indices", "missing-texture-path", "malformed-material-reference"].includes(issue.ruleId);
2328
+ const repaired = mode !== "strict" && issue.repairApplied && rule && rule.standardIds.includes(standard) && (!requiresLoss || lossPaths.has(issue.path));
2329
+ if (repaired) {
2330
+ repairs.push(Object.freeze({ ruleId: rule.id, originalValue: issue.originalValue, action: rule.action, severity: rule.severity, affectedPath: issue.path, confidence: rule.confidence }));
2331
+ diagnostics.push(Object.freeze({ ...base, kind: "repair", severity: rule.severity, ruleId: rule.id }));
2332
+ } else {
2333
+ accepted = false;
2334
+ diagnostics.push(Object.freeze({ ...base, kind: "blocking-error", severity: "error", ruleId: issue.ruleId }));
2335
+ }
2336
+ } else if (issue.kind === "unsupported") {
2337
+ accepted = false;
2338
+ diagnostics.push(Object.freeze({ ...issue, severity: "error" }));
2339
+ } else if (issue.kind === "loss") {
2340
+ if (!allowLoss) accepted = false;
2341
+ const loss = Object.freeze({ ...issue, severity: "warning" });
2342
+ losses.push(loss);
2343
+ diagnostics.push(loss);
2344
+ } else diagnostics.push(Object.freeze({ ...issue, severity: "warning" }));
2345
+ }
2346
+ return Object.freeze({ mode, standard, accepted, diagnostics: Object.freeze(diagnostics), repairs: Object.freeze(repairs), losses: Object.freeze(losses), ...mode === "forensic" ? { rawSource: args.rawSource } : {} });
2347
+ }
2348
+
2349
+ // src/resource-graph.ts
2350
+ var GPU_MODEL_RESOURCE_GRAPH_LIMITS = Object.freeze({
2351
+ nodes: 65536,
2352
+ edges: 262144,
2353
+ packages: 256,
2354
+ pathLength: 1024
2355
+ });
2356
+ var GPU_MODEL_RESOURCE_GRAPH_SCHEMA_VERSION = "plasius.gpu-model-resource-graph/1";
2357
+ var KINDS = ["resource", "accessor", "texture", "material", "skeleton", "skin", "animation", "provenance", "package"];
2358
+ var GpuModelResourceGraphError = class extends TypeError {
2359
+ constructor(code) {
2360
+ super(`Invalid GPU model resource graph: ${code}`);
2361
+ this.code = code;
2362
+ this.name = "GpuModelResourceGraphError";
2363
+ }
2364
+ code;
2365
+ };
2366
+ function fail3(code = "invalid-value") {
2367
+ throw new GpuModelResourceGraphError(code);
2368
+ }
2369
+ function identifier(value) {
2370
+ if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value)) fail3();
2371
+ return value;
2372
+ }
2373
+ function choice(value, choices) {
2374
+ if (typeof value !== "string" || !choices.includes(value)) fail3();
2375
+ return value;
2376
+ }
2377
+ function gpuModelResourceKey(kind, id) {
2378
+ return JSON.stringify([choice(kind, KINDS), identifier(id)]);
2379
+ }
2380
+ function record2(value, keys) {
2381
+ if (typeof value !== "object" || value === null || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail3();
2382
+ const names = Reflect.ownKeys(value);
2383
+ if (names.length !== keys.length || names.some((key) => typeof key !== "string" || !keys.includes(key))) fail3();
2384
+ const result = /* @__PURE__ */ Object.create(null);
2385
+ for (const key of keys) {
2386
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2387
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail3();
2388
+ result[key] = descriptor.value;
2389
+ }
2390
+ return result;
2391
+ }
2392
+ function array(value, maximum) {
2393
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) fail3();
2394
+ if (value.length > maximum) fail3("limit-exceeded");
2395
+ if (Reflect.ownKeys(value).length !== value.length + 1) fail3();
2396
+ const output = [];
2397
+ for (let index = 0; index < value.length; index++) {
2398
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
2399
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail3();
2400
+ output.push(descriptor.value);
2401
+ }
2402
+ return output;
2403
+ }
2404
+ function relativePath(value) {
2405
+ if (typeof value !== "string" || value.length > GPU_MODEL_RESOURCE_GRAPH_LIMITS.pathLength || !/^[A-Za-z0-9._/-]+$/u.test(value)) fail3();
2406
+ for (const part of value.split("/")) {
2407
+ if (!part || part.length > 255 || part === "." || part === ".." || part.endsWith(".") || /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\.|$)/iu.test(part)) fail3();
2408
+ }
2409
+ return value;
2410
+ }
2411
+ function parseLocation(value) {
2412
+ if (typeof value !== "object" || value === null) fail3();
2413
+ const kind = Object.getOwnPropertyDescriptor(value, "kind");
2414
+ if (!kind || !("value" in kind)) fail3();
2415
+ if (kind.value === "embedded") {
2416
+ record2(value, ["kind"]);
2417
+ return Object.freeze({ kind: "embedded" });
2418
+ }
2419
+ const location = record2(value, ["kind", "relativePath"]);
2420
+ if (location.kind !== "external") fail3();
2421
+ return Object.freeze({ kind: "external", relativePath: relativePath(location.relativePath) });
2422
+ }
2423
+ var graphs = /* @__PURE__ */ new WeakSet();
2424
+ function isGpuModelResourceGraph(value) {
2425
+ return typeof value === "object" && value !== null && graphs.has(value);
2426
+ }
2427
+ var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
2428
+ function createGpuModelResourceGraph(document, input) {
2429
+ try {
2430
+ return buildGraph(document, input);
2431
+ } catch (error) {
2432
+ if (error instanceof GpuModelResourceGraphError) throw error;
2433
+ fail3();
2434
+ }
2435
+ }
2436
+ function buildGraph(document, input) {
2437
+ if (!isGpuModelDocument(document)) fail3("unverified-document");
2438
+ const raw = record2(input, ["resources", "packages"]);
2439
+ const resourceInputs = array(raw.resources, GPU_MODEL_RESOURCE_GRAPH_LIMITS.nodes);
2440
+ const packageInputs = array(raw.packages, GPU_MODEL_RESOURCE_GRAPH_LIMITS.packages);
2441
+ const nodes = /* @__PURE__ */ new Map();
2442
+ let edgeCount = 0;
2443
+ const add = (kind, id, value, dependencies) => {
2444
+ const key = gpuModelResourceKey(kind, id);
2445
+ if (nodes.has(key)) fail3("duplicate-reference");
2446
+ if (nodes.size >= GPU_MODEL_RESOURCE_GRAPH_LIMITS.nodes) fail3("limit-exceeded");
2447
+ edgeCount += dependencies.length;
2448
+ if (edgeCount > GPU_MODEL_RESOURCE_GRAPH_LIMITS.edges) fail3("limit-exceeded");
2449
+ nodes.set(key, Object.freeze({ kind, id, key, value, dependencies: Object.freeze([...new Set(dependencies)].sort(compare)) }));
2450
+ };
2451
+ const resources = new Map(document.resources.map((resource) => [resource.id, resource]));
2452
+ const paths = /* @__PURE__ */ new Set();
2453
+ for (const entry of resourceInputs) {
2454
+ const binding = record2(entry, ["resourceId", "location", "exportEmbeddingPolicy"]);
2455
+ const id = identifier(binding.resourceId);
2456
+ const resource = resources.get(id);
2457
+ if (!resource) fail3("missing-reference");
2458
+ const location = parseLocation(binding.location);
2459
+ const exportEmbeddingPolicy = choice(binding.exportEmbeddingPolicy, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]);
2460
+ if (location.kind === "embedded" && exportEmbeddingPolicy === "forbid-embedding") fail3();
2461
+ if (location.kind === "external") {
2462
+ const path = location.relativePath.toLowerCase();
2463
+ if (paths.has(path)) fail3("duplicate-reference");
2464
+ paths.add(path);
2465
+ }
2466
+ add("resource", id, Object.freeze({ resource, location, exportEmbeddingPolicy }), []);
2467
+ }
2468
+ for (const path of paths) {
2469
+ for (let index = path.indexOf("/"); index !== -1; index = path.indexOf("/", index + 1)) {
2470
+ if (paths.has(path.slice(0, index))) fail3("duplicate-reference");
2471
+ }
2472
+ }
2473
+ if (nodes.size !== resources.size) fail3("missing-reference");
2474
+ addDocumentNodes(document, add);
2475
+ for (const entry of packageInputs) {
2476
+ const pkg = record2(entry, ["id", "members"]);
2477
+ const id = identifier(pkg.id);
2478
+ const members = [];
2479
+ const dependencies = /* @__PURE__ */ new Set();
2480
+ for (const entry2 of array(pkg.members, GPU_MODEL_RESOURCE_GRAPH_LIMITS.edges - edgeCount)) {
2481
+ const reference = record2(entry2, ["kind", "id"]);
2482
+ const member = Object.freeze({ kind: choice(reference.kind, KINDS), id: identifier(reference.id) });
2483
+ const key = gpuModelResourceKey(member.kind, member.id);
2484
+ if (dependencies.has(key)) fail3("duplicate-reference");
2485
+ dependencies.add(key);
2486
+ members.push(member);
2487
+ }
2488
+ members.sort((a, b) => compare(gpuModelResourceKey(a.kind, a.id), gpuModelResourceKey(b.kind, b.id)));
2489
+ add("package", id, Object.freeze({ id, members: Object.freeze(members) }), [...dependencies]);
2490
+ }
2491
+ validateDag(nodes);
2492
+ const result = Object.freeze({ schemaVersion: GPU_MODEL_RESOURCE_GRAPH_SCHEMA_VERSION, nodes: Object.freeze([...nodes.values()].sort((a, b) => compare(a.key, b.key))) });
2493
+ graphs.add(result);
2494
+ return result;
2495
+ }
2496
+ function addDocumentNodes(document, add) {
2497
+ const key = gpuModelResourceKey;
2498
+ for (const value of document.accessors) add("accessor", value.id, value, [key("resource", value.resourceId)]);
2499
+ for (const value of document.textures) add("texture", value.id, value, [key("resource", value.imageResourceId)]);
2500
+ for (const value of document.materials) add("material", value.id, value, Object.values(value.textures).map((binding) => key("texture", binding.textureId)));
2501
+ const joints = new Map(document.joints.map((joint) => [joint.id, joint]));
2502
+ for (const skeleton of document.skeletons) add("skeleton", skeleton.id, Object.freeze({ skeleton, joints: Object.freeze(skeleton.jointIds.map((id) => joints.get(id))) }), []);
2503
+ const primitives = new Map(document.meshes.flatMap((mesh) => mesh.primitives.map((primitive) => [primitive.id, primitive])));
2504
+ for (const value of document.skins) {
2505
+ const dependencies = [key("skeleton", value.skeletonId)];
2506
+ for (const weightSet of value.weightSets) {
2507
+ for (const attribute of primitives.get(weightSet.primitiveId).attributes) {
2508
+ if (/^(JOINTS|WEIGHTS)_/u.test(attribute.semantic)) dependencies.push(key("accessor", attribute.accessorId));
2509
+ }
2510
+ }
2511
+ add("skin", value.id, value, dependencies);
2512
+ }
2513
+ for (const value of document.animations) add("animation", value.id, value, value.samplers.flatMap((sampler) => [key("accessor", sampler.inputAccessorId), key("accessor", sampler.outputAccessorId)]));
2514
+ add("provenance", "source", document.provenance, []);
2515
+ }
2516
+ function validateDag(nodes) {
2517
+ const remaining = /* @__PURE__ */ new Map();
2518
+ const dependents = /* @__PURE__ */ new Map();
2519
+ const queue = [];
2520
+ for (const node of nodes.values()) {
2521
+ remaining.set(node.key, node.dependencies.length);
2522
+ if (!node.dependencies.length) queue.push(node.key);
2523
+ for (const dependency of node.dependencies) {
2524
+ if (!nodes.has(dependency)) fail3("missing-reference");
2525
+ const users = dependents.get(dependency) ?? [];
2526
+ users.push(node.key);
2527
+ dependents.set(dependency, users);
2528
+ }
2529
+ }
2530
+ for (let index = 0; index < queue.length; index++) {
2531
+ for (const dependent of dependents.get(queue[index]) ?? []) {
2532
+ const count = remaining.get(dependent) - 1;
2533
+ remaining.set(dependent, count);
2534
+ if (!count) queue.push(dependent);
2535
+ }
2536
+ }
2537
+ if (queue.length !== nodes.size) fail3("graph-cycle");
2538
+ }
2539
+
2085
2540
  // src/index.ts
2086
2541
  var packageName = "@plasius/gpu-model-core";
2087
2542
  var packageBootstrap = Object.freeze({
@@ -2092,8 +2547,12 @@ var packageBootstrap = Object.freeze({
2092
2547
  // Annotate the CommonJS export names for ESM import in node:
2093
2548
  0 && (module.exports = {
2094
2549
  CANONICAL_GPU_MODEL_COORDINATE_SYSTEM,
2550
+ GPU_MODEL_DIAGNOSTIC_LIMITS,
2095
2551
  GPU_MODEL_DOCUMENT_LIMITS,
2096
2552
  GPU_MODEL_DOCUMENT_SCHEMA_VERSION,
2553
+ GPU_MODEL_REPAIR_RULES,
2554
+ GPU_MODEL_RESOURCE_GRAPH_LIMITS,
2555
+ GPU_MODEL_RESOURCE_GRAPH_SCHEMA_VERSION,
2097
2556
  GPU_MODEL_RESOURCE_VERIFICATION_LIMITS,
2098
2557
  GPU_MODEL_STATIC_DEMO_FEATURE_FLAG,
2099
2558
  GPU_MODEL_STATIC_DEMO_LIMITS,
@@ -2102,15 +2561,21 @@ var packageBootstrap = Object.freeze({
2102
2561
  GPU_MODEL_STATIC_DEMO_MAX_EXTENT_METRES,
2103
2562
  GPU_MODEL_STATIC_DEMO_NORMALIZATION_CONTRACT,
2104
2563
  GPU_MODEL_STATIC_DEMO_PROFILE_VERSION,
2564
+ GpuModelDiagnosticsError,
2105
2565
  GpuModelDocumentError,
2566
+ GpuModelResourceGraphError,
2106
2567
  assertGpuModelStaticDemoNormalization,
2107
2568
  canCreateGpuModelDocument,
2108
2569
  createAndVerifyGpuModelDocument,
2109
2570
  createGpuModelDocument,
2571
+ createGpuModelResourceGraph,
2110
2572
  createGpuModelStaticDemoCompilerInput,
2111
2573
  encodeGpuModelDocumentCanonical,
2574
+ evaluateGpuModelDiagnostics,
2575
+ gpuModelResourceKey,
2112
2576
  hashGpuModelDocumentCanonical,
2113
2577
  isGpuModelDocument,
2578
+ isGpuModelResourceGraph,
2114
2579
  packageBootstrap,
2115
2580
  packageName,
2116
2581
  verifyGpuModelResource