@plasius/gpu-model-core 0.1.0 → 0.2.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,10 @@ 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,
26
28
  GPU_MODEL_RESOURCE_VERIFICATION_LIMITS: () => GPU_MODEL_RESOURCE_VERIFICATION_LIMITS,
27
29
  GPU_MODEL_STATIC_DEMO_FEATURE_FLAG: () => GPU_MODEL_STATIC_DEMO_FEATURE_FLAG,
28
30
  GPU_MODEL_STATIC_DEMO_LIMITS: () => GPU_MODEL_STATIC_DEMO_LIMITS,
@@ -31,6 +33,7 @@ __export(index_exports, {
31
33
  GPU_MODEL_STATIC_DEMO_MAX_EXTENT_METRES: () => GPU_MODEL_STATIC_DEMO_MAX_EXTENT_METRES,
32
34
  GPU_MODEL_STATIC_DEMO_NORMALIZATION_CONTRACT: () => GPU_MODEL_STATIC_DEMO_NORMALIZATION_CONTRACT,
33
35
  GPU_MODEL_STATIC_DEMO_PROFILE_VERSION: () => GPU_MODEL_STATIC_DEMO_PROFILE_VERSION,
36
+ GpuModelDiagnosticsError: () => GpuModelDiagnosticsError,
34
37
  GpuModelDocumentError: () => GpuModelDocumentError,
35
38
  assertGpuModelStaticDemoNormalization: () => assertGpuModelStaticDemoNormalization,
36
39
  canCreateGpuModelDocument: () => canCreateGpuModelDocument,
@@ -38,6 +41,7 @@ __export(index_exports, {
38
41
  createGpuModelDocument: () => createGpuModelDocument,
39
42
  createGpuModelStaticDemoCompilerInput: () => createGpuModelStaticDemoCompilerInput,
40
43
  encodeGpuModelDocumentCanonical: () => encodeGpuModelDocumentCanonical,
44
+ evaluateGpuModelDiagnostics: () => evaluateGpuModelDiagnostics,
41
45
  hashGpuModelDocumentCanonical: () => hashGpuModelDocumentCanonical,
42
46
  isGpuModelDocument: () => isGpuModelDocument,
43
47
  packageBootstrap: () => packageBootstrap,
@@ -220,9 +224,9 @@ function readRecord(value, path, allowedKeys) {
220
224
  }
221
225
  return value;
222
226
  }
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];
227
+ function requireKey(record2, key, path) {
228
+ if (!Object.hasOwn(record2, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
229
+ return record2[key];
226
230
  }
227
231
  function readArray(value, path, maximum, minimum = 0) {
228
232
  if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
@@ -301,9 +305,9 @@ function readNumberTuple(value, path, length, minimum, maximum) {
301
305
  const input = readArray(value, path, length, length);
302
306
  return Object.freeze(input.map((entry, index) => readFinite(entry, `${path}[${index}]`, minimum, maximum)));
303
307
  }
304
- function readOptional(record, key, parser, path) {
305
- if (!Object.hasOwn(record, key)) return void 0;
306
- return parser(record[key], `${path}.${key}`);
308
+ function readOptional(record2, key, parser, path) {
309
+ if (!Object.hasOwn(record2, key)) return void 0;
310
+ return parser(record2[key], `${path}.${key}`);
307
311
  }
308
312
  function assignOptional(target, key, value) {
309
313
  if (value !== void 0) Object.assign(target, { [key]: value });
@@ -325,17 +329,17 @@ function readMetadataValue(value, path, depth, budget) {
325
329
  return Object.freeze(input.map((entry, index) => readMetadataValue(entry, `${path}[${index}]`, depth + 1, budget)));
326
330
  }
327
331
  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);
332
+ const record2 = value;
333
+ const prototype = Object.getPrototypeOf(record2);
330
334
  if (prototype !== Object.prototype && prototype !== null) return fail("invalid-type", path, "metadata objects must be plain objects");
331
- const keys = Reflect.ownKeys(record);
335
+ const keys = Reflect.ownKeys(record2);
332
336
  if (keys.length > GPU_MODEL_DOCUMENT_LIMITS.metadataObjectKeys) return fail("limit-exceeded", path, "metadata object has too many keys");
333
337
  const output = {};
334
338
  for (const key of keys) {
335
339
  if (typeof key !== "string") return fail("unknown-key", path, "metadata symbol keys are not permitted");
336
340
  if (PROHIBITED_KEYS.has(key)) return fail("unknown-key", `${path}.${key}`, "prohibited metadata key");
337
341
  readString(key, `${path}.[key]`, GPU_MODEL_DOCUMENT_LIMITS.metadataKeyLength, METADATA_KEY);
338
- const descriptor = Object.getOwnPropertyDescriptor(record, key);
342
+ const descriptor = Object.getOwnPropertyDescriptor(record2, key);
339
343
  if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) return fail("invalid-type", `${path}.${key}`, "metadata properties must be enumerable data properties");
340
344
  output[key] = readMetadataValue(descriptor.value, `${path}.${key}`, depth + 1, budget);
341
345
  }
@@ -348,9 +352,9 @@ function readMetadata(value, path, budget) {
348
352
  }
349
353
  function parseCoordinateSystem(value) {
350
354
  const path = "$.coordinateSystem";
351
- const record = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
355
+ const record2 = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
352
356
  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");
357
+ if (requireKey(record2, key, path) !== expected) return fail("invalid-value", `${path}.${key}`, "coordinate system is not canonical");
354
358
  }
355
359
  return CANONICAL_GPU_MODEL_COORDINATE_SYSTEM;
356
360
  }
@@ -362,19 +366,19 @@ function parseMatrix(value, path, requireAffine = true) {
362
366
  return matrix;
363
367
  }
364
368
  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);
369
+ const record2 = readRecord(value, path, ["min", "max"]);
370
+ const min = readNumberTuple(requireKey(record2, "min", path), `${path}.min`, 3);
371
+ const max = readNumberTuple(requireKey(record2, "max", path), `${path}.max`, 3);
368
372
  for (let index = 0; index < 3; index += 1) {
369
373
  if (min[index] > max[index]) return fail("invalid-value", path, "bounds min cannot exceed max");
370
374
  }
371
375
  return { min, max };
372
376
  }
373
377
  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);
378
+ const record2 = readRecord(value, path, ["translation", "rotation", "scale"]);
379
+ const translation = readNumberTuple(requireKey(record2, "translation", path), `${path}.translation`, 3);
380
+ const rotation = readNumberTuple(requireKey(record2, "rotation", path), `${path}.rotation`, 4);
381
+ const scale = readNumberTuple(requireKey(record2, "scale", path), `${path}.scale`, 3);
378
382
  const magnitude = Math.hypot(...rotation);
379
383
  if (!almostEqual(magnitude, 1, 1e-4)) return fail("invalid-value", `${path}.rotation`, "rotation quaternion must be normalized");
380
384
  return { translation, rotation, scale };
@@ -396,25 +400,25 @@ function intrinsicBlobArrayBuffer(blob) {
396
400
  return Reflect.apply(blobArrayBuffer, blob, []);
397
401
  }
398
402
  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();
403
+ const record2 = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
404
+ const kind = readEnum(requireKey(record2, "kind", path), `${path}.kind`, ["buffer", "image"]);
405
+ const mimeType = readString(requireKey(record2, "mimeType", path), `${path}.mimeType`, 128, MIME_TYPE).toLowerCase();
402
406
  if (kind === "image" ? !IMAGE_MIME_TYPES.has(mimeType) : !BUFFER_MIME_TYPES.has(mimeType)) {
403
407
  return fail("invalid-value", `${path}.mimeType`, `unsupported ${kind} MIME type`);
404
408
  }
405
- const payload = requireKey(record, "payload", path);
409
+ const payload = requireKey(record2, "payload", path);
406
410
  if (!(payload instanceof Blob)) return fail("invalid-type", `${path}.payload`, "expected an immutable Blob payload");
407
411
  if (Object.getPrototypeOf(payload) !== Blob.prototype || Reflect.ownKeys(payload).some((key) => typeof key === "string")) {
408
412
  return fail("invalid-type", `${path}.payload`, "expected a native Blob without caller-defined properties");
409
413
  }
410
- const byteLength = readSafeInteger(requireKey(record, "byteLength", path), `${path}.byteLength`, 1);
414
+ const byteLength = readSafeInteger(requireKey(record2, "byteLength", path), `${path}.byteLength`, 1);
411
415
  if (intrinsicBlobSize(payload) !== byteLength) return fail("payload-length-mismatch", `${path}.payload`, "payload byte length does not match byteLength");
412
416
  const payloadType = intrinsicBlobType(payload);
413
417
  if (payloadType && payloadType.toLowerCase() !== mimeType) return fail("invalid-value", `${path}.payload`, "payload MIME type does not match mimeType");
414
418
  return {
415
- id: readId(requireKey(record, "id", path), `${path}.id`),
419
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
416
420
  kind,
417
- contentHash: readSha256(requireKey(record, "contentHash", path), `${path}.contentHash`),
421
+ contentHash: readSha256(requireKey(record2, "contentHash", path), `${path}.contentHash`),
418
422
  byteLength,
419
423
  mimeType,
420
424
  payload
@@ -423,11 +427,11 @@ function parseResourceHeader(value, path) {
423
427
  function resolveVerificationLimits(options) {
424
428
  const overrides = options?.limits;
425
429
  if (overrides === void 0) return GPU_MODEL_RESOURCE_VERIFICATION_LIMITS;
426
- const record = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
430
+ const record2 = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
427
431
  const output = { ...GPU_MODEL_RESOURCE_VERIFICATION_LIMITS };
428
432
  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);
433
+ if (!Object.hasOwn(record2, key)) continue;
434
+ const value = readSafeInteger(record2[key], `$.verification.limits.${key}`, 1, ceiling);
431
435
  Object.assign(output, { [key]: value });
432
436
  }
433
437
  if (output.maxImageBytes > output.maxResourceBytes) output.maxImageBytes = output.maxResourceBytes;
@@ -480,7 +484,7 @@ function verificationAbortError(signal, path) {
480
484
  function throwIfVerificationAborted(signal, path) {
481
485
  if (signal.aborted) throw verificationAbortError(signal, path);
482
486
  }
483
- function createBlobChunks(blob, chunkBytes, observed, signal, path, snapshot) {
487
+ function createBlobChunks(blob, chunkBytes, observed, signal, path, snapshot2) {
484
488
  return {
485
489
  async *[Symbol.asyncIterator]() {
486
490
  const size = intrinsicBlobSize(blob);
@@ -490,7 +494,7 @@ function createBlobChunks(blob, chunkBytes, observed, signal, path, snapshot) {
490
494
  const chunk = new Uint8Array(await intrinsicBlobArrayBuffer(intrinsicBlobSlice(blob, offset, end)));
491
495
  throwIfVerificationAborted(signal, path);
492
496
  observed.bytes += chunk.byteLength;
493
- if (snapshot) snapshot.set(chunk, offset);
497
+ if (snapshot2) snapshot2.set(chunk, offset);
494
498
  yield chunk;
495
499
  }
496
500
  throwIfVerificationAborted(signal, path);
@@ -539,13 +543,13 @@ function createVerificationDeadline(timeoutMs, callerSignal, path) {
539
543
  };
540
544
  }
541
545
  function parseInspection(value, path) {
542
- const record = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
546
+ const record2 = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
543
547
  const output = {
544
- valid: readBoolean(requireKey(record, "valid", path), `${path}.valid`),
545
- detectedMimeType: readString(requireKey(record, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
548
+ valid: readBoolean(requireKey(record2, "valid", path), `${path}.valid`),
549
+ detectedMimeType: readString(requireKey(record2, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
546
550
  };
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));
551
+ assignOptional(output, "width", readOptional(record2, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
552
+ assignOptional(output, "height", readOptional(record2, "height", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
549
553
  return output;
550
554
  }
551
555
  function bytesStartWith(bytes, signature) {
@@ -618,12 +622,12 @@ async function verifyParsedResource(resource, port, limits, options, path = "$.r
618
622
  } else if (inspection.width !== void 0 || inspection.height !== void 0) {
619
623
  return fail("resource-verification-failed", path, "buffer inspection must not report image dimensions");
620
624
  }
621
- const snapshot = new Uint8Array(resource.byteLength);
625
+ const snapshot2 = new Uint8Array(resource.byteLength);
622
626
  const digestObserved = { bytes: 0 };
623
627
  let digest;
624
628
  try {
625
629
  digest = await deadline.race(() => port.digestSha256(
626
- createBlobChunks(resource.payload, limits.chunkBytes, digestObserved, deadline.signal, path, snapshot),
630
+ createBlobChunks(resource.payload, limits.chunkBytes, digestObserved, deadline.signal, path, snapshot2),
627
631
  context
628
632
  ));
629
633
  } catch (error) {
@@ -636,13 +640,13 @@ async function verifyParsedResource(resource, port, limits, options, path = "$.r
636
640
  if (typeof digest !== "string" || !SHA_256.test(digest) || digest !== resource.contentHash) {
637
641
  return fail("resource-verification-failed", path, "resource digest does not match contentHash");
638
642
  }
639
- if (resource.kind === "image") validateImageMagic(resource, snapshot, inspection, path);
643
+ if (resource.kind === "image") validateImageMagic(resource, snapshot2, inspection, path);
640
644
  const verified = Object.freeze({
641
645
  ...resource,
642
- payload: new Blob([snapshot], { type: resource.mimeType })
646
+ payload: new Blob([snapshot2], { type: resource.mimeType })
643
647
  });
644
648
  verifiedResources.add(verified);
645
- verifiedResourceBytes.set(verified, snapshot);
649
+ verifiedResourceBytes.set(verified, snapshot2);
646
650
  verifiedResourceEvidence.set(verified, Object.freeze({ ...inspection }));
647
651
  return verified;
648
652
  } finally {
@@ -660,31 +664,31 @@ async function verifyGpuModelResource(value, port, options) {
660
664
  }
661
665
  }
662
666
  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}]`)));
667
+ const record2 = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
668
+ const children = Object.freeze(readArray(requireKey(record2, "children", path), `${path}.children`, GPU_MODEL_DOCUMENT_LIMITS.nodes).map((entry, index) => readId(entry, `${path}.children[${index}]`)));
665
669
  consumeBudget(budget, "sceneEdges", children.length, GPU_MODEL_DOCUMENT_LIMITS.sceneEdges, path, "scene edge");
666
670
  ensureUnique(children, `${path}.children`, "duplicate child node");
667
671
  const output = {
668
- id: readId(requireKey(record, "id", path), `${path}.id`),
672
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
669
673
  children,
670
- localMatrix: parseMatrix(requireKey(record, "localMatrix", path), `${path}.localMatrix`)
674
+ localMatrix: parseMatrix(requireKey(record2, "localMatrix", path), `${path}.localMatrix`)
671
675
  };
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));
676
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
677
+ assignOptional(output, "meshId", readOptional(record2, "meshId", readId, path));
678
+ assignOptional(output, "skinId", readOptional(record2, "skinId", readId, path));
679
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
676
680
  return output;
677
681
  }
678
682
  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);
683
+ const record2 = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
684
+ const componentType = readEnum(requireKey(record2, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
685
+ const elementType = readEnum(requireKey(record2, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
686
+ const normalized = readOptional(record2, "normalized", readBoolean, path);
683
687
  if (normalized === true && componentType === "f32") return fail("invalid-value", `${path}.normalized`, "floating-point accessors cannot be normalized");
684
688
  if (elementType.startsWith("mat") && componentType !== "f32") return fail("invalid-value", `${path}.componentType`, "matrix accessors must use f32 components");
685
689
  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);
690
+ const min = readOptional(record2, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
691
+ const max = readOptional(record2, "max", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
688
692
  if (min === void 0 !== (max === void 0)) return fail("invalid-value", path, "accessor min and max must be supplied together");
689
693
  if (min && max) {
690
694
  const [componentMinimum, componentMaximum, integer] = COMPONENT_RANGES[componentType];
@@ -697,25 +701,25 @@ function parseAccessor(value, path, budget) {
697
701
  }
698
702
  }
699
703
  }
700
- const count = readSafeInteger(requireKey(record, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
704
+ const count = readSafeInteger(requireKey(record2, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
701
705
  consumeBudget(budget, "accessorElements", count * expectedComponents, GPU_MODEL_DOCUMENT_LIMITS.accessorElements, path, "accessor element component");
702
706
  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`),
707
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
708
+ resourceId: readId(requireKey(record2, "resourceId", path), `${path}.resourceId`),
709
+ byteOffset: readSafeInteger(requireKey(record2, "byteOffset", path), `${path}.byteOffset`),
706
710
  count,
707
711
  componentType,
708
712
  elementType
709
713
  };
710
- assignOptional(output, "byteStride", readOptional(record, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
714
+ assignOptional(output, "byteStride", readOptional(record2, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
711
715
  assignOptional(output, "normalized", normalized);
712
716
  assignOptional(output, "min", min);
713
717
  assignOptional(output, "max", max);
714
718
  return output;
715
719
  }
716
720
  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);
721
+ const record2 = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
722
+ const attributesInput = readArray(requireKey(record2, "attributes", path), `${path}.attributes`, 64, 1);
719
723
  consumeBudget(budget, "primitiveAttributes", attributesInput.length, GPU_MODEL_DOCUMENT_LIMITS.primitiveAttributes, path, "primitive attribute");
720
724
  const attributes = Object.freeze(attributesInput.map((entry, index) => {
721
725
  const entryPath = `${path}.attributes[${index}]`;
@@ -728,53 +732,53 @@ function parsePrimitive(value, path, budget) {
728
732
  ensureUnique(attributes.map(({ semantic }) => semantic), `${path}.attributes`, "duplicate attribute semantic");
729
733
  if (!attributes.some(({ semantic }) => semantic === "POSITION")) return fail("invalid-value", `${path}.attributes`, "primitive requires POSITION");
730
734
  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"]),
735
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
736
+ topology: readEnum(requireKey(record2, "topology", path), `${path}.topology`, ["points", "lines", "line-strip", "triangles", "triangle-strip"]),
733
737
  attributes
734
738
  };
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));
739
+ assignOptional(output, "indicesAccessorId", readOptional(record2, "indicesAccessorId", readId, path));
740
+ assignOptional(output, "materialId", readOptional(record2, "materialId", readId, path));
741
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
738
742
  return output;
739
743
  }
740
744
  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);
745
+ const record2 = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
746
+ const input = readArray(requireKey(record2, "primitives", path), `${path}.primitives`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
743
747
  consumeBudget(budget, "primitives", input.length, GPU_MODEL_DOCUMENT_LIMITS.primitives, path, "primitive");
744
748
  const output = {
745
- id: readId(requireKey(record, "id", path), `${path}.id`),
749
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
746
750
  primitives: Object.freeze(input.map((entry, index) => parsePrimitive(entry, `${path}.primitives[${index}]`, budget)))
747
751
  };
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));
752
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
753
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
750
754
  return output;
751
755
  }
752
756
  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");
757
+ const record2 = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
758
+ if (Reflect.ownKeys(record2).length === 0) return fail("invalid-value", path, "texture transform cannot be empty");
755
759
  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));
760
+ assignOptional(output, "offset", readOptional(record2, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
761
+ assignOptional(output, "rotationRadians", readOptional(record2, "rotationRadians", readFinite, path));
762
+ assignOptional(output, "scale", readOptional(record2, "scale", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
759
763
  return output;
760
764
  }
761
765
  function parseTextureBinding(value, path) {
762
- const record = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
766
+ const record2 = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
763
767
  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)
768
+ textureId: readId(requireKey(record2, "textureId", path), `${path}.textureId`),
769
+ colorSpace: readEnum(requireKey(record2, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
770
+ intendedUsage: readEnum(requireKey(record2, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES)
767
771
  };
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));
772
+ assignOptional(output, "samplerId", readOptional(record2, "samplerId", readId, path));
773
+ assignOptional(output, "texCoordSet", readOptional(record2, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
774
+ assignOptional(output, "transform", readOptional(record2, "transform", parseTextureTransform, path));
771
775
  return output;
772
776
  }
773
777
  function parseMaterialTextures(value, path) {
774
778
  const slots = Object.keys(MATERIAL_TEXTURE_SLOTS);
775
- const record = readRecord(value, path, slots);
779
+ const record2 = readRecord(value, path, slots);
776
780
  const output = {};
777
- for (const slot of slots) assignOptional(output, slot, readOptional(record, slot, parseTextureBinding, path));
781
+ for (const slot of slots) assignOptional(output, slot, readOptional(record2, slot, parseTextureBinding, path));
778
782
  return output;
779
783
  }
780
784
  function parseMaterial(value, path, budget) {
@@ -800,88 +804,88 @@ function parseMaterial(value, path, budget) {
800
804
  "extensions",
801
805
  "sourceMetadata"
802
806
  ];
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);
807
+ const record2 = readRecord(value, path, keys);
808
+ const alphaMode = readEnum(requireKey(record2, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
809
+ const alphaCutoff = readOptional(record2, "alphaCutoff", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path);
806
810
  if (alphaMode === "mask" && alphaCutoff === void 0) return fail("invalid-value", `${path}.alphaCutoff`, "mask materials require alphaCutoff");
807
811
  if (alphaMode !== "mask" && alphaCutoff !== void 0) return fail("invalid-value", `${path}.alphaCutoff`, "alphaCutoff is only valid for mask materials");
808
812
  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"]),
813
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
814
+ workflow: readEnum(requireKey(record2, "workflow", path), `${path}.workflow`, ["metallic-roughness", "specular-glossiness", "unlit", "custom"]),
811
815
  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)
816
+ baseColorFactor: readNumberTuple(requireKey(record2, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
817
+ textures: parseMaterialTextures(requireKey(record2, "textures", path), `${path}.textures`),
818
+ extensions: readMetadata(requireKey(record2, "extensions", path), `${path}.extensions`, budget),
819
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
816
820
  };
817
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
821
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
818
822
  assignOptional(output, "alphaCutoff", alphaCutoff);
819
- assignOptional(output, "doubleSided", readOptional(record, "doubleSided", readBoolean, path));
823
+ assignOptional(output, "doubleSided", readOptional(record2, "doubleSided", readBoolean, path));
820
824
  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));
825
+ assignOptional(output, key, readOptional(record2, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
822
826
  }
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));
827
+ assignOptional(output, "normalScale", readOptional(record2, "normalScale", readFinite, path));
828
+ assignOptional(output, "emissiveFactor", readOptional(record2, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
829
+ assignOptional(output, "sheenColorFactor", readOptional(record2, "sheenColorFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0, 1), path));
826
830
  return output;
827
831
  }
828
832
  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));
833
+ const record2 = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
834
+ const output = { id: readId(requireKey(record2, "id", path), `${path}.id`) };
835
+ assignOptional(output, "wrapS", readOptional(record2, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
836
+ assignOptional(output, "wrapT", readOptional(record2, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
837
+ assignOptional(output, "minFilter", readOptional(record2, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
838
+ assignOptional(output, "magFilter", readOptional(record2, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
839
+ assignOptional(output, "mipFilter", readOptional(record2, "mipFilter", (entry, entryPath) => readEnum(entry, entryPath, ["nearest", "linear"]), path));
836
840
  return output;
837
841
  }
838
842
  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)));
843
+ const record2 = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
844
+ const usages = Object.freeze(readArray(requireKey(record2, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES.length, 1).map((entry, index) => readEnum(entry, `${path}.intendedUsage[${index}]`, TEXTURE_USAGES)));
841
845
  ensureUnique(usages, `${path}.intendedUsage`, "duplicate intended texture usage");
842
846
  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`),
847
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
848
+ contentHash: readSha256(requireKey(record2, "contentHash", path), `${path}.contentHash`),
849
+ imageResourceId: readId(requireKey(record2, "imageResourceId", path), `${path}.imageResourceId`),
846
850
  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)
851
+ colorSpace: readEnum(requireKey(record2, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
852
+ exportEmbeddingPolicy: readEnum(requireKey(record2, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
853
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
850
854
  };
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));
855
+ assignOptional(output, "fileName", readOptional(record2, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
856
+ assignOptional(output, "relativePath", readOptional(record2, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
857
+ assignOptional(output, "mimeType", readOptional(record2, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
858
+ assignOptional(output, "byteLength", readOptional(record2, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
859
+ assignOptional(output, "sampler", readOptional(record2, "sampler", parseTextureSampler, path));
856
860
  return output;
857
861
  }
858
862
  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}]`)));
863
+ const record2 = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
864
+ const jointIds = Object.freeze(readArray(requireKey(record2, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
865
+ const rootJointIds = Object.freeze(readArray(requireKey(record2, "rootJointIds", path), `${path}.rootJointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.rootJointIds[${index}]`)));
862
866
  consumeBudget(budget, "skinJoints", jointIds.length + rootJointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "rig joint");
863
867
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skeleton joint");
864
868
  ensureUnique(rootJointIds, `${path}.rootJointIds`, "duplicate root joint");
865
869
  const output = {
866
- id: readId(requireKey(record, "id", path), `${path}.id`),
870
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
867
871
  jointIds,
868
872
  rootJointIds,
869
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
873
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
870
874
  };
871
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
875
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
872
876
  return output;
873
877
  }
874
878
  function parseJoint(value, path, budget) {
875
- const record = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
879
+ const record2 = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
876
880
  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)
881
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
882
+ nodeId: readId(requireKey(record2, "nodeId", path), `${path}.nodeId`),
883
+ restLocalTransform: parseTransformTrs(requireKey(record2, "restLocalTransform", path), `${path}.restLocalTransform`),
884
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
881
885
  };
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));
886
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
887
+ assignOptional(output, "parentJointId", readOptional(record2, "parentJointId", readId, path));
888
+ assignOptional(output, "inverseBindMatrix", readOptional(record2, "inverseBindMatrix", parseMatrix, path));
885
889
  return output;
886
890
  }
887
891
  function parseWeightRows(value, path, rows, columns, integer, budget) {
@@ -895,43 +899,43 @@ function parseWeightRows(value, path, rows, columns, integer, budget) {
895
899
  }));
896
900
  }
897
901
  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);
902
+ const record2 = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
903
+ const influences = readSafeInteger(requireKey(record2, "influencesPerVertex", path), `${path}.influencesPerVertex`, 1, 32);
900
904
  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);
905
+ const jointRows = readArray(requireKey(record2, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
906
+ const weightRows = readArray(requireKey(record2, "weights", path), `${path}.weights`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
903
907
  if (jointRows.length !== weightRows.length) return fail("invalid-value", path, "joint and weight row counts must match");
904
908
  return {
905
- primitiveId: readId(requireKey(record, "primitiveId", path), `${path}.primitiveId`),
909
+ primitiveId: readId(requireKey(record2, "primitiveId", path), `${path}.primitiveId`),
906
910
  influencesPerVertex: influences,
907
911
  joints: parseWeightRows(jointRows, `${path}.joints`, jointRows.length, influences, true, budget),
908
912
  weights: parseWeightRows(weightRows, `${path}.weights`, weightRows.length, influences, false, budget)
909
913
  };
910
914
  }
911
915
  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}]`)));
916
+ const record2 = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
917
+ const jointIds = Object.freeze(readArray(requireKey(record2, "jointIds", path), `${path}.jointIds`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, 1).map((entry, index) => readId(entry, `${path}.jointIds[${index}]`)));
914
918
  consumeBudget(budget, "skinJoints", jointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "skin joint");
915
919
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skin joint");
916
- const inverseInput = readArray(requireKey(record, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
920
+ const inverseInput = readArray(requireKey(record2, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
917
921
  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);
922
+ const weightInput = readArray(requireKey(record2, "weightSets", path), `${path}.weightSets`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
919
923
  const weightSets = Object.freeze(weightInput.map((entry, index) => parseVertexWeightSet(entry, `${path}.weightSets[${index}]`, budget)));
920
924
  ensureUnique(weightSets.map(({ primitiveId }) => primitiveId), `${path}.weightSets`, "duplicate primitive weight set");
921
925
  return {
922
- id: readId(requireKey(record, "id", path), `${path}.id`),
923
- skeletonId: readId(requireKey(record, "skeletonId", path), `${path}.skeletonId`),
926
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
927
+ skeletonId: readId(requireKey(record2, "skeletonId", path), `${path}.skeletonId`),
924
928
  jointIds,
925
929
  inverseBindMatrices: Object.freeze(inverseInput.map((entry, index) => parseMatrix(entry, `${path}.inverseBindMatrices[${index}]`))),
926
930
  weightSets,
927
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
931
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
928
932
  };
929
933
  }
930
934
  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`) };
935
+ const record2 = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
936
+ const output = { primitiveId: readId(requireKey(record2, "primitiveId", path), `${path}.primitiveId`) };
933
937
  for (const key of ["positionAccessorId", "normalAccessorId", "tangentAccessorId"]) {
934
- assignOptional(output, key, readOptional(record, key, readId, path));
938
+ assignOptional(output, key, readOptional(record2, key, readId, path));
935
939
  }
936
940
  if (!output.positionAccessorId && !output.normalAccessorId && !output.tangentAccessorId) {
937
941
  return fail("invalid-value", path, "blend-shape delta requires at least one accessor");
@@ -939,8 +943,8 @@ function parseBlendShapeDelta(value, path) {
939
943
  return output;
940
944
  }
941
945
  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);
946
+ const record2 = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
947
+ const deltaInput = readArray(requireKey(record2, "deltas", path), `${path}.deltas`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
944
948
  consumeBudget(
945
949
  budget,
946
950
  "blendShapeDeltas",
@@ -952,96 +956,96 @@ function parseBlendShape(value, path, budget) {
952
956
  const deltas = Object.freeze(deltaInput.map((entry, index) => parseBlendShapeDelta(entry, `${path}.deltas[${index}]`)));
953
957
  ensureUnique(deltas.map(({ primitiveId }) => primitiveId), `${path}.deltas`, "duplicate blend-shape primitive delta");
954
958
  const output = {
955
- id: readId(requireKey(record, "id", path), `${path}.id`),
956
- meshId: readId(requireKey(record, "meshId", path), `${path}.meshId`),
959
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
960
+ meshId: readId(requireKey(record2, "meshId", path), `${path}.meshId`),
957
961
  deltas,
958
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
962
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
959
963
  };
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));
964
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
965
+ assignOptional(output, "defaultWeight", readOptional(record2, "defaultWeight", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
962
966
  return output;
963
967
  }
964
968
  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);
969
+ const record2 = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
970
+ const timesInput = readArray(requireKey(record2, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
971
+ const valuesInput = readArray(requireKey(record2, "outputValues", path), `${path}.outputValues`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
968
972
  consumeBudget(budget, "animationValues", timesInput.length + valuesInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationValues, path, "animation value");
969
973
  const times = Object.freeze(timesInput.map((entry, index) => readFinite(entry, `${path}.inputTimesSeconds[${index}]`, 0)));
970
974
  for (let index = 1; index < times.length; index += 1) {
971
975
  if (times[index] <= times[index - 1]) return fail("invalid-value", `${path}.inputTimesSeconds`, "animation input times must be strictly increasing");
972
976
  }
973
977
  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`),
978
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
979
+ inputAccessorId: readId(requireKey(record2, "inputAccessorId", path), `${path}.inputAccessorId`),
980
+ outputAccessorId: readId(requireKey(record2, "outputAccessorId", path), `${path}.outputAccessorId`),
977
981
  inputTimesSeconds: times,
978
982
  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)
983
+ interpolation: readEnum(requireKey(record2, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
984
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
981
985
  };
982
986
  }
983
987
  function parseAnimationChannel(value, path, budget) {
984
- const record = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
988
+ const record2 = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
985
989
  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)
990
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
991
+ samplerId: readId(requireKey(record2, "samplerId", path), `${path}.samplerId`),
992
+ targetType: readEnum(requireKey(record2, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
993
+ targetId: readId(requireKey(record2, "targetId", path), `${path}.targetId`),
994
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
991
995
  };
992
996
  }
993
997
  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);
998
+ const record2 = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
999
+ const samplerInput = readArray(requireKey(record2, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
1000
+ const channelInput = readArray(requireKey(record2, "channels", path), `${path}.channels`, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, 1);
997
1001
  consumeBudget(budget, "animationSamplers", samplerInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, path, "animation sampler");
998
1002
  consumeBudget(budget, "animationChannels", channelInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, path, "animation channel");
999
1003
  const output = {
1000
- id: readId(requireKey(record, "id", path), `${path}.id`),
1001
- durationSeconds: readFinite(requireKey(record, "durationSeconds", path), `${path}.durationSeconds`, 0),
1004
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
1005
+ durationSeconds: readFinite(requireKey(record2, "durationSeconds", path), `${path}.durationSeconds`, 0),
1002
1006
  samplers: Object.freeze(samplerInput.map((entry, index) => parseAnimationSampler(entry, `${path}.samplers[${index}]`, budget))),
1003
1007
  channels: Object.freeze(channelInput.map((entry, index) => parseAnimationChannel(entry, `${path}.channels[${index}]`, budget))),
1004
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1008
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
1005
1009
  };
1006
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
1010
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
1007
1011
  return output;
1008
1012
  }
1009
1013
  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");
1014
+ const record2 = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
1015
+ if (requireKey(record2, "kind", path) !== "brep-placeholder") return fail("invalid-value", `${path}.kind`, "unsupported analytic geometry kind");
1012
1016
  const output = {
1013
- id: readId(requireKey(record, "id", path), `${path}.id`),
1017
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
1014
1018
  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"])
1019
+ sourceResourceId: readId(requireKey(record2, "sourceResourceId", path), `${path}.sourceResourceId`),
1020
+ sourceUnit: readEnum(requireKey(record2, "sourceUnit", path), `${path}.sourceUnit`, ["metre", "millimetre", "centimetre", "inch", "foot", "unitless"])
1017
1021
  };
1018
- assignOptional(output, "tessellatedMeshId", readOptional(record, "tessellatedMeshId", readId, path));
1019
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1022
+ assignOptional(output, "tessellatedMeshId", readOptional(record2, "tessellatedMeshId", readId, path));
1023
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1020
1024
  return output;
1021
1025
  }
1022
1026
  function parseProvenance(value, budget) {
1023
1027
  const path = "$.provenance";
1024
- const record = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
1028
+ const record2 = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
1025
1029
  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)
1030
+ sourceFormat: readString(requireKey(record2, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
1031
+ sourceContentHash: readSha256(requireKey(record2, "sourceContentHash", path), `${path}.sourceContentHash`),
1032
+ converterId: readString(requireKey(record2, "converterId", path), `${path}.converterId`, 128, TOKEN),
1033
+ converterVersion: readString(requireKey(record2, "converterVersion", path), `${path}.converterVersion`, 128, TOKEN)
1030
1034
  };
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));
1035
+ assignOptional(output, "provider", readOptional(record2, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
1036
+ assignOptional(output, "sourceIdentifier", readOptional(record2, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
1037
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1034
1038
  return output;
1035
1039
  }
1036
1040
  function parseDiagnostic(value, path, budget) {
1037
- const record = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
1041
+ const record2 = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
1038
1042
  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)
1043
+ severity: readEnum(requireKey(record2, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
1044
+ code: readString(requireKey(record2, "code", path), `${path}.code`, 128, TOKEN),
1045
+ message: readString(requireKey(record2, "message", path), `${path}.message`, GPU_MODEL_DOCUMENT_LIMITS.diagnosticMessageLength)
1042
1046
  };
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));
1047
+ assignOptional(output, "path", readOptional(record2, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
1048
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
1045
1049
  return output;
1046
1050
  }
1047
1051
  function ensureUnique(values, path, detail = "duplicate identity") {
@@ -1682,11 +1686,11 @@ var DOCUMENT_KEYS = Object.freeze([
1682
1686
  ]);
1683
1687
  function parseGpuModelDocument(value) {
1684
1688
  const path = "$";
1685
- const record = readRecord(value, path, DOCUMENT_KEYS);
1686
- const schemaVersion = requireKey(record, "schemaVersion", path);
1689
+ const record2 = readRecord(value, path, DOCUMENT_KEYS);
1690
+ const schemaVersion = requireKey(record2, "schemaVersion", path);
1687
1691
  if (schemaVersion !== GPU_MODEL_DOCUMENT_SCHEMA_VERSION) return fail("invalid-value", "$.schemaVersion", "unsupported schema version");
1688
1692
  const parseCollection = (key, maximum, parser, minimum = 0) => {
1689
- const input = readArray(requireKey(record, key, path), `$.${key}`, maximum, minimum);
1693
+ const input = readArray(requireKey(record2, key, path), `$.${key}`, maximum, minimum);
1690
1694
  return Object.freeze(input.map((entry, index) => parser(entry, `$.${key}[${index}]`)));
1691
1695
  };
1692
1696
  const budget = {
@@ -1707,10 +1711,10 @@ function parseGpuModelDocument(value) {
1707
1711
  geometryWorldAccessorInstances: 0,
1708
1712
  geometryWorldVertices: 0
1709
1713
  };
1710
- const roots = Object.freeze(readArray(requireKey(record, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1714
+ const roots = Object.freeze(readArray(requireKey(record2, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1711
1715
  const document = {
1712
1716
  schemaVersion,
1713
- coordinateSystem: parseCoordinateSystem(requireKey(record, "coordinateSystem", path)),
1717
+ coordinateSystem: parseCoordinateSystem(requireKey(record2, "coordinateSystem", path)),
1714
1718
  roots,
1715
1719
  nodes: parseCollection("nodes", GPU_MODEL_DOCUMENT_LIMITS.nodes, (entry, entryPath) => parseNode(entry, entryPath, budget), 1),
1716
1720
  resources: parseCollection("resources", GPU_MODEL_DOCUMENT_LIMITS.resources, parseVerifiedResource, 1),
@@ -1724,10 +1728,10 @@ function parseGpuModelDocument(value) {
1724
1728
  blendShapes: parseCollection("blendShapes", GPU_MODEL_DOCUMENT_LIMITS.blendShapes, (entry, entryPath) => parseBlendShape(entry, entryPath, budget)),
1725
1729
  animations: parseCollection("animations", GPU_MODEL_DOCUMENT_LIMITS.animations, (entry, entryPath) => parseAnimation(entry, entryPath, budget)),
1726
1730
  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),
1731
+ bounds: parseBounds(requireKey(record2, "bounds", path), "$.bounds"),
1732
+ provenance: parseProvenance(requireKey(record2, "provenance", path), budget),
1729
1733
  diagnostics: parseCollection("diagnostics", GPU_MODEL_DOCUMENT_LIMITS.diagnostics, (entry, entryPath) => parseDiagnostic(entry, entryPath, budget)),
1730
- metadata: readMetadata(requireKey(record, "metadata", path), "$.metadata", budget)
1734
+ metadata: readMetadata(requireKey(record2, "metadata", path), "$.metadata", budget)
1731
1735
  };
1732
1736
  if (document.meshes.length === 0) return fail("invalid-value", "$.meshes", "v1 document bounds require at least one canonical mesh");
1733
1737
  validateReferences(document, budget);
@@ -1764,8 +1768,8 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1764
1768
  value.resources.forEach((resource, index) => assertVerifiedResourceEvidence(resource, limits, `$.resources[${index}]`));
1765
1769
  return value;
1766
1770
  }
1767
- const record = readRecord(value, "$", DOCUMENT_KEYS);
1768
- const resourceInput = readArray(requireKey(record, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1771
+ const record2 = readRecord(value, "$", DOCUMENT_KEYS);
1772
+ const resourceInput = readArray(requireKey(record2, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1769
1773
  const parsed = resourceInput.map((entry, index) => {
1770
1774
  if (typeof entry === "object" && entry !== null && verifiedResources.has(entry) && verifiedResourceBytes.has(entry)) return entry;
1771
1775
  return parseResourceHeader(entry, `$.resources[${index}]`);
@@ -1778,7 +1782,7 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1778
1782
  }
1779
1783
  const candidate = {};
1780
1784
  for (const key of DOCUMENT_KEYS) {
1781
- if (Object.hasOwn(record, key)) candidate[key] = record[key];
1785
+ if (Object.hasOwn(record2, key)) candidate[key] = record2[key];
1782
1786
  }
1783
1787
  candidate.resources = verified;
1784
1788
  return createGpuModelDocument(candidate);
@@ -1843,11 +1847,11 @@ async function hashGpuModelDocumentCanonical(document) {
1843
1847
  return Array.from(new Uint8Array(digest), (value) => value.toString(16).padStart(2, "0")).join("");
1844
1848
  }
1845
1849
  function resolveStaticDemoLimits(options) {
1846
- const record = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1847
- const enabled = readBoolean(requireKey(record, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1850
+ const record2 = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1851
+ const enabled = readBoolean(requireKey(record2, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1848
1852
  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));
1853
+ if (!Object.hasOwn(record2, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1854
+ const overrides = readRecord(record2.limits, "$.options.limits", Object.keys(GPU_MODEL_STATIC_DEMO_LIMITS));
1851
1855
  const limits = { ...GPU_MODEL_STATIC_DEMO_LIMITS };
1852
1856
  for (const [key, ceiling] of Object.entries(GPU_MODEL_STATIC_DEMO_LIMITS)) {
1853
1857
  if (Object.hasOwn(overrides, key)) limits[key] = readSafeInteger(overrides[key], `$.options.limits.${key}`, 1, ceiling);
@@ -2082,6 +2086,260 @@ async function createGpuModelStaticDemoCompilerInput(document, options) {
2082
2086
  });
2083
2087
  }
2084
2088
 
2089
+ // src/diagnostics.ts
2090
+ var GPU_MODEL_REPAIR_RULES = Object.freeze([
2091
+ {
2092
+ "id": "missing-normals",
2093
+ "action": "Generate deterministic vertex normals from validated triangle geometry.",
2094
+ "severity": "warning",
2095
+ "confidence": "high",
2096
+ "standardIds": [
2097
+ "gltf-glb",
2098
+ "obj-mtl",
2099
+ "fbx",
2100
+ "cad-bim"
2101
+ ],
2102
+ "policy": "Generate deterministic vertex normals from validated triangle geometry and record the generation basis."
2103
+ },
2104
+ {
2105
+ "id": "bad-winding",
2106
+ "action": "Normalize winding to the adapter coordinate convention.",
2107
+ "severity": "warning",
2108
+ "confidence": "medium",
2109
+ "standardIds": [
2110
+ "gltf-glb",
2111
+ "obj-mtl",
2112
+ "fbx",
2113
+ "cad-bim"
2114
+ ],
2115
+ "policy": "Normalize winding to the adapter coordinate convention and record the inferred orientation."
2116
+ },
2117
+ {
2118
+ "id": "invalid-indices",
2119
+ "action": "Drop the invalid primitive.",
2120
+ "severity": "error",
2121
+ "confidence": "medium",
2122
+ "standardIds": [
2123
+ "gltf-glb",
2124
+ "obj-mtl",
2125
+ "fbx",
2126
+ "cad-bim"
2127
+ ],
2128
+ "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."
2129
+ },
2130
+ {
2131
+ "id": "nan-transforms",
2132
+ "action": "Replace non-finite transform components with identity components.",
2133
+ "severity": "error",
2134
+ "confidence": "high",
2135
+ "standardIds": [
2136
+ "gltf-glb",
2137
+ "obj-mtl",
2138
+ "fbx",
2139
+ "cad-bim"
2140
+ ],
2141
+ "policy": "Replace the non-finite component with the identity transform component and record the source value and fallback."
2142
+ },
2143
+ {
2144
+ "id": "non-normalized-weights",
2145
+ "action": "Normalize finite non-negative skin weights.",
2146
+ "severity": "warning",
2147
+ "confidence": "high",
2148
+ "standardIds": [
2149
+ "gltf-glb",
2150
+ "fbx",
2151
+ "cad-bim"
2152
+ ],
2153
+ "policy": "Normalize finite, non-negative influences and record the pre-normalization weights and discarded influence policy."
2154
+ },
2155
+ {
2156
+ "id": "missing-texture-path",
2157
+ "action": "Leave the missing or unsafe texture slot unresolved.",
2158
+ "severity": "warning",
2159
+ "confidence": "low",
2160
+ "standardIds": [
2161
+ "gltf-glb",
2162
+ "obj-mtl",
2163
+ "fbx",
2164
+ "cad-bim"
2165
+ ],
2166
+ "policy": "Resolve only an explicitly bounded package-relative fallback; otherwise keep the material slot unresolved and emit a visible warning."
2167
+ },
2168
+ {
2169
+ "id": "malformed-material-reference",
2170
+ "action": "Attach the canonical default material.",
2171
+ "severity": "error",
2172
+ "confidence": "medium",
2173
+ "standardIds": [
2174
+ "gltf-glb",
2175
+ "obj-mtl",
2176
+ "fbx",
2177
+ "cad-bim"
2178
+ ],
2179
+ "policy": "Attach the canonical default material only when the target policy permits it and preserve the unresolved source reference."
2180
+ }
2181
+ ].map((rule) => Object.freeze({ ...rule, standardIds: Object.freeze(rule.standardIds) })));
2182
+ var GPU_MODEL_DIAGNOSTIC_LIMITS = Object.freeze({
2183
+ issues: 4096,
2184
+ values: 16384,
2185
+ depth: 12,
2186
+ members: 1024,
2187
+ string: 65536,
2188
+ bytes: 1048576
2189
+ });
2190
+ var GpuModelDiagnosticsError = class extends TypeError {
2191
+ constructor() {
2192
+ super("Invalid or over-budget GPU model diagnostics report");
2193
+ this.name = "GpuModelDiagnosticsError";
2194
+ }
2195
+ };
2196
+ function fail2() {
2197
+ throw new GpuModelDiagnosticsError();
2198
+ }
2199
+ function charge(budget, bytes) {
2200
+ budget.bytes += bytes;
2201
+ if (budget.bytes > GPU_MODEL_DIAGNOSTIC_LIMITS.bytes) fail2();
2202
+ }
2203
+ function textBytes(value) {
2204
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
2205
+ }
2206
+ function snapshot(value, depth, budget, ancestors, memberLimit = GPU_MODEL_DIAGNOSTIC_LIMITS.members) {
2207
+ if (++budget.values > GPU_MODEL_DIAGNOSTIC_LIMITS.values || depth > GPU_MODEL_DIAGNOSTIC_LIMITS.depth) fail2();
2208
+ if (value === null || typeof value === "boolean") {
2209
+ charge(budget, value === null ? 4 : value ? 4 : 5);
2210
+ return value;
2211
+ }
2212
+ if (typeof value === "number") {
2213
+ if (!Number.isFinite(value)) fail2();
2214
+ charge(budget, JSON.stringify(value).length);
2215
+ return Object.is(value, -0) ? 0 : value;
2216
+ }
2217
+ if (typeof value === "string") {
2218
+ if (value.length > GPU_MODEL_DIAGNOSTIC_LIMITS.string) fail2();
2219
+ charge(budget, textBytes(value));
2220
+ return value;
2221
+ }
2222
+ if (typeof value !== "object" || ancestors.has(value)) fail2();
2223
+ const array = Array.isArray(value);
2224
+ const prototype = Object.getPrototypeOf(value);
2225
+ if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) fail2();
2226
+ const keys = Reflect.ownKeys(value);
2227
+ const maxMembers = memberLimit;
2228
+ if (keys.length > maxMembers + (array ? 1 : 0)) fail2();
2229
+ ancestors.add(value);
2230
+ charge(budget, 2);
2231
+ if (array) {
2232
+ const length = Object.getOwnPropertyDescriptor(value, "length").value;
2233
+ if (length > maxMembers || keys.length !== length + 1) fail2();
2234
+ const result2 = [];
2235
+ for (let i = 0; i < length; i++) {
2236
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(i));
2237
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail2();
2238
+ if (i > 0) charge(budget, 1);
2239
+ result2.push(snapshot(descriptor.value, depth + 1, budget, ancestors));
2240
+ }
2241
+ ancestors.delete(value);
2242
+ return Object.freeze(result2);
2243
+ }
2244
+ const result = {};
2245
+ let count = 0;
2246
+ for (const key of keys) {
2247
+ if (typeof key !== "string" || key.length > 256 || ["__proto__", "constructor", "prototype"].includes(key)) fail2();
2248
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2249
+ if (!("value" in descriptor) || !descriptor.enumerable) fail2();
2250
+ if (count++ > 0) charge(budget, 1);
2251
+ charge(budget, textBytes(key) + 1);
2252
+ result[key] = snapshot(descriptor.value, depth + 1, budget, ancestors, depth === 0 && key === "issues" ? GPU_MODEL_DIAGNOSTIC_LIMITS.issues : GPU_MODEL_DIAGNOSTIC_LIMITS.members);
2253
+ }
2254
+ ancestors.delete(value);
2255
+ return Object.freeze(result);
2256
+ }
2257
+ function record(value) {
2258
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return fail2();
2259
+ return value;
2260
+ }
2261
+ function keysOnly(value, allowed) {
2262
+ if (Object.keys(value).some((key) => !allowed.includes(key))) fail2();
2263
+ }
2264
+ function enumValue(value, allowed) {
2265
+ if (typeof value !== "string" || !allowed.includes(value)) return fail2();
2266
+ return value;
2267
+ }
2268
+ function hasUnsafeText(value) {
2269
+ for (const character of value) {
2270
+ const point = character.codePointAt(0);
2271
+ if (point < 32 || point >= 127 && point <= 159 || point >= 8232 && point <= 8238 || point >= 8294 && point <= 8297 || point >= 55296 && point <= 57343) return true;
2272
+ }
2273
+ return false;
2274
+ }
2275
+ function text(value, max) {
2276
+ if (typeof value !== "string" || value.length === 0 || value.length > max || hasUnsafeText(value)) return fail2();
2277
+ return value;
2278
+ }
2279
+ function boolean(value) {
2280
+ if (typeof value !== "boolean") return fail2();
2281
+ return value;
2282
+ }
2283
+ function parseIssue(value) {
2284
+ const issue = record(value);
2285
+ const kind = enumValue(issue.kind, ["violation", "unsupported", "warning", "loss"]);
2286
+ const base = { code: text(issue.code, 128), path: text(issue.path, 1024), message: text(issue.message, 2048) };
2287
+ if (!base.path.startsWith("/")) fail2();
2288
+ const common = ["kind", "code", "path", "message"];
2289
+ if (kind === "violation") {
2290
+ keysOnly(issue, [...common, "ruleId", "originalValue", "repairApplied"]);
2291
+ if (!Object.hasOwn(issue, "originalValue")) fail2();
2292
+ return { ...base, kind, ruleId: text(issue.ruleId, 128), originalValue: issue.originalValue, repairApplied: boolean(issue.repairApplied) };
2293
+ }
2294
+ if (kind === "loss") {
2295
+ keysOnly(issue, [...common, "effect"]);
2296
+ return { ...base, kind, effect: enumValue(issue.effect, ["dropped", "approximated"]) };
2297
+ }
2298
+ keysOnly(issue, common);
2299
+ return { ...base, kind };
2300
+ }
2301
+ function evaluateGpuModelDiagnostics(value) {
2302
+ const budget = { values: 0, bytes: 0 };
2303
+ const args = record(snapshot(value, 0, budget, /* @__PURE__ */ new Set()));
2304
+ keysOnly(args, ["standard", "mode", "allowLoss", "issues", "rawSource", "maxEvidenceBytes"]);
2305
+ const standard = enumValue(args.standard, ["gltf-glb", "obj-mtl", "fbx", "cad-bim"]);
2306
+ const mode = args.mode === void 0 ? "strict" : enumValue(args.mode, ["strict", "tolerant", "forensic"]);
2307
+ const allowLoss = args.allowLoss === void 0 ? false : boolean(args.allowLoss);
2308
+ 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();
2309
+ if (mode === "forensic" ? !Object.hasOwn(args, "rawSource") : Object.hasOwn(args, "rawSource")) fail2();
2310
+ if (!Array.isArray(args.issues) || args.issues.length > GPU_MODEL_DIAGNOSTIC_LIMITS.issues) fail2();
2311
+ const issues = args.issues.map(parseIssue);
2312
+ const lossPaths = new Set(issues.filter((issue) => issue.kind === "loss").map((issue) => issue.path));
2313
+ const diagnostics = [];
2314
+ const repairs = [];
2315
+ const losses = [];
2316
+ let accepted = true;
2317
+ for (const issue of issues) {
2318
+ const base = { code: issue.code, path: issue.path, message: issue.message };
2319
+ if (issue.kind === "violation") {
2320
+ const rule = GPU_MODEL_REPAIR_RULES.find((rule2) => rule2.id === issue.ruleId);
2321
+ const requiresLoss = ["invalid-indices", "missing-texture-path", "malformed-material-reference"].includes(issue.ruleId);
2322
+ const repaired = mode !== "strict" && issue.repairApplied && rule && rule.standardIds.includes(standard) && (!requiresLoss || lossPaths.has(issue.path));
2323
+ if (repaired) {
2324
+ repairs.push(Object.freeze({ ruleId: rule.id, originalValue: issue.originalValue, action: rule.action, severity: rule.severity, affectedPath: issue.path, confidence: rule.confidence }));
2325
+ diagnostics.push(Object.freeze({ ...base, kind: "repair", severity: rule.severity, ruleId: rule.id }));
2326
+ } else {
2327
+ accepted = false;
2328
+ diagnostics.push(Object.freeze({ ...base, kind: "blocking-error", severity: "error", ruleId: issue.ruleId }));
2329
+ }
2330
+ } else if (issue.kind === "unsupported") {
2331
+ accepted = false;
2332
+ diagnostics.push(Object.freeze({ ...issue, severity: "error" }));
2333
+ } else if (issue.kind === "loss") {
2334
+ if (!allowLoss) accepted = false;
2335
+ const loss = Object.freeze({ ...issue, severity: "warning" });
2336
+ losses.push(loss);
2337
+ diagnostics.push(loss);
2338
+ } else diagnostics.push(Object.freeze({ ...issue, severity: "warning" }));
2339
+ }
2340
+ return Object.freeze({ mode, standard, accepted, diagnostics: Object.freeze(diagnostics), repairs: Object.freeze(repairs), losses: Object.freeze(losses), ...mode === "forensic" ? { rawSource: args.rawSource } : {} });
2341
+ }
2342
+
2085
2343
  // src/index.ts
2086
2344
  var packageName = "@plasius/gpu-model-core";
2087
2345
  var packageBootstrap = Object.freeze({
@@ -2092,8 +2350,10 @@ var packageBootstrap = Object.freeze({
2092
2350
  // Annotate the CommonJS export names for ESM import in node:
2093
2351
  0 && (module.exports = {
2094
2352
  CANONICAL_GPU_MODEL_COORDINATE_SYSTEM,
2353
+ GPU_MODEL_DIAGNOSTIC_LIMITS,
2095
2354
  GPU_MODEL_DOCUMENT_LIMITS,
2096
2355
  GPU_MODEL_DOCUMENT_SCHEMA_VERSION,
2356
+ GPU_MODEL_REPAIR_RULES,
2097
2357
  GPU_MODEL_RESOURCE_VERIFICATION_LIMITS,
2098
2358
  GPU_MODEL_STATIC_DEMO_FEATURE_FLAG,
2099
2359
  GPU_MODEL_STATIC_DEMO_LIMITS,
@@ -2102,6 +2362,7 @@ var packageBootstrap = Object.freeze({
2102
2362
  GPU_MODEL_STATIC_DEMO_MAX_EXTENT_METRES,
2103
2363
  GPU_MODEL_STATIC_DEMO_NORMALIZATION_CONTRACT,
2104
2364
  GPU_MODEL_STATIC_DEMO_PROFILE_VERSION,
2365
+ GpuModelDiagnosticsError,
2105
2366
  GpuModelDocumentError,
2106
2367
  assertGpuModelStaticDemoNormalization,
2107
2368
  canCreateGpuModelDocument,
@@ -2109,6 +2370,7 @@ var packageBootstrap = Object.freeze({
2109
2370
  createGpuModelDocument,
2110
2371
  createGpuModelStaticDemoCompilerInput,
2111
2372
  encodeGpuModelDocumentCanonical,
2373
+ evaluateGpuModelDiagnostics,
2112
2374
  hashGpuModelDocumentCanonical,
2113
2375
  isGpuModelDocument,
2114
2376
  packageBootstrap,