@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.js CHANGED
@@ -172,9 +172,9 @@ function readRecord(value, path, allowedKeys) {
172
172
  }
173
173
  return value;
174
174
  }
175
- function requireKey(record, key, path) {
176
- if (!Object.hasOwn(record, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
177
- return record[key];
175
+ function requireKey(record2, key, path) {
176
+ if (!Object.hasOwn(record2, key)) return fail("invalid-value", `${path}.${key}`, "required key is missing");
177
+ return record2[key];
178
178
  }
179
179
  function readArray(value, path, maximum, minimum = 0) {
180
180
  if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
@@ -253,9 +253,9 @@ function readNumberTuple(value, path, length, minimum, maximum) {
253
253
  const input = readArray(value, path, length, length);
254
254
  return Object.freeze(input.map((entry, index) => readFinite(entry, `${path}[${index}]`, minimum, maximum)));
255
255
  }
256
- function readOptional(record, key, parser, path) {
257
- if (!Object.hasOwn(record, key)) return void 0;
258
- return parser(record[key], `${path}.${key}`);
256
+ function readOptional(record2, key, parser, path) {
257
+ if (!Object.hasOwn(record2, key)) return void 0;
258
+ return parser(record2[key], `${path}.${key}`);
259
259
  }
260
260
  function assignOptional(target, key, value) {
261
261
  if (value !== void 0) Object.assign(target, { [key]: value });
@@ -277,17 +277,17 @@ function readMetadataValue(value, path, depth, budget) {
277
277
  return Object.freeze(input.map((entry, index) => readMetadataValue(entry, `${path}[${index}]`, depth + 1, budget)));
278
278
  }
279
279
  if (typeof value !== "object" || value === null) return fail("invalid-type", path, "metadata value is not JSON-compatible");
280
- const record = value;
281
- const prototype = Object.getPrototypeOf(record);
280
+ const record2 = value;
281
+ const prototype = Object.getPrototypeOf(record2);
282
282
  if (prototype !== Object.prototype && prototype !== null) return fail("invalid-type", path, "metadata objects must be plain objects");
283
- const keys = Reflect.ownKeys(record);
283
+ const keys = Reflect.ownKeys(record2);
284
284
  if (keys.length > GPU_MODEL_DOCUMENT_LIMITS.metadataObjectKeys) return fail("limit-exceeded", path, "metadata object has too many keys");
285
285
  const output = {};
286
286
  for (const key of keys) {
287
287
  if (typeof key !== "string") return fail("unknown-key", path, "metadata symbol keys are not permitted");
288
288
  if (PROHIBITED_KEYS.has(key)) return fail("unknown-key", `${path}.${key}`, "prohibited metadata key");
289
289
  readString(key, `${path}.[key]`, GPU_MODEL_DOCUMENT_LIMITS.metadataKeyLength, METADATA_KEY);
290
- const descriptor = Object.getOwnPropertyDescriptor(record, key);
290
+ const descriptor = Object.getOwnPropertyDescriptor(record2, key);
291
291
  if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) return fail("invalid-type", `${path}.${key}`, "metadata properties must be enumerable data properties");
292
292
  output[key] = readMetadataValue(descriptor.value, `${path}.${key}`, depth + 1, budget);
293
293
  }
@@ -300,9 +300,9 @@ function readMetadata(value, path, budget) {
300
300
  }
301
301
  function parseCoordinateSystem(value) {
302
302
  const path = "$.coordinateSystem";
303
- const record = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
303
+ const record2 = readRecord(value, path, ["unit", "upAxis", "forwardAxis", "handedness", "winding", "origin"]);
304
304
  for (const [key, expected] of Object.entries(CANONICAL_GPU_MODEL_COORDINATE_SYSTEM)) {
305
- if (requireKey(record, key, path) !== expected) return fail("invalid-value", `${path}.${key}`, "coordinate system is not canonical");
305
+ if (requireKey(record2, key, path) !== expected) return fail("invalid-value", `${path}.${key}`, "coordinate system is not canonical");
306
306
  }
307
307
  return CANONICAL_GPU_MODEL_COORDINATE_SYSTEM;
308
308
  }
@@ -314,19 +314,19 @@ function parseMatrix(value, path, requireAffine = true) {
314
314
  return matrix;
315
315
  }
316
316
  function parseBounds(value, path) {
317
- const record = readRecord(value, path, ["min", "max"]);
318
- const min = readNumberTuple(requireKey(record, "min", path), `${path}.min`, 3);
319
- const max = readNumberTuple(requireKey(record, "max", path), `${path}.max`, 3);
317
+ const record2 = readRecord(value, path, ["min", "max"]);
318
+ const min = readNumberTuple(requireKey(record2, "min", path), `${path}.min`, 3);
319
+ const max = readNumberTuple(requireKey(record2, "max", path), `${path}.max`, 3);
320
320
  for (let index = 0; index < 3; index += 1) {
321
321
  if (min[index] > max[index]) return fail("invalid-value", path, "bounds min cannot exceed max");
322
322
  }
323
323
  return { min, max };
324
324
  }
325
325
  function parseTransformTrs(value, path) {
326
- const record = readRecord(value, path, ["translation", "rotation", "scale"]);
327
- const translation = readNumberTuple(requireKey(record, "translation", path), `${path}.translation`, 3);
328
- const rotation = readNumberTuple(requireKey(record, "rotation", path), `${path}.rotation`, 4);
329
- const scale = readNumberTuple(requireKey(record, "scale", path), `${path}.scale`, 3);
326
+ const record2 = readRecord(value, path, ["translation", "rotation", "scale"]);
327
+ const translation = readNumberTuple(requireKey(record2, "translation", path), `${path}.translation`, 3);
328
+ const rotation = readNumberTuple(requireKey(record2, "rotation", path), `${path}.rotation`, 4);
329
+ const scale = readNumberTuple(requireKey(record2, "scale", path), `${path}.scale`, 3);
330
330
  const magnitude = Math.hypot(...rotation);
331
331
  if (!almostEqual(magnitude, 1, 1e-4)) return fail("invalid-value", `${path}.rotation`, "rotation quaternion must be normalized");
332
332
  return { translation, rotation, scale };
@@ -348,25 +348,25 @@ function intrinsicBlobArrayBuffer(blob) {
348
348
  return Reflect.apply(blobArrayBuffer, blob, []);
349
349
  }
350
350
  function parseResourceHeader(value, path) {
351
- const record = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
352
- const kind = readEnum(requireKey(record, "kind", path), `${path}.kind`, ["buffer", "image"]);
353
- const mimeType = readString(requireKey(record, "mimeType", path), `${path}.mimeType`, 128, MIME_TYPE).toLowerCase();
351
+ const record2 = readRecord(value, path, ["id", "kind", "contentHash", "byteLength", "mimeType", "payload"]);
352
+ const kind = readEnum(requireKey(record2, "kind", path), `${path}.kind`, ["buffer", "image"]);
353
+ const mimeType = readString(requireKey(record2, "mimeType", path), `${path}.mimeType`, 128, MIME_TYPE).toLowerCase();
354
354
  if (kind === "image" ? !IMAGE_MIME_TYPES.has(mimeType) : !BUFFER_MIME_TYPES.has(mimeType)) {
355
355
  return fail("invalid-value", `${path}.mimeType`, `unsupported ${kind} MIME type`);
356
356
  }
357
- const payload = requireKey(record, "payload", path);
357
+ const payload = requireKey(record2, "payload", path);
358
358
  if (!(payload instanceof Blob)) return fail("invalid-type", `${path}.payload`, "expected an immutable Blob payload");
359
359
  if (Object.getPrototypeOf(payload) !== Blob.prototype || Reflect.ownKeys(payload).some((key) => typeof key === "string")) {
360
360
  return fail("invalid-type", `${path}.payload`, "expected a native Blob without caller-defined properties");
361
361
  }
362
- const byteLength = readSafeInteger(requireKey(record, "byteLength", path), `${path}.byteLength`, 1);
362
+ const byteLength = readSafeInteger(requireKey(record2, "byteLength", path), `${path}.byteLength`, 1);
363
363
  if (intrinsicBlobSize(payload) !== byteLength) return fail("payload-length-mismatch", `${path}.payload`, "payload byte length does not match byteLength");
364
364
  const payloadType = intrinsicBlobType(payload);
365
365
  if (payloadType && payloadType.toLowerCase() !== mimeType) return fail("invalid-value", `${path}.payload`, "payload MIME type does not match mimeType");
366
366
  return {
367
- id: readId(requireKey(record, "id", path), `${path}.id`),
367
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
368
368
  kind,
369
- contentHash: readSha256(requireKey(record, "contentHash", path), `${path}.contentHash`),
369
+ contentHash: readSha256(requireKey(record2, "contentHash", path), `${path}.contentHash`),
370
370
  byteLength,
371
371
  mimeType,
372
372
  payload
@@ -375,11 +375,11 @@ function parseResourceHeader(value, path) {
375
375
  function resolveVerificationLimits(options) {
376
376
  const overrides = options?.limits;
377
377
  if (overrides === void 0) return GPU_MODEL_RESOURCE_VERIFICATION_LIMITS;
378
- const record = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
378
+ const record2 = readRecord(overrides, "$.verification.limits", Object.keys(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS));
379
379
  const output = { ...GPU_MODEL_RESOURCE_VERIFICATION_LIMITS };
380
380
  for (const [key, ceiling] of Object.entries(GPU_MODEL_RESOURCE_VERIFICATION_LIMITS)) {
381
- if (!Object.hasOwn(record, key)) continue;
382
- const value = readSafeInteger(record[key], `$.verification.limits.${key}`, 1, ceiling);
381
+ if (!Object.hasOwn(record2, key)) continue;
382
+ const value = readSafeInteger(record2[key], `$.verification.limits.${key}`, 1, ceiling);
383
383
  Object.assign(output, { [key]: value });
384
384
  }
385
385
  if (output.maxImageBytes > output.maxResourceBytes) output.maxImageBytes = output.maxResourceBytes;
@@ -432,7 +432,7 @@ function verificationAbortError(signal, path) {
432
432
  function throwIfVerificationAborted(signal, path) {
433
433
  if (signal.aborted) throw verificationAbortError(signal, path);
434
434
  }
435
- function createBlobChunks(blob, chunkBytes, observed, signal, path, snapshot) {
435
+ function createBlobChunks(blob, chunkBytes, observed, signal, path, snapshot2) {
436
436
  return {
437
437
  async *[Symbol.asyncIterator]() {
438
438
  const size = intrinsicBlobSize(blob);
@@ -442,7 +442,7 @@ function createBlobChunks(blob, chunkBytes, observed, signal, path, snapshot) {
442
442
  const chunk = new Uint8Array(await intrinsicBlobArrayBuffer(intrinsicBlobSlice(blob, offset, end)));
443
443
  throwIfVerificationAborted(signal, path);
444
444
  observed.bytes += chunk.byteLength;
445
- if (snapshot) snapshot.set(chunk, offset);
445
+ if (snapshot2) snapshot2.set(chunk, offset);
446
446
  yield chunk;
447
447
  }
448
448
  throwIfVerificationAborted(signal, path);
@@ -491,13 +491,13 @@ function createVerificationDeadline(timeoutMs, callerSignal, path) {
491
491
  };
492
492
  }
493
493
  function parseInspection(value, path) {
494
- const record = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
494
+ const record2 = readRecord(value, path, ["valid", "detectedMimeType", "width", "height"]);
495
495
  const output = {
496
- valid: readBoolean(requireKey(record, "valid", path), `${path}.valid`),
497
- detectedMimeType: readString(requireKey(record, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
496
+ valid: readBoolean(requireKey(record2, "valid", path), `${path}.valid`),
497
+ detectedMimeType: readString(requireKey(record2, "detectedMimeType", path), `${path}.detectedMimeType`, 128, MIME_TYPE).toLowerCase()
498
498
  };
499
- assignOptional(output, "width", readOptional(record, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
500
- assignOptional(output, "height", readOptional(record, "height", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
499
+ assignOptional(output, "width", readOptional(record2, "width", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
500
+ assignOptional(output, "height", readOptional(record2, "height", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
501
501
  return output;
502
502
  }
503
503
  function bytesStartWith(bytes, signature) {
@@ -570,12 +570,12 @@ async function verifyParsedResource(resource, port, limits, options, path = "$.r
570
570
  } else if (inspection.width !== void 0 || inspection.height !== void 0) {
571
571
  return fail("resource-verification-failed", path, "buffer inspection must not report image dimensions");
572
572
  }
573
- const snapshot = new Uint8Array(resource.byteLength);
573
+ const snapshot2 = new Uint8Array(resource.byteLength);
574
574
  const digestObserved = { bytes: 0 };
575
575
  let digest;
576
576
  try {
577
577
  digest = await deadline.race(() => port.digestSha256(
578
- createBlobChunks(resource.payload, limits.chunkBytes, digestObserved, deadline.signal, path, snapshot),
578
+ createBlobChunks(resource.payload, limits.chunkBytes, digestObserved, deadline.signal, path, snapshot2),
579
579
  context
580
580
  ));
581
581
  } catch (error) {
@@ -588,13 +588,13 @@ async function verifyParsedResource(resource, port, limits, options, path = "$.r
588
588
  if (typeof digest !== "string" || !SHA_256.test(digest) || digest !== resource.contentHash) {
589
589
  return fail("resource-verification-failed", path, "resource digest does not match contentHash");
590
590
  }
591
- if (resource.kind === "image") validateImageMagic(resource, snapshot, inspection, path);
591
+ if (resource.kind === "image") validateImageMagic(resource, snapshot2, inspection, path);
592
592
  const verified = Object.freeze({
593
593
  ...resource,
594
- payload: new Blob([snapshot], { type: resource.mimeType })
594
+ payload: new Blob([snapshot2], { type: resource.mimeType })
595
595
  });
596
596
  verifiedResources.add(verified);
597
- verifiedResourceBytes.set(verified, snapshot);
597
+ verifiedResourceBytes.set(verified, snapshot2);
598
598
  verifiedResourceEvidence.set(verified, Object.freeze({ ...inspection }));
599
599
  return verified;
600
600
  } finally {
@@ -612,31 +612,31 @@ async function verifyGpuModelResource(value, port, options) {
612
612
  }
613
613
  }
614
614
  function parseNode(value, path, budget) {
615
- const record = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
616
- const children = Object.freeze(readArray(requireKey(record, "children", path), `${path}.children`, GPU_MODEL_DOCUMENT_LIMITS.nodes).map((entry, index) => readId(entry, `${path}.children[${index}]`)));
615
+ const record2 = readRecord(value, path, ["id", "name", "children", "meshId", "skinId", "localMatrix", "metadata"]);
616
+ const children = Object.freeze(readArray(requireKey(record2, "children", path), `${path}.children`, GPU_MODEL_DOCUMENT_LIMITS.nodes).map((entry, index) => readId(entry, `${path}.children[${index}]`)));
617
617
  consumeBudget(budget, "sceneEdges", children.length, GPU_MODEL_DOCUMENT_LIMITS.sceneEdges, path, "scene edge");
618
618
  ensureUnique(children, `${path}.children`, "duplicate child node");
619
619
  const output = {
620
- id: readId(requireKey(record, "id", path), `${path}.id`),
620
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
621
621
  children,
622
- localMatrix: parseMatrix(requireKey(record, "localMatrix", path), `${path}.localMatrix`)
622
+ localMatrix: parseMatrix(requireKey(record2, "localMatrix", path), `${path}.localMatrix`)
623
623
  };
624
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
625
- assignOptional(output, "meshId", readOptional(record, "meshId", readId, path));
626
- assignOptional(output, "skinId", readOptional(record, "skinId", readId, path));
627
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
624
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
625
+ assignOptional(output, "meshId", readOptional(record2, "meshId", readId, path));
626
+ assignOptional(output, "skinId", readOptional(record2, "skinId", readId, path));
627
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
628
628
  return output;
629
629
  }
630
630
  function parseAccessor(value, path, budget) {
631
- const record = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
632
- const componentType = readEnum(requireKey(record, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
633
- const elementType = readEnum(requireKey(record, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
634
- const normalized = readOptional(record, "normalized", readBoolean, path);
631
+ const record2 = readRecord(value, path, ["id", "resourceId", "byteOffset", "byteStride", "count", "componentType", "elementType", "normalized", "min", "max"]);
632
+ const componentType = readEnum(requireKey(record2, "componentType", path), `${path}.componentType`, ["i8", "u8", "i16", "u16", "u32", "f32"]);
633
+ const elementType = readEnum(requireKey(record2, "elementType", path), `${path}.elementType`, ["scalar", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4"]);
634
+ const normalized = readOptional(record2, "normalized", readBoolean, path);
635
635
  if (normalized === true && componentType === "f32") return fail("invalid-value", `${path}.normalized`, "floating-point accessors cannot be normalized");
636
636
  if (elementType.startsWith("mat") && componentType !== "f32") return fail("invalid-value", `${path}.componentType`, "matrix accessors must use f32 components");
637
637
  const expectedComponents = ELEMENT_COMPONENTS[elementType];
638
- const min = readOptional(record, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
639
- const max = readOptional(record, "max", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
638
+ const min = readOptional(record2, "min", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
639
+ const max = readOptional(record2, "max", (entry, entryPath) => readNumberTuple(entry, entryPath, expectedComponents), path);
640
640
  if (min === void 0 !== (max === void 0)) return fail("invalid-value", path, "accessor min and max must be supplied together");
641
641
  if (min && max) {
642
642
  const [componentMinimum, componentMaximum, integer] = COMPONENT_RANGES[componentType];
@@ -649,25 +649,25 @@ function parseAccessor(value, path, budget) {
649
649
  }
650
650
  }
651
651
  }
652
- const count = readSafeInteger(requireKey(record, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
652
+ const count = readSafeInteger(requireKey(record2, "count", path), `${path}.count`, 1, GPU_MODEL_DOCUMENT_LIMITS.weightVertices);
653
653
  consumeBudget(budget, "accessorElements", count * expectedComponents, GPU_MODEL_DOCUMENT_LIMITS.accessorElements, path, "accessor element component");
654
654
  const output = {
655
- id: readId(requireKey(record, "id", path), `${path}.id`),
656
- resourceId: readId(requireKey(record, "resourceId", path), `${path}.resourceId`),
657
- byteOffset: readSafeInteger(requireKey(record, "byteOffset", path), `${path}.byteOffset`),
655
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
656
+ resourceId: readId(requireKey(record2, "resourceId", path), `${path}.resourceId`),
657
+ byteOffset: readSafeInteger(requireKey(record2, "byteOffset", path), `${path}.byteOffset`),
658
658
  count,
659
659
  componentType,
660
660
  elementType
661
661
  };
662
- assignOptional(output, "byteStride", readOptional(record, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
662
+ assignOptional(output, "byteStride", readOptional(record2, "byteStride", (entry, entryPath) => readSafeInteger(entry, entryPath, 1, 2048), path));
663
663
  assignOptional(output, "normalized", normalized);
664
664
  assignOptional(output, "min", min);
665
665
  assignOptional(output, "max", max);
666
666
  return output;
667
667
  }
668
668
  function parsePrimitive(value, path, budget) {
669
- const record = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
670
- const attributesInput = readArray(requireKey(record, "attributes", path), `${path}.attributes`, 64, 1);
669
+ const record2 = readRecord(value, path, ["id", "topology", "attributes", "indicesAccessorId", "materialId", "metadata"]);
670
+ const attributesInput = readArray(requireKey(record2, "attributes", path), `${path}.attributes`, 64, 1);
671
671
  consumeBudget(budget, "primitiveAttributes", attributesInput.length, GPU_MODEL_DOCUMENT_LIMITS.primitiveAttributes, path, "primitive attribute");
672
672
  const attributes = Object.freeze(attributesInput.map((entry, index) => {
673
673
  const entryPath = `${path}.attributes[${index}]`;
@@ -680,53 +680,53 @@ function parsePrimitive(value, path, budget) {
680
680
  ensureUnique(attributes.map(({ semantic }) => semantic), `${path}.attributes`, "duplicate attribute semantic");
681
681
  if (!attributes.some(({ semantic }) => semantic === "POSITION")) return fail("invalid-value", `${path}.attributes`, "primitive requires POSITION");
682
682
  const output = {
683
- id: readId(requireKey(record, "id", path), `${path}.id`),
684
- topology: readEnum(requireKey(record, "topology", path), `${path}.topology`, ["points", "lines", "line-strip", "triangles", "triangle-strip"]),
683
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
684
+ topology: readEnum(requireKey(record2, "topology", path), `${path}.topology`, ["points", "lines", "line-strip", "triangles", "triangle-strip"]),
685
685
  attributes
686
686
  };
687
- assignOptional(output, "indicesAccessorId", readOptional(record, "indicesAccessorId", readId, path));
688
- assignOptional(output, "materialId", readOptional(record, "materialId", readId, path));
689
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
687
+ assignOptional(output, "indicesAccessorId", readOptional(record2, "indicesAccessorId", readId, path));
688
+ assignOptional(output, "materialId", readOptional(record2, "materialId", readId, path));
689
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
690
690
  return output;
691
691
  }
692
692
  function parseMesh(value, path, budget) {
693
- const record = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
694
- const input = readArray(requireKey(record, "primitives", path), `${path}.primitives`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
693
+ const record2 = readRecord(value, path, ["id", "name", "primitives", "metadata"]);
694
+ const input = readArray(requireKey(record2, "primitives", path), `${path}.primitives`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
695
695
  consumeBudget(budget, "primitives", input.length, GPU_MODEL_DOCUMENT_LIMITS.primitives, path, "primitive");
696
696
  const output = {
697
- id: readId(requireKey(record, "id", path), `${path}.id`),
697
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
698
698
  primitives: Object.freeze(input.map((entry, index) => parsePrimitive(entry, `${path}.primitives[${index}]`, budget)))
699
699
  };
700
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
701
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
700
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
701
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
702
702
  return output;
703
703
  }
704
704
  function parseTextureTransform(value, path) {
705
- const record = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
706
- if (Reflect.ownKeys(record).length === 0) return fail("invalid-value", path, "texture transform cannot be empty");
705
+ const record2 = readRecord(value, path, ["offset", "rotationRadians", "scale"]);
706
+ if (Reflect.ownKeys(record2).length === 0) return fail("invalid-value", path, "texture transform cannot be empty");
707
707
  const output = {};
708
- assignOptional(output, "offset", readOptional(record, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
709
- assignOptional(output, "rotationRadians", readOptional(record, "rotationRadians", readFinite, path));
710
- assignOptional(output, "scale", readOptional(record, "scale", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
708
+ assignOptional(output, "offset", readOptional(record2, "offset", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
709
+ assignOptional(output, "rotationRadians", readOptional(record2, "rotationRadians", readFinite, path));
710
+ assignOptional(output, "scale", readOptional(record2, "scale", (entry, entryPath) => readNumberTuple(entry, entryPath, 2), path));
711
711
  return output;
712
712
  }
713
713
  function parseTextureBinding(value, path) {
714
- const record = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
714
+ const record2 = readRecord(value, path, ["textureId", "samplerId", "texCoordSet", "transform", "colorSpace", "intendedUsage"]);
715
715
  const output = {
716
- textureId: readId(requireKey(record, "textureId", path), `${path}.textureId`),
717
- colorSpace: readEnum(requireKey(record, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
718
- intendedUsage: readEnum(requireKey(record, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES)
716
+ textureId: readId(requireKey(record2, "textureId", path), `${path}.textureId`),
717
+ colorSpace: readEnum(requireKey(record2, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
718
+ intendedUsage: readEnum(requireKey(record2, "intendedUsage", path), `${path}.intendedUsage`, TEXTURE_USAGES)
719
719
  };
720
- assignOptional(output, "samplerId", readOptional(record, "samplerId", readId, path));
721
- assignOptional(output, "texCoordSet", readOptional(record, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
722
- assignOptional(output, "transform", readOptional(record, "transform", parseTextureTransform, path));
720
+ assignOptional(output, "samplerId", readOptional(record2, "samplerId", readId, path));
721
+ assignOptional(output, "texCoordSet", readOptional(record2, "texCoordSet", (entry, entryPath) => readSafeInteger(entry, entryPath, 0, 7), path));
722
+ assignOptional(output, "transform", readOptional(record2, "transform", parseTextureTransform, path));
723
723
  return output;
724
724
  }
725
725
  function parseMaterialTextures(value, path) {
726
726
  const slots = Object.keys(MATERIAL_TEXTURE_SLOTS);
727
- const record = readRecord(value, path, slots);
727
+ const record2 = readRecord(value, path, slots);
728
728
  const output = {};
729
- for (const slot of slots) assignOptional(output, slot, readOptional(record, slot, parseTextureBinding, path));
729
+ for (const slot of slots) assignOptional(output, slot, readOptional(record2, slot, parseTextureBinding, path));
730
730
  return output;
731
731
  }
732
732
  function parseMaterial(value, path, budget) {
@@ -752,88 +752,88 @@ function parseMaterial(value, path, budget) {
752
752
  "extensions",
753
753
  "sourceMetadata"
754
754
  ];
755
- const record = readRecord(value, path, keys);
756
- const alphaMode = readEnum(requireKey(record, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
757
- const alphaCutoff = readOptional(record, "alphaCutoff", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path);
755
+ const record2 = readRecord(value, path, keys);
756
+ const alphaMode = readEnum(requireKey(record2, "alphaMode", path), `${path}.alphaMode`, ["opaque", "mask", "blend"]);
757
+ const alphaCutoff = readOptional(record2, "alphaCutoff", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path);
758
758
  if (alphaMode === "mask" && alphaCutoff === void 0) return fail("invalid-value", `${path}.alphaCutoff`, "mask materials require alphaCutoff");
759
759
  if (alphaMode !== "mask" && alphaCutoff !== void 0) return fail("invalid-value", `${path}.alphaCutoff`, "alphaCutoff is only valid for mask materials");
760
760
  const output = {
761
- id: readId(requireKey(record, "id", path), `${path}.id`),
762
- workflow: readEnum(requireKey(record, "workflow", path), `${path}.workflow`, ["metallic-roughness", "specular-glossiness", "unlit", "custom"]),
761
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
762
+ workflow: readEnum(requireKey(record2, "workflow", path), `${path}.workflow`, ["metallic-roughness", "specular-glossiness", "unlit", "custom"]),
763
763
  alphaMode,
764
- baseColorFactor: readNumberTuple(requireKey(record, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
765
- textures: parseMaterialTextures(requireKey(record, "textures", path), `${path}.textures`),
766
- extensions: readMetadata(requireKey(record, "extensions", path), `${path}.extensions`, budget),
767
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
764
+ baseColorFactor: readNumberTuple(requireKey(record2, "baseColorFactor", path), `${path}.baseColorFactor`, 4, 0, 1),
765
+ textures: parseMaterialTextures(requireKey(record2, "textures", path), `${path}.textures`),
766
+ extensions: readMetadata(requireKey(record2, "extensions", path), `${path}.extensions`, budget),
767
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
768
768
  };
769
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
769
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
770
770
  assignOptional(output, "alphaCutoff", alphaCutoff);
771
- assignOptional(output, "doubleSided", readOptional(record, "doubleSided", readBoolean, path));
771
+ assignOptional(output, "doubleSided", readOptional(record2, "doubleSided", readBoolean, path));
772
772
  for (const key of ["metallicFactor", "roughnessFactor", "transmissionFactor", "clearcoatFactor", "clearcoatRoughnessFactor", "sheenRoughnessFactor", "occlusionStrength"]) {
773
- assignOptional(output, key, readOptional(record, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
773
+ assignOptional(output, key, readOptional(record2, key, (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
774
774
  }
775
- assignOptional(output, "normalScale", readOptional(record, "normalScale", readFinite, path));
776
- assignOptional(output, "emissiveFactor", readOptional(record, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
777
- assignOptional(output, "sheenColorFactor", readOptional(record, "sheenColorFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0, 1), path));
775
+ assignOptional(output, "normalScale", readOptional(record2, "normalScale", readFinite, path));
776
+ assignOptional(output, "emissiveFactor", readOptional(record2, "emissiveFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0), path));
777
+ assignOptional(output, "sheenColorFactor", readOptional(record2, "sheenColorFactor", (entry, entryPath) => readNumberTuple(entry, entryPath, 3, 0, 1), path));
778
778
  return output;
779
779
  }
780
780
  function parseTextureSampler(value, path) {
781
- const record = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
782
- const output = { id: readId(requireKey(record, "id", path), `${path}.id`) };
783
- assignOptional(output, "wrapS", readOptional(record, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
784
- assignOptional(output, "wrapT", readOptional(record, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
785
- assignOptional(output, "minFilter", readOptional(record, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
786
- assignOptional(output, "magFilter", readOptional(record, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
787
- assignOptional(output, "mipFilter", readOptional(record, "mipFilter", (entry, entryPath) => readEnum(entry, entryPath, ["nearest", "linear"]), path));
781
+ const record2 = readRecord(value, path, ["id", "wrapS", "wrapT", "minFilter", "magFilter", "mipFilter"]);
782
+ const output = { id: readId(requireKey(record2, "id", path), `${path}.id`) };
783
+ assignOptional(output, "wrapS", readOptional(record2, "wrapS", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
784
+ assignOptional(output, "wrapT", readOptional(record2, "wrapT", (entry, entryPath) => readEnum(entry, entryPath, ["repeat", "mirrored-repeat", "clamp-to-edge"]), path));
785
+ assignOptional(output, "minFilter", readOptional(record2, "minFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
786
+ assignOptional(output, "magFilter", readOptional(record2, "magFilter", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
787
+ assignOptional(output, "mipFilter", readOptional(record2, "mipFilter", (entry, entryPath) => readEnum(entry, entryPath, ["nearest", "linear"]), path));
788
788
  return output;
789
789
  }
790
790
  function parseTexture(value, path, budget) {
791
- const record = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
792
- 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)));
791
+ const record2 = readRecord(value, path, ["id", "fileName", "relativePath", "mimeType", "contentHash", "imageResourceId", "byteLength", "intendedUsage", "colorSpace", "sampler", "exportEmbeddingPolicy", "sourceMetadata"]);
792
+ 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)));
793
793
  ensureUnique(usages, `${path}.intendedUsage`, "duplicate intended texture usage");
794
794
  const output = {
795
- id: readId(requireKey(record, "id", path), `${path}.id`),
796
- contentHash: readSha256(requireKey(record, "contentHash", path), `${path}.contentHash`),
797
- imageResourceId: readId(requireKey(record, "imageResourceId", path), `${path}.imageResourceId`),
795
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
796
+ contentHash: readSha256(requireKey(record2, "contentHash", path), `${path}.contentHash`),
797
+ imageResourceId: readId(requireKey(record2, "imageResourceId", path), `${path}.imageResourceId`),
798
798
  intendedUsage: usages,
799
- colorSpace: readEnum(requireKey(record, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
800
- exportEmbeddingPolicy: readEnum(requireKey(record, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
801
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
799
+ colorSpace: readEnum(requireKey(record2, "colorSpace", path), `${path}.colorSpace`, ["srgb", "linear", "normal-map", "data"]),
800
+ exportEmbeddingPolicy: readEnum(requireKey(record2, "exportEmbeddingPolicy", path), `${path}.exportEmbeddingPolicy`, ["inherit-source", "prefer-embedded", "prefer-external", "forbid-embedding"]),
801
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
802
802
  };
803
- assignOptional(output, "fileName", readOptional(record, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
804
- assignOptional(output, "relativePath", readOptional(record, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
805
- assignOptional(output, "mimeType", readOptional(record, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
806
- assignOptional(output, "byteLength", readOptional(record, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
807
- assignOptional(output, "sampler", readOptional(record, "sampler", parseTextureSampler, path));
803
+ assignOptional(output, "fileName", readOptional(record2, "fileName", (entry, entryPath) => readString(entry, entryPath, 512, FILE_NAME), path));
804
+ assignOptional(output, "relativePath", readOptional(record2, "relativePath", (entry, entryPath) => readString(entry, entryPath, 2048, RELATIVE_PATH), path));
805
+ assignOptional(output, "mimeType", readOptional(record2, "mimeType", (entry, entryPath) => readString(entry, entryPath, 128, MIME_TYPE).toLowerCase(), path));
806
+ assignOptional(output, "byteLength", readOptional(record2, "byteLength", (entry, entryPath) => readSafeInteger(entry, entryPath, 1), path));
807
+ assignOptional(output, "sampler", readOptional(record2, "sampler", parseTextureSampler, path));
808
808
  return output;
809
809
  }
810
810
  function parseSkeleton(value, path, budget) {
811
- const record = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
812
- 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}]`)));
813
- 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}]`)));
811
+ const record2 = readRecord(value, path, ["id", "name", "jointIds", "rootJointIds", "sourceMetadata"]);
812
+ 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}]`)));
813
+ 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}]`)));
814
814
  consumeBudget(budget, "skinJoints", jointIds.length + rootJointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "rig joint");
815
815
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skeleton joint");
816
816
  ensureUnique(rootJointIds, `${path}.rootJointIds`, "duplicate root joint");
817
817
  const output = {
818
- id: readId(requireKey(record, "id", path), `${path}.id`),
818
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
819
819
  jointIds,
820
820
  rootJointIds,
821
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
821
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
822
822
  };
823
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
823
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
824
824
  return output;
825
825
  }
826
826
  function parseJoint(value, path, budget) {
827
- const record = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
827
+ const record2 = readRecord(value, path, ["id", "name", "nodeId", "parentJointId", "restLocalTransform", "inverseBindMatrix", "sourceMetadata"]);
828
828
  const output = {
829
- id: readId(requireKey(record, "id", path), `${path}.id`),
830
- nodeId: readId(requireKey(record, "nodeId", path), `${path}.nodeId`),
831
- restLocalTransform: parseTransformTrs(requireKey(record, "restLocalTransform", path), `${path}.restLocalTransform`),
832
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
829
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
830
+ nodeId: readId(requireKey(record2, "nodeId", path), `${path}.nodeId`),
831
+ restLocalTransform: parseTransformTrs(requireKey(record2, "restLocalTransform", path), `${path}.restLocalTransform`),
832
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
833
833
  };
834
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
835
- assignOptional(output, "parentJointId", readOptional(record, "parentJointId", readId, path));
836
- assignOptional(output, "inverseBindMatrix", readOptional(record, "inverseBindMatrix", parseMatrix, path));
834
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
835
+ assignOptional(output, "parentJointId", readOptional(record2, "parentJointId", readId, path));
836
+ assignOptional(output, "inverseBindMatrix", readOptional(record2, "inverseBindMatrix", parseMatrix, path));
837
837
  return output;
838
838
  }
839
839
  function parseWeightRows(value, path, rows, columns, integer, budget) {
@@ -847,43 +847,43 @@ function parseWeightRows(value, path, rows, columns, integer, budget) {
847
847
  }));
848
848
  }
849
849
  function parseVertexWeightSet(value, path, budget) {
850
- const record = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
851
- const influences = readSafeInteger(requireKey(record, "influencesPerVertex", path), `${path}.influencesPerVertex`, 1, 32);
850
+ const record2 = readRecord(value, path, ["primitiveId", "influencesPerVertex", "joints", "weights"]);
851
+ const influences = readSafeInteger(requireKey(record2, "influencesPerVertex", path), `${path}.influencesPerVertex`, 1, 32);
852
852
  if (influences % 4 !== 0) return fail("invalid-value", `${path}.influencesPerVertex`, "influencesPerVertex must be a multiple of four");
853
- const jointRows = readArray(requireKey(record, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
854
- const weightRows = readArray(requireKey(record, "weights", path), `${path}.weights`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
853
+ const jointRows = readArray(requireKey(record2, "joints", path), `${path}.joints`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
854
+ const weightRows = readArray(requireKey(record2, "weights", path), `${path}.weights`, GPU_MODEL_DOCUMENT_LIMITS.weightVertices, 1);
855
855
  if (jointRows.length !== weightRows.length) return fail("invalid-value", path, "joint and weight row counts must match");
856
856
  return {
857
- primitiveId: readId(requireKey(record, "primitiveId", path), `${path}.primitiveId`),
857
+ primitiveId: readId(requireKey(record2, "primitiveId", path), `${path}.primitiveId`),
858
858
  influencesPerVertex: influences,
859
859
  joints: parseWeightRows(jointRows, `${path}.joints`, jointRows.length, influences, true, budget),
860
860
  weights: parseWeightRows(weightRows, `${path}.weights`, weightRows.length, influences, false, budget)
861
861
  };
862
862
  }
863
863
  function parseSkin(value, path, budget) {
864
- const record = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
865
- 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}]`)));
864
+ const record2 = readRecord(value, path, ["id", "skeletonId", "jointIds", "inverseBindMatrices", "weightSets", "sourceMetadata"]);
865
+ 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}]`)));
866
866
  consumeBudget(budget, "skinJoints", jointIds.length, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, path, "skin joint");
867
867
  ensureUnique(jointIds, `${path}.jointIds`, "duplicate skin joint");
868
- const inverseInput = readArray(requireKey(record, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
868
+ const inverseInput = readArray(requireKey(record2, "inverseBindMatrices", path), `${path}.inverseBindMatrices`, GPU_MODEL_DOCUMENT_LIMITS.skinJoints, jointIds.length);
869
869
  if (inverseInput.length !== jointIds.length) return fail("invalid-value", `${path}.inverseBindMatrices`, "one inverse bind matrix is required per skin joint");
870
- const weightInput = readArray(requireKey(record, "weightSets", path), `${path}.weightSets`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
870
+ const weightInput = readArray(requireKey(record2, "weightSets", path), `${path}.weightSets`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
871
871
  const weightSets = Object.freeze(weightInput.map((entry, index) => parseVertexWeightSet(entry, `${path}.weightSets[${index}]`, budget)));
872
872
  ensureUnique(weightSets.map(({ primitiveId }) => primitiveId), `${path}.weightSets`, "duplicate primitive weight set");
873
873
  return {
874
- id: readId(requireKey(record, "id", path), `${path}.id`),
875
- skeletonId: readId(requireKey(record, "skeletonId", path), `${path}.skeletonId`),
874
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
875
+ skeletonId: readId(requireKey(record2, "skeletonId", path), `${path}.skeletonId`),
876
876
  jointIds,
877
877
  inverseBindMatrices: Object.freeze(inverseInput.map((entry, index) => parseMatrix(entry, `${path}.inverseBindMatrices[${index}]`))),
878
878
  weightSets,
879
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
879
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
880
880
  };
881
881
  }
882
882
  function parseBlendShapeDelta(value, path) {
883
- const record = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
884
- const output = { primitiveId: readId(requireKey(record, "primitiveId", path), `${path}.primitiveId`) };
883
+ const record2 = readRecord(value, path, ["primitiveId", "positionAccessorId", "normalAccessorId", "tangentAccessorId"]);
884
+ const output = { primitiveId: readId(requireKey(record2, "primitiveId", path), `${path}.primitiveId`) };
885
885
  for (const key of ["positionAccessorId", "normalAccessorId", "tangentAccessorId"]) {
886
- assignOptional(output, key, readOptional(record, key, readId, path));
886
+ assignOptional(output, key, readOptional(record2, key, readId, path));
887
887
  }
888
888
  if (!output.positionAccessorId && !output.normalAccessorId && !output.tangentAccessorId) {
889
889
  return fail("invalid-value", path, "blend-shape delta requires at least one accessor");
@@ -891,8 +891,8 @@ function parseBlendShapeDelta(value, path) {
891
891
  return output;
892
892
  }
893
893
  function parseBlendShape(value, path, budget) {
894
- const record = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
895
- const deltaInput = readArray(requireKey(record, "deltas", path), `${path}.deltas`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
894
+ const record2 = readRecord(value, path, ["id", "meshId", "name", "defaultWeight", "deltas", "sourceMetadata"]);
895
+ const deltaInput = readArray(requireKey(record2, "deltas", path), `${path}.deltas`, GPU_MODEL_DOCUMENT_LIMITS.primitives, 1);
896
896
  consumeBudget(
897
897
  budget,
898
898
  "blendShapeDeltas",
@@ -904,96 +904,96 @@ function parseBlendShape(value, path, budget) {
904
904
  const deltas = Object.freeze(deltaInput.map((entry, index) => parseBlendShapeDelta(entry, `${path}.deltas[${index}]`)));
905
905
  ensureUnique(deltas.map(({ primitiveId }) => primitiveId), `${path}.deltas`, "duplicate blend-shape primitive delta");
906
906
  const output = {
907
- id: readId(requireKey(record, "id", path), `${path}.id`),
908
- meshId: readId(requireKey(record, "meshId", path), `${path}.meshId`),
907
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
908
+ meshId: readId(requireKey(record2, "meshId", path), `${path}.meshId`),
909
909
  deltas,
910
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
910
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
911
911
  };
912
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
913
- assignOptional(output, "defaultWeight", readOptional(record, "defaultWeight", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
912
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
913
+ assignOptional(output, "defaultWeight", readOptional(record2, "defaultWeight", (entry, entryPath) => readFinite(entry, entryPath, 0, 1), path));
914
914
  return output;
915
915
  }
916
916
  function parseAnimationSampler(value, path, budget) {
917
- const record = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
918
- const timesInput = readArray(requireKey(record, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
919
- const valuesInput = readArray(requireKey(record, "outputValues", path), `${path}.outputValues`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
917
+ const record2 = readRecord(value, path, ["id", "inputAccessorId", "outputAccessorId", "inputTimesSeconds", "outputValues", "interpolation", "sourceMetadata"]);
918
+ const timesInput = readArray(requireKey(record2, "inputTimesSeconds", path), `${path}.inputTimesSeconds`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
919
+ const valuesInput = readArray(requireKey(record2, "outputValues", path), `${path}.outputValues`, GPU_MODEL_DOCUMENT_LIMITS.animationValues, 1);
920
920
  consumeBudget(budget, "animationValues", timesInput.length + valuesInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationValues, path, "animation value");
921
921
  const times = Object.freeze(timesInput.map((entry, index) => readFinite(entry, `${path}.inputTimesSeconds[${index}]`, 0)));
922
922
  for (let index = 1; index < times.length; index += 1) {
923
923
  if (times[index] <= times[index - 1]) return fail("invalid-value", `${path}.inputTimesSeconds`, "animation input times must be strictly increasing");
924
924
  }
925
925
  return {
926
- id: readId(requireKey(record, "id", path), `${path}.id`),
927
- inputAccessorId: readId(requireKey(record, "inputAccessorId", path), `${path}.inputAccessorId`),
928
- outputAccessorId: readId(requireKey(record, "outputAccessorId", path), `${path}.outputAccessorId`),
926
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
927
+ inputAccessorId: readId(requireKey(record2, "inputAccessorId", path), `${path}.inputAccessorId`),
928
+ outputAccessorId: readId(requireKey(record2, "outputAccessorId", path), `${path}.outputAccessorId`),
929
929
  inputTimesSeconds: times,
930
930
  outputValues: Object.freeze(valuesInput.map((entry, index) => readFinite(entry, `${path}.outputValues[${index}]`))),
931
- interpolation: readEnum(requireKey(record, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
932
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
931
+ interpolation: readEnum(requireKey(record2, "interpolation", path), `${path}.interpolation`, ["step", "linear", "cubic-spline"]),
932
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
933
933
  };
934
934
  }
935
935
  function parseAnimationChannel(value, path, budget) {
936
- const record = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
936
+ const record2 = readRecord(value, path, ["id", "samplerId", "targetType", "targetId", "sourceMetadata"]);
937
937
  return {
938
- id: readId(requireKey(record, "id", path), `${path}.id`),
939
- samplerId: readId(requireKey(record, "samplerId", path), `${path}.samplerId`),
940
- targetType: readEnum(requireKey(record, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
941
- targetId: readId(requireKey(record, "targetId", path), `${path}.targetId`),
942
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
938
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
939
+ samplerId: readId(requireKey(record2, "samplerId", path), `${path}.samplerId`),
940
+ targetType: readEnum(requireKey(record2, "targetType", path), `${path}.targetType`, ["node-translation", "node-rotation", "node-scale", "weights"]),
941
+ targetId: readId(requireKey(record2, "targetId", path), `${path}.targetId`),
942
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
943
943
  };
944
944
  }
945
945
  function parseAnimation(value, path, budget) {
946
- const record = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
947
- const samplerInput = readArray(requireKey(record, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
948
- const channelInput = readArray(requireKey(record, "channels", path), `${path}.channels`, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, 1);
946
+ const record2 = readRecord(value, path, ["id", "name", "durationSeconds", "samplers", "channels", "sourceMetadata"]);
947
+ const samplerInput = readArray(requireKey(record2, "samplers", path), `${path}.samplers`, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, 1);
948
+ const channelInput = readArray(requireKey(record2, "channels", path), `${path}.channels`, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, 1);
949
949
  consumeBudget(budget, "animationSamplers", samplerInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationSamplers, path, "animation sampler");
950
950
  consumeBudget(budget, "animationChannels", channelInput.length, GPU_MODEL_DOCUMENT_LIMITS.animationChannels, path, "animation channel");
951
951
  const output = {
952
- id: readId(requireKey(record, "id", path), `${path}.id`),
953
- durationSeconds: readFinite(requireKey(record, "durationSeconds", path), `${path}.durationSeconds`, 0),
952
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
953
+ durationSeconds: readFinite(requireKey(record2, "durationSeconds", path), `${path}.durationSeconds`, 0),
954
954
  samplers: Object.freeze(samplerInput.map((entry, index) => parseAnimationSampler(entry, `${path}.samplers[${index}]`, budget))),
955
955
  channels: Object.freeze(channelInput.map((entry, index) => parseAnimationChannel(entry, `${path}.channels[${index}]`, budget))),
956
- sourceMetadata: readMetadata(requireKey(record, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
956
+ sourceMetadata: readMetadata(requireKey(record2, "sourceMetadata", path), `${path}.sourceMetadata`, budget)
957
957
  };
958
- assignOptional(output, "name", readOptional(record, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
958
+ assignOptional(output, "name", readOptional(record2, "name", (entry, entryPath) => readString(entry, entryPath, GPU_MODEL_DOCUMENT_LIMITS.nameLength), path));
959
959
  return output;
960
960
  }
961
961
  function parseAnalyticGeometry(value, path, budget) {
962
- const record = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
963
- if (requireKey(record, "kind", path) !== "brep-placeholder") return fail("invalid-value", `${path}.kind`, "unsupported analytic geometry kind");
962
+ const record2 = readRecord(value, path, ["id", "kind", "sourceResourceId", "sourceUnit", "tessellatedMeshId", "metadata"]);
963
+ if (requireKey(record2, "kind", path) !== "brep-placeholder") return fail("invalid-value", `${path}.kind`, "unsupported analytic geometry kind");
964
964
  const output = {
965
- id: readId(requireKey(record, "id", path), `${path}.id`),
965
+ id: readId(requireKey(record2, "id", path), `${path}.id`),
966
966
  kind: "brep-placeholder",
967
- sourceResourceId: readId(requireKey(record, "sourceResourceId", path), `${path}.sourceResourceId`),
968
- sourceUnit: readEnum(requireKey(record, "sourceUnit", path), `${path}.sourceUnit`, ["metre", "millimetre", "centimetre", "inch", "foot", "unitless"])
967
+ sourceResourceId: readId(requireKey(record2, "sourceResourceId", path), `${path}.sourceResourceId`),
968
+ sourceUnit: readEnum(requireKey(record2, "sourceUnit", path), `${path}.sourceUnit`, ["metre", "millimetre", "centimetre", "inch", "foot", "unitless"])
969
969
  };
970
- assignOptional(output, "tessellatedMeshId", readOptional(record, "tessellatedMeshId", readId, path));
971
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
970
+ assignOptional(output, "tessellatedMeshId", readOptional(record2, "tessellatedMeshId", readId, path));
971
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
972
972
  return output;
973
973
  }
974
974
  function parseProvenance(value, budget) {
975
975
  const path = "$.provenance";
976
- const record = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
976
+ const record2 = readRecord(value, path, ["sourceFormat", "sourceContentHash", "converterId", "converterVersion", "provider", "sourceIdentifier", "metadata"]);
977
977
  const output = {
978
- sourceFormat: readString(requireKey(record, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
979
- sourceContentHash: readSha256(requireKey(record, "sourceContentHash", path), `${path}.sourceContentHash`),
980
- converterId: readString(requireKey(record, "converterId", path), `${path}.converterId`, 128, TOKEN),
981
- converterVersion: readString(requireKey(record, "converterVersion", path), `${path}.converterVersion`, 128, TOKEN)
978
+ sourceFormat: readString(requireKey(record2, "sourceFormat", path), `${path}.sourceFormat`, 128, TOKEN),
979
+ sourceContentHash: readSha256(requireKey(record2, "sourceContentHash", path), `${path}.sourceContentHash`),
980
+ converterId: readString(requireKey(record2, "converterId", path), `${path}.converterId`, 128, TOKEN),
981
+ converterVersion: readString(requireKey(record2, "converterVersion", path), `${path}.converterVersion`, 128, TOKEN)
982
982
  };
983
- assignOptional(output, "provider", readOptional(record, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
984
- assignOptional(output, "sourceIdentifier", readOptional(record, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
985
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
983
+ assignOptional(output, "provider", readOptional(record2, "provider", (entry, entryPath) => readString(entry, entryPath, 128, TOKEN), path));
984
+ assignOptional(output, "sourceIdentifier", readOptional(record2, "sourceIdentifier", (entry, entryPath) => readString(entry, entryPath, 2048), path));
985
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
986
986
  return output;
987
987
  }
988
988
  function parseDiagnostic(value, path, budget) {
989
- const record = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
989
+ const record2 = readRecord(value, path, ["severity", "code", "message", "path", "metadata"]);
990
990
  const output = {
991
- severity: readEnum(requireKey(record, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
992
- code: readString(requireKey(record, "code", path), `${path}.code`, 128, TOKEN),
993
- message: readString(requireKey(record, "message", path), `${path}.message`, GPU_MODEL_DOCUMENT_LIMITS.diagnosticMessageLength)
991
+ severity: readEnum(requireKey(record2, "severity", path), `${path}.severity`, ["info", "warning", "error"]),
992
+ code: readString(requireKey(record2, "code", path), `${path}.code`, 128, TOKEN),
993
+ message: readString(requireKey(record2, "message", path), `${path}.message`, GPU_MODEL_DOCUMENT_LIMITS.diagnosticMessageLength)
994
994
  };
995
- assignOptional(output, "path", readOptional(record, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
996
- assignOptional(output, "metadata", readOptional(record, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
995
+ assignOptional(output, "path", readOptional(record2, "path", (entry, entryPath) => readString(entry, entryPath, 512), path));
996
+ assignOptional(output, "metadata", readOptional(record2, "metadata", (entry, entryPath) => readMetadata(entry, entryPath, budget), path));
997
997
  return output;
998
998
  }
999
999
  function ensureUnique(values, path, detail = "duplicate identity") {
@@ -1634,11 +1634,11 @@ var DOCUMENT_KEYS = Object.freeze([
1634
1634
  ]);
1635
1635
  function parseGpuModelDocument(value) {
1636
1636
  const path = "$";
1637
- const record = readRecord(value, path, DOCUMENT_KEYS);
1638
- const schemaVersion = requireKey(record, "schemaVersion", path);
1637
+ const record2 = readRecord(value, path, DOCUMENT_KEYS);
1638
+ const schemaVersion = requireKey(record2, "schemaVersion", path);
1639
1639
  if (schemaVersion !== GPU_MODEL_DOCUMENT_SCHEMA_VERSION) return fail("invalid-value", "$.schemaVersion", "unsupported schema version");
1640
1640
  const parseCollection = (key, maximum, parser, minimum = 0) => {
1641
- const input = readArray(requireKey(record, key, path), `$.${key}`, maximum, minimum);
1641
+ const input = readArray(requireKey(record2, key, path), `$.${key}`, maximum, minimum);
1642
1642
  return Object.freeze(input.map((entry, index) => parser(entry, `$.${key}[${index}]`)));
1643
1643
  };
1644
1644
  const budget = {
@@ -1659,10 +1659,10 @@ function parseGpuModelDocument(value) {
1659
1659
  geometryWorldAccessorInstances: 0,
1660
1660
  geometryWorldVertices: 0
1661
1661
  };
1662
- const roots = Object.freeze(readArray(requireKey(record, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1662
+ const roots = Object.freeze(readArray(requireKey(record2, "roots", path), "$.roots", GPU_MODEL_DOCUMENT_LIMITS.roots, 1).map((entry, index) => readId(entry, `$.roots[${index}]`)));
1663
1663
  const document = {
1664
1664
  schemaVersion,
1665
- coordinateSystem: parseCoordinateSystem(requireKey(record, "coordinateSystem", path)),
1665
+ coordinateSystem: parseCoordinateSystem(requireKey(record2, "coordinateSystem", path)),
1666
1666
  roots,
1667
1667
  nodes: parseCollection("nodes", GPU_MODEL_DOCUMENT_LIMITS.nodes, (entry, entryPath) => parseNode(entry, entryPath, budget), 1),
1668
1668
  resources: parseCollection("resources", GPU_MODEL_DOCUMENT_LIMITS.resources, parseVerifiedResource, 1),
@@ -1676,10 +1676,10 @@ function parseGpuModelDocument(value) {
1676
1676
  blendShapes: parseCollection("blendShapes", GPU_MODEL_DOCUMENT_LIMITS.blendShapes, (entry, entryPath) => parseBlendShape(entry, entryPath, budget)),
1677
1677
  animations: parseCollection("animations", GPU_MODEL_DOCUMENT_LIMITS.animations, (entry, entryPath) => parseAnimation(entry, entryPath, budget)),
1678
1678
  analyticGeometry: parseCollection("analyticGeometry", GPU_MODEL_DOCUMENT_LIMITS.analyticGeometry, (entry, entryPath) => parseAnalyticGeometry(entry, entryPath, budget)),
1679
- bounds: parseBounds(requireKey(record, "bounds", path), "$.bounds"),
1680
- provenance: parseProvenance(requireKey(record, "provenance", path), budget),
1679
+ bounds: parseBounds(requireKey(record2, "bounds", path), "$.bounds"),
1680
+ provenance: parseProvenance(requireKey(record2, "provenance", path), budget),
1681
1681
  diagnostics: parseCollection("diagnostics", GPU_MODEL_DOCUMENT_LIMITS.diagnostics, (entry, entryPath) => parseDiagnostic(entry, entryPath, budget)),
1682
- metadata: readMetadata(requireKey(record, "metadata", path), "$.metadata", budget)
1682
+ metadata: readMetadata(requireKey(record2, "metadata", path), "$.metadata", budget)
1683
1683
  };
1684
1684
  if (document.meshes.length === 0) return fail("invalid-value", "$.meshes", "v1 document bounds require at least one canonical mesh");
1685
1685
  validateReferences(document, budget);
@@ -1716,8 +1716,8 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1716
1716
  value.resources.forEach((resource, index) => assertVerifiedResourceEvidence(resource, limits, `$.resources[${index}]`));
1717
1717
  return value;
1718
1718
  }
1719
- const record = readRecord(value, "$", DOCUMENT_KEYS);
1720
- const resourceInput = readArray(requireKey(record, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1719
+ const record2 = readRecord(value, "$", DOCUMENT_KEYS);
1720
+ const resourceInput = readArray(requireKey(record2, "resources", "$"), "$.resources", GPU_MODEL_DOCUMENT_LIMITS.resources, 1);
1721
1721
  const parsed = resourceInput.map((entry, index) => {
1722
1722
  if (typeof entry === "object" && entry !== null && verifiedResources.has(entry) && verifiedResourceBytes.has(entry)) return entry;
1723
1723
  return parseResourceHeader(entry, `$.resources[${index}]`);
@@ -1730,7 +1730,7 @@ async function createAndVerifyGpuModelDocument(value, port, options) {
1730
1730
  }
1731
1731
  const candidate = {};
1732
1732
  for (const key of DOCUMENT_KEYS) {
1733
- if (Object.hasOwn(record, key)) candidate[key] = record[key];
1733
+ if (Object.hasOwn(record2, key)) candidate[key] = record2[key];
1734
1734
  }
1735
1735
  candidate.resources = verified;
1736
1736
  return createGpuModelDocument(candidate);
@@ -1795,11 +1795,11 @@ async function hashGpuModelDocumentCanonical(document) {
1795
1795
  return Array.from(new Uint8Array(digest), (value) => value.toString(16).padStart(2, "0")).join("");
1796
1796
  }
1797
1797
  function resolveStaticDemoLimits(options) {
1798
- const record = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1799
- const enabled = readBoolean(requireKey(record, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1798
+ const record2 = readRecord(options, "$.options", ["pvoxModelsEnabled", "limits"]);
1799
+ const enabled = readBoolean(requireKey(record2, "pvoxModelsEnabled", "$.options"), "$.options.pvoxModelsEnabled");
1800
1800
  if (!enabled) return fail("feature-disabled", "$.options.pvoxModelsEnabled", `${GPU_MODEL_STATIC_DEMO_FEATURE_FLAG} is disabled`);
1801
- if (!Object.hasOwn(record, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1802
- const overrides = readRecord(record.limits, "$.options.limits", Object.keys(GPU_MODEL_STATIC_DEMO_LIMITS));
1801
+ if (!Object.hasOwn(record2, "limits")) return GPU_MODEL_STATIC_DEMO_LIMITS;
1802
+ const overrides = readRecord(record2.limits, "$.options.limits", Object.keys(GPU_MODEL_STATIC_DEMO_LIMITS));
1803
1803
  const limits = { ...GPU_MODEL_STATIC_DEMO_LIMITS };
1804
1804
  for (const [key, ceiling] of Object.entries(GPU_MODEL_STATIC_DEMO_LIMITS)) {
1805
1805
  if (Object.hasOwn(overrides, key)) limits[key] = readSafeInteger(overrides[key], `$.options.limits.${key}`, 1, ceiling);
@@ -2034,6 +2034,260 @@ async function createGpuModelStaticDemoCompilerInput(document, options) {
2034
2034
  });
2035
2035
  }
2036
2036
 
2037
+ // src/diagnostics.ts
2038
+ var GPU_MODEL_REPAIR_RULES = Object.freeze([
2039
+ {
2040
+ "id": "missing-normals",
2041
+ "action": "Generate deterministic vertex normals from validated triangle geometry.",
2042
+ "severity": "warning",
2043
+ "confidence": "high",
2044
+ "standardIds": [
2045
+ "gltf-glb",
2046
+ "obj-mtl",
2047
+ "fbx",
2048
+ "cad-bim"
2049
+ ],
2050
+ "policy": "Generate deterministic vertex normals from validated triangle geometry and record the generation basis."
2051
+ },
2052
+ {
2053
+ "id": "bad-winding",
2054
+ "action": "Normalize winding to the adapter coordinate convention.",
2055
+ "severity": "warning",
2056
+ "confidence": "medium",
2057
+ "standardIds": [
2058
+ "gltf-glb",
2059
+ "obj-mtl",
2060
+ "fbx",
2061
+ "cad-bim"
2062
+ ],
2063
+ "policy": "Normalize winding to the adapter coordinate convention and record the inferred orientation."
2064
+ },
2065
+ {
2066
+ "id": "invalid-indices",
2067
+ "action": "Drop the invalid primitive.",
2068
+ "severity": "error",
2069
+ "confidence": "medium",
2070
+ "standardIds": [
2071
+ "gltf-glb",
2072
+ "obj-mtl",
2073
+ "fbx",
2074
+ "cad-bim"
2075
+ ],
2076
+ "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."
2077
+ },
2078
+ {
2079
+ "id": "nan-transforms",
2080
+ "action": "Replace non-finite transform components with identity components.",
2081
+ "severity": "error",
2082
+ "confidence": "high",
2083
+ "standardIds": [
2084
+ "gltf-glb",
2085
+ "obj-mtl",
2086
+ "fbx",
2087
+ "cad-bim"
2088
+ ],
2089
+ "policy": "Replace the non-finite component with the identity transform component and record the source value and fallback."
2090
+ },
2091
+ {
2092
+ "id": "non-normalized-weights",
2093
+ "action": "Normalize finite non-negative skin weights.",
2094
+ "severity": "warning",
2095
+ "confidence": "high",
2096
+ "standardIds": [
2097
+ "gltf-glb",
2098
+ "fbx",
2099
+ "cad-bim"
2100
+ ],
2101
+ "policy": "Normalize finite, non-negative influences and record the pre-normalization weights and discarded influence policy."
2102
+ },
2103
+ {
2104
+ "id": "missing-texture-path",
2105
+ "action": "Leave the missing or unsafe texture slot unresolved.",
2106
+ "severity": "warning",
2107
+ "confidence": "low",
2108
+ "standardIds": [
2109
+ "gltf-glb",
2110
+ "obj-mtl",
2111
+ "fbx",
2112
+ "cad-bim"
2113
+ ],
2114
+ "policy": "Resolve only an explicitly bounded package-relative fallback; otherwise keep the material slot unresolved and emit a visible warning."
2115
+ },
2116
+ {
2117
+ "id": "malformed-material-reference",
2118
+ "action": "Attach the canonical default material.",
2119
+ "severity": "error",
2120
+ "confidence": "medium",
2121
+ "standardIds": [
2122
+ "gltf-glb",
2123
+ "obj-mtl",
2124
+ "fbx",
2125
+ "cad-bim"
2126
+ ],
2127
+ "policy": "Attach the canonical default material only when the target policy permits it and preserve the unresolved source reference."
2128
+ }
2129
+ ].map((rule) => Object.freeze({ ...rule, standardIds: Object.freeze(rule.standardIds) })));
2130
+ var GPU_MODEL_DIAGNOSTIC_LIMITS = Object.freeze({
2131
+ issues: 4096,
2132
+ values: 16384,
2133
+ depth: 12,
2134
+ members: 1024,
2135
+ string: 65536,
2136
+ bytes: 1048576
2137
+ });
2138
+ var GpuModelDiagnosticsError = class extends TypeError {
2139
+ constructor() {
2140
+ super("Invalid or over-budget GPU model diagnostics report");
2141
+ this.name = "GpuModelDiagnosticsError";
2142
+ }
2143
+ };
2144
+ function fail2() {
2145
+ throw new GpuModelDiagnosticsError();
2146
+ }
2147
+ function charge(budget, bytes) {
2148
+ budget.bytes += bytes;
2149
+ if (budget.bytes > GPU_MODEL_DIAGNOSTIC_LIMITS.bytes) fail2();
2150
+ }
2151
+ function textBytes(value) {
2152
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
2153
+ }
2154
+ function snapshot(value, depth, budget, ancestors, memberLimit = GPU_MODEL_DIAGNOSTIC_LIMITS.members) {
2155
+ if (++budget.values > GPU_MODEL_DIAGNOSTIC_LIMITS.values || depth > GPU_MODEL_DIAGNOSTIC_LIMITS.depth) fail2();
2156
+ if (value === null || typeof value === "boolean") {
2157
+ charge(budget, value === null ? 4 : value ? 4 : 5);
2158
+ return value;
2159
+ }
2160
+ if (typeof value === "number") {
2161
+ if (!Number.isFinite(value)) fail2();
2162
+ charge(budget, JSON.stringify(value).length);
2163
+ return Object.is(value, -0) ? 0 : value;
2164
+ }
2165
+ if (typeof value === "string") {
2166
+ if (value.length > GPU_MODEL_DIAGNOSTIC_LIMITS.string) fail2();
2167
+ charge(budget, textBytes(value));
2168
+ return value;
2169
+ }
2170
+ if (typeof value !== "object" || ancestors.has(value)) fail2();
2171
+ const array = Array.isArray(value);
2172
+ const prototype = Object.getPrototypeOf(value);
2173
+ if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) fail2();
2174
+ const keys = Reflect.ownKeys(value);
2175
+ const maxMembers = memberLimit;
2176
+ if (keys.length > maxMembers + (array ? 1 : 0)) fail2();
2177
+ ancestors.add(value);
2178
+ charge(budget, 2);
2179
+ if (array) {
2180
+ const length = Object.getOwnPropertyDescriptor(value, "length").value;
2181
+ if (length > maxMembers || keys.length !== length + 1) fail2();
2182
+ const result2 = [];
2183
+ for (let i = 0; i < length; i++) {
2184
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(i));
2185
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail2();
2186
+ if (i > 0) charge(budget, 1);
2187
+ result2.push(snapshot(descriptor.value, depth + 1, budget, ancestors));
2188
+ }
2189
+ ancestors.delete(value);
2190
+ return Object.freeze(result2);
2191
+ }
2192
+ const result = {};
2193
+ let count = 0;
2194
+ for (const key of keys) {
2195
+ if (typeof key !== "string" || key.length > 256 || ["__proto__", "constructor", "prototype"].includes(key)) fail2();
2196
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
2197
+ if (!("value" in descriptor) || !descriptor.enumerable) fail2();
2198
+ if (count++ > 0) charge(budget, 1);
2199
+ charge(budget, textBytes(key) + 1);
2200
+ result[key] = snapshot(descriptor.value, depth + 1, budget, ancestors, depth === 0 && key === "issues" ? GPU_MODEL_DIAGNOSTIC_LIMITS.issues : GPU_MODEL_DIAGNOSTIC_LIMITS.members);
2201
+ }
2202
+ ancestors.delete(value);
2203
+ return Object.freeze(result);
2204
+ }
2205
+ function record(value) {
2206
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return fail2();
2207
+ return value;
2208
+ }
2209
+ function keysOnly(value, allowed) {
2210
+ if (Object.keys(value).some((key) => !allowed.includes(key))) fail2();
2211
+ }
2212
+ function enumValue(value, allowed) {
2213
+ if (typeof value !== "string" || !allowed.includes(value)) return fail2();
2214
+ return value;
2215
+ }
2216
+ function hasUnsafeText(value) {
2217
+ for (const character of value) {
2218
+ const point = character.codePointAt(0);
2219
+ if (point < 32 || point >= 127 && point <= 159 || point >= 8232 && point <= 8238 || point >= 8294 && point <= 8297 || point >= 55296 && point <= 57343) return true;
2220
+ }
2221
+ return false;
2222
+ }
2223
+ function text(value, max) {
2224
+ if (typeof value !== "string" || value.length === 0 || value.length > max || hasUnsafeText(value)) return fail2();
2225
+ return value;
2226
+ }
2227
+ function boolean(value) {
2228
+ if (typeof value !== "boolean") return fail2();
2229
+ return value;
2230
+ }
2231
+ function parseIssue(value) {
2232
+ const issue = record(value);
2233
+ const kind = enumValue(issue.kind, ["violation", "unsupported", "warning", "loss"]);
2234
+ const base = { code: text(issue.code, 128), path: text(issue.path, 1024), message: text(issue.message, 2048) };
2235
+ if (!base.path.startsWith("/")) fail2();
2236
+ const common = ["kind", "code", "path", "message"];
2237
+ if (kind === "violation") {
2238
+ keysOnly(issue, [...common, "ruleId", "originalValue", "repairApplied"]);
2239
+ if (!Object.hasOwn(issue, "originalValue")) fail2();
2240
+ return { ...base, kind, ruleId: text(issue.ruleId, 128), originalValue: issue.originalValue, repairApplied: boolean(issue.repairApplied) };
2241
+ }
2242
+ if (kind === "loss") {
2243
+ keysOnly(issue, [...common, "effect"]);
2244
+ return { ...base, kind, effect: enumValue(issue.effect, ["dropped", "approximated"]) };
2245
+ }
2246
+ keysOnly(issue, common);
2247
+ return { ...base, kind };
2248
+ }
2249
+ function evaluateGpuModelDiagnostics(value) {
2250
+ const budget = { values: 0, bytes: 0 };
2251
+ const args = record(snapshot(value, 0, budget, /* @__PURE__ */ new Set()));
2252
+ keysOnly(args, ["standard", "mode", "allowLoss", "issues", "rawSource", "maxEvidenceBytes"]);
2253
+ const standard = enumValue(args.standard, ["gltf-glb", "obj-mtl", "fbx", "cad-bim"]);
2254
+ const mode = args.mode === void 0 ? "strict" : enumValue(args.mode, ["strict", "tolerant", "forensic"]);
2255
+ const allowLoss = args.allowLoss === void 0 ? false : boolean(args.allowLoss);
2256
+ 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();
2257
+ if (mode === "forensic" ? !Object.hasOwn(args, "rawSource") : Object.hasOwn(args, "rawSource")) fail2();
2258
+ if (!Array.isArray(args.issues) || args.issues.length > GPU_MODEL_DIAGNOSTIC_LIMITS.issues) fail2();
2259
+ const issues = args.issues.map(parseIssue);
2260
+ const lossPaths = new Set(issues.filter((issue) => issue.kind === "loss").map((issue) => issue.path));
2261
+ const diagnostics = [];
2262
+ const repairs = [];
2263
+ const losses = [];
2264
+ let accepted = true;
2265
+ for (const issue of issues) {
2266
+ const base = { code: issue.code, path: issue.path, message: issue.message };
2267
+ if (issue.kind === "violation") {
2268
+ const rule = GPU_MODEL_REPAIR_RULES.find((rule2) => rule2.id === issue.ruleId);
2269
+ const requiresLoss = ["invalid-indices", "missing-texture-path", "malformed-material-reference"].includes(issue.ruleId);
2270
+ const repaired = mode !== "strict" && issue.repairApplied && rule && rule.standardIds.includes(standard) && (!requiresLoss || lossPaths.has(issue.path));
2271
+ if (repaired) {
2272
+ repairs.push(Object.freeze({ ruleId: rule.id, originalValue: issue.originalValue, action: rule.action, severity: rule.severity, affectedPath: issue.path, confidence: rule.confidence }));
2273
+ diagnostics.push(Object.freeze({ ...base, kind: "repair", severity: rule.severity, ruleId: rule.id }));
2274
+ } else {
2275
+ accepted = false;
2276
+ diagnostics.push(Object.freeze({ ...base, kind: "blocking-error", severity: "error", ruleId: issue.ruleId }));
2277
+ }
2278
+ } else if (issue.kind === "unsupported") {
2279
+ accepted = false;
2280
+ diagnostics.push(Object.freeze({ ...issue, severity: "error" }));
2281
+ } else if (issue.kind === "loss") {
2282
+ if (!allowLoss) accepted = false;
2283
+ const loss = Object.freeze({ ...issue, severity: "warning" });
2284
+ losses.push(loss);
2285
+ diagnostics.push(loss);
2286
+ } else diagnostics.push(Object.freeze({ ...issue, severity: "warning" }));
2287
+ }
2288
+ return Object.freeze({ mode, standard, accepted, diagnostics: Object.freeze(diagnostics), repairs: Object.freeze(repairs), losses: Object.freeze(losses), ...mode === "forensic" ? { rawSource: args.rawSource } : {} });
2289
+ }
2290
+
2037
2291
  // src/index.ts
2038
2292
  var packageName = "@plasius/gpu-model-core";
2039
2293
  var packageBootstrap = Object.freeze({
@@ -2043,8 +2297,10 @@ var packageBootstrap = Object.freeze({
2043
2297
  });
2044
2298
  export {
2045
2299
  CANONICAL_GPU_MODEL_COORDINATE_SYSTEM,
2300
+ GPU_MODEL_DIAGNOSTIC_LIMITS,
2046
2301
  GPU_MODEL_DOCUMENT_LIMITS,
2047
2302
  GPU_MODEL_DOCUMENT_SCHEMA_VERSION,
2303
+ GPU_MODEL_REPAIR_RULES,
2048
2304
  GPU_MODEL_RESOURCE_VERIFICATION_LIMITS,
2049
2305
  GPU_MODEL_STATIC_DEMO_FEATURE_FLAG,
2050
2306
  GPU_MODEL_STATIC_DEMO_LIMITS,
@@ -2053,6 +2309,7 @@ export {
2053
2309
  GPU_MODEL_STATIC_DEMO_MAX_EXTENT_METRES,
2054
2310
  GPU_MODEL_STATIC_DEMO_NORMALIZATION_CONTRACT,
2055
2311
  GPU_MODEL_STATIC_DEMO_PROFILE_VERSION,
2312
+ GpuModelDiagnosticsError,
2056
2313
  GpuModelDocumentError,
2057
2314
  assertGpuModelStaticDemoNormalization,
2058
2315
  canCreateGpuModelDocument,
@@ -2060,6 +2317,7 @@ export {
2060
2317
  createGpuModelDocument,
2061
2318
  createGpuModelStaticDemoCompilerInput,
2062
2319
  encodeGpuModelDocumentCanonical,
2320
+ evaluateGpuModelDiagnostics,
2063
2321
  hashGpuModelDocumentCanonical,
2064
2322
  isGpuModelDocument,
2065
2323
  packageBootstrap,