@plasius/asset-contracts 0.7.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 +18 -0
- package/README.md +49 -3
- package/dist/index.cjs +269 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -2
- package/dist/index.d.ts +68 -2
- package/dist/index.js +251 -58
- package/dist/index.js.map +1 -1
- package/docs/adrs/adr-0012-model-resource-promotion-closure.md +92 -0
- package/docs/adrs/index.md +1 -0
- package/docs/tdrs/index.md +1 -0
- package/docs/tdrs/tdr-0007-model-resource-promotion-closure.md +63 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/model-resource-promotion.ts +219 -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,11 +64,13 @@ __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,
|
|
68
69
|
MODEL_TEXTURE_RESOURCE_LIMITS: () => MODEL_TEXTURE_RESOURCE_LIMITS,
|
|
69
70
|
MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE: () => MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
|
|
70
71
|
ModelConversionDiagnosticsError: () => ModelConversionDiagnosticsError,
|
|
71
72
|
ModelResourceManifestError: () => ModelResourceManifestError,
|
|
73
|
+
ModelResourcePromotionError: () => ModelResourcePromotionError,
|
|
72
74
|
ModelTextureResourceError: () => ModelTextureResourceError,
|
|
73
75
|
PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN: () => PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
|
|
74
76
|
PVOX_ASSET_MANIFEST_VERSION: () => PVOX_ASSET_MANIFEST_VERSION,
|
|
@@ -204,6 +206,7 @@ __export(index_exports, {
|
|
|
204
206
|
createModelRequestSpecV2: () => createModelRequestSpecV2,
|
|
205
207
|
createModelResolution: () => createModelResolution,
|
|
206
208
|
createModelResolutionV2: () => createModelResolutionV2,
|
|
209
|
+
createModelResourcePromotionRecord: () => createModelResourcePromotionRecord,
|
|
207
210
|
createModelResourceRef: () => createModelResourceRef,
|
|
208
211
|
createModelRightsAssessment: () => createModelRightsAssessment,
|
|
209
212
|
createModelTechnicalProfile: () => createModelTechnicalProfile,
|
|
@@ -267,7 +270,7 @@ __export(index_exports, {
|
|
|
267
270
|
validateGpuAssetFiles: () => validateGpuAssetFiles
|
|
268
271
|
});
|
|
269
272
|
module.exports = __toCommonJS(index_exports);
|
|
270
|
-
var
|
|
273
|
+
var import_gpu_shader5 = require("@plasius/gpu-shader");
|
|
271
274
|
|
|
272
275
|
// src/asset-version.ts
|
|
273
276
|
var ASSET_VERSION_PATTERN = /^[0-9A-Za-z][0-9A-Za-z._-]{0,127}$/u;
|
|
@@ -3052,7 +3055,7 @@ function requireEnum(value, allowed, fieldName) {
|
|
|
3052
3055
|
}
|
|
3053
3056
|
function requireReasonCodes(value, fieldName) {
|
|
3054
3057
|
const entries = asDenseArray(value, fieldName, 0, 32);
|
|
3055
|
-
const result = entries.map((
|
|
3058
|
+
const result = entries.map((entry2, index) => requireReasonCode(entry2, `${fieldName}[${index}]`));
|
|
3056
3059
|
if (new Set(result).size !== result.length) throw new Error(`${fieldName} must not contain duplicates.`);
|
|
3057
3060
|
return result;
|
|
3058
3061
|
}
|
|
@@ -3374,7 +3377,7 @@ function createPvoxRuntimeRequestProfileV1(value) {
|
|
|
3374
3377
|
const record2 = asRecord(value, fieldName);
|
|
3375
3378
|
exactKeys(record2, ["profileId", "fidelityProfileId", "capabilityProfileId", "geometryMode", "requiredCapabilities", "limits"], fieldName);
|
|
3376
3379
|
const requestedCapabilityEntries = asDenseArray(record2.requiredCapabilities, `${fieldName}.requiredCapabilities`, 1, PVOX_CAPABILITIES.length);
|
|
3377
|
-
const requiredCapabilities = requestedCapabilityEntries.map((
|
|
3380
|
+
const requiredCapabilities = requestedCapabilityEntries.map((entry2, index) => requireEnum(entry2, PVOX_CAPABILITIES, `${fieldName}.requiredCapabilities[${index}]`));
|
|
3378
3381
|
if (new Set(requiredCapabilities).size !== requiredCapabilities.length) {
|
|
3379
3382
|
throw new Error(`${fieldName}.requiredCapabilities must not contain duplicates.`);
|
|
3380
3383
|
}
|
|
@@ -3537,7 +3540,7 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3537
3540
|
}
|
|
3538
3541
|
if (expectedOffset !== artifact.byteLength) throw new Error("PvoxAssetManifestV1 pages must exactly cover the artifact bytes.");
|
|
3539
3542
|
const lodEntries = asDenseArray(record2.lods, "PvoxAssetManifestV1.lods", 1, 4);
|
|
3540
|
-
const lods = lodEntries.map((
|
|
3543
|
+
const lods = lodEntries.map((entry2, index) => createLod(entry2, index, pages.length));
|
|
3541
3544
|
for (const [index, current] of lods.entries()) {
|
|
3542
3545
|
if (current.level !== index) throw new Error("PvoxAssetManifestV1 LOD levels must be contiguous from LOD0.");
|
|
3543
3546
|
const previous = lods[index - 1];
|
|
@@ -3773,7 +3776,7 @@ function createPhysicalPropertyEvidence(input) {
|
|
|
3773
3776
|
const reviewedBy = record2.reviewedBy === void 0 ? void 0 : requireToken(record2.reviewedBy, "PhysicalPropertyEvidence.reviewedBy");
|
|
3774
3777
|
const reviewedAt = record2.reviewedAt === void 0 ? void 0 : requireTimestamp(record2.reviewedAt, "PhysicalPropertyEvidence.reviewedAt");
|
|
3775
3778
|
const reviewToken = record2.reviewToken === void 0 ? void 0 : requireAttestation(record2.reviewToken, "PhysicalPropertyEvidence.reviewToken");
|
|
3776
|
-
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.");
|
|
3777
3780
|
if (provenance === "inferred" && reviewedBy === void 0) throw new Error("Inferred PhysicalPropertyEvidence requires human review.");
|
|
3778
3781
|
return deepFreeze2({
|
|
3779
3782
|
property,
|
|
@@ -3796,9 +3799,9 @@ function createPvoxPhysicalRegionInventoryV1(input) {
|
|
|
3796
3799
|
exactKeys(record2, ["inventoryVersion", "subjectContentHash", "inventoryHash", "inventoryHashAttestation", "validationEvidenceHash", "entries"], fieldName);
|
|
3797
3800
|
if (record2.inventoryVersion !== PVOX_PHYSICAL_REGION_INVENTORY_VERSION) throw new Error(`${fieldName}.inventoryVersion is unsupported.`);
|
|
3798
3801
|
const entriesInput = asDenseArray(record2.entries, `${fieldName}.entries`, 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions);
|
|
3799
|
-
const entries = entriesInput.map((
|
|
3802
|
+
const entries = entriesInput.map((entry2, index) => {
|
|
3800
3803
|
const entryName = `${fieldName}.entries[${index}]`;
|
|
3801
|
-
const item = asRecord(
|
|
3804
|
+
const item = asRecord(entry2, entryName);
|
|
3802
3805
|
exactKeys(item, ["regionIndex", "physicalPaletteIndex", "regionId", "materialId"], entryName);
|
|
3803
3806
|
return {
|
|
3804
3807
|
regionIndex: requireInteger(item.regionIndex, `${entryName}.regionIndex`, 0, PVOX_DEFAULT_LIMITS.maximumMaterialRegions - 1),
|
|
@@ -3807,7 +3810,7 @@ function createPvoxPhysicalRegionInventoryV1(input) {
|
|
|
3807
3810
|
materialId: requireToken(item.materialId, `${entryName}.materialId`)
|
|
3808
3811
|
};
|
|
3809
3812
|
});
|
|
3810
|
-
if (entries.some((
|
|
3813
|
+
if (entries.some((entry2, index) => entry2.regionIndex !== index)) throw new Error(`${fieldName} regionIndex values must be contiguous and canonically ordered.`);
|
|
3811
3814
|
const pairs = entries.map(({ regionId, materialId }) => `${regionId}\0${materialId}`);
|
|
3812
3815
|
if (new Set(pairs).size !== pairs.length) throw new Error(`${fieldName} region/material pairs must be unique.`);
|
|
3813
3816
|
const inventoryHash = requireSha256(record2.inventoryHash, `${fieldName}.inventoryHash`);
|
|
@@ -3944,9 +3947,9 @@ function createPvoxEditJournalCurrentState(input, baseManifest) {
|
|
|
3944
3947
|
const record2 = asRecord(input, fieldName);
|
|
3945
3948
|
exactKeys(record2, ["baseContentHash", "basePageSetHash", "placementId", "gridVersion", "revision", "rootHash", "latestJournalHash", "pageHashes"], fieldName);
|
|
3946
3949
|
const pageEntries = asDenseArray(record2.pageHashes, `${fieldName}.pageHashes`, baseManifest.pages.length, baseManifest.pages.length);
|
|
3947
|
-
const pageHashes = pageEntries.map((
|
|
3950
|
+
const pageHashes = pageEntries.map((entry2, index) => {
|
|
3948
3951
|
const entryName = `${fieldName}.pageHashes[${index}]`;
|
|
3949
|
-
const page = asRecord(
|
|
3952
|
+
const page = asRecord(entry2, entryName);
|
|
3950
3953
|
exactKeys(page, ["pageIndex", "sha256"], entryName);
|
|
3951
3954
|
const pageIndex = requireInteger(page.pageIndex, `${entryName}.pageIndex`, 0, baseManifest.pages.length - 1);
|
|
3952
3955
|
if (pageIndex !== index) throw new Error(`${fieldName}.pageHashes must be complete and ordered by pageIndex.`);
|
|
@@ -3997,9 +4000,9 @@ function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
|
3997
4000
|
}
|
|
3998
4001
|
}
|
|
3999
4002
|
const patchEntries = asDenseArray(record2.patches, "PvoxEditJournal.patches", 1, PVOX_MAX_EDIT_PATCHES);
|
|
4000
|
-
const patches = patchEntries.map((
|
|
4003
|
+
const patches = patchEntries.map((entry2, index) => {
|
|
4001
4004
|
const fieldName = `PvoxEditJournal.patches[${index}]`;
|
|
4002
|
-
const patch = asRecord(
|
|
4005
|
+
const patch = asRecord(entry2, fieldName);
|
|
4003
4006
|
exactKeys(patch, ["fieldKind", "lodLevel", "partitionIndex", "hierarchyDepth", "mortonCode", "expectedPageIndex", "expectedPageHash", "operation", "expectedBrickHash", "resultingBrickHash", "resultingPageHash"], fieldName);
|
|
4004
4007
|
const mortonCode = requireString(patch.mortonCode, `${fieldName}.mortonCode`, 16);
|
|
4005
4008
|
if (!MORTON_CODE.test(mortonCode)) throw new Error(`${fieldName}.mortonCode must be a canonical Morton code.`);
|
|
@@ -4076,7 +4079,7 @@ function createPvoxEditJournalChain(input, baseManifestInput) {
|
|
|
4076
4079
|
const journals = [];
|
|
4077
4080
|
const operationIds = /* @__PURE__ */ new Set();
|
|
4078
4081
|
const currentPageHashes = new Map(baseManifest.pages.map(({ pageIndex, sha256: sha2562 }) => [pageIndex, sha2562]));
|
|
4079
|
-
for (const
|
|
4082
|
+
for (const entry2 of entries) {
|
|
4080
4083
|
const previous = journals.at(-1);
|
|
4081
4084
|
const currentState = previous === void 0 ? void 0 : {
|
|
4082
4085
|
baseContentHash: previous.baseContentHash,
|
|
@@ -4088,7 +4091,7 @@ function createPvoxEditJournalChain(input, baseManifestInput) {
|
|
|
4088
4091
|
latestJournalHash: previous.journalHash,
|
|
4089
4092
|
pageHashes: baseManifest.pages.map(({ pageIndex }) => ({ pageIndex, sha256: currentPageHashes.get(pageIndex) }))
|
|
4090
4093
|
};
|
|
4091
|
-
const journal = createPvoxEditJournal(
|
|
4094
|
+
const journal = createPvoxEditJournal(entry2, baseManifest, currentState);
|
|
4092
4095
|
if (previous === void 0 && (journal.expectedRevision !== 0 || journal.previousJournalHash !== PVOX_EDIT_JOURNAL_GENESIS_HASH)) {
|
|
4093
4096
|
throw new Error("PvoxEditJournalChain must begin at the canonical genesis revision.");
|
|
4094
4097
|
}
|
|
@@ -4146,10 +4149,10 @@ function createTransform2(value, fieldName) {
|
|
|
4146
4149
|
exactKeys(record2, ["translationMetres", "rotationQuaternion", "scale"], fieldName);
|
|
4147
4150
|
const translationMetres = requireVector(record2.translationMetres, `${fieldName}.translationMetres`);
|
|
4148
4151
|
const rotationEntries = asDenseArray(record2.rotationQuaternion, `${fieldName}.rotationQuaternion`, 4, 4);
|
|
4149
|
-
const rotationQuaternion = rotationEntries.map((
|
|
4152
|
+
const rotationQuaternion = rotationEntries.map((entry2, index) => requireNumber(entry2, `${fieldName}.rotationQuaternion[${index}]`, -1, 1));
|
|
4150
4153
|
if (Math.abs(Math.hypot(...rotationQuaternion) - 1) > 1e-4) throw new Error(`${fieldName}.rotationQuaternion must be normalized.`);
|
|
4151
4154
|
const scale = requireVector(record2.scale, `${fieldName}.scale`);
|
|
4152
|
-
if (scale.some((
|
|
4155
|
+
if (scale.some((entry2) => entry2 <= 0)) throw new Error(`${fieldName}.scale values must be positive.`);
|
|
4153
4156
|
return { translationMetres, rotationQuaternion, scale };
|
|
4154
4157
|
}
|
|
4155
4158
|
function createAssemblyChild2(value, index, resolutionId, candidateId) {
|
|
@@ -4317,10 +4320,10 @@ function createModelProcessingManifestV2(input) {
|
|
|
4317
4320
|
}
|
|
4318
4321
|
const capabilityEntries = asDenseArray(record2.capabilities, "ModelProcessingManifestV2.capabilities", PVOX_CAPABILITIES.length, PVOX_CAPABILITIES.length);
|
|
4319
4322
|
const capabilities = capabilityEntries.map(createVoxelCapabilityAssessment);
|
|
4320
|
-
const byCapability = new Map(capabilities.map((
|
|
4323
|
+
const byCapability = new Map(capabilities.map((entry2) => [entry2.capability, entry2]));
|
|
4321
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.");
|
|
4322
4325
|
const orderedCapabilities = PVOX_CAPABILITIES.map((name) => byCapability.get(name));
|
|
4323
|
-
if (orderedCapabilities.some((
|
|
4326
|
+
if (orderedCapabilities.some((entry2) => entry2.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 capability evidence must bind the binaryClosureHash.");
|
|
4324
4327
|
const capabilityEvidenceSetHash = requireSha256(record2.capabilityEvidenceSetHash, "ModelProcessingManifestV2.capabilityEvidenceSetHash");
|
|
4325
4328
|
const capabilityEvidenceSetAttestation = createHashAttestation(
|
|
4326
4329
|
record2.capabilityEvidenceSetAttestation,
|
|
@@ -4329,7 +4332,7 @@ function createModelProcessingManifestV2(input) {
|
|
|
4329
4332
|
"ModelProcessingManifestV2.capabilityEvidenceSetAttestation"
|
|
4330
4333
|
);
|
|
4331
4334
|
const requiredCapabilityEntries = asDenseArray(record2.requiredCapabilities, "ModelProcessingManifestV2.requiredCapabilities", 1, PVOX_CAPABILITIES.length);
|
|
4332
|
-
const requiredCapabilities = requiredCapabilityEntries.map((
|
|
4335
|
+
const requiredCapabilities = requiredCapabilityEntries.map((entry2, index) => requireEnum(entry2, PVOX_CAPABILITIES, `ModelProcessingManifestV2.requiredCapabilities[${index}]`));
|
|
4333
4336
|
if (new Set(requiredCapabilities).size !== requiredCapabilities.length) throw new Error("ModelProcessingManifestV2.requiredCapabilities must be unique.");
|
|
4334
4337
|
if (!requiredCapabilities.includes("rendering")) throw new Error("ModelProcessingManifestV2 rendering capability is always required.");
|
|
4335
4338
|
if (technicalProfile.hasCollision !== (byCapability.get("collision")?.status === "supported")) throw new Error("ModelProcessingManifestV2 collision capability must match technicalProfile.hasCollision.");
|
|
@@ -4342,9 +4345,9 @@ function createModelProcessingManifestV2(input) {
|
|
|
4342
4345
|
}
|
|
4343
4346
|
const physicalEntries = asDenseArray(record2.physicalProperties, "ModelProcessingManifestV2.physicalProperties", 0, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries);
|
|
4344
4347
|
const physicalProperties = physicalEntries.map(createPhysicalPropertyEvidence);
|
|
4345
|
-
const byRegionProperty = new Map(physicalProperties.map((
|
|
4348
|
+
const byRegionProperty = new Map(physicalProperties.map((entry2) => [`${entry2.regionId}\0${entry2.materialId}\0${entry2.property}`, entry2]));
|
|
4346
4349
|
if (byRegionProperty.size !== physicalProperties.length) throw new Error("ModelProcessingManifestV2 physical properties must be unique per region, material, and property.");
|
|
4347
|
-
if (physicalProperties.some((
|
|
4350
|
+
if (physicalProperties.some((entry2) => entry2.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 physical evidence must bind the PVOX binary closure subject.");
|
|
4348
4351
|
const authoritativePairs = physicalRegionInventory.entries.map(({ regionId, materialId }) => `${regionId}\0${materialId}`);
|
|
4349
4352
|
const authoritativePairSet = new Set(authoritativePairs);
|
|
4350
4353
|
if (physicalProperties.some(({ regionId, materialId }) => !authoritativePairSet.has(`${regionId}\0${materialId}`))) {
|
|
@@ -4426,7 +4429,7 @@ function createModelProcessingManifestV2(input) {
|
|
|
4426
4429
|
throw new Error("ModelProcessingManifestV2 fidelity evidence must exactly match every governed PVOX LOD0 metric.");
|
|
4427
4430
|
}
|
|
4428
4431
|
const childEntries = asDenseArray(record2.children, "ModelProcessingManifestV2.children", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
|
|
4429
|
-
const children = childEntries.map((
|
|
4432
|
+
const children = childEntries.map((entry2, index) => createAssemblyChild2(entry2, index, resolutionId, candidateId));
|
|
4430
4433
|
const kind = requireEnum(record2.kind, ["leaf", "assembly"], "ModelProcessingManifestV2.kind");
|
|
4431
4434
|
if (kind === "leaf" && children.length !== 0) throw new Error("A leaf ModelProcessingManifestV2 cannot contain children.");
|
|
4432
4435
|
if (kind === "leaf" && technicalProfile.partitionCount !== 1) throw new Error("A leaf ModelProcessingManifestV2 must have one partition.");
|
|
@@ -4509,9 +4512,9 @@ function createCandidateAssetRef2(value) {
|
|
|
4509
4512
|
}
|
|
4510
4513
|
function createViews(value, resolutionId, candidateId) {
|
|
4511
4514
|
const entries = asDenseArray(value, "ModelCandidateV2.views", 4, 4);
|
|
4512
|
-
const parsed = entries.map((
|
|
4515
|
+
const parsed = entries.map((entry2, index) => {
|
|
4513
4516
|
const fieldName = `ModelCandidateV2.views[${index}]`;
|
|
4514
|
-
const record2 = asRecord(
|
|
4517
|
+
const record2 = asRecord(entry2, fieldName);
|
|
4515
4518
|
exactKeys(record2, ["kind", "imageUri", "sha256", "contentType", "width", "height"], fieldName);
|
|
4516
4519
|
const kind = requireEnum(record2.kind, MODEL_CONFIRMATION_VIEW_KINDS, `${fieldName}.kind`);
|
|
4517
4520
|
const expectedUri = `mcp://models/resolutions/${resolutionId}/candidates/${candidateId}/${kind}.png`;
|
|
@@ -4519,7 +4522,7 @@ function createViews(value, resolutionId, candidateId) {
|
|
|
4519
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.`);
|
|
4520
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 };
|
|
4521
4524
|
});
|
|
4522
|
-
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.");
|
|
4523
4526
|
return parsed;
|
|
4524
4527
|
}
|
|
4525
4528
|
function createRenderEvidence2(value) {
|
|
@@ -4718,7 +4721,7 @@ function createModelCandidateV2(input) {
|
|
|
4718
4721
|
);
|
|
4719
4722
|
const gateEntries = asDenseArray(record2.hardGates, "ModelCandidateV2.hardGates", PVOX_NON_OVERRIDABLE_GATE_KINDS.length, PVOX_NON_OVERRIDABLE_GATE_KINDS.length);
|
|
4720
4723
|
const gates = gateEntries.map(createPvoxGateEvidence);
|
|
4721
|
-
const byGate = new Map(gates.map((
|
|
4724
|
+
const byGate = new Map(gates.map((entry2) => [entry2.kind, entry2]));
|
|
4722
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.");
|
|
4723
4726
|
const orderedGates = PVOX_NON_OVERRIDABLE_GATE_KINDS.map((kind) => byGate.get(kind));
|
|
4724
4727
|
const concreteGateHashes = {
|
|
@@ -4741,7 +4744,7 @@ function createModelCandidateV2(input) {
|
|
|
4741
4744
|
...!match.hardConstraintPass ? ["semantic-hard-constraints-failed"] : [],
|
|
4742
4745
|
...!["allowed", "attribution-required"].includes(rights.status) ? [`rights:${rights.status}`] : [],
|
|
4743
4746
|
...processingManifest.requiredCapabilities.flatMap((capability) => {
|
|
4744
|
-
const evidence = processingManifest.capabilities.find((
|
|
4747
|
+
const evidence = processingManifest.capabilities.find((entry2) => entry2.capability === capability);
|
|
4745
4748
|
return evidence.status === "supported" ? [] : [`capability:${capability}:${evidence.status}`];
|
|
4746
4749
|
}),
|
|
4747
4750
|
...orderedGates.flatMap((gate) => gate.outcome === "passed" ? [] : gate.reasonCodes.map((reason) => `${gate.kind}:${reason}`))
|
|
@@ -4769,7 +4772,7 @@ function createModelCandidateV2(input) {
|
|
|
4769
4772
|
if (record2.confirmationRequired !== false) throw new Error("Diagnostic ModelCandidateV2 confirmationRequired must be false.");
|
|
4770
4773
|
if (record2.blockingReasonCodes !== void 0) {
|
|
4771
4774
|
const suppliedEntries = asDenseArray(record2.blockingReasonCodes, "ModelCandidateV2.blockingReasonCodes", 1, 256);
|
|
4772
|
-
const suppliedBlockingReasons = suppliedEntries.map((
|
|
4775
|
+
const suppliedBlockingReasons = suppliedEntries.map((entry2, index) => requireReasonCode(entry2, `ModelCandidateV2.blockingReasonCodes[${index}]`));
|
|
4773
4776
|
if (JSON.stringify(suppliedBlockingReasons) !== JSON.stringify(blockingReasonCodes)) throw new Error("Diagnostic ModelCandidateV2 blockingReasonCodes must be derived from its evidence.");
|
|
4774
4777
|
}
|
|
4775
4778
|
return deepFreeze2({ ...common, admissionStatus: "diagnostic", blockingReasonCodes, confirmationRequired: false });
|
|
@@ -4823,7 +4826,7 @@ function createModelCandidateV2(input) {
|
|
|
4823
4826
|
});
|
|
4824
4827
|
}
|
|
4825
4828
|
function isModelCandidateV2Confirmable(candidate) {
|
|
4826
|
-
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");
|
|
4827
4830
|
}
|
|
4828
4831
|
function createModelCandidateConfirmationV2(input, expectedCandidate, expectedResolutionId) {
|
|
4829
4832
|
const record2 = asRecord(input, "ModelCandidateConfirmationV2");
|
|
@@ -4920,7 +4923,7 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4920
4923
|
const candidateAssetRef = createCandidateAssetRef2(record2.candidateAssetRef);
|
|
4921
4924
|
if (JSON.stringify(candidateAssetRef) !== JSON.stringify(candidate.assetRef)) throw new Error("ModelPromotionReceiptV2 must bind the exact proposed candidate identity.");
|
|
4922
4925
|
const childClosureEntries = asDenseArray(record2.assemblyChildClosureHashes, "ModelPromotionReceiptV2.assemblyChildClosureHashes", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
|
|
4923
|
-
const assemblyChildClosureHashes = childClosureEntries.map((
|
|
4926
|
+
const assemblyChildClosureHashes = childClosureEntries.map((entry2, index) => requireSha256(entry2, `ModelPromotionReceiptV2.assemblyChildClosureHashes[${index}]`));
|
|
4924
4927
|
const expectedChildClosureHashes = candidate.processingManifest.children.map(({ assetRef }) => assetRef.binaryClosureHash);
|
|
4925
4928
|
if (JSON.stringify(assemblyChildClosureHashes) !== JSON.stringify(expectedChildClosureHashes)) throw new Error("ModelPromotionReceiptV2 child closure sequence must match the canonical assembly order.");
|
|
4926
4929
|
assertDataOnlyTree(record2.finalAssetRef, "ModelPromotionReceiptV2.finalAssetRef");
|
|
@@ -5181,12 +5184,12 @@ function copyPvoxByteInput(value, fieldName, maximumByteLength) {
|
|
|
5181
5184
|
if (!(value instanceof Uint8Array)) throw new Error(`${fieldName} must be a Uint8Array.`);
|
|
5182
5185
|
let byteLength;
|
|
5183
5186
|
try {
|
|
5184
|
-
const
|
|
5185
|
-
const
|
|
5186
|
-
const
|
|
5187
|
-
if (
|
|
5188
|
-
byteLength =
|
|
5189
|
-
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);
|
|
5190
5193
|
if (typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer) {
|
|
5191
5194
|
throw new Error(`${fieldName} must not use shared mutable storage.`);
|
|
5192
5195
|
}
|
|
@@ -6956,9 +6959,9 @@ function snapshot(value, budget, depth = 0) {
|
|
|
6956
6959
|
return value;
|
|
6957
6960
|
}
|
|
6958
6961
|
if (Array.isArray(value)) return Object.freeze(list(value).map((item) => snapshot(item, budget, depth + 1)));
|
|
6959
|
-
const
|
|
6960
|
-
for (const key of Object.keys(
|
|
6961
|
-
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);
|
|
6962
6965
|
}
|
|
6963
6966
|
function path(value) {
|
|
6964
6967
|
if (typeof value !== "string" || value.length > 1024 || !value.length) fail();
|
|
@@ -7030,14 +7033,14 @@ async function qualify(input) {
|
|
|
7030
7033
|
contentType(file.contentType);
|
|
7031
7034
|
}
|
|
7032
7035
|
const entries = list(inventory.entries).map(record);
|
|
7033
|
-
uniquePaths(entries.map((
|
|
7036
|
+
uniquePaths(entries.map((entry2) => path(entry2.path)));
|
|
7034
7037
|
const ids = /* @__PURE__ */ new Set();
|
|
7035
|
-
for (const
|
|
7036
|
-
if (Object.keys(
|
|
7037
|
-
ids.add(
|
|
7038
|
-
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);
|
|
7039
7042
|
if (!descriptor) fail("inventory-mismatch");
|
|
7040
|
-
if (
|
|
7043
|
+
if (entry2.contentType !== void 0 && contentType(entry2.contentType) !== descriptor.contentType) fail("content-mismatch");
|
|
7041
7044
|
}
|
|
7042
7045
|
if (entries.length !== descriptors.size) fail("inventory-mismatch");
|
|
7043
7046
|
const payloads = list(root.files).map((value) => {
|
|
@@ -7180,12 +7183,12 @@ async function qualifyModelTextureResources(input, options = {}) {
|
|
|
7180
7183
|
]);
|
|
7181
7184
|
check();
|
|
7182
7185
|
const files = new Map(manifest.files.map((file) => [file.path, file]));
|
|
7183
|
-
const roles = new Map(inventory.entries.map((
|
|
7184
|
-
for (const
|
|
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));
|
|
7185
7188
|
const resources = new Map(graph.nodes.filter((node) => node.kind === "resource").map((node) => [node.id, node.value]));
|
|
7186
7189
|
if (bindings.length !== resources.size || new Set(bindings.map((b) => b.resourceId)).size !== bindings.length) fail2("resource-mismatch");
|
|
7187
7190
|
const resourceFiles = /* @__PURE__ */ new Set();
|
|
7188
|
-
const
|
|
7191
|
+
const qualified2 = bindings.map((binding) => {
|
|
7189
7192
|
const value = resources.get(binding.resourceId);
|
|
7190
7193
|
const file = files.get(binding.filePath);
|
|
7191
7194
|
if (!value || !file) fail2("resource-mismatch");
|
|
@@ -7218,7 +7221,7 @@ async function qualifyModelTextureResources(input, options = {}) {
|
|
|
7218
7221
|
contractVersion: 1,
|
|
7219
7222
|
manifest,
|
|
7220
7223
|
source: source.value,
|
|
7221
|
-
resources: Object.freeze(
|
|
7224
|
+
resources: Object.freeze(qualified2.sort((a, b) => compare(a.resourceId, b.resourceId))),
|
|
7222
7225
|
materials: Object.freeze(materials),
|
|
7223
7226
|
textures: Object.freeze(textures),
|
|
7224
7227
|
derived: Object.freeze(derived.sort((a, b) => compare(a.filePath, b.filePath)))
|
|
@@ -7237,6 +7240,9 @@ function tokenPath(value) {
|
|
|
7237
7240
|
return value;
|
|
7238
7241
|
}
|
|
7239
7242
|
|
|
7243
|
+
// src/model-resource-promotion.ts
|
|
7244
|
+
var import_gpu_shader4 = require("@plasius/gpu-shader");
|
|
7245
|
+
|
|
7240
7246
|
// src/model-conversion-diagnostics.ts
|
|
7241
7247
|
var MODEL_CONVERSION_DIAGNOSTIC_LIMITS = Object.freeze({ issues: 100, resources: 100, values: 16384, depth: 12, string: 2048, characters: 262144, timeoutMs: 6e4 });
|
|
7242
7248
|
var ModelConversionDiagnosticsError = class extends TypeError {
|
|
@@ -7329,9 +7335,9 @@ async function qualifyModelConversionDiagnostics(input, options = {}) {
|
|
|
7329
7335
|
only(binding, ["path", "resource"]);
|
|
7330
7336
|
if (typeof binding.path !== "string" || !binding.path.startsWith("/") || binding.path === "/" || resources.has(binding.path)) fail3();
|
|
7331
7337
|
const resource = createModelResourceRef(binding.resource);
|
|
7332
|
-
const
|
|
7333
|
-
if (identities.has(resource.uri) && identities.get(resource.uri) !==
|
|
7334
|
-
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);
|
|
7335
7341
|
resources.set(binding.path, resource);
|
|
7336
7342
|
}
|
|
7337
7343
|
check();
|
|
@@ -7384,6 +7390,193 @@ async function qualifyModelConversionDiagnostics(input, options = {}) {
|
|
|
7384
7390
|
}
|
|
7385
7391
|
}
|
|
7386
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();
|
|
7574
|
+
} finally {
|
|
7575
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
7576
|
+
if (signal && onAbort) AbortSignal.prototype.removeEventListener.call(signal, "abort", onAbort);
|
|
7577
|
+
}
|
|
7578
|
+
}
|
|
7579
|
+
|
|
7387
7580
|
// src/index.ts
|
|
7388
7581
|
var ASSET_CONTRACTS_PACKAGE = "@plasius/asset-contracts";
|
|
7389
7582
|
var UNIFIED_ASSET_PIPELINE_FEATURE_FLAG_ID = "asset.pipeline.unified-ai-assets.enabled";
|
|
@@ -7564,7 +7757,7 @@ function createModelAssetManifest(input) {
|
|
|
7564
7757
|
"screenshot",
|
|
7565
7758
|
"metadata"
|
|
7566
7759
|
]);
|
|
7567
|
-
const descriptor = (0,
|
|
7760
|
+
const descriptor = (0, import_gpu_shader5.parseModelGpuCompatibilityDescriptor)({
|
|
7568
7761
|
modelId: base.assetId,
|
|
7569
7762
|
version: base.version,
|
|
7570
7763
|
gpuInterface: input.gpuInterface,
|
|
@@ -7594,7 +7787,7 @@ function createGpuInterfaceAssetManifest(input) {
|
|
|
7594
7787
|
);
|
|
7595
7788
|
assertAllowedFileRoles(base, ["gpu-interface-manifest"]);
|
|
7596
7789
|
assertRoleCount(base, "gpu-interface-manifest", 1);
|
|
7597
|
-
const gpuInterfaceManifest = (0,
|
|
7790
|
+
const gpuInterfaceManifest = (0, import_gpu_shader5.parseGpuInterfaceManifest)(input.gpuInterfaceManifest);
|
|
7598
7791
|
assertImmutableAssetVersion(gpuInterfaceManifest.interfaceVersion);
|
|
7599
7792
|
assertDomainIdentity(
|
|
7600
7793
|
base,
|
|
@@ -7617,7 +7810,7 @@ function createShaderAssetManifest(input) {
|
|
|
7617
7810
|
);
|
|
7618
7811
|
assertAllowedFileRoles(base, ["shader-manifest", "wgsl"]);
|
|
7619
7812
|
assertRoleCount(base, "shader-manifest", 1);
|
|
7620
|
-
const shaderManifest = (0,
|
|
7813
|
+
const shaderManifest = (0, import_gpu_shader5.parseShaderVersionManifest)(input.shaderManifest);
|
|
7621
7814
|
assertShaderManifestVersions(shaderManifest);
|
|
7622
7815
|
assertDomainIdentity(base, shaderManifest.shaderId, shaderManifest.version, "Shader");
|
|
7623
7816
|
assertShaderModuleFiles(base.files, shaderManifest);
|
|
@@ -7636,7 +7829,7 @@ function createShaderStyleProfileAssetManifest(input) {
|
|
|
7636
7829
|
);
|
|
7637
7830
|
assertAllowedFileRoles(base, ["shader-style-profile-manifest"]);
|
|
7638
7831
|
assertRoleCount(base, "shader-style-profile-manifest", 1);
|
|
7639
|
-
const styleProfileManifest = (0,
|
|
7832
|
+
const styleProfileManifest = (0, import_gpu_shader5.parseShaderStyleProfileManifest)(input.styleProfileManifest);
|
|
7640
7833
|
assertStyleProfileManifestVersions(styleProfileManifest);
|
|
7641
7834
|
assertDomainIdentity(base, styleProfileManifest.profileId, styleProfileManifest.version, "Style profile");
|
|
7642
7835
|
return Object.freeze({ ...base, styleProfileManifest });
|
|
@@ -7700,8 +7893,8 @@ function createShaderValidationEvidenceRef(input) {
|
|
|
7700
7893
|
sha256: assertSha256(attestation2.sha256, "Shader validation evidence attestation sha256")
|
|
7701
7894
|
})
|
|
7702
7895
|
};
|
|
7703
|
-
const supportedUniversal =
|
|
7704
|
-
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);
|
|
7705
7898
|
if (!supportedUniversal && !supportedAdditive) {
|
|
7706
7899
|
throw new Error("Shader validation evidence must target a supported WebGPU matrix policy.");
|
|
7707
7900
|
}
|
|
@@ -7712,7 +7905,7 @@ function createShaderValidationEvidenceRef(input) {
|
|
|
7712
7905
|
}
|
|
7713
7906
|
async function createGpuInterfaceRef(input) {
|
|
7714
7907
|
assertImmutableAssetVersion(input.manifest.interfaceVersion);
|
|
7715
|
-
const manifest = (0,
|
|
7908
|
+
const manifest = (0, import_gpu_shader5.parseGpuInterfaceManifest)(input.manifest);
|
|
7716
7909
|
assertAssetId(manifest.interfaceId);
|
|
7717
7910
|
assertImmutableAssetVersion(manifest.interfaceVersion);
|
|
7718
7911
|
const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "GPU interface");
|
|
@@ -7727,7 +7920,7 @@ async function createGpuInterfaceRef(input) {
|
|
|
7727
7920
|
}
|
|
7728
7921
|
async function createShaderVersionRef(input) {
|
|
7729
7922
|
assertImmutableAssetVersion(input.manifest.version);
|
|
7730
|
-
const manifest = (0,
|
|
7923
|
+
const manifest = (0, import_gpu_shader5.parseShaderVersionManifest)(input.manifest);
|
|
7731
7924
|
assertAssetId(manifest.shaderId);
|
|
7732
7925
|
assertShaderManifestVersions(manifest);
|
|
7733
7926
|
const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Shader");
|
|
@@ -7740,7 +7933,7 @@ async function createShaderVersionRef(input) {
|
|
|
7740
7933
|
}
|
|
7741
7934
|
async function createShaderStyleProfileRef(input) {
|
|
7742
7935
|
assertImmutableAssetVersion(input.manifest.version);
|
|
7743
|
-
const manifest = (0,
|
|
7936
|
+
const manifest = (0, import_gpu_shader5.parseShaderStyleProfileManifest)(input.manifest);
|
|
7744
7937
|
assertAssetId(manifest.profileId);
|
|
7745
7938
|
assertStyleProfileManifestVersions(manifest);
|
|
7746
7939
|
const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Style profile");
|
|
@@ -7785,7 +7978,7 @@ async function validateGpuAssetFiles(input) {
|
|
|
7785
7978
|
if (bytes.byteLength !== file.byteLength) {
|
|
7786
7979
|
throw new Error(`GPU asset file ${file.path} byte length differs from its descriptor.`);
|
|
7787
7980
|
}
|
|
7788
|
-
if (await (0,
|
|
7981
|
+
if (await (0, import_gpu_shader5.computeSha256)(bytes) !== file.sha256) {
|
|
7789
7982
|
throw new Error(`GPU asset file ${file.path} digest differs from its descriptor.`);
|
|
7790
7983
|
}
|
|
7791
7984
|
if (file.contentType === ASSET_JSON_CONTENT_TYPE) {
|
|
@@ -7804,13 +7997,13 @@ async function validateGpuAssetFiles(input) {
|
|
|
7804
7997
|
}
|
|
7805
7998
|
const expectedDomainManifest = domainManifestForAsset(manifest);
|
|
7806
7999
|
if (expectedDomainManifest !== null) {
|
|
7807
|
-
const parsedEntrypoint = (0,
|
|
7808
|
-
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)) {
|
|
7809
8002
|
throw new Error("GPU asset entrypoint bytes differ from the validated domain manifest.");
|
|
7810
8003
|
}
|
|
7811
8004
|
} else if (manifest.assetKind === "shader-validation-evidence") {
|
|
7812
8005
|
assertEvidenceEntrypointIdentity(
|
|
7813
|
-
(0,
|
|
8006
|
+
(0, import_gpu_shader5.parseJsonBytes)(entrypointBytes, "Shader validation evidence entrypoint"),
|
|
7814
8007
|
manifest.validationEvidence
|
|
7815
8008
|
);
|
|
7816
8009
|
}
|
|
@@ -8034,7 +8227,7 @@ function assertTypedRuntimeManifestUri(manifest, value) {
|
|
|
8034
8227
|
}
|
|
8035
8228
|
function assertEvidenceEntrypointIdentity(value, ref2) {
|
|
8036
8229
|
const evidence = assertRecord2(value, "Shader validation evidence entrypoint");
|
|
8037
|
-
if (evidence.contractVersion !==
|
|
8230
|
+
if (evidence.contractVersion !== import_gpu_shader5.SHADER_VALIDATION_EVIDENCE_VERSION || evidence.evidenceId !== ref2.evidenceId || evidence.status !== "passed") {
|
|
8038
8231
|
throw new Error("Shader validation evidence bytes do not identify passing declared evidence.");
|
|
8039
8232
|
}
|
|
8040
8233
|
const matrixRef = assertRecord2(evidence.matrixRef, "Shader validation evidence matrixRef");
|
|
@@ -8050,9 +8243,9 @@ function assertEvidenceEntrypointIdentity(value, ref2) {
|
|
|
8050
8243
|
}
|
|
8051
8244
|
}
|
|
8052
8245
|
function assertCanonicalJsonBytes(bytes, fieldName) {
|
|
8053
|
-
const value = (0,
|
|
8246
|
+
const value = (0, import_gpu_shader5.parseJsonBytes)(bytes, fieldName);
|
|
8054
8247
|
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
8055
|
-
if ((0,
|
|
8248
|
+
if ((0, import_gpu_shader5.canonicalizeGpuContract)(value) !== text) {
|
|
8056
8249
|
throw new Error(`${fieldName} must use canonical JSON serialization.`);
|
|
8057
8250
|
}
|
|
8058
8251
|
}
|
|
@@ -8061,11 +8254,11 @@ async function bindDomainManifestBytes(bytes, manifest, fieldName) {
|
|
|
8061
8254
|
throw new Error(`${fieldName} manifest bytes must be a Uint8Array.`);
|
|
8062
8255
|
}
|
|
8063
8256
|
assertCanonicalJsonBytes(bytes, `${fieldName} manifest`);
|
|
8064
|
-
const parsed = (0,
|
|
8065
|
-
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)) {
|
|
8066
8259
|
throw new Error(`${fieldName} manifest bytes differ from the supplied manifest.`);
|
|
8067
8260
|
}
|
|
8068
|
-
return (0,
|
|
8261
|
+
return (0, import_gpu_shader5.computeSha256)(bytes);
|
|
8069
8262
|
}
|
|
8070
8263
|
function assertRoleCount(manifest, role, expectedCount) {
|
|
8071
8264
|
if (manifest.files.filter((file) => file.role === role).length !== expectedCount) {
|
|
@@ -8187,11 +8380,13 @@ function assertRequiredString(value, fieldName) {
|
|
|
8187
8380
|
MODEL_RESOLUTION_V2_JSON_SCHEMAS,
|
|
8188
8381
|
MODEL_RESOLUTION_V2_STATES,
|
|
8189
8382
|
MODEL_RESOURCE_MANIFEST_LIMITS,
|
|
8383
|
+
MODEL_RESOURCE_PROMOTION_LIMITS,
|
|
8190
8384
|
MODEL_RIGHTS_STATUSES,
|
|
8191
8385
|
MODEL_TEXTURE_RESOURCE_LIMITS,
|
|
8192
8386
|
MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
|
|
8193
8387
|
ModelConversionDiagnosticsError,
|
|
8194
8388
|
ModelResourceManifestError,
|
|
8389
|
+
ModelResourcePromotionError,
|
|
8195
8390
|
ModelTextureResourceError,
|
|
8196
8391
|
PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
|
|
8197
8392
|
PVOX_ASSET_MANIFEST_VERSION,
|
|
@@ -8327,6 +8522,7 @@ function assertRequiredString(value, fieldName) {
|
|
|
8327
8522
|
createModelRequestSpecV2,
|
|
8328
8523
|
createModelResolution,
|
|
8329
8524
|
createModelResolutionV2,
|
|
8525
|
+
createModelResourcePromotionRecord,
|
|
8330
8526
|
createModelResourceRef,
|
|
8331
8527
|
createModelRightsAssessment,
|
|
8332
8528
|
createModelTechnicalProfile,
|