@plasius/asset-contracts 0.6.0 → 0.8.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
@@ -43,8 +43,8 @@ __export(index_exports, {
43
43
  ASSET_SOURCE_ADAPTERS: () => ASSET_SOURCE_ADAPTERS,
44
44
  ASSET_WGSL_CONTENT_TYPE: () => ASSET_WGSL_CONTENT_TYPE,
45
45
  CANONICAL_MODEL_COORDINATE_SYSTEM: () => CANONICAL_MODEL_COORDINATE_SYSTEM,
46
- GPU_SHADER_STORE_FEATURE_FLAG: () => import_gpu_shader4.GPU_SHADER_STORE_FEATURE_FLAG,
47
- GPU_SHADER_STYLE_SELECTION_CAPABILITY: () => import_gpu_shader4.GPU_SHADER_STYLE_SELECTION_CAPABILITY,
46
+ GPU_SHADER_STORE_FEATURE_FLAG: () => import_gpu_shader5.GPU_SHADER_STORE_FEATURE_FLAG,
47
+ GPU_SHADER_STYLE_SELECTION_CAPABILITY: () => import_gpu_shader5.GPU_SHADER_STYLE_SELECTION_CAPABILITY,
48
48
  MODEL_ASSET_KINDS: () => MODEL_ASSET_KINDS,
49
49
  MODEL_CANDIDATE_HARD_GATE_KINDS: () => MODEL_CANDIDATE_HARD_GATE_KINDS,
50
50
  MODEL_CONFIRMATION_VIEW_KINDS: () => MODEL_CONFIRMATION_VIEW_KINDS,
@@ -64,10 +64,14 @@ __export(index_exports, {
64
64
  MODEL_RESOLUTION_V2_JSON_SCHEMAS: () => MODEL_RESOLUTION_V2_JSON_SCHEMAS,
65
65
  MODEL_RESOLUTION_V2_STATES: () => MODEL_RESOLUTION_V2_STATES,
66
66
  MODEL_RESOURCE_MANIFEST_LIMITS: () => MODEL_RESOURCE_MANIFEST_LIMITS,
67
+ MODEL_RESOURCE_PROMOTION_LIMITS: () => MODEL_RESOURCE_PROMOTION_LIMITS,
67
68
  MODEL_RIGHTS_STATUSES: () => MODEL_RIGHTS_STATUSES,
69
+ MODEL_TEXTURE_RESOURCE_LIMITS: () => MODEL_TEXTURE_RESOURCE_LIMITS,
68
70
  MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE: () => MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
69
71
  ModelConversionDiagnosticsError: () => ModelConversionDiagnosticsError,
70
72
  ModelResourceManifestError: () => ModelResourceManifestError,
73
+ ModelResourcePromotionError: () => ModelResourcePromotionError,
74
+ ModelTextureResourceError: () => ModelTextureResourceError,
71
75
  PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN: () => PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
72
76
  PVOX_ASSET_MANIFEST_VERSION: () => PVOX_ASSET_MANIFEST_VERSION,
73
77
  PVOX_BINARY_CLOSURE_HASH_DOMAIN: () => PVOX_BINARY_CLOSURE_HASH_DOMAIN,
@@ -202,6 +206,7 @@ __export(index_exports, {
202
206
  createModelRequestSpecV2: () => createModelRequestSpecV2,
203
207
  createModelResolution: () => createModelResolution,
204
208
  createModelResolutionV2: () => createModelResolutionV2,
209
+ createModelResourcePromotionRecord: () => createModelResourcePromotionRecord,
205
210
  createModelResourceRef: () => createModelResourceRef,
206
211
  createModelRightsAssessment: () => createModelRightsAssessment,
207
212
  createModelTechnicalProfile: () => createModelTechnicalProfile,
@@ -260,11 +265,12 @@ __export(index_exports, {
260
265
  projectPvoxNamedJsonClosureV1: () => projectPvoxNamedJsonClosureV1,
261
266
  qualifyModelConversionDiagnostics: () => qualifyModelConversionDiagnostics,
262
267
  qualifyModelResourceManifest: () => qualifyModelResourceManifest,
268
+ qualifyModelTextureResources: () => qualifyModelTextureResources,
263
269
  toLegacyModelResolutionState: () => toLegacyModelResolutionState,
264
270
  validateGpuAssetFiles: () => validateGpuAssetFiles
265
271
  });
266
272
  module.exports = __toCommonJS(index_exports);
267
- var import_gpu_shader4 = require("@plasius/gpu-shader");
273
+ var import_gpu_shader5 = require("@plasius/gpu-shader");
268
274
 
269
275
  // src/asset-version.ts
270
276
  var ASSET_VERSION_PATTERN = /^[0-9A-Za-z][0-9A-Za-z._-]{0,127}$/u;
@@ -448,11 +454,11 @@ function assertNoDirectUrl(value, fieldName) {
448
454
  }
449
455
  }
450
456
  function requiredToken(value, fieldName) {
451
- const token2 = requiredString(value, fieldName, 128);
452
- if (!TOKEN_PATTERN.test(token2)) {
457
+ const token3 = requiredString(value, fieldName, 128);
458
+ if (!TOKEN_PATTERN.test(token3)) {
453
459
  throw new Error(`${fieldName} must be a token up to 128 characters.`);
454
460
  }
455
- return token2;
461
+ return token3;
456
462
  }
457
463
  function requiredModelPathSegment(value, fieldName) {
458
464
  const segment = requiredString(value, fieldName, 128);
@@ -3049,7 +3055,7 @@ function requireEnum(value, allowed, fieldName) {
3049
3055
  }
3050
3056
  function requireReasonCodes(value, fieldName) {
3051
3057
  const entries = asDenseArray(value, fieldName, 0, 32);
3052
- const result = entries.map((entry, index) => requireReasonCode(entry, `${fieldName}[${index}]`));
3058
+ const result = entries.map((entry2, index) => requireReasonCode(entry2, `${fieldName}[${index}]`));
3053
3059
  if (new Set(result).size !== result.length) throw new Error(`${fieldName} must not contain duplicates.`);
3054
3060
  return result;
3055
3061
  }
@@ -3371,7 +3377,7 @@ function createPvoxRuntimeRequestProfileV1(value) {
3371
3377
  const record2 = asRecord(value, fieldName);
3372
3378
  exactKeys(record2, ["profileId", "fidelityProfileId", "capabilityProfileId", "geometryMode", "requiredCapabilities", "limits"], fieldName);
3373
3379
  const requestedCapabilityEntries = asDenseArray(record2.requiredCapabilities, `${fieldName}.requiredCapabilities`, 1, PVOX_CAPABILITIES.length);
3374
- const requiredCapabilities = requestedCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `${fieldName}.requiredCapabilities[${index}]`));
3380
+ const requiredCapabilities = requestedCapabilityEntries.map((entry2, index) => requireEnum(entry2, PVOX_CAPABILITIES, `${fieldName}.requiredCapabilities[${index}]`));
3375
3381
  if (new Set(requiredCapabilities).size !== requiredCapabilities.length) {
3376
3382
  throw new Error(`${fieldName}.requiredCapabilities must not contain duplicates.`);
3377
3383
  }
@@ -3534,7 +3540,7 @@ function createPvoxAssetManifestV1(input) {
3534
3540
  }
3535
3541
  if (expectedOffset !== artifact.byteLength) throw new Error("PvoxAssetManifestV1 pages must exactly cover the artifact bytes.");
3536
3542
  const lodEntries = asDenseArray(record2.lods, "PvoxAssetManifestV1.lods", 1, 4);
3537
- const lods = lodEntries.map((entry, index) => createLod(entry, index, pages.length));
3543
+ const lods = lodEntries.map((entry2, index) => createLod(entry2, index, pages.length));
3538
3544
  for (const [index, current] of lods.entries()) {
3539
3545
  if (current.level !== index) throw new Error("PvoxAssetManifestV1 LOD levels must be contiguous from LOD0.");
3540
3546
  const previous = lods[index - 1];
@@ -3770,7 +3776,7 @@ function createPhysicalPropertyEvidence(input) {
3770
3776
  const reviewedBy = record2.reviewedBy === void 0 ? void 0 : requireToken(record2.reviewedBy, "PhysicalPropertyEvidence.reviewedBy");
3771
3777
  const reviewedAt = record2.reviewedAt === void 0 ? void 0 : requireTimestamp(record2.reviewedAt, "PhysicalPropertyEvidence.reviewedAt");
3772
3778
  const reviewToken = record2.reviewToken === void 0 ? void 0 : requireAttestation(record2.reviewToken, "PhysicalPropertyEvidence.reviewToken");
3773
- if (new Set([reviewedBy, reviewedAt, reviewToken].map((entry) => entry === void 0)).size !== 1) throw new Error("PhysicalPropertyEvidence signed review identity, timestamp, and token must be supplied together.");
3779
+ if (new Set([reviewedBy, reviewedAt, reviewToken].map((entry2) => entry2 === void 0)).size !== 1) throw new Error("PhysicalPropertyEvidence signed review identity, timestamp, and token must be supplied together.");
3774
3780
  if (provenance === "inferred" && reviewedBy === void 0) throw new Error("Inferred PhysicalPropertyEvidence requires human review.");
3775
3781
  return deepFreeze2({
3776
3782
  property,
@@ -3793,9 +3799,9 @@ function createPvoxPhysicalRegionInventoryV1(input) {
3793
3799
  exactKeys(record2, ["inventoryVersion", "subjectContentHash", "inventoryHash", "inventoryHashAttestation", "validationEvidenceHash", "entries"], fieldName);
3794
3800
  if (record2.inventoryVersion !== PVOX_PHYSICAL_REGION_INVENTORY_VERSION) throw new Error(`${fieldName}.inventoryVersion is unsupported.`);
3795
3801
  const entriesInput = asDenseArray(record2.entries, `${fieldName}.entries`, 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions);
3796
- const entries = entriesInput.map((entry, index) => {
3802
+ const entries = entriesInput.map((entry2, index) => {
3797
3803
  const entryName = `${fieldName}.entries[${index}]`;
3798
- const item = asRecord(entry, entryName);
3804
+ const item = asRecord(entry2, entryName);
3799
3805
  exactKeys(item, ["regionIndex", "physicalPaletteIndex", "regionId", "materialId"], entryName);
3800
3806
  return {
3801
3807
  regionIndex: requireInteger(item.regionIndex, `${entryName}.regionIndex`, 0, PVOX_DEFAULT_LIMITS.maximumMaterialRegions - 1),
@@ -3804,7 +3810,7 @@ function createPvoxPhysicalRegionInventoryV1(input) {
3804
3810
  materialId: requireToken(item.materialId, `${entryName}.materialId`)
3805
3811
  };
3806
3812
  });
3807
- if (entries.some((entry, index) => entry.regionIndex !== index)) throw new Error(`${fieldName} regionIndex values must be contiguous and canonically ordered.`);
3813
+ if (entries.some((entry2, index) => entry2.regionIndex !== index)) throw new Error(`${fieldName} regionIndex values must be contiguous and canonically ordered.`);
3808
3814
  const pairs = entries.map(({ regionId, materialId }) => `${regionId}\0${materialId}`);
3809
3815
  if (new Set(pairs).size !== pairs.length) throw new Error(`${fieldName} region/material pairs must be unique.`);
3810
3816
  const inventoryHash = requireSha256(record2.inventoryHash, `${fieldName}.inventoryHash`);
@@ -3941,9 +3947,9 @@ function createPvoxEditJournalCurrentState(input, baseManifest) {
3941
3947
  const record2 = asRecord(input, fieldName);
3942
3948
  exactKeys(record2, ["baseContentHash", "basePageSetHash", "placementId", "gridVersion", "revision", "rootHash", "latestJournalHash", "pageHashes"], fieldName);
3943
3949
  const pageEntries = asDenseArray(record2.pageHashes, `${fieldName}.pageHashes`, baseManifest.pages.length, baseManifest.pages.length);
3944
- const pageHashes = pageEntries.map((entry, index) => {
3950
+ const pageHashes = pageEntries.map((entry2, index) => {
3945
3951
  const entryName = `${fieldName}.pageHashes[${index}]`;
3946
- const page = asRecord(entry, entryName);
3952
+ const page = asRecord(entry2, entryName);
3947
3953
  exactKeys(page, ["pageIndex", "sha256"], entryName);
3948
3954
  const pageIndex = requireInteger(page.pageIndex, `${entryName}.pageIndex`, 0, baseManifest.pages.length - 1);
3949
3955
  if (pageIndex !== index) throw new Error(`${fieldName}.pageHashes must be complete and ordered by pageIndex.`);
@@ -3994,9 +4000,9 @@ function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
3994
4000
  }
3995
4001
  }
3996
4002
  const patchEntries = asDenseArray(record2.patches, "PvoxEditJournal.patches", 1, PVOX_MAX_EDIT_PATCHES);
3997
- const patches = patchEntries.map((entry, index) => {
4003
+ const patches = patchEntries.map((entry2, index) => {
3998
4004
  const fieldName = `PvoxEditJournal.patches[${index}]`;
3999
- const patch = asRecord(entry, fieldName);
4005
+ const patch = asRecord(entry2, fieldName);
4000
4006
  exactKeys(patch, ["fieldKind", "lodLevel", "partitionIndex", "hierarchyDepth", "mortonCode", "expectedPageIndex", "expectedPageHash", "operation", "expectedBrickHash", "resultingBrickHash", "resultingPageHash"], fieldName);
4001
4007
  const mortonCode = requireString(patch.mortonCode, `${fieldName}.mortonCode`, 16);
4002
4008
  if (!MORTON_CODE.test(mortonCode)) throw new Error(`${fieldName}.mortonCode must be a canonical Morton code.`);
@@ -4073,7 +4079,7 @@ function createPvoxEditJournalChain(input, baseManifestInput) {
4073
4079
  const journals = [];
4074
4080
  const operationIds = /* @__PURE__ */ new Set();
4075
4081
  const currentPageHashes = new Map(baseManifest.pages.map(({ pageIndex, sha256: sha2562 }) => [pageIndex, sha2562]));
4076
- for (const entry of entries) {
4082
+ for (const entry2 of entries) {
4077
4083
  const previous = journals.at(-1);
4078
4084
  const currentState = previous === void 0 ? void 0 : {
4079
4085
  baseContentHash: previous.baseContentHash,
@@ -4085,7 +4091,7 @@ function createPvoxEditJournalChain(input, baseManifestInput) {
4085
4091
  latestJournalHash: previous.journalHash,
4086
4092
  pageHashes: baseManifest.pages.map(({ pageIndex }) => ({ pageIndex, sha256: currentPageHashes.get(pageIndex) }))
4087
4093
  };
4088
- const journal = createPvoxEditJournal(entry, baseManifest, currentState);
4094
+ const journal = createPvoxEditJournal(entry2, baseManifest, currentState);
4089
4095
  if (previous === void 0 && (journal.expectedRevision !== 0 || journal.previousJournalHash !== PVOX_EDIT_JOURNAL_GENESIS_HASH)) {
4090
4096
  throw new Error("PvoxEditJournalChain must begin at the canonical genesis revision.");
4091
4097
  }
@@ -4143,10 +4149,10 @@ function createTransform2(value, fieldName) {
4143
4149
  exactKeys(record2, ["translationMetres", "rotationQuaternion", "scale"], fieldName);
4144
4150
  const translationMetres = requireVector(record2.translationMetres, `${fieldName}.translationMetres`);
4145
4151
  const rotationEntries = asDenseArray(record2.rotationQuaternion, `${fieldName}.rotationQuaternion`, 4, 4);
4146
- const rotationQuaternion = rotationEntries.map((entry, index) => requireNumber(entry, `${fieldName}.rotationQuaternion[${index}]`, -1, 1));
4152
+ const rotationQuaternion = rotationEntries.map((entry2, index) => requireNumber(entry2, `${fieldName}.rotationQuaternion[${index}]`, -1, 1));
4147
4153
  if (Math.abs(Math.hypot(...rotationQuaternion) - 1) > 1e-4) throw new Error(`${fieldName}.rotationQuaternion must be normalized.`);
4148
4154
  const scale = requireVector(record2.scale, `${fieldName}.scale`);
4149
- if (scale.some((entry) => entry <= 0)) throw new Error(`${fieldName}.scale values must be positive.`);
4155
+ if (scale.some((entry2) => entry2 <= 0)) throw new Error(`${fieldName}.scale values must be positive.`);
4150
4156
  return { translationMetres, rotationQuaternion, scale };
4151
4157
  }
4152
4158
  function createAssemblyChild2(value, index, resolutionId, candidateId) {
@@ -4314,10 +4320,10 @@ function createModelProcessingManifestV2(input) {
4314
4320
  }
4315
4321
  const capabilityEntries = asDenseArray(record2.capabilities, "ModelProcessingManifestV2.capabilities", PVOX_CAPABILITIES.length, PVOX_CAPABILITIES.length);
4316
4322
  const capabilities = capabilityEntries.map(createVoxelCapabilityAssessment);
4317
- const byCapability = new Map(capabilities.map((entry) => [entry.capability, entry]));
4323
+ const byCapability = new Map(capabilities.map((entry2) => [entry2.capability, entry2]));
4318
4324
  if (byCapability.size !== PVOX_CAPABILITIES.length || PVOX_CAPABILITIES.some((name) => !byCapability.has(name))) throw new Error("ModelProcessingManifestV2 must include every PVOX capability exactly once.");
4319
4325
  const orderedCapabilities = PVOX_CAPABILITIES.map((name) => byCapability.get(name));
4320
- if (orderedCapabilities.some((entry) => entry.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 capability evidence must bind the binaryClosureHash.");
4326
+ if (orderedCapabilities.some((entry2) => entry2.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 capability evidence must bind the binaryClosureHash.");
4321
4327
  const capabilityEvidenceSetHash = requireSha256(record2.capabilityEvidenceSetHash, "ModelProcessingManifestV2.capabilityEvidenceSetHash");
4322
4328
  const capabilityEvidenceSetAttestation = createHashAttestation(
4323
4329
  record2.capabilityEvidenceSetAttestation,
@@ -4326,7 +4332,7 @@ function createModelProcessingManifestV2(input) {
4326
4332
  "ModelProcessingManifestV2.capabilityEvidenceSetAttestation"
4327
4333
  );
4328
4334
  const requiredCapabilityEntries = asDenseArray(record2.requiredCapabilities, "ModelProcessingManifestV2.requiredCapabilities", 1, PVOX_CAPABILITIES.length);
4329
- const requiredCapabilities = requiredCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `ModelProcessingManifestV2.requiredCapabilities[${index}]`));
4335
+ const requiredCapabilities = requiredCapabilityEntries.map((entry2, index) => requireEnum(entry2, PVOX_CAPABILITIES, `ModelProcessingManifestV2.requiredCapabilities[${index}]`));
4330
4336
  if (new Set(requiredCapabilities).size !== requiredCapabilities.length) throw new Error("ModelProcessingManifestV2.requiredCapabilities must be unique.");
4331
4337
  if (!requiredCapabilities.includes("rendering")) throw new Error("ModelProcessingManifestV2 rendering capability is always required.");
4332
4338
  if (technicalProfile.hasCollision !== (byCapability.get("collision")?.status === "supported")) throw new Error("ModelProcessingManifestV2 collision capability must match technicalProfile.hasCollision.");
@@ -4339,9 +4345,9 @@ function createModelProcessingManifestV2(input) {
4339
4345
  }
4340
4346
  const physicalEntries = asDenseArray(record2.physicalProperties, "ModelProcessingManifestV2.physicalProperties", 0, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries);
4341
4347
  const physicalProperties = physicalEntries.map(createPhysicalPropertyEvidence);
4342
- const byRegionProperty = new Map(physicalProperties.map((entry) => [`${entry.regionId}\0${entry.materialId}\0${entry.property}`, entry]));
4348
+ const byRegionProperty = new Map(physicalProperties.map((entry2) => [`${entry2.regionId}\0${entry2.materialId}\0${entry2.property}`, entry2]));
4343
4349
  if (byRegionProperty.size !== physicalProperties.length) throw new Error("ModelProcessingManifestV2 physical properties must be unique per region, material, and property.");
4344
- if (physicalProperties.some((entry) => entry.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 physical evidence must bind the PVOX binary closure subject.");
4350
+ if (physicalProperties.some((entry2) => entry2.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 physical evidence must bind the PVOX binary closure subject.");
4345
4351
  const authoritativePairs = physicalRegionInventory.entries.map(({ regionId, materialId }) => `${regionId}\0${materialId}`);
4346
4352
  const authoritativePairSet = new Set(authoritativePairs);
4347
4353
  if (physicalProperties.some(({ regionId, materialId }) => !authoritativePairSet.has(`${regionId}\0${materialId}`))) {
@@ -4423,7 +4429,7 @@ function createModelProcessingManifestV2(input) {
4423
4429
  throw new Error("ModelProcessingManifestV2 fidelity evidence must exactly match every governed PVOX LOD0 metric.");
4424
4430
  }
4425
4431
  const childEntries = asDenseArray(record2.children, "ModelProcessingManifestV2.children", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
4426
- const children = childEntries.map((entry, index) => createAssemblyChild2(entry, index, resolutionId, candidateId));
4432
+ const children = childEntries.map((entry2, index) => createAssemblyChild2(entry2, index, resolutionId, candidateId));
4427
4433
  const kind = requireEnum(record2.kind, ["leaf", "assembly"], "ModelProcessingManifestV2.kind");
4428
4434
  if (kind === "leaf" && children.length !== 0) throw new Error("A leaf ModelProcessingManifestV2 cannot contain children.");
4429
4435
  if (kind === "leaf" && technicalProfile.partitionCount !== 1) throw new Error("A leaf ModelProcessingManifestV2 must have one partition.");
@@ -4506,9 +4512,9 @@ function createCandidateAssetRef2(value) {
4506
4512
  }
4507
4513
  function createViews(value, resolutionId, candidateId) {
4508
4514
  const entries = asDenseArray(value, "ModelCandidateV2.views", 4, 4);
4509
- const parsed = entries.map((entry, index) => {
4515
+ const parsed = entries.map((entry2, index) => {
4510
4516
  const fieldName = `ModelCandidateV2.views[${index}]`;
4511
- const record2 = asRecord(entry, fieldName);
4517
+ const record2 = asRecord(entry2, fieldName);
4512
4518
  exactKeys(record2, ["kind", "imageUri", "sha256", "contentType", "width", "height"], fieldName);
4513
4519
  const kind = requireEnum(record2.kind, MODEL_CONFIRMATION_VIEW_KINDS, `${fieldName}.kind`);
4514
4520
  const expectedUri = `mcp://models/resolutions/${resolutionId}/candidates/${candidateId}/${kind}.png`;
@@ -4516,7 +4522,7 @@ function createViews(value, resolutionId, candidateId) {
4516
4522
  if (record2.contentType !== "image/png" || record2.width !== MODEL_CONFIRMATION_VIEW_SIZE_PX || record2.height !== MODEL_CONFIRMATION_VIEW_SIZE_PX) throw new Error(`${fieldName} must be an authenticated 1024x1024 PNG.`);
4517
4523
  return { kind, imageUri: expectedUri, sha256: requireSha256(record2.sha256, `${fieldName}.sha256`), contentType: "image/png", width: MODEL_CONFIRMATION_VIEW_SIZE_PX, height: MODEL_CONFIRMATION_VIEW_SIZE_PX };
4518
4524
  });
4519
- if (parsed.some((entry, index) => entry.kind !== MODEL_CONFIRMATION_VIEW_KINDS[index])) throw new Error("ModelCandidateV2.views must be ordered front, left, top, isometric.");
4525
+ if (parsed.some((entry2, index) => entry2.kind !== MODEL_CONFIRMATION_VIEW_KINDS[index])) throw new Error("ModelCandidateV2.views must be ordered front, left, top, isometric.");
4520
4526
  return parsed;
4521
4527
  }
4522
4528
  function createRenderEvidence2(value) {
@@ -4715,7 +4721,7 @@ function createModelCandidateV2(input) {
4715
4721
  );
4716
4722
  const gateEntries = asDenseArray(record2.hardGates, "ModelCandidateV2.hardGates", PVOX_NON_OVERRIDABLE_GATE_KINDS.length, PVOX_NON_OVERRIDABLE_GATE_KINDS.length);
4717
4723
  const gates = gateEntries.map(createPvoxGateEvidence);
4718
- const byGate = new Map(gates.map((entry) => [entry.kind, entry]));
4724
+ const byGate = new Map(gates.map((entry2) => [entry2.kind, entry2]));
4719
4725
  if (byGate.size !== PVOX_NON_OVERRIDABLE_GATE_KINDS.length || PVOX_NON_OVERRIDABLE_GATE_KINDS.some((kind) => !byGate.has(kind))) throw new Error("ModelCandidateV2 must contain every non-overridable hard gate exactly once.");
4720
4726
  const orderedGates = PVOX_NON_OVERRIDABLE_GATE_KINDS.map((kind) => byGate.get(kind));
4721
4727
  const concreteGateHashes = {
@@ -4738,7 +4744,7 @@ function createModelCandidateV2(input) {
4738
4744
  ...!match.hardConstraintPass ? ["semantic-hard-constraints-failed"] : [],
4739
4745
  ...!["allowed", "attribution-required"].includes(rights.status) ? [`rights:${rights.status}`] : [],
4740
4746
  ...processingManifest.requiredCapabilities.flatMap((capability) => {
4741
- const evidence = processingManifest.capabilities.find((entry) => entry.capability === capability);
4747
+ const evidence = processingManifest.capabilities.find((entry2) => entry2.capability === capability);
4742
4748
  return evidence.status === "supported" ? [] : [`capability:${capability}:${evidence.status}`];
4743
4749
  }),
4744
4750
  ...orderedGates.flatMap((gate) => gate.outcome === "passed" ? [] : gate.reasonCodes.map((reason) => `${gate.kind}:${reason}`))
@@ -4766,7 +4772,7 @@ function createModelCandidateV2(input) {
4766
4772
  if (record2.confirmationRequired !== false) throw new Error("Diagnostic ModelCandidateV2 confirmationRequired must be false.");
4767
4773
  if (record2.blockingReasonCodes !== void 0) {
4768
4774
  const suppliedEntries = asDenseArray(record2.blockingReasonCodes, "ModelCandidateV2.blockingReasonCodes", 1, 256);
4769
- const suppliedBlockingReasons = suppliedEntries.map((entry, index) => requireReasonCode(entry, `ModelCandidateV2.blockingReasonCodes[${index}]`));
4775
+ const suppliedBlockingReasons = suppliedEntries.map((entry2, index) => requireReasonCode(entry2, `ModelCandidateV2.blockingReasonCodes[${index}]`));
4770
4776
  if (JSON.stringify(suppliedBlockingReasons) !== JSON.stringify(blockingReasonCodes)) throw new Error("Diagnostic ModelCandidateV2 blockingReasonCodes must be derived from its evidence.");
4771
4777
  }
4772
4778
  return deepFreeze2({ ...common, admissionStatus: "diagnostic", blockingReasonCodes, confirmationRequired: false });
@@ -4820,7 +4826,7 @@ function createModelCandidateV2(input) {
4820
4826
  });
4821
4827
  }
4822
4828
  function isModelCandidateV2Confirmable(candidate) {
4823
- return candidate.admissionStatus === "confirmable" && candidate.confirmationRequired && candidate.match.hardConstraintPass && candidate.match.assurance !== "none" && (candidate.rights.status === "allowed" || candidate.rights.status === "attribution-required") && candidate.processingManifest.fidelity.outcome === "passed" && candidate.hardGates.length === PVOX_NON_OVERRIDABLE_GATE_KINDS.length && candidate.hardGates.every(({ outcome }) => outcome === "passed") && candidate.processingManifest.requiredCapabilities.every((name) => candidate.processingManifest.capabilities.find((entry) => entry.capability === name)?.status === "supported");
4829
+ return candidate.admissionStatus === "confirmable" && candidate.confirmationRequired && candidate.match.hardConstraintPass && candidate.match.assurance !== "none" && (candidate.rights.status === "allowed" || candidate.rights.status === "attribution-required") && candidate.processingManifest.fidelity.outcome === "passed" && candidate.hardGates.length === PVOX_NON_OVERRIDABLE_GATE_KINDS.length && candidate.hardGates.every(({ outcome }) => outcome === "passed") && candidate.processingManifest.requiredCapabilities.every((name) => candidate.processingManifest.capabilities.find((entry2) => entry2.capability === name)?.status === "supported");
4824
4830
  }
4825
4831
  function createModelCandidateConfirmationV2(input, expectedCandidate, expectedResolutionId) {
4826
4832
  const record2 = asRecord(input, "ModelCandidateConfirmationV2");
@@ -4917,7 +4923,7 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
4917
4923
  const candidateAssetRef = createCandidateAssetRef2(record2.candidateAssetRef);
4918
4924
  if (JSON.stringify(candidateAssetRef) !== JSON.stringify(candidate.assetRef)) throw new Error("ModelPromotionReceiptV2 must bind the exact proposed candidate identity.");
4919
4925
  const childClosureEntries = asDenseArray(record2.assemblyChildClosureHashes, "ModelPromotionReceiptV2.assemblyChildClosureHashes", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
4920
- const assemblyChildClosureHashes = childClosureEntries.map((entry, index) => requireSha256(entry, `ModelPromotionReceiptV2.assemblyChildClosureHashes[${index}]`));
4926
+ const assemblyChildClosureHashes = childClosureEntries.map((entry2, index) => requireSha256(entry2, `ModelPromotionReceiptV2.assemblyChildClosureHashes[${index}]`));
4921
4927
  const expectedChildClosureHashes = candidate.processingManifest.children.map(({ assetRef }) => assetRef.binaryClosureHash);
4922
4928
  if (JSON.stringify(assemblyChildClosureHashes) !== JSON.stringify(expectedChildClosureHashes)) throw new Error("ModelPromotionReceiptV2 child closure sequence must match the canonical assembly order.");
4923
4929
  assertDataOnlyTree(record2.finalAssetRef, "ModelPromotionReceiptV2.finalAssetRef");
@@ -5178,12 +5184,12 @@ function copyPvoxByteInput(value, fieldName, maximumByteLength) {
5178
5184
  if (!(value instanceof Uint8Array)) throw new Error(`${fieldName} must be a Uint8Array.`);
5179
5185
  let byteLength;
5180
5186
  try {
5181
- const typedArrayPrototype2 = Object.getPrototypeOf(Uint8Array.prototype);
5182
- const byteLengthGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "byteLength")?.get;
5183
- const bufferGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "buffer")?.get;
5184
- if (byteLengthGetter2 === void 0 || bufferGetter2 === void 0) throw new Error("Typed-array intrinsics are unavailable.");
5185
- byteLength = byteLengthGetter2.call(value);
5186
- const buffer = bufferGetter2.call(value);
5187
+ const typedArrayPrototype3 = Object.getPrototypeOf(Uint8Array.prototype);
5188
+ const byteLengthGetter3 = Object.getOwnPropertyDescriptor(typedArrayPrototype3, "byteLength")?.get;
5189
+ const bufferGetter3 = Object.getOwnPropertyDescriptor(typedArrayPrototype3, "buffer")?.get;
5190
+ if (byteLengthGetter3 === void 0 || bufferGetter3 === void 0) throw new Error("Typed-array intrinsics are unavailable.");
5191
+ byteLength = byteLengthGetter3.call(value);
5192
+ const buffer = bufferGetter3.call(value);
5187
5193
  if (typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer) {
5188
5194
  throw new Error(`${fieldName} must not use shared mutable storage.`);
5189
5195
  }
@@ -6953,9 +6959,9 @@ function snapshot(value, budget, depth = 0) {
6953
6959
  return value;
6954
6960
  }
6955
6961
  if (Array.isArray(value)) return Object.freeze(list(value).map((item) => snapshot(item, budget, depth + 1)));
6956
- const fields2 = record(value);
6957
- for (const key of Object.keys(fields2)) fields2[key] = snapshot(fields2[key], budget, depth + 1);
6958
- return Object.freeze(fields2);
6962
+ const fields4 = record(value);
6963
+ for (const key of Object.keys(fields4)) fields4[key] = snapshot(fields4[key], budget, depth + 1);
6964
+ return Object.freeze(fields4);
6959
6965
  }
6960
6966
  function path(value) {
6961
6967
  if (typeof value !== "string" || value.length > 1024 || !value.length) fail();
@@ -7027,14 +7033,14 @@ async function qualify(input) {
7027
7033
  contentType(file.contentType);
7028
7034
  }
7029
7035
  const entries = list(inventory.entries).map(record);
7030
- uniquePaths(entries.map((entry) => path(entry.path)));
7036
+ uniquePaths(entries.map((entry2) => path(entry2.path)));
7031
7037
  const ids = /* @__PURE__ */ new Set();
7032
- for (const entry of entries) {
7033
- if (Object.keys(entry).some((key) => !["id", "path", "role", "contentType"].includes(key)) || typeof entry.id !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(entry.id) || ids.has(entry.id) || !["mesh", "material", "texture", "animation", "auxiliary"].includes(entry.role)) fail();
7034
- ids.add(entry.id);
7035
- const descriptor = descriptors.get(entry.path);
7038
+ for (const entry2 of entries) {
7039
+ if (Object.keys(entry2).some((key) => !["id", "path", "role", "contentType"].includes(key)) || typeof entry2.id !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(entry2.id) || ids.has(entry2.id) || !["mesh", "material", "texture", "animation", "auxiliary"].includes(entry2.role)) fail();
7040
+ ids.add(entry2.id);
7041
+ const descriptor = descriptors.get(entry2.path);
7036
7042
  if (!descriptor) fail("inventory-mismatch");
7037
- if (entry.contentType !== void 0 && contentType(entry.contentType) !== descriptor.contentType) fail("content-mismatch");
7043
+ if (entry2.contentType !== void 0 && contentType(entry2.contentType) !== descriptor.contentType) fail("content-mismatch");
7038
7044
  }
7039
7045
  if (entries.length !== descriptors.size) fail("inventory-mismatch");
7040
7046
  const payloads = list(root.files).map((value) => {
@@ -7064,6 +7070,179 @@ async function qualify(input) {
7064
7070
  return Object.freeze({ ...manifest, files: Object.freeze([...manifest.files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) });
7065
7071
  }
7066
7072
 
7073
+ // src/model-texture-resources.ts
7074
+ var import_gpu_model_core = require("@plasius/gpu-model-core");
7075
+ var MODEL_TEXTURE_RESOURCE_LIMITS = Object.freeze({ resources: 16384, derived: 4096, nodes: 65536, values: 262144, characters: 2097152, depth: 16, timeoutMs: 6e4 });
7076
+ var ModelTextureResourceError = class extends TypeError {
7077
+ constructor(code) {
7078
+ super(`Invalid model texture resources: ${code}`);
7079
+ this.code = code;
7080
+ this.name = "ModelTextureResourceError";
7081
+ }
7082
+ code;
7083
+ };
7084
+ function fail2(code = "invalid-input") {
7085
+ throw new ModelTextureResourceError(code);
7086
+ }
7087
+ function fields(value, allowed) {
7088
+ if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail2();
7089
+ const keys = Reflect.ownKeys(value);
7090
+ if (keys.length > 1024) fail2("limit-exceeded");
7091
+ const result = /* @__PURE__ */ Object.create(null);
7092
+ for (const key of keys) {
7093
+ if (typeof key !== "string" || key.length > 128 || ["__proto__", "prototype", "constructor"].includes(key) || allowed && !allowed.includes(key)) fail2();
7094
+ const property = Object.getOwnPropertyDescriptor(value, key);
7095
+ if (!("value" in property) || !property.enumerable) fail2();
7096
+ result[key] = property.value;
7097
+ }
7098
+ return result;
7099
+ }
7100
+ function array(value, maximum) {
7101
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) fail2();
7102
+ if (value.length > maximum) fail2("limit-exceeded");
7103
+ if (Reflect.ownKeys(value).length !== value.length + 1) fail2();
7104
+ const result = [];
7105
+ for (let i = 0; i < value.length; i++) {
7106
+ const property = Object.getOwnPropertyDescriptor(value, String(i));
7107
+ if (!property || !("value" in property) || !property.enumerable) fail2();
7108
+ result.push(property.value);
7109
+ }
7110
+ return result;
7111
+ }
7112
+ function snapshot2(value, budget, depth = 0) {
7113
+ if (--budget.values < 0 || depth > MODEL_TEXTURE_RESOURCE_LIMITS.depth) fail2("limit-exceeded");
7114
+ if (value === null || value === void 0 || typeof value === "boolean") return value;
7115
+ if (typeof value === "number" && Number.isFinite(value)) return value;
7116
+ if (typeof value === "string") {
7117
+ budget.characters -= value.length;
7118
+ if (value.length > 2048 || budget.characters < 0) fail2("limit-exceeded");
7119
+ return value;
7120
+ }
7121
+ if (Array.isArray(value)) return Object.freeze(array(value, MODEL_TEXTURE_RESOURCE_LIMITS.resources).map((v) => snapshot2(v, budget, depth + 1)));
7122
+ const result = fields(value);
7123
+ for (const key of Object.keys(result)) {
7124
+ budget.characters -= key.length;
7125
+ if (budget.characters < 0) fail2("limit-exceeded");
7126
+ result[key] = snapshot2(result[key], budget, depth + 1);
7127
+ }
7128
+ return Object.freeze(result);
7129
+ }
7130
+ function token2(value) {
7131
+ if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value)) fail2();
7132
+ return value;
7133
+ }
7134
+ var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
7135
+ var imageMime = (value) => ["image/png", "image/jpeg", "image/webp", "image/ktx2"].includes(value);
7136
+ var bufferMime = (value) => ["application/octet-stream", "application/vnd.plasius.gpu-buffer"].includes(value);
7137
+ function storageRole(role, file) {
7138
+ const valid = role === "texture" ? file.role === "texture" && imageMime(file.contentType) : role === "material" || role === "animation" ? file.role === "metadata" && file.contentType === "application/json" : role === "mesh" ? file.role === "binary" && bufferMime(file.contentType) || file.role === "model" && (file.contentType.startsWith("model/") || ["application/json", "application/octet-stream", "text/plain"].includes(file.contentType)) : role === "auxiliary" && (file.role === "binary" && bufferMime(file.contentType) || file.role === "metadata" && file.contentType === "application/json");
7139
+ if (!valid) fail2("role-mismatch");
7140
+ }
7141
+ async function qualifyModelTextureResources(input, options = {}) {
7142
+ let timer;
7143
+ let signal;
7144
+ let onAbort;
7145
+ try {
7146
+ const started = performance.now();
7147
+ const controls = fields(options, ["signal", "timeoutMs"]);
7148
+ const timeoutMs = controls.timeoutMs === void 0 ? 1e4 : controls.timeoutMs;
7149
+ if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MODEL_TEXTURE_RESOURCE_LIMITS.timeoutMs) fail2();
7150
+ signal = controls.signal;
7151
+ const aborted = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted").get;
7152
+ const check = () => {
7153
+ if (signal !== void 0 && aborted.call(signal)) fail2("aborted");
7154
+ if (performance.now() - started >= timeoutMs) fail2("deadline-exceeded");
7155
+ };
7156
+ check();
7157
+ const root = fields(input, ["manifest", "packageManifest", "files", "graph", "resources", "derived"]);
7158
+ const graph = root.graph;
7159
+ if (!(0, import_gpu_model_core.isGpuModelResourceGraph)(graph)) fail2("unverified-graph");
7160
+ if (graph.nodes.length > MODEL_TEXTURE_RESOURCE_LIMITS.nodes) fail2("limit-exceeded");
7161
+ const budget = { values: MODEL_TEXTURE_RESOURCE_LIMITS.values, characters: MODEL_TEXTURE_RESOURCE_LIMITS.characters };
7162
+ const inventory = snapshot2(root.packageManifest, budget);
7163
+ const bindings = array(snapshot2(root.resources, budget), MODEL_TEXTURE_RESOURCE_LIMITS.resources).map((value) => {
7164
+ const binding = fields(value, ["resourceId", "filePath"]);
7165
+ return Object.freeze({ resourceId: token2(binding.resourceId), filePath: tokenPath(binding.filePath) });
7166
+ });
7167
+ const derived = array(snapshot2(root.derived, budget), MODEL_TEXTURE_RESOURCE_LIMITS.derived).map((value) => {
7168
+ const record2 = fields(value, ["role", "filePath", "sourceResourceIds"]);
7169
+ if (record2.role !== "gpu-upload" && record2.role !== "texture-atlas") fail2("role-mismatch");
7170
+ const ids = array(record2.sourceResourceIds, MODEL_TEXTURE_RESOURCE_LIMITS.resources).map(token2);
7171
+ if (!ids.length || new Set(ids).size !== ids.length) fail2("resource-mismatch");
7172
+ return Object.freeze({ role: record2.role, filePath: tokenPath(record2.filePath), sourceResourceIds: Object.freeze(ids.sort(compare)) });
7173
+ });
7174
+ check();
7175
+ const interrupted = new Promise((_resolve, reject) => {
7176
+ timer = setTimeout(() => reject(new ModelTextureResourceError("deadline-exceeded")), Math.max(1, timeoutMs - (performance.now() - started)));
7177
+ onAbort = () => reject(new ModelTextureResourceError("aborted"));
7178
+ if (signal) AbortSignal.prototype.addEventListener.call(signal, "abort", onAbort, { once: true });
7179
+ });
7180
+ const manifest = await Promise.race([
7181
+ qualifyModelResourceManifest({ manifest: root.manifest, files: root.files, packageManifest: inventory, signal, timeoutMs }),
7182
+ interrupted
7183
+ ]);
7184
+ check();
7185
+ const files = new Map(manifest.files.map((file) => [file.path, file]));
7186
+ const roles = new Map(inventory.entries.map((entry2) => [entry2.path, entry2.role]));
7187
+ for (const entry2 of inventory.entries) storageRole(entry2.role, files.get(entry2.path));
7188
+ const resources = new Map(graph.nodes.filter((node) => node.kind === "resource").map((node) => [node.id, node.value]));
7189
+ if (bindings.length !== resources.size || new Set(bindings.map((b) => b.resourceId)).size !== bindings.length) fail2("resource-mismatch");
7190
+ const resourceFiles = /* @__PURE__ */ new Set();
7191
+ const qualified2 = bindings.map((binding) => {
7192
+ const value = resources.get(binding.resourceId);
7193
+ const file = files.get(binding.filePath);
7194
+ if (!value || !file) fail2("resource-mismatch");
7195
+ const { resource, location, exportEmbeddingPolicy } = value;
7196
+ if (location.kind === "external") {
7197
+ if (binding.filePath !== location.relativePath || file.sha256 !== resource.contentHash || file.byteLength !== resource.byteLength || file.contentType !== resource.mimeType) fail2("resource-mismatch");
7198
+ if (resource.kind === "image" ? roles.get(file.path) !== "texture" : file.role !== "binary") fail2("role-mismatch");
7199
+ } else if (binding.filePath !== manifest.entrypoint || file.role !== "model") fail2("resource-mismatch");
7200
+ resourceFiles.add(binding.filePath);
7201
+ return Object.freeze({ ...binding, kind: resource.kind, contentHash: resource.contentHash, byteLength: resource.byteLength, mimeType: resource.mimeType, location, exportEmbeddingPolicy });
7202
+ });
7203
+ const outputFiles = /* @__PURE__ */ new Set();
7204
+ for (const output of derived) {
7205
+ const file = files.get(output.filePath);
7206
+ if (!file || outputFiles.has(output.filePath) || resourceFiles.has(output.filePath)) fail2("resource-mismatch");
7207
+ outputFiles.add(output.filePath);
7208
+ for (const id of output.sourceResourceIds) {
7209
+ const source2 = resources.get(id);
7210
+ if (!source2 || output.role === "texture-atlas" && source2.resource.kind !== "image") fail2("resource-mismatch");
7211
+ }
7212
+ if (output.role === "gpu-upload" ? file.role !== "binary" || !bufferMime(file.contentType) || roles.get(file.path) !== "auxiliary" : file.role !== "texture" || !imageMime(file.contentType) || roles.get(file.path) !== "texture") fail2("role-mismatch");
7213
+ }
7214
+ const materials = graph.nodes.filter((node) => node.kind === "material").map((node) => node.value);
7215
+ const textures = graph.nodes.filter((node) => node.kind === "texture").map((node) => node.value);
7216
+ const source = graph.nodes.find((node) => node.kind === "provenance");
7217
+ if (source.kind !== "provenance") fail2();
7218
+ snapshot2({ materials, textures, source: source.value }, budget);
7219
+ check();
7220
+ return Object.freeze({
7221
+ contractVersion: 1,
7222
+ manifest,
7223
+ source: source.value,
7224
+ resources: Object.freeze(qualified2.sort((a, b) => compare(a.resourceId, b.resourceId))),
7225
+ materials: Object.freeze(materials),
7226
+ textures: Object.freeze(textures),
7227
+ derived: Object.freeze(derived.sort((a, b) => compare(a.filePath, b.filePath)))
7228
+ });
7229
+ } catch (error) {
7230
+ if (error instanceof ModelTextureResourceError) throw error;
7231
+ if (error instanceof ModelResourceManifestError) return fail2(error.code === "aborted" || error.code === "deadline-exceeded" || error.code === "limit-exceeded" ? error.code : "package-mismatch");
7232
+ return fail2();
7233
+ } finally {
7234
+ if (timer !== void 0) clearTimeout(timer);
7235
+ if (signal && onAbort) AbortSignal.prototype.removeEventListener.call(signal, "abort", onAbort);
7236
+ }
7237
+ }
7238
+ function tokenPath(value) {
7239
+ if (typeof value !== "string" || !value.length || value.length > 1024) fail2();
7240
+ return value;
7241
+ }
7242
+
7243
+ // src/model-resource-promotion.ts
7244
+ var import_gpu_shader4 = require("@plasius/gpu-shader");
7245
+
7067
7246
  // src/model-conversion-diagnostics.ts
7068
7247
  var MODEL_CONVERSION_DIAGNOSTIC_LIMITS = Object.freeze({ issues: 100, resources: 100, values: 16384, depth: 12, string: 2048, characters: 262144, timeoutMs: 6e4 });
7069
7248
  var ModelConversionDiagnosticsError = class extends TypeError {
@@ -7074,50 +7253,50 @@ var ModelConversionDiagnosticsError = class extends TypeError {
7074
7253
  }
7075
7254
  code;
7076
7255
  };
7077
- function fail2(code = "invalid-input") {
7256
+ function fail3(code = "invalid-input") {
7078
7257
  throw new ModelConversionDiagnosticsError(code);
7079
7258
  }
7080
- function fields(value) {
7081
- if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail2();
7259
+ function fields2(value) {
7260
+ if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail3();
7082
7261
  const result = /* @__PURE__ */ Object.create(null);
7083
7262
  const keys = Reflect.ownKeys(value);
7084
- if (keys.length > 1024) fail2("limit-exceeded");
7263
+ if (keys.length > 1024) fail3("limit-exceeded");
7085
7264
  for (const key of keys) {
7086
- if (typeof key !== "string" || key.length > 128 || ["__proto__", "prototype", "constructor"].includes(key)) fail2();
7265
+ if (typeof key !== "string" || key.length > 128 || ["__proto__", "prototype", "constructor"].includes(key)) fail3();
7087
7266
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
7088
- if (!("value" in descriptor) || !descriptor.enumerable) fail2();
7267
+ if (!("value" in descriptor) || !descriptor.enumerable) fail3();
7089
7268
  result[key] = descriptor.value;
7090
7269
  }
7091
7270
  return result;
7092
7271
  }
7093
7272
  function only(record2, keys) {
7094
- if (Object.keys(record2).some((key) => !keys.includes(key))) fail2();
7273
+ if (Object.keys(record2).some((key) => !keys.includes(key))) fail3();
7095
7274
  }
7096
- function snapshot2(value, budget, depth = 0) {
7097
- if (--budget.values < 0 || depth > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.depth) fail2("limit-exceeded");
7275
+ function snapshot3(value, budget, depth = 0) {
7276
+ if (--budget.values < 0 || depth > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.depth) fail3("limit-exceeded");
7098
7277
  if (value === null || typeof value === "boolean") return value;
7099
7278
  if (typeof value === "string") {
7100
7279
  budget.characters -= value.length;
7101
- if (value.length > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.string || budget.characters < 0) fail2("limit-exceeded");
7280
+ if (value.length > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.string || budget.characters < 0) fail3("limit-exceeded");
7102
7281
  return value;
7103
7282
  }
7104
7283
  if (typeof value === "number" && Number.isFinite(value)) return value;
7105
7284
  if (Array.isArray(value)) {
7106
- if (Object.getPrototypeOf(value) !== Array.prototype || value.length > 1024) fail2("limit-exceeded");
7107
- if (Reflect.ownKeys(value).length !== value.length + 1) fail2();
7285
+ if (Object.getPrototypeOf(value) !== Array.prototype || value.length > 1024) fail3("limit-exceeded");
7286
+ if (Reflect.ownKeys(value).length !== value.length + 1) fail3();
7108
7287
  const result = [];
7109
7288
  for (let i = 0; i < value.length; i++) {
7110
7289
  const descriptor = Object.getOwnPropertyDescriptor(value, String(i));
7111
- if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail2();
7112
- result.push(snapshot2(descriptor.value, budget, depth + 1));
7290
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail3();
7291
+ result.push(snapshot3(descriptor.value, budget, depth + 1));
7113
7292
  }
7114
7293
  return Object.freeze(result);
7115
7294
  }
7116
- const record2 = fields(value);
7295
+ const record2 = fields2(value);
7117
7296
  for (const key of Object.keys(record2)) {
7118
7297
  budget.characters -= key.length;
7119
- if (budget.characters < 0) fail2("limit-exceeded");
7120
- record2[key] = snapshot2(record2[key], budget, depth + 1);
7298
+ if (budget.characters < 0) fail3("limit-exceeded");
7299
+ record2[key] = snapshot3(record2[key], budget, depth + 1);
7121
7300
  }
7122
7301
  return Object.freeze(record2);
7123
7302
  }
@@ -7130,35 +7309,35 @@ async function qualifyModelConversionDiagnostics(input, options = {}) {
7130
7309
  let onAbort;
7131
7310
  try {
7132
7311
  const start = performance.now();
7133
- const controls = fields(options);
7312
+ const controls = fields2(options);
7134
7313
  only(controls, ["signal", "timeoutMs"]);
7135
7314
  const timeoutMs = controls.timeoutMs === void 0 ? 1e4 : controls.timeoutMs;
7136
- if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.timeoutMs) fail2();
7315
+ if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.timeoutMs) fail3();
7137
7316
  signal = controls.signal;
7138
7317
  const aborted = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted").get;
7139
7318
  const check = () => {
7140
- if (signal !== void 0 && aborted.call(signal)) fail2("aborted");
7141
- if (performance.now() - start >= timeoutMs) fail2("deadline-exceeded");
7319
+ if (signal !== void 0 && aborted.call(signal)) fail3("aborted");
7320
+ if (performance.now() - start >= timeoutMs) fail3("deadline-exceeded");
7142
7321
  };
7143
7322
  check();
7144
- const args = fields(snapshot2(input, { values: MODEL_CONVERSION_DIAGNOSTIC_LIMITS.values, characters: MODEL_CONVERSION_DIAGNOSTIC_LIMITS.characters }));
7323
+ const args = fields2(snapshot3(input, { values: MODEL_CONVERSION_DIAGNOSTIC_LIMITS.values, characters: MODEL_CONVERSION_DIAGNOSTIC_LIMITS.characters }));
7145
7324
  only(args, ["outcome", "source", "report", "resources"]);
7146
- if (!["completed", "unsupported", "failed"].includes(args.outcome)) fail2();
7325
+ if (!["completed", "unsupported", "failed"].includes(args.outcome)) fail3();
7147
7326
  const source = createModelProvenance(args.source);
7148
- const reportInput = fields(args.report);
7327
+ const reportInput = fields2(args.report);
7149
7328
  const maximumIssues = MODEL_CONVERSION_DIAGNOSTIC_LIMITS.issues - (args.outcome === "completed" ? 0 : 1);
7150
- if (!Array.isArray(reportInput.issues) || reportInput.issues.length > maximumIssues) fail2("limit-exceeded");
7151
- if (!Array.isArray(args.resources) || args.resources.length > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.resources) fail2("limit-exceeded");
7329
+ if (!Array.isArray(reportInput.issues) || reportInput.issues.length > maximumIssues) fail3("limit-exceeded");
7330
+ if (!Array.isArray(args.resources) || args.resources.length > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.resources) fail3("limit-exceeded");
7152
7331
  const resources = /* @__PURE__ */ new Map();
7153
7332
  const identities = /* @__PURE__ */ new Map();
7154
7333
  for (const value of args.resources) {
7155
- const binding = fields(value);
7334
+ const binding = fields2(value);
7156
7335
  only(binding, ["path", "resource"]);
7157
- if (typeof binding.path !== "string" || !binding.path.startsWith("/") || binding.path === "/" || resources.has(binding.path)) fail2();
7336
+ if (typeof binding.path !== "string" || !binding.path.startsWith("/") || binding.path === "/" || resources.has(binding.path)) fail3();
7158
7337
  const resource = createModelResourceRef(binding.resource);
7159
- const identity = JSON.stringify(resource);
7160
- if (identities.has(resource.uri) && identities.get(resource.uri) !== identity) fail2();
7161
- identities.set(resource.uri, identity);
7338
+ const identity2 = JSON.stringify(resource);
7339
+ if (identities.has(resource.uri) && identities.get(resource.uri) !== identity2) fail3();
7340
+ identities.set(resource.uri, identity2);
7162
7341
  resources.set(binding.path, resource);
7163
7342
  }
7164
7343
  check();
@@ -7172,7 +7351,7 @@ async function qualifyModelConversionDiagnostics(input, options = {}) {
7172
7351
  const report = core.evaluateGpuModelDiagnostics(reportInput);
7173
7352
  const diagnostics = report.diagnostics.map((diagnostic) => {
7174
7353
  const resource = resources.get(diagnostic.path);
7175
- if (diagnostic.path !== "/" && !resource) fail2("unbound-resource");
7354
+ if (diagnostic.path !== "/" && !resource) fail3("unbound-resource");
7176
7355
  const base = resource ? { resource } : {};
7177
7356
  switch (diagnostic.kind) {
7178
7357
  case "warning":
@@ -7204,7 +7383,194 @@ async function qualifyModelConversionDiagnostics(input, options = {}) {
7204
7383
  });
7205
7384
  } catch (error) {
7206
7385
  if (error instanceof ModelConversionDiagnosticsError) throw error;
7207
- return fail2();
7386
+ return fail3();
7387
+ } finally {
7388
+ if (timer !== void 0) clearTimeout(timer);
7389
+ if (signal && onAbort) AbortSignal.prototype.removeEventListener.call(signal, "abort", onAbort);
7390
+ }
7391
+ }
7392
+
7393
+ // src/model-resource-promotion.ts
7394
+ var MODEL_RESOURCE_PROMOTION_LIMITS = Object.freeze({ bindings: 1024, values: 262144, characters: 2097152, depth: 24, timeoutMs: 6e4 });
7395
+ var ModelResourcePromotionError = class extends TypeError {
7396
+ constructor(code) {
7397
+ super(`Invalid model resource promotion: ${code}`);
7398
+ this.code = code;
7399
+ this.name = "ModelResourcePromotionError";
7400
+ }
7401
+ code;
7402
+ };
7403
+ var qualified = /* @__PURE__ */ new WeakSet();
7404
+ function fail4(code = "invalid-input") {
7405
+ throw new ModelResourcePromotionError(code);
7406
+ }
7407
+ function fields3(value, allowed) {
7408
+ if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail4();
7409
+ const keys = Reflect.ownKeys(value);
7410
+ if (keys.length > 1024) fail4("limit-exceeded");
7411
+ const result = /* @__PURE__ */ Object.create(null);
7412
+ for (const key of keys) {
7413
+ if (typeof key !== "string" || key.length > 128 || ["__proto__", "constructor", "prototype"].includes(key) || allowed && !allowed.includes(key)) fail4();
7414
+ const property = Object.getOwnPropertyDescriptor(value, key);
7415
+ if (!("value" in property) || !property.enumerable) fail4();
7416
+ result[key] = property.value;
7417
+ }
7418
+ return result;
7419
+ }
7420
+ function array2(value, maximum) {
7421
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) fail4();
7422
+ if (value.length > maximum) fail4("limit-exceeded");
7423
+ if (Reflect.ownKeys(value).length !== value.length + 1) fail4();
7424
+ return Array.from({ length: value.length }, (_, i) => {
7425
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(i));
7426
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail4();
7427
+ return descriptor.value;
7428
+ });
7429
+ }
7430
+ function snapshot4(value, budget, depth = 0) {
7431
+ if (--budget.values < 0 || depth > MODEL_RESOURCE_PROMOTION_LIMITS.depth) fail4("limit-exceeded");
7432
+ if (value === null || value === void 0 || typeof value === "boolean") return value;
7433
+ if (typeof value === "number" && Number.isFinite(value)) return value;
7434
+ if (typeof value === "string") {
7435
+ budget.characters -= value.length;
7436
+ if (value.length > 2048 || budget.characters < 0) fail4("limit-exceeded");
7437
+ return value;
7438
+ }
7439
+ if (Array.isArray(value)) return Object.freeze(array2(value, MODEL_RESOURCE_MANIFEST_LIMITS.files).map((v) => snapshot4(v, budget, depth + 1)));
7440
+ const result = fields3(value);
7441
+ for (const key of Object.keys(result)) {
7442
+ budget.characters -= key.length;
7443
+ if (budget.characters < 0) fail4("limit-exceeded");
7444
+ result[key] = snapshot4(result[key], budget, depth + 1);
7445
+ }
7446
+ return Object.freeze(result);
7447
+ }
7448
+ var typedArrayPrototype2 = Object.getPrototypeOf(Uint8Array.prototype);
7449
+ var byteLengthGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "byteLength").get;
7450
+ var bufferGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "buffer").get;
7451
+ function copyFiles(value) {
7452
+ let total = 0;
7453
+ const files = array2(value, MODEL_RESOURCE_MANIFEST_LIMITS.files).map((v) => {
7454
+ const file = fields3(v, ["path", "bytes", "contentType"]);
7455
+ const bytes = file.bytes;
7456
+ if (typeof file.path !== "string" || typeof file.contentType !== "string" || !(bytes instanceof Uint8Array) || Object.getPrototypeOf(bytes) !== Uint8Array.prototype || !(bufferGetter2.call(bytes) instanceof ArrayBuffer)) fail4();
7457
+ const length = byteLengthGetter2.call(bytes);
7458
+ total += length;
7459
+ if (length > MODEL_RESOURCE_MANIFEST_LIMITS.fileBytes || total > MODEL_RESOURCE_MANIFEST_LIMITS.totalBytes) fail4("limit-exceeded");
7460
+ return { path: file.path, bytes, contentType: file.contentType };
7461
+ });
7462
+ return files.map((f) => ({ ...f, bytes: new Uint8Array(f.bytes) }));
7463
+ }
7464
+ var same = (a, b) => (0, import_gpu_shader4.canonicalizeGpuContract)(a) === (0, import_gpu_shader4.canonicalizeGpuContract)(b);
7465
+ var entry = (m) => m.files.find((f) => f.path === m.entrypoint);
7466
+ var identity = (m) => `${m.assetId}/${m.version}`;
7467
+ async function createModelResourcePromotionRecord(input, options = {}) {
7468
+ let timer;
7469
+ let signal;
7470
+ let onAbort;
7471
+ try {
7472
+ const started = performance.now();
7473
+ const controls = fields3(options, ["signal", "timeoutMs"]);
7474
+ const timeoutMs = controls.timeoutMs === void 0 ? 1e4 : controls.timeoutMs;
7475
+ if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MODEL_RESOURCE_PROMOTION_LIMITS.timeoutMs) fail4();
7476
+ signal = controls.signal;
7477
+ const aborted = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted").get;
7478
+ const check = () => {
7479
+ if (signal !== void 0 && aborted.call(signal)) fail4("aborted");
7480
+ if (performance.now() - started >= timeoutMs) fail4("deadline-exceeded");
7481
+ };
7482
+ check();
7483
+ const root = fields3(input, ["promotion", "source", "output", "diagnostics", "resourceBindings", "candidate", "confirmation", "rollback"]);
7484
+ const budget = { values: MODEL_RESOURCE_PROMOTION_LIMITS.values, characters: MODEL_RESOURCE_PROMOTION_LIMITS.characters };
7485
+ const snap = (v) => snapshot4(v, budget);
7486
+ const promotionInput = snap(root.promotion);
7487
+ const sourceInput = fields3(root.source, ["manifest", "packageManifest", "files"]);
7488
+ const outputInput = fields3(root.output, ["manifest", "packageManifest", "files", "graph", "resources", "derived"]);
7489
+ const sourceMetadata = { manifest: snap(sourceInput.manifest), packageManifest: snap(sourceInput.packageManifest) };
7490
+ const outputMetadata = {
7491
+ manifest: snap(outputInput.manifest),
7492
+ packageManifest: snap(outputInput.packageManifest),
7493
+ graph: outputInput.graph,
7494
+ resources: snap(outputInput.resources),
7495
+ derived: snap(outputInput.derived)
7496
+ };
7497
+ const rawDiagnostics = snap(root.diagnostics);
7498
+ const candidate = createModelCandidate(snap(root.candidate));
7499
+ const confirmation = createModelCandidateConfirmation(snap(root.confirmation), candidate, candidate.resolutionId);
7500
+ const bindings = array2(snap(root.resourceBindings), MODEL_RESOURCE_PROMOTION_LIMITS.bindings).map((v) => {
7501
+ const b = fields3(v, ["resource", "package", "filePath"]);
7502
+ if (b.package !== "source" && b.package !== "output" || typeof b.filePath !== "string") fail4();
7503
+ return Object.freeze({ resource: createModelResourceRef(b.resource), package: b.package, filePath: b.filePath });
7504
+ }).sort((a, b) => a.resource.uri < b.resource.uri ? -1 : a.resource.uri > b.resource.uri ? 1 : 0);
7505
+ const history = root.rollback === void 0 ? void 0 : fields3(root.rollback, ["prior", "current"]);
7506
+ if (history && (!qualified.has(history.prior) || !qualified.has(history.current))) fail4("rollback-mismatch");
7507
+ const prior = history?.prior;
7508
+ const current = history?.current;
7509
+ const sourceFiles = copyFiles(sourceInput.files);
7510
+ const outputFiles = copyFiles(outputInput.files);
7511
+ check();
7512
+ const interrupted = new Promise((_resolve, reject) => {
7513
+ timer = setTimeout(() => reject(new ModelResourcePromotionError("deadline-exceeded")), Math.max(1, timeoutMs - (performance.now() - started)));
7514
+ onAbort = () => reject(new ModelResourcePromotionError("aborted"));
7515
+ if (signal) AbortSignal.prototype.addEventListener.call(signal, "abort", onAbort, { once: true });
7516
+ });
7517
+ const work = async () => {
7518
+ const source = await qualifyModelResourceManifest({ ...sourceMetadata, files: sourceFiles, signal, timeoutMs });
7519
+ check();
7520
+ const output = await qualifyModelTextureResources({ ...outputMetadata, files: outputFiles }, { signal, timeoutMs });
7521
+ check();
7522
+ const diagnostics = await qualifyModelConversionDiagnostics(rawDiagnostics, { signal, timeoutMs });
7523
+ check();
7524
+ assertImmutableAssetVersion(source.version);
7525
+ assertImmutableAssetVersion(output.manifest.version);
7526
+ if (identity(source) === identity(output.manifest) || !same(output.manifest, createModelAssetManifest({ ...promotionInput.manifest, files: [...promotionInput.manifest.files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0) })) || output.manifest.assetKind !== "model") fail4("package-mismatch");
7527
+ const converter = candidate.processingManifest.converter;
7528
+ if (!diagnostics.technicalAccepted || !same(diagnostics.source, candidate.provenance) || diagnostics.source.contentHash !== entry(source).sha256 || output.source.sourceContentHash !== entry(source).sha256 || converter.sourceContentHash !== entry(source).sha256 || converter.outputContentHash !== entry(output.manifest).sha256 || converter.id !== output.source.converterId || converter.version !== output.source.converterVersion || converter.sourceFormat !== output.source.sourceFormat || !same(diagnostics.converter.diagnostics, converter.diagnostics) || !same(diagnostics.converter.losses, converter.losses)) fail4("evidence-mismatch");
7529
+ const byUri = /* @__PURE__ */ new Map();
7530
+ for (const binding of bindings) {
7531
+ const file = (binding.package === "source" ? source : output.manifest).files.find((f) => f.path === binding.filePath);
7532
+ if (!file || byUri.has(binding.resource.uri) || file.sha256 !== binding.resource.sha256 || file.byteLength !== binding.resource.byteLength || file.contentType !== binding.resource.contentType) fail4("evidence-mismatch");
7533
+ byUri.set(binding.resource.uri, binding);
7534
+ }
7535
+ const requireResource = (resource, outputOnly = false) => {
7536
+ const binding = byUri.get(resource.uri);
7537
+ if (!binding || !same(binding.resource, resource) || outputOnly && binding.package !== "output") fail4("evidence-mismatch");
7538
+ return binding;
7539
+ };
7540
+ for (const d of diagnostics.diagnostics) if (d.resource) requireResource(d.resource);
7541
+ for (const lod of candidate.processingManifest.lods) requireResource(lod.resource, true);
7542
+ if (candidate.processingManifest.collision.resource) requireResource(candidate.processingManifest.collision.resource, true);
7543
+ for (const f of candidate.processingManifest.fidelityEvidence) if (f.evidenceResource) requireResource(f.evidenceResource, true);
7544
+ if (requireResource(candidate.processingManifest.lods[0].resource, true).filePath !== output.manifest.entrypoint) fail4("evidence-mismatch");
7545
+ if (candidate.assetRef.disposition === "existing" && (candidate.assetRef.asset.assetId !== output.manifest.assetId || candidate.assetRef.asset.version !== output.manifest.version)) fail4("evidence-mismatch");
7546
+ if (promotionInput.sourceAdapter !== output.manifest.sourceAdapter || promotionInput.reviewReport.passed !== true || promotionInput.reviewReport.findings.some((f) => f.severity === "blocking")) fail4("gate-blocked");
7547
+ const expectedSuffix = `/${output.manifest.assetId}/${output.manifest.version}/${output.manifest.entrypoint}`;
7548
+ const runtimeUri = new URL(promotionInput.runtimeManifestUri);
7549
+ if (!runtimeUri.pathname.endsWith(expectedSuffix)) fail4("evidence-mismatch");
7550
+ const promotion = await createGpuAssetPromotionRecord(promotionInput, new Map(outputFiles.map((f) => [f.path, f.bytes])));
7551
+ check();
7552
+ const closure = { contractVersion: 1, source, output, diagnostics, resourceBindings: Object.freeze(bindings), candidate, confirmation };
7553
+ const closureHash = await (0, import_gpu_shader4.computeSha256)(new TextEncoder().encode((0, import_gpu_shader4.canonicalizeGpuContract)({ contractVersion: 1, source, output, diagnostics, resourceBindings: bindings })));
7554
+ check();
7555
+ let rollback;
7556
+ if (promotion.outcome === "rolled-back") {
7557
+ if (!prior || !current || prior.promotion.assetId !== promotion.assetId || current.promotion.assetId !== promotion.assetId || prior.promotion.runtimeChannel !== promotion.runtimeChannel || current.promotion.runtimeChannel !== promotion.runtimeChannel || promotion.version !== prior.promotion.version || promotion.version === current.promotion.version || promotion.rollbackOfVersion !== current.promotion.version || closureHash !== prior.closureHash || !same(promotion.manifest, prior.promotion.manifest) || promotion.runtimeManifestUri !== prior.promotion.runtimeManifestUri) fail4("rollback-mismatch");
7558
+ rollback = Object.freeze({ fromVersion: current.promotion.version, fromClosureHash: current.closureHash, toVersion: prior.promotion.version, toClosureHash: prior.closureHash });
7559
+ } else if (history || promotion.rollbackOfVersion !== void 0) fail4("rollback-mismatch");
7560
+ const result = Object.freeze({ ...closure, promotion, closureHash, ...rollback ? { rollback } : {} });
7561
+ qualified.add(result);
7562
+ return result;
7563
+ };
7564
+ return await Promise.race([work(), interrupted]);
7565
+ } catch (error) {
7566
+ let code;
7567
+ try {
7568
+ code = Object.getOwnPropertyDescriptor(error, "code")?.value;
7569
+ } catch {
7570
+ return fail4();
7571
+ }
7572
+ if (["invalid-input", "limit-exceeded", "package-mismatch", "evidence-mismatch", "gate-blocked", "rollback-mismatch", "aborted", "deadline-exceeded"].includes(code)) return fail4(code);
7573
+ return fail4();
7208
7574
  } finally {
7209
7575
  if (timer !== void 0) clearTimeout(timer);
7210
7576
  if (signal && onAbort) AbortSignal.prototype.removeEventListener.call(signal, "abort", onAbort);
@@ -7391,7 +7757,7 @@ function createModelAssetManifest(input) {
7391
7757
  "screenshot",
7392
7758
  "metadata"
7393
7759
  ]);
7394
- const descriptor = (0, import_gpu_shader4.parseModelGpuCompatibilityDescriptor)({
7760
+ const descriptor = (0, import_gpu_shader5.parseModelGpuCompatibilityDescriptor)({
7395
7761
  modelId: base.assetId,
7396
7762
  version: base.version,
7397
7763
  gpuInterface: input.gpuInterface,
@@ -7421,7 +7787,7 @@ function createGpuInterfaceAssetManifest(input) {
7421
7787
  );
7422
7788
  assertAllowedFileRoles(base, ["gpu-interface-manifest"]);
7423
7789
  assertRoleCount(base, "gpu-interface-manifest", 1);
7424
- const gpuInterfaceManifest = (0, import_gpu_shader4.parseGpuInterfaceManifest)(input.gpuInterfaceManifest);
7790
+ const gpuInterfaceManifest = (0, import_gpu_shader5.parseGpuInterfaceManifest)(input.gpuInterfaceManifest);
7425
7791
  assertImmutableAssetVersion(gpuInterfaceManifest.interfaceVersion);
7426
7792
  assertDomainIdentity(
7427
7793
  base,
@@ -7444,7 +7810,7 @@ function createShaderAssetManifest(input) {
7444
7810
  );
7445
7811
  assertAllowedFileRoles(base, ["shader-manifest", "wgsl"]);
7446
7812
  assertRoleCount(base, "shader-manifest", 1);
7447
- const shaderManifest = (0, import_gpu_shader4.parseShaderVersionManifest)(input.shaderManifest);
7813
+ const shaderManifest = (0, import_gpu_shader5.parseShaderVersionManifest)(input.shaderManifest);
7448
7814
  assertShaderManifestVersions(shaderManifest);
7449
7815
  assertDomainIdentity(base, shaderManifest.shaderId, shaderManifest.version, "Shader");
7450
7816
  assertShaderModuleFiles(base.files, shaderManifest);
@@ -7463,7 +7829,7 @@ function createShaderStyleProfileAssetManifest(input) {
7463
7829
  );
7464
7830
  assertAllowedFileRoles(base, ["shader-style-profile-manifest"]);
7465
7831
  assertRoleCount(base, "shader-style-profile-manifest", 1);
7466
- const styleProfileManifest = (0, import_gpu_shader4.parseShaderStyleProfileManifest)(input.styleProfileManifest);
7832
+ const styleProfileManifest = (0, import_gpu_shader5.parseShaderStyleProfileManifest)(input.styleProfileManifest);
7467
7833
  assertStyleProfileManifestVersions(styleProfileManifest);
7468
7834
  assertDomainIdentity(base, styleProfileManifest.profileId, styleProfileManifest.version, "Style profile");
7469
7835
  return Object.freeze({ ...base, styleProfileManifest });
@@ -7527,8 +7893,8 @@ function createShaderValidationEvidenceRef(input) {
7527
7893
  sha256: assertSha256(attestation2.sha256, "Shader validation evidence attestation sha256")
7528
7894
  })
7529
7895
  };
7530
- const supportedUniversal = import_gpu_shader4.SUPPORTED_STABLE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
7531
- const supportedAdditive = import_gpu_shader4.SUPPORTED_ADDITIVE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
7896
+ const supportedUniversal = import_gpu_shader5.SUPPORTED_STABLE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
7897
+ const supportedAdditive = import_gpu_shader5.SUPPORTED_ADDITIVE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
7532
7898
  if (!supportedUniversal && !supportedAdditive) {
7533
7899
  throw new Error("Shader validation evidence must target a supported WebGPU matrix policy.");
7534
7900
  }
@@ -7539,7 +7905,7 @@ function createShaderValidationEvidenceRef(input) {
7539
7905
  }
7540
7906
  async function createGpuInterfaceRef(input) {
7541
7907
  assertImmutableAssetVersion(input.manifest.interfaceVersion);
7542
- const manifest = (0, import_gpu_shader4.parseGpuInterfaceManifest)(input.manifest);
7908
+ const manifest = (0, import_gpu_shader5.parseGpuInterfaceManifest)(input.manifest);
7543
7909
  assertAssetId(manifest.interfaceId);
7544
7910
  assertImmutableAssetVersion(manifest.interfaceVersion);
7545
7911
  const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "GPU interface");
@@ -7554,7 +7920,7 @@ async function createGpuInterfaceRef(input) {
7554
7920
  }
7555
7921
  async function createShaderVersionRef(input) {
7556
7922
  assertImmutableAssetVersion(input.manifest.version);
7557
- const manifest = (0, import_gpu_shader4.parseShaderVersionManifest)(input.manifest);
7923
+ const manifest = (0, import_gpu_shader5.parseShaderVersionManifest)(input.manifest);
7558
7924
  assertAssetId(manifest.shaderId);
7559
7925
  assertShaderManifestVersions(manifest);
7560
7926
  const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Shader");
@@ -7567,7 +7933,7 @@ async function createShaderVersionRef(input) {
7567
7933
  }
7568
7934
  async function createShaderStyleProfileRef(input) {
7569
7935
  assertImmutableAssetVersion(input.manifest.version);
7570
- const manifest = (0, import_gpu_shader4.parseShaderStyleProfileManifest)(input.manifest);
7936
+ const manifest = (0, import_gpu_shader5.parseShaderStyleProfileManifest)(input.manifest);
7571
7937
  assertAssetId(manifest.profileId);
7572
7938
  assertStyleProfileManifestVersions(manifest);
7573
7939
  const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Style profile");
@@ -7612,7 +7978,7 @@ async function validateGpuAssetFiles(input) {
7612
7978
  if (bytes.byteLength !== file.byteLength) {
7613
7979
  throw new Error(`GPU asset file ${file.path} byte length differs from its descriptor.`);
7614
7980
  }
7615
- if (await (0, import_gpu_shader4.computeSha256)(bytes) !== file.sha256) {
7981
+ if (await (0, import_gpu_shader5.computeSha256)(bytes) !== file.sha256) {
7616
7982
  throw new Error(`GPU asset file ${file.path} digest differs from its descriptor.`);
7617
7983
  }
7618
7984
  if (file.contentType === ASSET_JSON_CONTENT_TYPE) {
@@ -7631,13 +7997,13 @@ async function validateGpuAssetFiles(input) {
7631
7997
  }
7632
7998
  const expectedDomainManifest = domainManifestForAsset(manifest);
7633
7999
  if (expectedDomainManifest !== null) {
7634
- const parsedEntrypoint = (0, import_gpu_shader4.parseJsonBytes)(entrypointBytes, "GPU asset manifest entrypoint");
7635
- if ((0, import_gpu_shader4.canonicalizeGpuContract)(parsedEntrypoint) !== (0, import_gpu_shader4.canonicalizeGpuContract)(expectedDomainManifest)) {
8000
+ const parsedEntrypoint = (0, import_gpu_shader5.parseJsonBytes)(entrypointBytes, "GPU asset manifest entrypoint");
8001
+ if ((0, import_gpu_shader5.canonicalizeGpuContract)(parsedEntrypoint) !== (0, import_gpu_shader5.canonicalizeGpuContract)(expectedDomainManifest)) {
7636
8002
  throw new Error("GPU asset entrypoint bytes differ from the validated domain manifest.");
7637
8003
  }
7638
8004
  } else if (manifest.assetKind === "shader-validation-evidence") {
7639
8005
  assertEvidenceEntrypointIdentity(
7640
- (0, import_gpu_shader4.parseJsonBytes)(entrypointBytes, "Shader validation evidence entrypoint"),
8006
+ (0, import_gpu_shader5.parseJsonBytes)(entrypointBytes, "Shader validation evidence entrypoint"),
7641
8007
  manifest.validationEvidence
7642
8008
  );
7643
8009
  }
@@ -7861,7 +8227,7 @@ function assertTypedRuntimeManifestUri(manifest, value) {
7861
8227
  }
7862
8228
  function assertEvidenceEntrypointIdentity(value, ref2) {
7863
8229
  const evidence = assertRecord2(value, "Shader validation evidence entrypoint");
7864
- if (evidence.contractVersion !== import_gpu_shader4.SHADER_VALIDATION_EVIDENCE_VERSION || evidence.evidenceId !== ref2.evidenceId || evidence.status !== "passed") {
8230
+ if (evidence.contractVersion !== import_gpu_shader5.SHADER_VALIDATION_EVIDENCE_VERSION || evidence.evidenceId !== ref2.evidenceId || evidence.status !== "passed") {
7865
8231
  throw new Error("Shader validation evidence bytes do not identify passing declared evidence.");
7866
8232
  }
7867
8233
  const matrixRef = assertRecord2(evidence.matrixRef, "Shader validation evidence matrixRef");
@@ -7877,9 +8243,9 @@ function assertEvidenceEntrypointIdentity(value, ref2) {
7877
8243
  }
7878
8244
  }
7879
8245
  function assertCanonicalJsonBytes(bytes, fieldName) {
7880
- const value = (0, import_gpu_shader4.parseJsonBytes)(bytes, fieldName);
8246
+ const value = (0, import_gpu_shader5.parseJsonBytes)(bytes, fieldName);
7881
8247
  const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
7882
- if ((0, import_gpu_shader4.canonicalizeGpuContract)(value) !== text) {
8248
+ if ((0, import_gpu_shader5.canonicalizeGpuContract)(value) !== text) {
7883
8249
  throw new Error(`${fieldName} must use canonical JSON serialization.`);
7884
8250
  }
7885
8251
  }
@@ -7888,11 +8254,11 @@ async function bindDomainManifestBytes(bytes, manifest, fieldName) {
7888
8254
  throw new Error(`${fieldName} manifest bytes must be a Uint8Array.`);
7889
8255
  }
7890
8256
  assertCanonicalJsonBytes(bytes, `${fieldName} manifest`);
7891
- const parsed = (0, import_gpu_shader4.parseJsonBytes)(bytes, `${fieldName} manifest`);
7892
- if ((0, import_gpu_shader4.canonicalizeGpuContract)(parsed) !== (0, import_gpu_shader4.canonicalizeGpuContract)(manifest)) {
8257
+ const parsed = (0, import_gpu_shader5.parseJsonBytes)(bytes, `${fieldName} manifest`);
8258
+ if ((0, import_gpu_shader5.canonicalizeGpuContract)(parsed) !== (0, import_gpu_shader5.canonicalizeGpuContract)(manifest)) {
7893
8259
  throw new Error(`${fieldName} manifest bytes differ from the supplied manifest.`);
7894
8260
  }
7895
- return (0, import_gpu_shader4.computeSha256)(bytes);
8261
+ return (0, import_gpu_shader5.computeSha256)(bytes);
7896
8262
  }
7897
8263
  function assertRoleCount(manifest, role, expectedCount) {
7898
8264
  if (manifest.files.filter((file) => file.role === role).length !== expectedCount) {
@@ -8014,10 +8380,14 @@ function assertRequiredString(value, fieldName) {
8014
8380
  MODEL_RESOLUTION_V2_JSON_SCHEMAS,
8015
8381
  MODEL_RESOLUTION_V2_STATES,
8016
8382
  MODEL_RESOURCE_MANIFEST_LIMITS,
8383
+ MODEL_RESOURCE_PROMOTION_LIMITS,
8017
8384
  MODEL_RIGHTS_STATUSES,
8385
+ MODEL_TEXTURE_RESOURCE_LIMITS,
8018
8386
  MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
8019
8387
  ModelConversionDiagnosticsError,
8020
8388
  ModelResourceManifestError,
8389
+ ModelResourcePromotionError,
8390
+ ModelTextureResourceError,
8021
8391
  PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
8022
8392
  PVOX_ASSET_MANIFEST_VERSION,
8023
8393
  PVOX_BINARY_CLOSURE_HASH_DOMAIN,
@@ -8152,6 +8522,7 @@ function assertRequiredString(value, fieldName) {
8152
8522
  createModelRequestSpecV2,
8153
8523
  createModelResolution,
8154
8524
  createModelResolutionV2,
8525
+ createModelResourcePromotionRecord,
8155
8526
  createModelResourceRef,
8156
8527
  createModelRightsAssessment,
8157
8528
  createModelTechnicalProfile,
@@ -8210,6 +8581,7 @@ function assertRequiredString(value, fieldName) {
8210
8581
  projectPvoxNamedJsonClosureV1,
8211
8582
  qualifyModelConversionDiagnostics,
8212
8583
  qualifyModelResourceManifest,
8584
+ qualifyModelTextureResources,
8213
8585
  toLegacyModelResolutionState,
8214
8586
  validateGpuAssetFiles
8215
8587
  });