@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/CHANGELOG.md +36 -0
- package/README.md +114 -2
- package/dist/index.cjs +474 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +131 -2
- package/dist/index.d.ts +131 -2
- package/dist/index.js +453 -87
- package/dist/index.js.map +1 -1
- package/docs/adrs/adr-0011-reloadable-texture-resource-roles.md +92 -0
- package/docs/adrs/adr-0012-model-resource-promotion-closure.md +92 -0
- package/docs/adrs/index.md +2 -0
- package/docs/tdrs/index.md +2 -0
- package/docs/tdrs/tdr-0006-reloadable-texture-resource-roles.md +91 -0
- package/docs/tdrs/tdr-0007-model-resource-promotion-closure.md +63 -0
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/model-resource-promotion.ts +219 -0
- package/src/model-texture-resources.ts +215 -0
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: () =>
|
|
47
|
-
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
|
|
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
|
|
452
|
-
if (!TOKEN_PATTERN.test(
|
|
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
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
3802
|
+
const entries = entriesInput.map((entry2, index) => {
|
|
3797
3803
|
const entryName = `${fieldName}.entries[${index}]`;
|
|
3798
|
-
const item = asRecord(
|
|
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((
|
|
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((
|
|
3950
|
+
const pageHashes = pageEntries.map((entry2, index) => {
|
|
3945
3951
|
const entryName = `${fieldName}.pageHashes[${index}]`;
|
|
3946
|
-
const page = asRecord(
|
|
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((
|
|
4003
|
+
const patches = patchEntries.map((entry2, index) => {
|
|
3998
4004
|
const fieldName = `PvoxEditJournal.patches[${index}]`;
|
|
3999
|
-
const patch = asRecord(
|
|
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
|
|
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(
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
4515
|
+
const parsed = entries.map((entry2, index) => {
|
|
4510
4516
|
const fieldName = `ModelCandidateV2.views[${index}]`;
|
|
4511
|
-
const record2 = asRecord(
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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((
|
|
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
|
|
5182
|
-
const
|
|
5183
|
-
const
|
|
5184
|
-
if (
|
|
5185
|
-
byteLength =
|
|
5186
|
-
const buffer =
|
|
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
|
|
6957
|
-
for (const key of Object.keys(
|
|
6958
|
-
return Object.freeze(
|
|
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((
|
|
7036
|
+
uniquePaths(entries.map((entry2) => path(entry2.path)));
|
|
7031
7037
|
const ids = /* @__PURE__ */ new Set();
|
|
7032
|
-
for (const
|
|
7033
|
-
if (Object.keys(
|
|
7034
|
-
ids.add(
|
|
7035
|
-
const descriptor = descriptors.get(
|
|
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 (
|
|
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
|
|
7256
|
+
function fail3(code = "invalid-input") {
|
|
7078
7257
|
throw new ModelConversionDiagnosticsError(code);
|
|
7079
7258
|
}
|
|
7080
|
-
function
|
|
7081
|
-
if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
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)
|
|
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))
|
|
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)
|
|
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)))
|
|
7273
|
+
if (Object.keys(record2).some((key) => !keys.includes(key))) fail3();
|
|
7095
7274
|
}
|
|
7096
|
-
function
|
|
7097
|
-
if (--budget.values < 0 || depth > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.depth)
|
|
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)
|
|
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)
|
|
7107
|
-
if (Reflect.ownKeys(value).length !== value.length + 1)
|
|
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)
|
|
7112
|
-
result.push(
|
|
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 =
|
|
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)
|
|
7120
|
-
record2[key] =
|
|
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 =
|
|
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)
|
|
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))
|
|
7141
|
-
if (performance.now() - start >= timeoutMs)
|
|
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 =
|
|
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))
|
|
7325
|
+
if (!["completed", "unsupported", "failed"].includes(args.outcome)) fail3();
|
|
7147
7326
|
const source = createModelProvenance(args.source);
|
|
7148
|
-
const reportInput =
|
|
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)
|
|
7151
|
-
if (!Array.isArray(args.resources) || args.resources.length > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.resources)
|
|
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 =
|
|
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))
|
|
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
|
|
7160
|
-
if (identities.has(resource.uri) && identities.get(resource.uri) !==
|
|
7161
|
-
identities.set(resource.uri,
|
|
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)
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
7531
|
-
const supportedAdditive =
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
7635
|
-
if ((0,
|
|
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,
|
|
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 !==
|
|
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,
|
|
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,
|
|
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,
|
|
7892
|
-
if ((0,
|
|
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,
|
|
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
|
});
|