@plasius/asset-contracts 0.4.6 → 0.6.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 +42 -0
- package/README.md +122 -0
- package/dist/index.cjs +1293 -954
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -1
- package/dist/index.d.ts +100 -1
- package/dist/index.js +1258 -935
- package/dist/index.js.map +1 -1
- package/docs/adrs/adr-0009-model-resource-manifest-qualification.md +74 -0
- package/docs/adrs/adr-0010-public-conversion-diagnostics.md +61 -0
- package/docs/adrs/index.md +2 -0
- package/docs/tdrs/index.md +2 -0
- package/docs/tdrs/tdr-0004-model-resource-manifest-qualification.md +70 -0
- package/docs/tdrs/tdr-0005-public-conversion-diagnostics.md +51 -0
- package/package.json +3 -2
- package/src/index.ts +2 -0
- package/src/model-conversion-diagnostics.ts +185 -0
- package/src/model-resource-manifest.ts +198 -0
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -33,12 +43,13 @@ __export(index_exports, {
|
|
|
33
43
|
ASSET_SOURCE_ADAPTERS: () => ASSET_SOURCE_ADAPTERS,
|
|
34
44
|
ASSET_WGSL_CONTENT_TYPE: () => ASSET_WGSL_CONTENT_TYPE,
|
|
35
45
|
CANONICAL_MODEL_COORDINATE_SYSTEM: () => CANONICAL_MODEL_COORDINATE_SYSTEM,
|
|
36
|
-
GPU_SHADER_STORE_FEATURE_FLAG: () =>
|
|
37
|
-
GPU_SHADER_STYLE_SELECTION_CAPABILITY: () =>
|
|
46
|
+
GPU_SHADER_STORE_FEATURE_FLAG: () => import_gpu_shader4.GPU_SHADER_STORE_FEATURE_FLAG,
|
|
47
|
+
GPU_SHADER_STYLE_SELECTION_CAPABILITY: () => import_gpu_shader4.GPU_SHADER_STYLE_SELECTION_CAPABILITY,
|
|
38
48
|
MODEL_ASSET_KINDS: () => MODEL_ASSET_KINDS,
|
|
39
49
|
MODEL_CANDIDATE_HARD_GATE_KINDS: () => MODEL_CANDIDATE_HARD_GATE_KINDS,
|
|
40
50
|
MODEL_CONFIRMATION_VIEW_KINDS: () => MODEL_CONFIRMATION_VIEW_KINDS,
|
|
41
51
|
MODEL_CONFIRMATION_VIEW_SIZE_PX: () => MODEL_CONFIRMATION_VIEW_SIZE_PX,
|
|
52
|
+
MODEL_CONVERSION_DIAGNOSTIC_LIMITS: () => MODEL_CONVERSION_DIAGNOSTIC_LIMITS,
|
|
42
53
|
MODEL_GENERATOR_DISABLED_REASON_CODE: () => MODEL_GENERATOR_DISABLED_REASON_CODE,
|
|
43
54
|
MODEL_MATCH_ASSURANCE_BANDS: () => MODEL_MATCH_ASSURANCE_BANDS,
|
|
44
55
|
MODEL_MATCH_ASSURANCE_THRESHOLDS: () => MODEL_MATCH_ASSURANCE_THRESHOLDS,
|
|
@@ -52,8 +63,11 @@ __export(index_exports, {
|
|
|
52
63
|
MODEL_RESOLUTION_V2_CONTRACT_VERSION: () => MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
53
64
|
MODEL_RESOLUTION_V2_JSON_SCHEMAS: () => MODEL_RESOLUTION_V2_JSON_SCHEMAS,
|
|
54
65
|
MODEL_RESOLUTION_V2_STATES: () => MODEL_RESOLUTION_V2_STATES,
|
|
66
|
+
MODEL_RESOURCE_MANIFEST_LIMITS: () => MODEL_RESOURCE_MANIFEST_LIMITS,
|
|
55
67
|
MODEL_RIGHTS_STATUSES: () => MODEL_RIGHTS_STATUSES,
|
|
56
68
|
MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE: () => MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
|
|
69
|
+
ModelConversionDiagnosticsError: () => ModelConversionDiagnosticsError,
|
|
70
|
+
ModelResourceManifestError: () => ModelResourceManifestError,
|
|
57
71
|
PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN: () => PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
|
|
58
72
|
PVOX_ASSET_MANIFEST_VERSION: () => PVOX_ASSET_MANIFEST_VERSION,
|
|
59
73
|
PVOX_BINARY_CLOSURE_HASH_DOMAIN: () => PVOX_BINARY_CLOSURE_HASH_DOMAIN,
|
|
@@ -244,11 +258,13 @@ __export(index_exports, {
|
|
|
244
258
|
normalizeAssetId: () => normalizeAssetId,
|
|
245
259
|
normalizePvoxRootHeaderForHashV1: () => normalizePvoxRootHeaderForHashV1,
|
|
246
260
|
projectPvoxNamedJsonClosureV1: () => projectPvoxNamedJsonClosureV1,
|
|
261
|
+
qualifyModelConversionDiagnostics: () => qualifyModelConversionDiagnostics,
|
|
262
|
+
qualifyModelResourceManifest: () => qualifyModelResourceManifest,
|
|
247
263
|
toLegacyModelResolutionState: () => toLegacyModelResolutionState,
|
|
248
264
|
validateGpuAssetFiles: () => validateGpuAssetFiles
|
|
249
265
|
});
|
|
250
266
|
module.exports = __toCommonJS(index_exports);
|
|
251
|
-
var
|
|
267
|
+
var import_gpu_shader4 = require("@plasius/gpu-shader");
|
|
252
268
|
|
|
253
269
|
// src/asset-version.ts
|
|
254
270
|
var ASSET_VERSION_PATTERN = /^[0-9A-Za-z][0-9A-Za-z._-]{0,127}$/u;
|
|
@@ -396,14 +412,14 @@ function assertRecord(value, fieldName) {
|
|
|
396
412
|
}
|
|
397
413
|
return value;
|
|
398
414
|
}
|
|
399
|
-
function assertAllowedKeys(
|
|
400
|
-
const unexpected = Object.keys(
|
|
415
|
+
function assertAllowedKeys(record2, allowedKeys, fieldName) {
|
|
416
|
+
const unexpected = Object.keys(record2).find((key) => !allowedKeys.includes(key));
|
|
401
417
|
if (unexpected !== void 0) {
|
|
402
418
|
throw new Error(`${fieldName} contains unexpected field ${unexpected}.`);
|
|
403
419
|
}
|
|
404
420
|
}
|
|
405
|
-
function assertContractVersion(
|
|
406
|
-
if (
|
|
421
|
+
function assertContractVersion(record2, fieldName) {
|
|
422
|
+
if (record2.contractVersion !== void 0 && record2.contractVersion !== MODEL_RESOLUTION_CONTRACT_VERSION) {
|
|
407
423
|
throw new Error(`${fieldName} contractVersion is not supported.`);
|
|
408
424
|
}
|
|
409
425
|
}
|
|
@@ -526,22 +542,22 @@ function requiredVector3(value, fieldName, positive = false) {
|
|
|
526
542
|
];
|
|
527
543
|
}
|
|
528
544
|
function createBounds(value, fieldName) {
|
|
529
|
-
const
|
|
530
|
-
assertAllowedKeys(
|
|
531
|
-
const min = requiredVector3(
|
|
532
|
-
const max = requiredVector3(
|
|
545
|
+
const record2 = assertRecord(value, fieldName);
|
|
546
|
+
assertAllowedKeys(record2, ["min", "max"], fieldName);
|
|
547
|
+
const min = requiredVector3(record2.min, `${fieldName}.min`);
|
|
548
|
+
const max = requiredVector3(record2.max, `${fieldName}.max`);
|
|
533
549
|
if (min.some((axis, index) => axis >= max[index])) {
|
|
534
550
|
throw new Error(`${fieldName} min axes must be lower than max axes.`);
|
|
535
551
|
}
|
|
536
552
|
return { min, max };
|
|
537
553
|
}
|
|
538
554
|
function createDimensions(value, fieldName) {
|
|
539
|
-
const
|
|
540
|
-
assertAllowedKeys(
|
|
555
|
+
const record2 = assertRecord(value, fieldName);
|
|
556
|
+
assertAllowedKeys(record2, ["width", "height", "depth"], fieldName);
|
|
541
557
|
return {
|
|
542
|
-
width: requiredFiniteNumber(
|
|
543
|
-
height: requiredFiniteNumber(
|
|
544
|
-
depth: requiredFiniteNumber(
|
|
558
|
+
width: requiredFiniteNumber(record2.width, `${fieldName}.width`, Number.EPSILON),
|
|
559
|
+
height: requiredFiniteNumber(record2.height, `${fieldName}.height`, Number.EPSILON),
|
|
560
|
+
depth: requiredFiniteNumber(record2.depth, `${fieldName}.depth`, Number.EPSILON)
|
|
545
561
|
};
|
|
546
562
|
}
|
|
547
563
|
function assertModelUri(value, fieldName) {
|
|
@@ -625,8 +641,8 @@ function opaqueSourceAssetId(value, fieldName = "ModelProvenance.sourceAssetId")
|
|
|
625
641
|
return value;
|
|
626
642
|
}
|
|
627
643
|
function createHardConstraints(value) {
|
|
628
|
-
const
|
|
629
|
-
assertAllowedKeys(
|
|
644
|
+
const record2 = value === void 0 ? {} : assertRecord(value, "ModelRequestSpec.hardConstraints");
|
|
645
|
+
assertAllowedKeys(record2, [
|
|
630
646
|
"boundsMetres",
|
|
631
647
|
"dimensionsMetres",
|
|
632
648
|
"maxTriangles",
|
|
@@ -648,62 +664,62 @@ function createHardConstraints(value) {
|
|
|
648
664
|
collision: "optional",
|
|
649
665
|
partition: "allowed"
|
|
650
666
|
};
|
|
651
|
-
if (
|
|
652
|
-
result.boundsMetres = createBounds(
|
|
667
|
+
if (record2.boundsMetres !== void 0) {
|
|
668
|
+
result.boundsMetres = createBounds(record2.boundsMetres, "ModelRequestSpec.hardConstraints.boundsMetres");
|
|
653
669
|
}
|
|
654
|
-
if (
|
|
655
|
-
result.dimensionsMetres = createDimensions(
|
|
670
|
+
if (record2.dimensionsMetres !== void 0) {
|
|
671
|
+
result.dimensionsMetres = createDimensions(record2.dimensionsMetres, "ModelRequestSpec.hardConstraints.dimensionsMetres");
|
|
656
672
|
}
|
|
657
|
-
if (
|
|
673
|
+
if (record2.maxTriangles !== void 0) {
|
|
658
674
|
result.maxTriangles = requiredInteger(
|
|
659
|
-
|
|
675
|
+
record2.maxTriangles,
|
|
660
676
|
"ModelRequestSpec.hardConstraints.maxTriangles",
|
|
661
677
|
1,
|
|
662
678
|
STATIC_WORLD_V1_MODEL_POLICY.maxTriangles
|
|
663
679
|
);
|
|
664
680
|
}
|
|
665
|
-
if (
|
|
681
|
+
if (record2.maxBytes !== void 0) {
|
|
666
682
|
result.maxBytes = requiredInteger(
|
|
667
|
-
|
|
683
|
+
record2.maxBytes,
|
|
668
684
|
"ModelRequestSpec.hardConstraints.maxBytes",
|
|
669
685
|
1,
|
|
670
686
|
STATIC_WORLD_V1_MODEL_POLICY.maxBytes
|
|
671
687
|
);
|
|
672
688
|
}
|
|
673
|
-
if (
|
|
689
|
+
if (record2.maxTextureBytes !== void 0) {
|
|
674
690
|
result.maxTextureBytes = requiredInteger(
|
|
675
|
-
|
|
691
|
+
record2.maxTextureBytes,
|
|
676
692
|
"ModelRequestSpec.hardConstraints.maxTextureBytes",
|
|
677
693
|
1,
|
|
678
694
|
STATIC_WORLD_V1_MODEL_POLICY.maxTextureBytes
|
|
679
695
|
);
|
|
680
696
|
}
|
|
681
|
-
if (
|
|
697
|
+
if (record2.maxTextureDimensionPx !== void 0) {
|
|
682
698
|
result.maxTextureDimensionPx = requiredInteger(
|
|
683
|
-
|
|
699
|
+
record2.maxTextureDimensionPx,
|
|
684
700
|
"ModelRequestSpec.hardConstraints.maxTextureDimensionPx",
|
|
685
701
|
1,
|
|
686
702
|
STATIC_WORLD_V1_MODEL_POLICY.maxTextureDimensionPx
|
|
687
703
|
);
|
|
688
704
|
}
|
|
689
|
-
if (
|
|
705
|
+
if (record2.maxPartitionCellMetres !== void 0) {
|
|
690
706
|
result.maxPartitionCellMetres = requiredFiniteNumber(
|
|
691
|
-
|
|
707
|
+
record2.maxPartitionCellMetres,
|
|
692
708
|
"ModelRequestSpec.hardConstraints.maxPartitionCellMetres",
|
|
693
709
|
Number.EPSILON,
|
|
694
710
|
STATIC_WORLD_V1_MODEL_POLICY.maxPartitionCellMetres
|
|
695
711
|
);
|
|
696
712
|
}
|
|
697
|
-
if (
|
|
698
|
-
result.lod = requiredEnum(
|
|
713
|
+
if (record2.lod !== void 0) {
|
|
714
|
+
result.lod = requiredEnum(record2.lod, ["required", "optional", "forbidden"], "ModelRequestSpec.hardConstraints.lod");
|
|
699
715
|
}
|
|
700
|
-
if (
|
|
701
|
-
result.collision = requiredEnum(
|
|
716
|
+
if (record2.collision !== void 0) {
|
|
717
|
+
result.collision = requiredEnum(record2.collision, ["required", "optional", "forbidden"], "ModelRequestSpec.hardConstraints.collision");
|
|
702
718
|
}
|
|
703
|
-
if (
|
|
704
|
-
result.partition = requiredEnum(
|
|
719
|
+
if (record2.partition !== void 0) {
|
|
720
|
+
result.partition = requiredEnum(record2.partition, ["single", "allowed", "required"], "ModelRequestSpec.hardConstraints.partition");
|
|
705
721
|
}
|
|
706
|
-
if (
|
|
722
|
+
if (record2.maxTextureBytes === void 0 && result.maxBytes !== void 0) {
|
|
707
723
|
result.maxTextureBytes = Math.min(result.maxTextureBytes ?? result.maxBytes, result.maxBytes);
|
|
708
724
|
}
|
|
709
725
|
if (result.maxBytes !== void 0 && result.maxTextureBytes !== void 0 && result.maxTextureBytes > result.maxBytes) {
|
|
@@ -728,8 +744,8 @@ function createSoftPreferences(value) {
|
|
|
728
744
|
if (value === void 0) {
|
|
729
745
|
return {};
|
|
730
746
|
}
|
|
731
|
-
const
|
|
732
|
-
assertAllowedKeys(
|
|
747
|
+
const record2 = assertRecord(value, "ModelRequestSpec.softPreferences");
|
|
748
|
+
assertAllowedKeys(record2, [
|
|
733
749
|
"category",
|
|
734
750
|
"style",
|
|
735
751
|
"materials",
|
|
@@ -740,22 +756,22 @@ function createSoftPreferences(value) {
|
|
|
740
756
|
], "ModelRequestSpec.softPreferences");
|
|
741
757
|
const result = {};
|
|
742
758
|
for (const field of ["category", "style", "era", "condition"]) {
|
|
743
|
-
const normalized = optionalString(
|
|
759
|
+
const normalized = optionalString(record2[field], `ModelRequestSpec.softPreferences.${field}`, 80);
|
|
744
760
|
if (normalized !== void 0) {
|
|
745
761
|
assertNoDirectUrl(normalized, `ModelRequestSpec.softPreferences.${field}`);
|
|
746
762
|
result[field] = normalized;
|
|
747
763
|
}
|
|
748
764
|
}
|
|
749
765
|
for (const field of ["materials", "colors", "tags"]) {
|
|
750
|
-
if (
|
|
751
|
-
result[field] = stringList(
|
|
766
|
+
if (record2[field] !== void 0) {
|
|
767
|
+
result[field] = stringList(record2[field], `ModelRequestSpec.softPreferences.${field}`);
|
|
752
768
|
}
|
|
753
769
|
}
|
|
754
770
|
return result;
|
|
755
771
|
}
|
|
756
772
|
function createModelRequestSpec(input) {
|
|
757
|
-
const
|
|
758
|
-
assertAllowedKeys(
|
|
773
|
+
const record2 = assertRecord(input, "ModelRequestSpec");
|
|
774
|
+
assertAllowedKeys(record2, [
|
|
759
775
|
"contractVersion",
|
|
760
776
|
"policyProfileId",
|
|
761
777
|
"query",
|
|
@@ -766,21 +782,21 @@ function createModelRequestSpec(input) {
|
|
|
766
782
|
"softPreferences",
|
|
767
783
|
"exclusions"
|
|
768
784
|
], "ModelRequestSpec");
|
|
769
|
-
assertContractVersion(
|
|
770
|
-
if (
|
|
785
|
+
assertContractVersion(record2, "ModelRequestSpec");
|
|
786
|
+
if (record2.policyProfileId !== void 0 && record2.policyProfileId !== STATIC_WORLD_V1_MODEL_POLICY.id) {
|
|
771
787
|
throw new Error("ModelRequestSpec.policyProfileId must be static-world-v1.");
|
|
772
788
|
}
|
|
773
|
-
const query = requiredString(
|
|
789
|
+
const query = requiredString(record2.query, "ModelRequestSpec.query", MAX_QUERY_LENGTH);
|
|
774
790
|
assertNoDirectUrl(query, "ModelRequestSpec.query");
|
|
775
791
|
const revision = requiredInteger(
|
|
776
|
-
|
|
792
|
+
record2.revision,
|
|
777
793
|
`ModelRequestSpec revision (maximum ${MODEL_REQUEST_MAX_REVISION})`,
|
|
778
794
|
0,
|
|
779
795
|
MODEL_REQUEST_MAX_REVISION
|
|
780
796
|
);
|
|
781
797
|
let locale;
|
|
782
|
-
if (
|
|
783
|
-
const requestedLocale = requiredString(
|
|
798
|
+
if (record2.locale !== void 0) {
|
|
799
|
+
const requestedLocale = requiredString(record2.locale, "ModelRequestSpec.locale", 64);
|
|
784
800
|
try {
|
|
785
801
|
[locale] = Intl.getCanonicalLocales(requestedLocale);
|
|
786
802
|
} catch {
|
|
@@ -790,7 +806,7 @@ function createModelRequestSpec(input) {
|
|
|
790
806
|
throw new Error("ModelRequestSpec.locale must be a valid BCP 47 locale.");
|
|
791
807
|
}
|
|
792
808
|
}
|
|
793
|
-
const rankerId =
|
|
809
|
+
const rankerId = record2.rankerId === void 0 ? void 0 : requiredToken(record2.rankerId, "ModelRequestSpec.rankerId");
|
|
794
810
|
const result = {
|
|
795
811
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
796
812
|
policyProfileId: STATIC_WORLD_V1_MODEL_POLICY.id,
|
|
@@ -798,9 +814,9 @@ function createModelRequestSpec(input) {
|
|
|
798
814
|
revision,
|
|
799
815
|
...locale === void 0 ? {} : { locale },
|
|
800
816
|
...rankerId === void 0 ? {} : { rankerId },
|
|
801
|
-
hardConstraints: createHardConstraints(
|
|
802
|
-
softPreferences: createSoftPreferences(
|
|
803
|
-
exclusions:
|
|
817
|
+
hardConstraints: createHardConstraints(record2.hardConstraints),
|
|
818
|
+
softPreferences: createSoftPreferences(record2.softPreferences),
|
|
819
|
+
exclusions: record2.exclusions === void 0 ? [] : stringList(record2.exclusions, "ModelRequestSpec.exclusions")
|
|
804
820
|
};
|
|
805
821
|
return deepFreeze(result);
|
|
806
822
|
}
|
|
@@ -838,8 +854,8 @@ function classifyModelMatchAssurance(score, hardConstraintPass, evidenceMode, ex
|
|
|
838
854
|
return MODEL_MATCH_ASSURANCE_ORDER[rawAssurance] <= MODEL_MATCH_ASSURANCE_ORDER[assuranceCeiling] ? rawAssurance : assuranceCeiling;
|
|
839
855
|
}
|
|
840
856
|
function createRankerRef(value) {
|
|
841
|
-
const
|
|
842
|
-
assertAllowedKeys(
|
|
857
|
+
const record2 = assertRecord(value, "ModelMatchAssessment.ranker");
|
|
858
|
+
assertAllowedKeys(record2, [
|
|
843
859
|
"id",
|
|
844
860
|
"version",
|
|
845
861
|
"calibrationId",
|
|
@@ -848,25 +864,25 @@ function createRankerRef(value) {
|
|
|
848
864
|
"assuranceCeiling"
|
|
849
865
|
], "ModelMatchAssessment.ranker");
|
|
850
866
|
return {
|
|
851
|
-
id: requiredToken(
|
|
852
|
-
version: assetVersion(
|
|
853
|
-
calibrationId: requiredToken(
|
|
854
|
-
calibrationVersion: assetVersion(
|
|
867
|
+
id: requiredToken(record2.id, "ModelMatchAssessment.ranker.id"),
|
|
868
|
+
version: assetVersion(record2.version, "ModelMatchAssessment.ranker.version"),
|
|
869
|
+
calibrationId: requiredToken(record2.calibrationId, "ModelMatchAssessment.ranker.calibrationId"),
|
|
870
|
+
calibrationVersion: assetVersion(record2.calibrationVersion, "ModelMatchAssessment.ranker.calibrationVersion"),
|
|
855
871
|
evidenceMode: requiredEnum(
|
|
856
|
-
|
|
872
|
+
record2.evidenceMode,
|
|
857
873
|
MODEL_RANKER_EVIDENCE_MODES,
|
|
858
874
|
"ModelMatchAssessment.ranker.evidenceMode"
|
|
859
875
|
),
|
|
860
876
|
assuranceCeiling: requiredEnum(
|
|
861
|
-
|
|
877
|
+
record2.assuranceCeiling,
|
|
862
878
|
MODEL_MATCH_ASSURANCE_BANDS,
|
|
863
879
|
"ModelMatchAssessment.ranker.assuranceCeiling"
|
|
864
880
|
)
|
|
865
881
|
};
|
|
866
882
|
}
|
|
867
883
|
function createModelMatchAssessment(input) {
|
|
868
|
-
const
|
|
869
|
-
assertAllowedKeys(
|
|
884
|
+
const record2 = assertRecord(input, "ModelMatchAssessment");
|
|
885
|
+
assertAllowedKeys(record2, [
|
|
870
886
|
"contractVersion",
|
|
871
887
|
"score",
|
|
872
888
|
"assurance",
|
|
@@ -879,19 +895,19 @@ function createModelMatchAssessment(input) {
|
|
|
879
895
|
"candidateId",
|
|
880
896
|
"candidateContentHash"
|
|
881
897
|
], "ModelMatchAssessment");
|
|
882
|
-
assertContractVersion(
|
|
883
|
-
const score = requiredFiniteNumber(
|
|
884
|
-
const hardConstraintPass = requiredBoolean(
|
|
885
|
-
const exactMatch = requiredBoolean(
|
|
886
|
-
const ranker = createRankerRef(
|
|
887
|
-
const request = createModelRequestSpec(
|
|
898
|
+
assertContractVersion(record2, "ModelMatchAssessment");
|
|
899
|
+
const score = requiredFiniteNumber(record2.score, "ModelMatchAssessment.score", 0, 1);
|
|
900
|
+
const hardConstraintPass = requiredBoolean(record2.hardConstraintPass, "ModelMatchAssessment.hardConstraintPass");
|
|
901
|
+
const exactMatch = requiredBoolean(record2.exactMatch, "ModelMatchAssessment.exactMatch");
|
|
902
|
+
const ranker = createRankerRef(record2.ranker);
|
|
903
|
+
const request = createModelRequestSpec(record2.request);
|
|
888
904
|
if (request.rankerId !== void 0 && request.rankerId !== ranker.id) {
|
|
889
905
|
throw new Error("ModelMatchAssessment ranker.id must match the caller-selected request rankerId.");
|
|
890
906
|
}
|
|
891
|
-
if (
|
|
892
|
-
requiredEnum(
|
|
907
|
+
if (record2.assurance !== void 0) {
|
|
908
|
+
requiredEnum(record2.assurance, MODEL_MATCH_ASSURANCE_BANDS, "ModelMatchAssessment.assurance");
|
|
893
909
|
}
|
|
894
|
-
let reasonCodes2 = stringList(
|
|
910
|
+
let reasonCodes2 = stringList(record2.reasonCodes, "ModelMatchAssessment.reasonCodes", {
|
|
895
911
|
tokens: true
|
|
896
912
|
});
|
|
897
913
|
if (!hardConstraintPass && reasonCodes2.length === 0) {
|
|
@@ -924,18 +940,18 @@ function createModelMatchAssessment(input) {
|
|
|
924
940
|
exactMatch,
|
|
925
941
|
reasonCodes: reasonCodes2,
|
|
926
942
|
ranker,
|
|
927
|
-
fidelityWarnings: stringList(
|
|
943
|
+
fidelityWarnings: stringList(record2.fidelityWarnings ?? [], "ModelMatchAssessment.fidelityWarnings"),
|
|
928
944
|
request,
|
|
929
|
-
candidateId: requiredToken(
|
|
945
|
+
candidateId: requiredToken(record2.candidateId, "ModelMatchAssessment.candidateId"),
|
|
930
946
|
candidateContentHash: requiredSha256(
|
|
931
|
-
|
|
947
|
+
record2.candidateContentHash,
|
|
932
948
|
"ModelMatchAssessment.candidateContentHash"
|
|
933
949
|
)
|
|
934
950
|
});
|
|
935
951
|
}
|
|
936
952
|
function createModelTechnicalProfile(input) {
|
|
937
|
-
const
|
|
938
|
-
assertAllowedKeys(
|
|
953
|
+
const record2 = assertRecord(input, "ModelTechnicalProfile");
|
|
954
|
+
assertAllowedKeys(record2, [
|
|
939
955
|
"boundsMetres",
|
|
940
956
|
"dimensionsMetres",
|
|
941
957
|
"triangleCount",
|
|
@@ -947,22 +963,22 @@ function createModelTechnicalProfile(input) {
|
|
|
947
963
|
"partitionCount",
|
|
948
964
|
"partitionCellMetres"
|
|
949
965
|
], "ModelTechnicalProfile");
|
|
950
|
-
const boundsMetres = createBounds(
|
|
951
|
-
const dimensionsMetres = createDimensions(
|
|
966
|
+
const boundsMetres = createBounds(record2.boundsMetres, "ModelTechnicalProfile.boundsMetres");
|
|
967
|
+
const dimensionsMetres = createDimensions(record2.dimensionsMetres, "ModelTechnicalProfile.dimensionsMetres");
|
|
952
968
|
const derivedDimensions = boundsMetres.max.map((axis, index) => axis - boundsMetres.min[index]);
|
|
953
969
|
const suppliedDimensions = [dimensionsMetres.width, dimensionsMetres.height, dimensionsMetres.depth];
|
|
954
970
|
if (derivedDimensions.some((axis, index) => Math.abs(axis - suppliedDimensions[index]) > Math.max(1e-6, Math.abs(axis) * 1e-6))) {
|
|
955
971
|
throw new Error("ModelTechnicalProfile dimensionsMetres must match boundsMetres.");
|
|
956
972
|
}
|
|
957
|
-
const byteLength = requiredInteger(
|
|
973
|
+
const byteLength = requiredInteger(record2.byteLength, "ModelTechnicalProfile.byteLength", 1, Number.MAX_SAFE_INTEGER);
|
|
958
974
|
const textureByteLength = requiredInteger(
|
|
959
|
-
|
|
975
|
+
record2.textureByteLength,
|
|
960
976
|
"ModelTechnicalProfile.textureByteLength",
|
|
961
977
|
0,
|
|
962
978
|
Number.MAX_SAFE_INTEGER
|
|
963
979
|
);
|
|
964
980
|
const maxTextureDimensionPx = requiredInteger(
|
|
965
|
-
|
|
981
|
+
record2.maxTextureDimensionPx,
|
|
966
982
|
"ModelTechnicalProfile.maxTextureDimensionPx",
|
|
967
983
|
0,
|
|
968
984
|
32768
|
|
@@ -978,15 +994,15 @@ function createModelTechnicalProfile(input) {
|
|
|
978
994
|
return deepFreeze({
|
|
979
995
|
boundsMetres,
|
|
980
996
|
dimensionsMetres,
|
|
981
|
-
triangleCount: requiredInteger(
|
|
997
|
+
triangleCount: requiredInteger(record2.triangleCount, "ModelTechnicalProfile.triangleCount", 1, 1e9),
|
|
982
998
|
byteLength,
|
|
983
999
|
textureByteLength,
|
|
984
1000
|
maxTextureDimensionPx,
|
|
985
|
-
lodCount: requiredInteger(
|
|
986
|
-
hasCollision: requiredBoolean(
|
|
987
|
-
partitionCount: requiredInteger(
|
|
1001
|
+
lodCount: requiredInteger(record2.lodCount, "ModelTechnicalProfile.lodCount", 1, 4),
|
|
1002
|
+
hasCollision: requiredBoolean(record2.hasCollision, "ModelTechnicalProfile.hasCollision"),
|
|
1003
|
+
partitionCount: requiredInteger(record2.partitionCount, "ModelTechnicalProfile.partitionCount", 1, 1e4),
|
|
988
1004
|
partitionCellMetres: requiredFiniteNumber(
|
|
989
|
-
|
|
1005
|
+
record2.partitionCellMetres,
|
|
990
1006
|
"ModelTechnicalProfile.partitionCellMetres",
|
|
991
1007
|
Number.EPSILON,
|
|
992
1008
|
1e6
|
|
@@ -994,19 +1010,19 @@ function createModelTechnicalProfile(input) {
|
|
|
994
1010
|
});
|
|
995
1011
|
}
|
|
996
1012
|
function createAttribution(value) {
|
|
997
|
-
const
|
|
998
|
-
assertAllowedKeys(
|
|
999
|
-
const sourcePageUri =
|
|
1013
|
+
const record2 = assertRecord(value, "ModelRightsAssessment.attribution");
|
|
1014
|
+
assertAllowedKeys(record2, ["title", "creator", "notice", "sourcePageUri"], "ModelRightsAssessment.attribution");
|
|
1015
|
+
const sourcePageUri = record2.sourcePageUri === void 0 ? void 0 : assertPublicPageUri(record2.sourcePageUri, "ModelRightsAssessment.attribution.sourcePageUri");
|
|
1000
1016
|
return {
|
|
1001
|
-
title: requiredString(
|
|
1002
|
-
creator: requiredString(
|
|
1003
|
-
notice: requiredString(
|
|
1017
|
+
title: requiredString(record2.title, "ModelRightsAssessment.attribution.title", 256),
|
|
1018
|
+
creator: requiredString(record2.creator, "ModelRightsAssessment.attribution.creator", 256),
|
|
1019
|
+
notice: requiredString(record2.notice, "ModelRightsAssessment.attribution.notice", 512),
|
|
1004
1020
|
...sourcePageUri === void 0 ? {} : { sourcePageUri }
|
|
1005
1021
|
};
|
|
1006
1022
|
}
|
|
1007
1023
|
function createModelRightsAssessment(input) {
|
|
1008
|
-
const
|
|
1009
|
-
assertAllowedKeys(
|
|
1024
|
+
const record2 = assertRecord(input, "ModelRightsAssessment");
|
|
1025
|
+
assertAllowedKeys(record2, [
|
|
1010
1026
|
"decisionId",
|
|
1011
1027
|
"decisionToken",
|
|
1012
1028
|
"policyId",
|
|
@@ -1020,8 +1036,8 @@ function createModelRightsAssessment(input) {
|
|
|
1020
1036
|
"attribution",
|
|
1021
1037
|
"reviewedAt"
|
|
1022
1038
|
], "ModelRightsAssessment");
|
|
1023
|
-
const status = requiredEnum(
|
|
1024
|
-
const attribution =
|
|
1039
|
+
const status = requiredEnum(record2.status, MODEL_RIGHTS_STATUSES, "ModelRightsAssessment.status");
|
|
1040
|
+
const attribution = record2.attribution === void 0 ? void 0 : createAttribution(record2.attribution);
|
|
1025
1041
|
if (status === "attribution-required" && attribution === void 0) {
|
|
1026
1042
|
throw new Error("ModelRightsAssessment attribution is required for attribution-required rights.");
|
|
1027
1043
|
}
|
|
@@ -1029,29 +1045,29 @@ function createModelRightsAssessment(input) {
|
|
|
1029
1045
|
throw new Error("ModelRightsAssessment attribution-required rights require a public attribution sourcePageUri.");
|
|
1030
1046
|
}
|
|
1031
1047
|
return deepFreeze({
|
|
1032
|
-
decisionId: requiredToken(
|
|
1048
|
+
decisionId: requiredToken(record2.decisionId, "ModelRightsAssessment.decisionId"),
|
|
1033
1049
|
decisionToken: requiredAttestationToken(
|
|
1034
|
-
|
|
1050
|
+
record2.decisionToken,
|
|
1035
1051
|
"ModelRightsAssessment.decisionToken"
|
|
1036
1052
|
),
|
|
1037
|
-
policyId: requiredToken(
|
|
1038
|
-
policyVersion: assetVersion(
|
|
1039
|
-
sourceId: requiredToken(
|
|
1040
|
-
sourceAssetId: opaqueSourceAssetId(
|
|
1053
|
+
policyId: requiredToken(record2.policyId, "ModelRightsAssessment.policyId"),
|
|
1054
|
+
policyVersion: assetVersion(record2.policyVersion, "ModelRightsAssessment.policyVersion"),
|
|
1055
|
+
sourceId: requiredToken(record2.sourceId, "ModelRightsAssessment.sourceId"),
|
|
1056
|
+
sourceAssetId: opaqueSourceAssetId(record2.sourceAssetId, "ModelRightsAssessment.sourceAssetId"),
|
|
1041
1057
|
sourceContentHash: requiredSha256(
|
|
1042
|
-
|
|
1058
|
+
record2.sourceContentHash,
|
|
1043
1059
|
"ModelRightsAssessment.sourceContentHash"
|
|
1044
1060
|
),
|
|
1045
1061
|
status,
|
|
1046
|
-
licenseId: requiredToken(
|
|
1047
|
-
evidencePageUri: assertPublicPageUri(
|
|
1062
|
+
licenseId: requiredToken(record2.licenseId, "ModelRightsAssessment.licenseId"),
|
|
1063
|
+
evidencePageUri: assertPublicPageUri(record2.evidencePageUri, "ModelRightsAssessment.evidencePageUri"),
|
|
1048
1064
|
...attribution === void 0 ? {} : { attribution },
|
|
1049
|
-
reviewedAt: requiredTimestamp(
|
|
1065
|
+
reviewedAt: requiredTimestamp(record2.reviewedAt, "ModelRightsAssessment.reviewedAt")
|
|
1050
1066
|
});
|
|
1051
1067
|
}
|
|
1052
1068
|
function createModelProvenance(input) {
|
|
1053
|
-
const
|
|
1054
|
-
assertAllowedKeys(
|
|
1069
|
+
const record2 = assertRecord(input, "ModelProvenance");
|
|
1070
|
+
assertAllowedKeys(record2, [
|
|
1055
1071
|
"kind",
|
|
1056
1072
|
"sourceId",
|
|
1057
1073
|
"sourceAssetId",
|
|
@@ -1059,9 +1075,9 @@ function createModelProvenance(input) {
|
|
|
1059
1075
|
"contentHash",
|
|
1060
1076
|
"capturedAt"
|
|
1061
1077
|
], "ModelProvenance");
|
|
1062
|
-
const kind = requiredEnum(
|
|
1063
|
-
const sourceAssetId = opaqueSourceAssetId(
|
|
1064
|
-
const sourcePageUri =
|
|
1078
|
+
const kind = requiredEnum(record2.kind, MODEL_PROVENANCE_KINDS, "ModelProvenance kind");
|
|
1079
|
+
const sourceAssetId = opaqueSourceAssetId(record2.sourceAssetId);
|
|
1080
|
+
const sourcePageUri = record2.sourcePageUri === void 0 ? void 0 : assertPublicPageUri(record2.sourcePageUri, "ModelProvenance.sourcePageUri");
|
|
1065
1081
|
if (kind === "provider" && sourcePageUri === void 0) {
|
|
1066
1082
|
throw new Error("ModelProvenance provider provenance requires sourcePageUri.");
|
|
1067
1083
|
}
|
|
@@ -1070,16 +1086,16 @@ function createModelProvenance(input) {
|
|
|
1070
1086
|
}
|
|
1071
1087
|
return deepFreeze({
|
|
1072
1088
|
kind,
|
|
1073
|
-
sourceId: requiredToken(
|
|
1089
|
+
sourceId: requiredToken(record2.sourceId, "ModelProvenance.sourceId"),
|
|
1074
1090
|
sourceAssetId,
|
|
1075
1091
|
...sourcePageUri === void 0 ? {} : { sourcePageUri },
|
|
1076
|
-
contentHash: requiredSha256(
|
|
1077
|
-
capturedAt: requiredTimestamp(
|
|
1092
|
+
contentHash: requiredSha256(record2.contentHash, "ModelProvenance.contentHash"),
|
|
1093
|
+
capturedAt: requiredTimestamp(record2.capturedAt, "ModelProvenance.capturedAt")
|
|
1078
1094
|
});
|
|
1079
1095
|
}
|
|
1080
1096
|
function createModelAssetRef(input) {
|
|
1081
|
-
const
|
|
1082
|
-
assertAllowedKeys(
|
|
1097
|
+
const record2 = assertRecord(input, "ModelAssetRef");
|
|
1098
|
+
assertAllowedKeys(record2, [
|
|
1083
1099
|
"contractVersion",
|
|
1084
1100
|
"assetId",
|
|
1085
1101
|
"version",
|
|
@@ -1087,10 +1103,10 @@ function createModelAssetRef(input) {
|
|
|
1087
1103
|
"contentHash",
|
|
1088
1104
|
"runtimeManifestUri"
|
|
1089
1105
|
], "ModelAssetRef");
|
|
1090
|
-
assertContractVersion(
|
|
1091
|
-
const validatedAssetId = assetId(
|
|
1092
|
-
const validatedVersion = assertImmutableAssetVersion(
|
|
1093
|
-
const runtimeManifestUri = assertModelUri(
|
|
1106
|
+
assertContractVersion(record2, "ModelAssetRef");
|
|
1107
|
+
const validatedAssetId = assetId(record2.assetId, "ModelAssetRef.assetId");
|
|
1108
|
+
const validatedVersion = assertImmutableAssetVersion(record2.version);
|
|
1109
|
+
const runtimeManifestUri = assertModelUri(record2.runtimeManifestUri, "ModelAssetRef.runtimeManifestUri");
|
|
1094
1110
|
const expectedManifestUri = `mcp://models/catalog/${validatedAssetId}/versions/${validatedVersion}/manifest`;
|
|
1095
1111
|
if (runtimeManifestUri !== expectedManifestUri) {
|
|
1096
1112
|
throw new Error(
|
|
@@ -1101,21 +1117,21 @@ function createModelAssetRef(input) {
|
|
|
1101
1117
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1102
1118
|
assetId: validatedAssetId,
|
|
1103
1119
|
version: validatedVersion,
|
|
1104
|
-
kind: requiredEnum(
|
|
1105
|
-
contentHash: requiredSha256(
|
|
1120
|
+
kind: requiredEnum(record2.kind, MODEL_ASSET_KINDS, "ModelAssetRef.kind"),
|
|
1121
|
+
contentHash: requiredSha256(record2.contentHash, "ModelAssetRef.contentHash"),
|
|
1106
1122
|
runtimeManifestUri
|
|
1107
1123
|
});
|
|
1108
1124
|
}
|
|
1109
1125
|
function createCandidateAssetRef(value) {
|
|
1110
|
-
const
|
|
1111
|
-
const disposition = requiredEnum(
|
|
1126
|
+
const record2 = assertRecord(value, "ModelCandidate.assetRef");
|
|
1127
|
+
const disposition = requiredEnum(record2.disposition, ["existing", "proposed"], "ModelCandidate.assetRef.disposition");
|
|
1112
1128
|
if (disposition === "existing") {
|
|
1113
|
-
assertAllowedKeys(
|
|
1114
|
-
const asset = createModelAssetRef(
|
|
1115
|
-
if (
|
|
1129
|
+
assertAllowedKeys(record2, ["disposition", "asset", "kind", "contentHash"], "ModelCandidate.assetRef");
|
|
1130
|
+
const asset = createModelAssetRef(record2.asset);
|
|
1131
|
+
if (record2.kind !== void 0 && record2.kind !== asset.kind) {
|
|
1116
1132
|
throw new Error("ModelCandidate existing assetRef kind must match asset.");
|
|
1117
1133
|
}
|
|
1118
|
-
if (
|
|
1134
|
+
if (record2.contentHash !== void 0 && record2.contentHash !== asset.contentHash) {
|
|
1119
1135
|
throw new Error("ModelCandidate existing assetRef contentHash must match asset.");
|
|
1120
1136
|
}
|
|
1121
1137
|
return {
|
|
@@ -1125,27 +1141,27 @@ function createCandidateAssetRef(value) {
|
|
|
1125
1141
|
asset
|
|
1126
1142
|
};
|
|
1127
1143
|
}
|
|
1128
|
-
assertAllowedKeys(
|
|
1144
|
+
assertAllowedKeys(record2, ["disposition", "proposalId", "kind", "contentHash"], "ModelCandidate.assetRef");
|
|
1129
1145
|
return {
|
|
1130
1146
|
disposition,
|
|
1131
|
-
proposalId: requiredToken(
|
|
1132
|
-
kind: requiredEnum(
|
|
1133
|
-
contentHash: requiredSha256(
|
|
1147
|
+
proposalId: requiredToken(record2.proposalId, "ModelCandidate.assetRef.proposalId"),
|
|
1148
|
+
kind: requiredEnum(record2.kind, MODEL_ASSET_KINDS, "ModelCandidate.assetRef.kind"),
|
|
1149
|
+
contentHash: requiredSha256(record2.contentHash, "ModelCandidate.assetRef.contentHash")
|
|
1134
1150
|
};
|
|
1135
1151
|
}
|
|
1136
1152
|
function createConfirmationView(value, index) {
|
|
1137
1153
|
const fieldName = `ModelCandidate.views[${index}]`;
|
|
1138
|
-
const
|
|
1139
|
-
assertAllowedKeys(
|
|
1140
|
-
const width = requiredInteger(
|
|
1141
|
-
const height = requiredInteger(
|
|
1142
|
-
if (
|
|
1154
|
+
const record2 = assertRecord(value, fieldName);
|
|
1155
|
+
assertAllowedKeys(record2, ["kind", "imageUri", "sha256", "contentType", "width", "height"], fieldName);
|
|
1156
|
+
const width = requiredInteger(record2.width, `${fieldName}.width`, MODEL_CONFIRMATION_VIEW_SIZE_PX, MODEL_CONFIRMATION_VIEW_SIZE_PX);
|
|
1157
|
+
const height = requiredInteger(record2.height, `${fieldName}.height`, MODEL_CONFIRMATION_VIEW_SIZE_PX, MODEL_CONFIRMATION_VIEW_SIZE_PX);
|
|
1158
|
+
if (record2.contentType !== "image/png") {
|
|
1143
1159
|
throw new Error(`${fieldName}.contentType must be image/png.`);
|
|
1144
1160
|
}
|
|
1145
1161
|
return {
|
|
1146
|
-
kind: requiredEnum(
|
|
1147
|
-
imageUri: assertModelUri(
|
|
1148
|
-
sha256: requiredSha256(
|
|
1162
|
+
kind: requiredEnum(record2.kind, MODEL_CONFIRMATION_VIEW_KINDS, `${fieldName}.kind`),
|
|
1163
|
+
imageUri: assertModelUri(record2.imageUri, `${fieldName}.imageUri`),
|
|
1164
|
+
sha256: requiredSha256(record2.sha256, `${fieldName}.sha256`),
|
|
1149
1165
|
contentType: "image/png",
|
|
1150
1166
|
width,
|
|
1151
1167
|
height
|
|
@@ -1179,8 +1195,8 @@ function createConfirmationViews(value, resolutionId, candidateId) {
|
|
|
1179
1195
|
return [ordered[0], ordered[1], ordered[2], ordered[3]];
|
|
1180
1196
|
}
|
|
1181
1197
|
function createRenderEvidence(value, manifest, views) {
|
|
1182
|
-
const
|
|
1183
|
-
assertAllowedKeys(
|
|
1198
|
+
const record2 = assertRecord(value, "ModelCandidate.renderEvidence");
|
|
1199
|
+
assertAllowedKeys(record2, [
|
|
1184
1200
|
"renderId",
|
|
1185
1201
|
"rendererId",
|
|
1186
1202
|
"rendererVersion",
|
|
@@ -1192,35 +1208,35 @@ function createRenderEvidence(value, manifest, views) {
|
|
|
1192
1208
|
"renderedAt",
|
|
1193
1209
|
"attestationToken"
|
|
1194
1210
|
], "ModelCandidate.renderEvidence");
|
|
1195
|
-
if (
|
|
1211
|
+
if (record2.processingManifestId !== manifest.manifestId) {
|
|
1196
1212
|
throw new Error("ModelCandidate.renderEvidence.processingManifestId must match processingManifest.");
|
|
1197
1213
|
}
|
|
1198
|
-
if (
|
|
1214
|
+
if (record2.sourceContentHash !== manifest.lods[0]?.resource.sha256) {
|
|
1199
1215
|
throw new Error("ModelCandidate.renderEvidence.sourceContentHash must match canonical LOD0.");
|
|
1200
1216
|
}
|
|
1201
|
-
if (!Array.isArray(
|
|
1217
|
+
if (!Array.isArray(record2.viewSha256s) || record2.viewSha256s.length !== 4) {
|
|
1202
1218
|
throw new Error("ModelCandidate.renderEvidence.viewSha256s must contain four ordered hashes.");
|
|
1203
1219
|
}
|
|
1204
|
-
const viewSha256s =
|
|
1220
|
+
const viewSha256s = record2.viewSha256s.map((digest, index) => requiredSha256(digest, `ModelCandidate.renderEvidence.viewSha256s[${index}]`));
|
|
1205
1221
|
if (viewSha256s.some((digest, index) => digest !== views[index]?.sha256)) {
|
|
1206
1222
|
throw new Error("ModelCandidate.renderEvidence view hashes must match the candidate view pack.");
|
|
1207
1223
|
}
|
|
1208
|
-
const renderedAt = requiredTimestamp(
|
|
1224
|
+
const renderedAt = requiredTimestamp(record2.renderedAt, "ModelCandidate.renderEvidence.renderedAt");
|
|
1209
1225
|
if (Date.parse(renderedAt) < Date.parse(manifest.processedAt)) {
|
|
1210
1226
|
throw new Error("ModelCandidate.renderEvidence.renderedAt must not precede processing.");
|
|
1211
1227
|
}
|
|
1212
1228
|
return {
|
|
1213
|
-
renderId: requiredToken(
|
|
1214
|
-
rendererId: requiredToken(
|
|
1215
|
-
rendererVersion: assetVersion(
|
|
1216
|
-
settingsId: requiredToken(
|
|
1217
|
-
settingsVersion: assetVersion(
|
|
1229
|
+
renderId: requiredToken(record2.renderId, "ModelCandidate.renderEvidence.renderId"),
|
|
1230
|
+
rendererId: requiredToken(record2.rendererId, "ModelCandidate.renderEvidence.rendererId"),
|
|
1231
|
+
rendererVersion: assetVersion(record2.rendererVersion, "ModelCandidate.renderEvidence.rendererVersion"),
|
|
1232
|
+
settingsId: requiredToken(record2.settingsId, "ModelCandidate.renderEvidence.settingsId"),
|
|
1233
|
+
settingsVersion: assetVersion(record2.settingsVersion, "ModelCandidate.renderEvidence.settingsVersion"),
|
|
1218
1234
|
processingManifestId: manifest.manifestId,
|
|
1219
1235
|
sourceContentHash: manifest.contentHash,
|
|
1220
1236
|
viewSha256s: [viewSha256s[0], viewSha256s[1], viewSha256s[2], viewSha256s[3]],
|
|
1221
1237
|
renderedAt,
|
|
1222
1238
|
attestationToken: requiredAttestationToken(
|
|
1223
|
-
|
|
1239
|
+
record2.attestationToken,
|
|
1224
1240
|
"ModelCandidate.renderEvidence.attestationToken"
|
|
1225
1241
|
)
|
|
1226
1242
|
};
|
|
@@ -1231,8 +1247,8 @@ function createCandidateHardGates(value, provenance, manifest) {
|
|
|
1231
1247
|
}
|
|
1232
1248
|
const gates = value.map((item, index) => {
|
|
1233
1249
|
const fieldName = `ModelCandidate.hardGates[${index}]`;
|
|
1234
|
-
const
|
|
1235
|
-
assertAllowedKeys(
|
|
1250
|
+
const record2 = assertRecord(item, fieldName);
|
|
1251
|
+
assertAllowedKeys(record2, [
|
|
1236
1252
|
"kind",
|
|
1237
1253
|
"outcome",
|
|
1238
1254
|
"validatorId",
|
|
@@ -1242,26 +1258,26 @@ function createCandidateHardGates(value, provenance, manifest) {
|
|
|
1242
1258
|
"evaluatedAt",
|
|
1243
1259
|
"attestationToken"
|
|
1244
1260
|
], fieldName);
|
|
1245
|
-
const kind = requiredEnum(
|
|
1246
|
-
const subjectContentHash = requiredSha256(
|
|
1261
|
+
const kind = requiredEnum(record2.kind, MODEL_CANDIDATE_HARD_GATE_KINDS, `${fieldName}.kind`);
|
|
1262
|
+
const subjectContentHash = requiredSha256(record2.subjectContentHash, `${fieldName}.subjectContentHash`);
|
|
1247
1263
|
const expectedHash = kind === "malware-scan" ? provenance.contentHash : manifest.closureHash;
|
|
1248
1264
|
if (subjectContentHash !== expectedHash) {
|
|
1249
1265
|
throw new Error(`${fieldName}.subjectContentHash must match the gate subject.`);
|
|
1250
1266
|
}
|
|
1251
|
-
const outcome = requiredEnum(
|
|
1252
|
-
const reasonCodes2 = stringList(
|
|
1267
|
+
const outcome = requiredEnum(record2.outcome, ["passed", "blocked"], `${fieldName}.outcome`);
|
|
1268
|
+
const reasonCodes2 = stringList(record2.reasonCodes ?? [], `${fieldName}.reasonCodes`, { tokens: true });
|
|
1253
1269
|
if (outcome === "blocked" && reasonCodes2.length === 0) {
|
|
1254
1270
|
throw new Error(`${fieldName}.reasonCodes must explain a blocked hard gate.`);
|
|
1255
1271
|
}
|
|
1256
1272
|
return {
|
|
1257
1273
|
kind,
|
|
1258
1274
|
outcome,
|
|
1259
|
-
validatorId: requiredToken(
|
|
1260
|
-
validatorVersion: assetVersion(
|
|
1275
|
+
validatorId: requiredToken(record2.validatorId, `${fieldName}.validatorId`),
|
|
1276
|
+
validatorVersion: assetVersion(record2.validatorVersion, `${fieldName}.validatorVersion`),
|
|
1261
1277
|
subjectContentHash,
|
|
1262
1278
|
reasonCodes: reasonCodes2,
|
|
1263
|
-
evaluatedAt: requiredTimestamp(
|
|
1264
|
-
attestationToken: requiredAttestationToken(
|
|
1279
|
+
evaluatedAt: requiredTimestamp(record2.evaluatedAt, `${fieldName}.evaluatedAt`),
|
|
1280
|
+
attestationToken: requiredAttestationToken(record2.attestationToken, `${fieldName}.attestationToken`)
|
|
1265
1281
|
};
|
|
1266
1282
|
});
|
|
1267
1283
|
if (new Set(gates.map((gate) => gate.kind)).size !== MODEL_CANDIDATE_HARD_GATE_KINDS.length) {
|
|
@@ -1274,8 +1290,8 @@ function createCandidateHardGates(value, provenance, manifest) {
|
|
|
1274
1290
|
return MODEL_CANDIDATE_HARD_GATE_KINDS.map((kind) => byKind.get(kind));
|
|
1275
1291
|
}
|
|
1276
1292
|
function createModelCandidate(input) {
|
|
1277
|
-
const
|
|
1278
|
-
assertAllowedKeys(
|
|
1293
|
+
const record2 = assertRecord(input, "ModelCandidate");
|
|
1294
|
+
assertAllowedKeys(record2, [
|
|
1279
1295
|
"contractVersion",
|
|
1280
1296
|
"resolutionId",
|
|
1281
1297
|
"candidateId",
|
|
@@ -1291,24 +1307,24 @@ function createModelCandidate(input) {
|
|
|
1291
1307
|
"confirmationToken",
|
|
1292
1308
|
"confirmationRequired"
|
|
1293
1309
|
], "ModelCandidate");
|
|
1294
|
-
assertContractVersion(
|
|
1295
|
-
if (
|
|
1310
|
+
assertContractVersion(record2, "ModelCandidate");
|
|
1311
|
+
if (record2.confirmationRequired !== void 0 && record2.confirmationRequired !== true) {
|
|
1296
1312
|
throw new Error("ModelCandidate confirmationRequired must always be true.");
|
|
1297
1313
|
}
|
|
1298
|
-
if (typeof
|
|
1314
|
+
if (typeof record2.confirmationToken !== "string" || !CONFIRMATION_TOKEN_PATTERN.test(record2.confirmationToken)) {
|
|
1299
1315
|
throw new Error("ModelCandidate confirmationToken must be a bounded opaque token.");
|
|
1300
1316
|
}
|
|
1301
|
-
const resolutionId = requiredModelPathSegment(
|
|
1302
|
-
const candidateId = requiredModelPathSegment(
|
|
1303
|
-
const assetRef = createCandidateAssetRef(
|
|
1304
|
-
const technicalProfile = createModelTechnicalProfile(
|
|
1305
|
-
const processingManifest = createModelProcessingManifest(
|
|
1317
|
+
const resolutionId = requiredModelPathSegment(record2.resolutionId, "ModelCandidate.resolutionId");
|
|
1318
|
+
const candidateId = requiredModelPathSegment(record2.candidateId, "ModelCandidate.candidateId");
|
|
1319
|
+
const assetRef = createCandidateAssetRef(record2.assetRef);
|
|
1320
|
+
const technicalProfile = createModelTechnicalProfile(record2.technicalProfile);
|
|
1321
|
+
const processingManifest = createModelProcessingManifest(record2.processingManifest);
|
|
1306
1322
|
if (processingManifest.resolutionId !== resolutionId || processingManifest.candidateId !== candidateId || processingManifest.kind !== assetRef.kind || processingManifest.contentHash !== assetRef.contentHash || JSON.stringify(processingManifest.technicalProfile) !== JSON.stringify(technicalProfile)) {
|
|
1307
1323
|
throw new Error(
|
|
1308
1324
|
"ModelCandidate processingManifest must match candidateId, asset kind/contentHash, and technicalProfile."
|
|
1309
1325
|
);
|
|
1310
1326
|
}
|
|
1311
|
-
const match = createModelMatchAssessment(
|
|
1327
|
+
const match = createModelMatchAssessment(record2.match);
|
|
1312
1328
|
if (match.candidateId !== candidateId || match.candidateContentHash !== assetRef.contentHash) {
|
|
1313
1329
|
throw new Error("ModelCandidate match assessment must bind candidateId and candidate contentHash.");
|
|
1314
1330
|
}
|
|
@@ -1328,11 +1344,11 @@ function createModelCandidate(input) {
|
|
|
1328
1344
|
"ModelCandidate match reasonCodes must include every failed hard constraint."
|
|
1329
1345
|
);
|
|
1330
1346
|
}
|
|
1331
|
-
const provenance = createModelProvenance(
|
|
1347
|
+
const provenance = createModelProvenance(record2.provenance);
|
|
1332
1348
|
if (provenance.contentHash !== processingManifest.converter.sourceContentHash) {
|
|
1333
1349
|
throw new Error("ModelCandidate provenance contentHash must match converter sourceContentHash.");
|
|
1334
1350
|
}
|
|
1335
|
-
const rights = createModelRightsAssessment(
|
|
1351
|
+
const rights = createModelRightsAssessment(record2.rights);
|
|
1336
1352
|
if (rights.sourceId !== provenance.sourceId || rights.sourceAssetId !== provenance.sourceAssetId || rights.sourceContentHash !== provenance.contentHash) {
|
|
1337
1353
|
throw new Error("ModelCandidate rights decision must bind the exact provenance source and content hash.");
|
|
1338
1354
|
}
|
|
@@ -1344,9 +1360,9 @@ function createModelCandidate(input) {
|
|
|
1344
1360
|
if (processingManifest.fidelityGate.outcome === "blocked" && !match.reasonCodes.includes("fidelity-blocked")) {
|
|
1345
1361
|
throw new Error("ModelCandidate blocked fidelity must be recorded in match reasonCodes.");
|
|
1346
1362
|
}
|
|
1347
|
-
const views = createConfirmationViews(
|
|
1348
|
-
const renderEvidence = createRenderEvidence(
|
|
1349
|
-
const hardGates = createCandidateHardGates(
|
|
1363
|
+
const views = createConfirmationViews(record2.views, resolutionId, candidateId);
|
|
1364
|
+
const renderEvidence = createRenderEvidence(record2.renderEvidence, processingManifest, views);
|
|
1365
|
+
const hardGates = createCandidateHardGates(record2.hardGates, provenance, processingManifest);
|
|
1350
1366
|
return deepFreeze({
|
|
1351
1367
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1352
1368
|
resolutionId,
|
|
@@ -1360,7 +1376,7 @@ function createModelCandidate(input) {
|
|
|
1360
1376
|
views,
|
|
1361
1377
|
renderEvidence,
|
|
1362
1378
|
hardGates,
|
|
1363
|
-
confirmationToken:
|
|
1379
|
+
confirmationToken: record2.confirmationToken,
|
|
1364
1380
|
confirmationRequired: true
|
|
1365
1381
|
});
|
|
1366
1382
|
}
|
|
@@ -1372,8 +1388,8 @@ function isModelCandidateConfirmable(candidate) {
|
|
|
1372
1388
|
return candidate.confirmationRequired && candidate.match.hardConstraintPass && candidate.match.assurance !== "none" && (candidate.rights.status === "allowed" || candidate.rights.status === "attribution-required") && !blockingEvidence && candidate.processingManifest.fidelityGate.outcome !== "blocked" && candidate.hardGates.length === MODEL_CANDIDATE_HARD_GATE_KINDS.length && candidate.hardGates.every((gate) => gate.outcome === "passed") && candidate.views.length === MODEL_CONFIRMATION_VIEW_KINDS.length;
|
|
1373
1389
|
}
|
|
1374
1390
|
function createModelCandidateConfirmation(input, expectedCandidate, expectedResolutionId) {
|
|
1375
|
-
const
|
|
1376
|
-
assertAllowedKeys(
|
|
1391
|
+
const record2 = assertRecord(input, "ModelCandidateConfirmation");
|
|
1392
|
+
assertAllowedKeys(record2, [
|
|
1377
1393
|
"contractVersion",
|
|
1378
1394
|
"confirmationId",
|
|
1379
1395
|
"resolutionId",
|
|
@@ -1384,7 +1400,7 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1384
1400
|
"confirmedAt",
|
|
1385
1401
|
"semanticRiskAccepted"
|
|
1386
1402
|
], "ModelCandidateConfirmation");
|
|
1387
|
-
assertContractVersion(
|
|
1403
|
+
assertContractVersion(record2, "ModelCandidateConfirmation");
|
|
1388
1404
|
const candidate = createModelCandidate(expectedCandidate);
|
|
1389
1405
|
const resolutionId = requiredModelPathSegment(
|
|
1390
1406
|
expectedResolutionId,
|
|
@@ -1395,19 +1411,19 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1395
1411
|
"ModelCandidateConfirmation expected candidate must belong to the expected resolutionId."
|
|
1396
1412
|
);
|
|
1397
1413
|
}
|
|
1398
|
-
if (
|
|
1414
|
+
if (record2.resolutionId !== resolutionId) {
|
|
1399
1415
|
throw new Error("ModelCandidateConfirmation.resolutionId must match the expected resolutionId.");
|
|
1400
1416
|
}
|
|
1401
|
-
if (
|
|
1417
|
+
if (record2.candidateId !== candidate.candidateId) {
|
|
1402
1418
|
throw new Error("ModelCandidateConfirmation.candidateId must match the confirmed candidate.");
|
|
1403
1419
|
}
|
|
1404
|
-
if (typeof
|
|
1420
|
+
if (typeof record2.confirmationToken !== "string" || !CONFIRMATION_TOKEN_PATTERN.test(record2.confirmationToken) || record2.confirmationToken !== candidate.confirmationToken) {
|
|
1405
1421
|
throw new Error("ModelCandidateConfirmation.confirmationToken must match the signed candidate token.");
|
|
1406
1422
|
}
|
|
1407
|
-
if (!Array.isArray(
|
|
1423
|
+
if (!Array.isArray(record2.viewSha256s) || record2.viewSha256s.length !== 4) {
|
|
1408
1424
|
throw new Error("ModelCandidateConfirmation.viewSha256s must contain the ordered four candidate view hashes.");
|
|
1409
1425
|
}
|
|
1410
|
-
const viewSha256s =
|
|
1426
|
+
const viewSha256s = record2.viewSha256s.map((value, index) => requiredSha256(value, `ModelCandidateConfirmation.viewSha256s[${index}]`));
|
|
1411
1427
|
const expectedHashes = candidate.views.map((view) => view.sha256);
|
|
1412
1428
|
if (viewSha256s.some((digest, index) => digest !== expectedHashes[index])) {
|
|
1413
1429
|
throw new Error("ModelCandidateConfirmation view hashes must match the ordered candidate views.");
|
|
@@ -1416,7 +1432,7 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1416
1432
|
throw new Error("ModelCandidateConfirmation candidate is not confirmable because a hard review gate is blocking.");
|
|
1417
1433
|
}
|
|
1418
1434
|
const semanticRiskAccepted = requiredBoolean(
|
|
1419
|
-
|
|
1435
|
+
record2.semanticRiskAccepted,
|
|
1420
1436
|
"ModelCandidateConfirmation.semanticRiskAccepted"
|
|
1421
1437
|
);
|
|
1422
1438
|
if (candidate.match.assurance === "low" && !semanticRiskAccepted) {
|
|
@@ -1425,10 +1441,10 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1425
1441
|
if (candidate.match.assurance === "high" && semanticRiskAccepted) {
|
|
1426
1442
|
throw new Error("ModelCandidateConfirmation high assurance must not record a semantic risk override.");
|
|
1427
1443
|
}
|
|
1428
|
-
const confirmedBy = requiredString(
|
|
1444
|
+
const confirmedBy = requiredString(record2.confirmedBy, "ModelCandidateConfirmation.confirmedBy", 256);
|
|
1429
1445
|
assertNoDirectUrl(confirmedBy, "ModelCandidateConfirmation.confirmedBy");
|
|
1430
1446
|
const confirmedAt = requiredTimestamp(
|
|
1431
|
-
|
|
1447
|
+
record2.confirmedAt,
|
|
1432
1448
|
"ModelCandidateConfirmation.confirmedAt"
|
|
1433
1449
|
);
|
|
1434
1450
|
const latestEvidenceTime = Math.max(
|
|
@@ -1446,7 +1462,7 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1446
1462
|
}
|
|
1447
1463
|
return deepFreeze({
|
|
1448
1464
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1449
|
-
confirmationId: requiredToken(
|
|
1465
|
+
confirmationId: requiredToken(record2.confirmationId, "ModelCandidateConfirmation.confirmationId"),
|
|
1450
1466
|
resolutionId,
|
|
1451
1467
|
candidateId: candidate.candidateId,
|
|
1452
1468
|
confirmationToken: candidate.confirmationToken,
|
|
@@ -1457,33 +1473,33 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1457
1473
|
});
|
|
1458
1474
|
}
|
|
1459
1475
|
function createModelResourceRef(input) {
|
|
1460
|
-
const
|
|
1461
|
-
assertAllowedKeys(
|
|
1462
|
-
const
|
|
1463
|
-
if (!CONTENT_TYPE_PATTERN.test(
|
|
1476
|
+
const record2 = assertRecord(input, "ModelResourceRef");
|
|
1477
|
+
assertAllowedKeys(record2, ["uri", "byteLength", "sha256", "contentType"], "ModelResourceRef");
|
|
1478
|
+
const contentType2 = requiredString(record2.contentType, "ModelResourceRef.contentType", 128);
|
|
1479
|
+
if (!CONTENT_TYPE_PATTERN.test(contentType2)) {
|
|
1464
1480
|
throw new Error("ModelResourceRef.contentType must be a valid media type.");
|
|
1465
1481
|
}
|
|
1466
1482
|
return deepFreeze({
|
|
1467
|
-
uri: assertModelUri(
|
|
1468
|
-
byteLength: requiredInteger(
|
|
1469
|
-
sha256: requiredSha256(
|
|
1470
|
-
contentType
|
|
1483
|
+
uri: assertModelUri(record2.uri, "ModelResourceRef.uri"),
|
|
1484
|
+
byteLength: requiredInteger(record2.byteLength, "ModelResourceRef.byteLength", 1, Number.MAX_SAFE_INTEGER),
|
|
1485
|
+
sha256: requiredSha256(record2.sha256, "ModelResourceRef.sha256"),
|
|
1486
|
+
contentType: contentType2
|
|
1471
1487
|
});
|
|
1472
1488
|
}
|
|
1473
1489
|
function createLodRecord(value, index) {
|
|
1474
1490
|
const fieldName = `ModelProcessingManifest.lods[${index}]`;
|
|
1475
|
-
const
|
|
1476
|
-
assertAllowedKeys(
|
|
1477
|
-
const resource = createModelResourceRef(
|
|
1491
|
+
const record2 = assertRecord(value, fieldName);
|
|
1492
|
+
assertAllowedKeys(record2, ["level", "resource", "triangleCount", "geometricErrorMetres"], fieldName);
|
|
1493
|
+
const resource = createModelResourceRef(record2.resource);
|
|
1478
1494
|
if (resource.contentType !== "model/gltf-binary") {
|
|
1479
1495
|
throw new Error(`${fieldName}.resource.contentType must be model/gltf-binary for canonical GLB output.`);
|
|
1480
1496
|
}
|
|
1481
1497
|
return {
|
|
1482
|
-
level: requiredInteger(
|
|
1498
|
+
level: requiredInteger(record2.level, `${fieldName}.level`, 0, 3),
|
|
1483
1499
|
resource,
|
|
1484
|
-
triangleCount: requiredInteger(
|
|
1500
|
+
triangleCount: requiredInteger(record2.triangleCount, `${fieldName}.triangleCount`, 1, 1e9),
|
|
1485
1501
|
geometricErrorMetres: requiredFiniteNumber(
|
|
1486
|
-
|
|
1502
|
+
record2.geometricErrorMetres,
|
|
1487
1503
|
`${fieldName}.geometricErrorMetres`,
|
|
1488
1504
|
0,
|
|
1489
1505
|
1e6
|
|
@@ -1491,40 +1507,40 @@ function createLodRecord(value, index) {
|
|
|
1491
1507
|
};
|
|
1492
1508
|
}
|
|
1493
1509
|
function createCollisionRecord(value) {
|
|
1494
|
-
const
|
|
1495
|
-
assertAllowedKeys(
|
|
1510
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.collision");
|
|
1511
|
+
assertAllowedKeys(record2, ["kind", "resource"], "ModelProcessingManifest.collision");
|
|
1496
1512
|
const kind = requiredEnum(
|
|
1497
|
-
|
|
1513
|
+
record2.kind,
|
|
1498
1514
|
["none", "box", "convex-hull", "triangle-mesh", "compound"],
|
|
1499
1515
|
"ModelProcessingManifest.collision.kind"
|
|
1500
1516
|
);
|
|
1501
1517
|
if (kind === "none") {
|
|
1502
|
-
if (
|
|
1518
|
+
if (record2.resource !== void 0) {
|
|
1503
1519
|
throw new Error("ModelProcessingManifest collision kind none must not include a resource.");
|
|
1504
1520
|
}
|
|
1505
1521
|
return { kind };
|
|
1506
1522
|
}
|
|
1507
|
-
if (
|
|
1523
|
+
if (record2.resource === void 0) {
|
|
1508
1524
|
throw new Error("ModelProcessingManifest non-empty collision kinds require a resource.");
|
|
1509
1525
|
}
|
|
1510
|
-
const resource = createModelResourceRef(
|
|
1526
|
+
const resource = createModelResourceRef(record2.resource);
|
|
1511
1527
|
if (resource.contentType !== "model/gltf-binary") {
|
|
1512
1528
|
throw new Error("ModelProcessingManifest collision resources must be model/gltf-binary GLBs.");
|
|
1513
1529
|
}
|
|
1514
1530
|
return { kind, resource };
|
|
1515
1531
|
}
|
|
1516
1532
|
function createTransform(value, fieldName) {
|
|
1517
|
-
const
|
|
1518
|
-
assertAllowedKeys(
|
|
1519
|
-
const translationMetres = requiredVector3(
|
|
1520
|
-
if (!Array.isArray(
|
|
1533
|
+
const record2 = assertRecord(value, fieldName);
|
|
1534
|
+
assertAllowedKeys(record2, ["translationMetres", "rotationQuaternion", "scale"], fieldName);
|
|
1535
|
+
const translationMetres = requiredVector3(record2.translationMetres, `${fieldName}.translationMetres`);
|
|
1536
|
+
if (!Array.isArray(record2.rotationQuaternion) || record2.rotationQuaternion.length !== 4) {
|
|
1521
1537
|
throw new Error(`${fieldName}.rotationQuaternion must contain exactly four numbers.`);
|
|
1522
1538
|
}
|
|
1523
1539
|
const rotationQuaternion = [
|
|
1524
|
-
requiredFiniteNumber(
|
|
1525
|
-
requiredFiniteNumber(
|
|
1526
|
-
requiredFiniteNumber(
|
|
1527
|
-
requiredFiniteNumber(
|
|
1540
|
+
requiredFiniteNumber(record2.rotationQuaternion[0], `${fieldName}.rotationQuaternion[0]`, -1, 1),
|
|
1541
|
+
requiredFiniteNumber(record2.rotationQuaternion[1], `${fieldName}.rotationQuaternion[1]`, -1, 1),
|
|
1542
|
+
requiredFiniteNumber(record2.rotationQuaternion[2], `${fieldName}.rotationQuaternion[2]`, -1, 1),
|
|
1543
|
+
requiredFiniteNumber(record2.rotationQuaternion[3], `${fieldName}.rotationQuaternion[3]`, -1, 1)
|
|
1528
1544
|
];
|
|
1529
1545
|
const magnitude = Math.hypot(...rotationQuaternion);
|
|
1530
1546
|
if (Math.abs(magnitude - 1) > 1e-4) {
|
|
@@ -1533,31 +1549,31 @@ function createTransform(value, fieldName) {
|
|
|
1533
1549
|
return {
|
|
1534
1550
|
translationMetres,
|
|
1535
1551
|
rotationQuaternion,
|
|
1536
|
-
scale: requiredVector3(
|
|
1552
|
+
scale: requiredVector3(record2.scale, `${fieldName}.scale`, true)
|
|
1537
1553
|
};
|
|
1538
1554
|
}
|
|
1539
1555
|
function createAssemblyChildAssetRef(value, resolutionId, candidateId, fieldName) {
|
|
1540
|
-
const
|
|
1541
|
-
if (
|
|
1556
|
+
const record2 = assertRecord(value, fieldName);
|
|
1557
|
+
if (record2.disposition !== "staged-derived") {
|
|
1542
1558
|
const catalogRef = createModelAssetRef(value);
|
|
1543
1559
|
if (catalogRef.kind !== "leaf") {
|
|
1544
1560
|
throw new Error("ModelProcessingManifest v1 assembly child assetRef values must be leaf assets.");
|
|
1545
1561
|
}
|
|
1546
1562
|
return catalogRef;
|
|
1547
1563
|
}
|
|
1548
|
-
assertAllowedKeys(
|
|
1564
|
+
assertAllowedKeys(record2, [
|
|
1549
1565
|
"disposition",
|
|
1550
1566
|
"derivedId",
|
|
1551
1567
|
"kind",
|
|
1552
1568
|
"contentHash",
|
|
1553
1569
|
"processingManifestUri"
|
|
1554
1570
|
], fieldName);
|
|
1555
|
-
if (
|
|
1571
|
+
if (record2.kind !== "leaf") {
|
|
1556
1572
|
throw new Error("ModelProcessingManifest staged-derived child assetRef kind must be leaf.");
|
|
1557
1573
|
}
|
|
1558
|
-
const derivedId = requiredModelPathSegment(
|
|
1574
|
+
const derivedId = requiredModelPathSegment(record2.derivedId, `${fieldName}.derivedId`);
|
|
1559
1575
|
const processingManifestUri = assertModelUri(
|
|
1560
|
-
|
|
1576
|
+
record2.processingManifestUri,
|
|
1561
1577
|
`${fieldName}.processingManifestUri`
|
|
1562
1578
|
);
|
|
1563
1579
|
const expectedUri = `mcp://models/resolutions/${resolutionId}/candidates/${candidateId}/children/${derivedId}/manifest`;
|
|
@@ -1568,18 +1584,18 @@ function createAssemblyChildAssetRef(value, resolutionId, candidateId, fieldName
|
|
|
1568
1584
|
disposition: "staged-derived",
|
|
1569
1585
|
derivedId,
|
|
1570
1586
|
kind: "leaf",
|
|
1571
|
-
contentHash: requiredSha256(
|
|
1587
|
+
contentHash: requiredSha256(record2.contentHash, `${fieldName}.contentHash`),
|
|
1572
1588
|
processingManifestUri
|
|
1573
1589
|
};
|
|
1574
1590
|
}
|
|
1575
1591
|
function createAssemblyChild(value, index, resolutionId, candidateId) {
|
|
1576
1592
|
const fieldName = `ModelProcessingManifest.children[${index}]`;
|
|
1577
|
-
const
|
|
1578
|
-
assertAllowedKeys(
|
|
1579
|
-
const instanceId = requiredToken(
|
|
1580
|
-
const parentInstanceId =
|
|
1593
|
+
const record2 = assertRecord(value, fieldName);
|
|
1594
|
+
assertAllowedKeys(record2, ["instanceId", "parentInstanceId", "assetRef", "transform"], fieldName);
|
|
1595
|
+
const instanceId = requiredToken(record2.instanceId, `${fieldName}.instanceId`);
|
|
1596
|
+
const parentInstanceId = record2.parentInstanceId === void 0 ? void 0 : requiredToken(record2.parentInstanceId, `${fieldName}.parentInstanceId`);
|
|
1581
1597
|
const childAssetRef = createAssemblyChildAssetRef(
|
|
1582
|
-
|
|
1598
|
+
record2.assetRef,
|
|
1583
1599
|
resolutionId,
|
|
1584
1600
|
candidateId,
|
|
1585
1601
|
`${fieldName}.assetRef`
|
|
@@ -1588,21 +1604,21 @@ function createAssemblyChild(value, index, resolutionId, candidateId) {
|
|
|
1588
1604
|
instanceId,
|
|
1589
1605
|
...parentInstanceId === void 0 ? {} : { parentInstanceId },
|
|
1590
1606
|
assetRef: childAssetRef,
|
|
1591
|
-
transform: createTransform(
|
|
1607
|
+
transform: createTransform(record2.transform, `${fieldName}.transform`)
|
|
1592
1608
|
};
|
|
1593
1609
|
}
|
|
1594
1610
|
function createDiagnostic(value, fieldName) {
|
|
1595
|
-
const
|
|
1596
|
-
assertAllowedKeys(
|
|
1611
|
+
const record2 = assertRecord(value, fieldName);
|
|
1612
|
+
assertAllowedKeys(record2, ["severity", "code", "message"], fieldName);
|
|
1597
1613
|
return {
|
|
1598
|
-
severity: requiredEnum(
|
|
1599
|
-
code: requiredToken(
|
|
1600
|
-
message: requiredString(
|
|
1614
|
+
severity: requiredEnum(record2.severity, ["info", "warning", "blocking"], `${fieldName}.severity`),
|
|
1615
|
+
code: requiredToken(record2.code, `${fieldName}.code`),
|
|
1616
|
+
message: requiredString(record2.message, `${fieldName}.message`, 1024)
|
|
1601
1617
|
};
|
|
1602
1618
|
}
|
|
1603
1619
|
function createConverterEvidence(value) {
|
|
1604
|
-
const
|
|
1605
|
-
assertAllowedKeys(
|
|
1620
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.converter");
|
|
1621
|
+
assertAllowedKeys(record2, [
|
|
1606
1622
|
"id",
|
|
1607
1623
|
"version",
|
|
1608
1624
|
"sourceFormat",
|
|
@@ -1612,42 +1628,42 @@ function createConverterEvidence(value) {
|
|
|
1612
1628
|
"diagnostics",
|
|
1613
1629
|
"losses"
|
|
1614
1630
|
], "ModelProcessingManifest.converter");
|
|
1615
|
-
if (!Array.isArray(
|
|
1631
|
+
if (!Array.isArray(record2.diagnostics) || record2.diagnostics.length > 100) {
|
|
1616
1632
|
throw new Error("ModelProcessingManifest.converter.diagnostics must be a bounded array.");
|
|
1617
1633
|
}
|
|
1618
|
-
if (!Array.isArray(
|
|
1634
|
+
if (!Array.isArray(record2.losses) || record2.losses.length > 100) {
|
|
1619
1635
|
throw new Error("ModelProcessingManifest.converter.losses must be a bounded array.");
|
|
1620
1636
|
}
|
|
1621
1637
|
return {
|
|
1622
|
-
id: requiredToken(
|
|
1623
|
-
version: assetVersion(
|
|
1624
|
-
sourceFormat: requiredToken(
|
|
1625
|
-
targetFormat: requiredToken(
|
|
1626
|
-
sourceContentHash: requiredSha256(
|
|
1627
|
-
outputContentHash: requiredSha256(
|
|
1628
|
-
diagnostics:
|
|
1629
|
-
losses:
|
|
1638
|
+
id: requiredToken(record2.id, "ModelProcessingManifest.converter.id"),
|
|
1639
|
+
version: assetVersion(record2.version, "ModelProcessingManifest.converter.version"),
|
|
1640
|
+
sourceFormat: requiredToken(record2.sourceFormat, "ModelProcessingManifest.converter.sourceFormat"),
|
|
1641
|
+
targetFormat: requiredToken(record2.targetFormat, "ModelProcessingManifest.converter.targetFormat"),
|
|
1642
|
+
sourceContentHash: requiredSha256(record2.sourceContentHash, "ModelProcessingManifest.converter.sourceContentHash"),
|
|
1643
|
+
outputContentHash: requiredSha256(record2.outputContentHash, "ModelProcessingManifest.converter.outputContentHash"),
|
|
1644
|
+
diagnostics: record2.diagnostics.map((item, index) => createDiagnostic(item, `ModelProcessingManifest.converter.diagnostics[${index}]`)),
|
|
1645
|
+
losses: record2.losses.map((item, index) => createDiagnostic(item, `ModelProcessingManifest.converter.losses[${index}]`))
|
|
1630
1646
|
};
|
|
1631
1647
|
}
|
|
1632
1648
|
function createFidelityEvidence(value, index) {
|
|
1633
1649
|
const fieldName = `ModelProcessingManifest.fidelityEvidence[${index}]`;
|
|
1634
|
-
const
|
|
1635
|
-
assertAllowedKeys(
|
|
1636
|
-
const evidenceResource =
|
|
1650
|
+
const record2 = assertRecord(value, fieldName);
|
|
1651
|
+
assertAllowedKeys(record2, ["aspect", "outcome", "message", "evidenceResource"], fieldName);
|
|
1652
|
+
const evidenceResource = record2.evidenceResource === void 0 ? void 0 : createModelResourceRef(record2.evidenceResource);
|
|
1637
1653
|
return {
|
|
1638
1654
|
aspect: requiredEnum(
|
|
1639
|
-
|
|
1655
|
+
record2.aspect,
|
|
1640
1656
|
["geometry", "materials", "textures", "rigging", "animation", "metadata"],
|
|
1641
1657
|
`${fieldName}.aspect`
|
|
1642
1658
|
),
|
|
1643
|
-
outcome: requiredEnum(
|
|
1644
|
-
message: requiredString(
|
|
1659
|
+
outcome: requiredEnum(record2.outcome, ["preserved", "approximated", "lost"], `${fieldName}.outcome`),
|
|
1660
|
+
message: requiredString(record2.message, `${fieldName}.message`, 1024),
|
|
1645
1661
|
...evidenceResource === void 0 ? {} : { evidenceResource }
|
|
1646
1662
|
};
|
|
1647
1663
|
}
|
|
1648
1664
|
function createCollisionPolicyEvidence(value, collision) {
|
|
1649
|
-
const
|
|
1650
|
-
assertAllowedKeys(
|
|
1665
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.collisionPolicy");
|
|
1666
|
+
assertAllowedKeys(record2, [
|
|
1651
1667
|
"profileId",
|
|
1652
1668
|
"profileVersion",
|
|
1653
1669
|
"disposition",
|
|
@@ -1655,7 +1671,7 @@ function createCollisionPolicyEvidence(value, collision) {
|
|
|
1655
1671
|
"decisionToken"
|
|
1656
1672
|
], "ModelProcessingManifest.collisionPolicy");
|
|
1657
1673
|
const disposition = requiredEnum(
|
|
1658
|
-
|
|
1674
|
+
record2.disposition,
|
|
1659
1675
|
["proxy-required", "none-allowed"],
|
|
1660
1676
|
"ModelProcessingManifest.collisionPolicy.disposition"
|
|
1661
1677
|
);
|
|
@@ -1663,22 +1679,22 @@ function createCollisionPolicyEvidence(value, collision) {
|
|
|
1663
1679
|
throw new Error("ModelProcessingManifest collisionPolicy must explicitly authorize collision none or require a proxy.");
|
|
1664
1680
|
}
|
|
1665
1681
|
return {
|
|
1666
|
-
profileId: requiredToken(
|
|
1682
|
+
profileId: requiredToken(record2.profileId, "ModelProcessingManifest.collisionPolicy.profileId"),
|
|
1667
1683
|
profileVersion: assetVersion(
|
|
1668
|
-
|
|
1684
|
+
record2.profileVersion,
|
|
1669
1685
|
"ModelProcessingManifest.collisionPolicy.profileVersion"
|
|
1670
1686
|
),
|
|
1671
1687
|
disposition,
|
|
1672
|
-
category: requiredToken(
|
|
1688
|
+
category: requiredToken(record2.category, "ModelProcessingManifest.collisionPolicy.category"),
|
|
1673
1689
|
decisionToken: requiredAttestationToken(
|
|
1674
|
-
|
|
1690
|
+
record2.decisionToken,
|
|
1675
1691
|
"ModelProcessingManifest.collisionPolicy.decisionToken"
|
|
1676
1692
|
)
|
|
1677
1693
|
};
|
|
1678
1694
|
}
|
|
1679
1695
|
function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
1680
|
-
const
|
|
1681
|
-
assertAllowedKeys(
|
|
1696
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.fidelityGate");
|
|
1697
|
+
assertAllowedKeys(record2, [
|
|
1682
1698
|
"profileId",
|
|
1683
1699
|
"profileVersion",
|
|
1684
1700
|
"outcome",
|
|
@@ -1687,7 +1703,7 @@ function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
|
1687
1703
|
"decisionToken"
|
|
1688
1704
|
], "ModelProcessingManifest.fidelityGate");
|
|
1689
1705
|
const expectedAspects = ["geometry", "materials", "textures"];
|
|
1690
|
-
if (!Array.isArray(
|
|
1706
|
+
if (!Array.isArray(record2.requiredAspects) || record2.requiredAspects.length !== expectedAspects.length || record2.requiredAspects.some((aspect, index) => aspect !== expectedAspects[index])) {
|
|
1691
1707
|
throw new Error("ModelProcessingManifest.fidelityGate.requiredAspects must be geometry, materials, textures.");
|
|
1692
1708
|
}
|
|
1693
1709
|
const byAspect = new Map(fidelityEvidence.map((evidence) => [evidence.aspect, evidence]));
|
|
@@ -1705,7 +1721,7 @@ function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
|
1705
1721
|
) || hasWarningLoss;
|
|
1706
1722
|
const derivedOutcome = hasBlockingConverterEvidence || geometryLost ? "blocked" : hasUnresolvedFidelity ? "low-only" : "passed";
|
|
1707
1723
|
const outcome = requiredEnum(
|
|
1708
|
-
|
|
1724
|
+
record2.outcome,
|
|
1709
1725
|
["passed", "low-only", "blocked"],
|
|
1710
1726
|
"ModelProcessingManifest.fidelityGate.outcome"
|
|
1711
1727
|
);
|
|
@@ -1713,13 +1729,13 @@ function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
|
1713
1729
|
throw new Error("ModelProcessingManifest fidelityGate outcome must match converter and fidelity evidence.");
|
|
1714
1730
|
}
|
|
1715
1731
|
return {
|
|
1716
|
-
profileId: requiredToken(
|
|
1717
|
-
profileVersion: assetVersion(
|
|
1732
|
+
profileId: requiredToken(record2.profileId, "ModelProcessingManifest.fidelityGate.profileId"),
|
|
1733
|
+
profileVersion: assetVersion(record2.profileVersion, "ModelProcessingManifest.fidelityGate.profileVersion"),
|
|
1718
1734
|
outcome,
|
|
1719
1735
|
requiredAspects: expectedAspects,
|
|
1720
|
-
evaluatedAt: requiredTimestamp(
|
|
1736
|
+
evaluatedAt: requiredTimestamp(record2.evaluatedAt, "ModelProcessingManifest.fidelityGate.evaluatedAt"),
|
|
1721
1737
|
decisionToken: requiredAttestationToken(
|
|
1722
|
-
|
|
1738
|
+
record2.decisionToken,
|
|
1723
1739
|
"ModelProcessingManifest.fidelityGate.decisionToken"
|
|
1724
1740
|
)
|
|
1725
1741
|
};
|
|
@@ -1728,15 +1744,15 @@ function assertCoordinateSystem(value) {
|
|
|
1728
1744
|
if (value === void 0) {
|
|
1729
1745
|
return;
|
|
1730
1746
|
}
|
|
1731
|
-
const
|
|
1732
|
-
assertAllowedKeys(
|
|
1747
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.coordinateSystem");
|
|
1748
|
+
assertAllowedKeys(record2, [
|
|
1733
1749
|
"unit",
|
|
1734
1750
|
"upAxis",
|
|
1735
1751
|
"forwardAxis",
|
|
1736
1752
|
"origin",
|
|
1737
1753
|
"outwardFaceWinding"
|
|
1738
1754
|
], "ModelProcessingManifest.coordinateSystem");
|
|
1739
|
-
if (
|
|
1755
|
+
if (record2.unit !== CANONICAL_MODEL_COORDINATE_SYSTEM.unit || record2.upAxis !== CANONICAL_MODEL_COORDINATE_SYSTEM.upAxis || record2.forwardAxis !== CANONICAL_MODEL_COORDINATE_SYSTEM.forwardAxis || record2.origin !== CANONICAL_MODEL_COORDINATE_SYSTEM.origin || record2.outwardFaceWinding !== CANONICAL_MODEL_COORDINATE_SYSTEM.outwardFaceWinding) {
|
|
1740
1756
|
throw new Error(
|
|
1741
1757
|
"ModelProcessingManifest.coordinateSystem must be metres, Y-up, -Z forward, floor-centred, and counter-clockwise outward."
|
|
1742
1758
|
);
|
|
@@ -1780,8 +1796,8 @@ function assertAssemblyHierarchy(children) {
|
|
|
1780
1796
|
}
|
|
1781
1797
|
}
|
|
1782
1798
|
function createModelProcessingManifest(input) {
|
|
1783
|
-
const
|
|
1784
|
-
assertAllowedKeys(
|
|
1799
|
+
const record2 = assertRecord(input, "ModelProcessingManifest");
|
|
1800
|
+
assertAllowedKeys(record2, [
|
|
1785
1801
|
"contractVersion",
|
|
1786
1802
|
"manifestId",
|
|
1787
1803
|
"resolutionId",
|
|
@@ -1800,23 +1816,23 @@ function createModelProcessingManifest(input) {
|
|
|
1800
1816
|
"fidelityGate",
|
|
1801
1817
|
"processedAt"
|
|
1802
1818
|
], "ModelProcessingManifest");
|
|
1803
|
-
assertContractVersion(
|
|
1819
|
+
assertContractVersion(record2, "ModelProcessingManifest");
|
|
1804
1820
|
const resolutionId = requiredModelPathSegment(
|
|
1805
|
-
|
|
1821
|
+
record2.resolutionId,
|
|
1806
1822
|
"ModelProcessingManifest.resolutionId"
|
|
1807
1823
|
);
|
|
1808
1824
|
const candidateId = requiredModelPathSegment(
|
|
1809
|
-
|
|
1825
|
+
record2.candidateId,
|
|
1810
1826
|
"ModelProcessingManifest.candidateId"
|
|
1811
1827
|
);
|
|
1812
|
-
assertCoordinateSystem(
|
|
1813
|
-
const kind = requiredEnum(
|
|
1814
|
-
const contentHash = requiredSha256(
|
|
1815
|
-
const closureHash = requiredSha256(
|
|
1828
|
+
assertCoordinateSystem(record2.coordinateSystem);
|
|
1829
|
+
const kind = requiredEnum(record2.kind, MODEL_ASSET_KINDS, "ModelProcessingManifest.kind");
|
|
1830
|
+
const contentHash = requiredSha256(record2.contentHash, "ModelProcessingManifest.contentHash");
|
|
1831
|
+
const closureHash = requiredSha256(record2.closureHash, "ModelProcessingManifest.closureHash");
|
|
1816
1832
|
if (kind === "leaf" && closureHash !== contentHash) {
|
|
1817
1833
|
throw new Error("ModelProcessingManifest leaf closureHash must match contentHash.");
|
|
1818
1834
|
}
|
|
1819
|
-
const technicalProfile = createModelTechnicalProfile(
|
|
1835
|
+
const technicalProfile = createModelTechnicalProfile(record2.technicalProfile);
|
|
1820
1836
|
const [minimumX, minimumY, minimumZ] = technicalProfile.boundsMetres.min;
|
|
1821
1837
|
const [maximumX, , maximumZ] = technicalProfile.boundsMetres.max;
|
|
1822
1838
|
if (Math.abs(minimumY) > MODEL_CANONICAL_ORIGIN_TOLERANCE_METRES || Math.abs((minimumX + maximumX) / 2) > MODEL_CANONICAL_ORIGIN_TOLERANCE_METRES || Math.abs((minimumZ + maximumZ) / 2) > MODEL_CANONICAL_ORIGIN_TOLERANCE_METRES) {
|
|
@@ -1824,10 +1840,10 @@ function createModelProcessingManifest(input) {
|
|
|
1824
1840
|
"ModelProcessingManifest floor-centred origin requires minY and the X/Z bounds centre to be zero."
|
|
1825
1841
|
);
|
|
1826
1842
|
}
|
|
1827
|
-
if (!Array.isArray(
|
|
1843
|
+
if (!Array.isArray(record2.lods) || record2.lods.length < 1 || record2.lods.length > 4) {
|
|
1828
1844
|
throw new Error("ModelProcessingManifest.lods must contain one to four adaptive levels beginning with LOD0.");
|
|
1829
1845
|
}
|
|
1830
|
-
const lods =
|
|
1846
|
+
const lods = record2.lods.map(createLodRecord);
|
|
1831
1847
|
for (const [index, lod] of lods.entries()) {
|
|
1832
1848
|
if (lod.level !== index) {
|
|
1833
1849
|
throw new Error("ModelProcessingManifest LOD levels must be contiguous and begin with LOD0.");
|
|
@@ -1873,8 +1889,8 @@ function createModelProcessingManifest(input) {
|
|
|
1873
1889
|
`ModelProcessingManifest.lods[${index}].resource`
|
|
1874
1890
|
);
|
|
1875
1891
|
}
|
|
1876
|
-
const collision = createCollisionRecord(
|
|
1877
|
-
const collisionPolicy = createCollisionPolicyEvidence(
|
|
1892
|
+
const collision = createCollisionRecord(record2.collision);
|
|
1893
|
+
const collisionPolicy = createCollisionPolicyEvidence(record2.collisionPolicy, collision);
|
|
1878
1894
|
if (technicalProfile.hasCollision !== (collision.kind !== "none")) {
|
|
1879
1895
|
throw new Error("ModelProcessingManifest technicalProfile.hasCollision must match collision evidence.");
|
|
1880
1896
|
}
|
|
@@ -1889,10 +1905,10 @@ function createModelProcessingManifest(input) {
|
|
|
1889
1905
|
"ModelProcessingManifest.collision.resource"
|
|
1890
1906
|
);
|
|
1891
1907
|
}
|
|
1892
|
-
if (!Array.isArray(
|
|
1908
|
+
if (!Array.isArray(record2.children) || record2.children.length > 256) {
|
|
1893
1909
|
throw new Error("ModelProcessingManifest.children must be a bounded array.");
|
|
1894
1910
|
}
|
|
1895
|
-
const children =
|
|
1911
|
+
const children = record2.children.map((child, index) => createAssemblyChild(child, index, resolutionId, candidateId));
|
|
1896
1912
|
if (kind === "leaf" && children.length !== 0) {
|
|
1897
1913
|
throw new Error("ModelProcessingManifest leaf models must not contain assembly children.");
|
|
1898
1914
|
}
|
|
@@ -1906,17 +1922,17 @@ function createModelProcessingManifest(input) {
|
|
|
1906
1922
|
throw new Error("ModelProcessingManifest assembly instanceId values must be unique.");
|
|
1907
1923
|
}
|
|
1908
1924
|
assertAssemblyHierarchy(children);
|
|
1909
|
-
const converter = createConverterEvidence(
|
|
1925
|
+
const converter = createConverterEvidence(record2.converter);
|
|
1910
1926
|
if (converter.targetFormat !== "glb") {
|
|
1911
1927
|
throw new Error("ModelProcessingManifest converter.targetFormat must be glb.");
|
|
1912
1928
|
}
|
|
1913
1929
|
if (converter.outputContentHash !== contentHash) {
|
|
1914
1930
|
throw new Error("ModelProcessingManifest converter outputContentHash must match contentHash.");
|
|
1915
1931
|
}
|
|
1916
|
-
if (!Array.isArray(
|
|
1932
|
+
if (!Array.isArray(record2.fidelityEvidence) || record2.fidelityEvidence.length > 100) {
|
|
1917
1933
|
throw new Error("ModelProcessingManifest.fidelityEvidence must be a bounded array.");
|
|
1918
1934
|
}
|
|
1919
|
-
const fidelityEvidence =
|
|
1935
|
+
const fidelityEvidence = record2.fidelityEvidence.map(createFidelityEvidence);
|
|
1920
1936
|
if (new Set(fidelityEvidence.map((evidence) => evidence.aspect)).size !== fidelityEvidence.length) {
|
|
1921
1937
|
throw new Error("ModelProcessingManifest fidelity evidence aspects must be unique.");
|
|
1922
1938
|
}
|
|
@@ -1930,10 +1946,10 @@ function createModelProcessingManifest(input) {
|
|
|
1930
1946
|
);
|
|
1931
1947
|
}
|
|
1932
1948
|
}
|
|
1933
|
-
const fidelityGate = createFidelityGateEvidence(
|
|
1949
|
+
const fidelityGate = createFidelityGateEvidence(record2.fidelityGate, fidelityEvidence, converter);
|
|
1934
1950
|
return deepFreeze({
|
|
1935
1951
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1936
|
-
manifestId: requiredToken(
|
|
1952
|
+
manifestId: requiredToken(record2.manifestId, "ModelProcessingManifest.manifestId"),
|
|
1937
1953
|
resolutionId,
|
|
1938
1954
|
candidateId,
|
|
1939
1955
|
kind,
|
|
@@ -1948,7 +1964,7 @@ function createModelProcessingManifest(input) {
|
|
|
1948
1964
|
converter,
|
|
1949
1965
|
fidelityEvidence,
|
|
1950
1966
|
fidelityGate,
|
|
1951
|
-
processedAt: requiredTimestamp(
|
|
1967
|
+
processedAt: requiredTimestamp(record2.processedAt, "ModelProcessingManifest.processedAt")
|
|
1952
1968
|
});
|
|
1953
1969
|
}
|
|
1954
1970
|
function modelCandidateEquivalent(left, right) {
|
|
@@ -1967,8 +1983,8 @@ function createModelPromotionReceipt(input, resolutionId, candidate, confirmatio
|
|
|
1967
1983
|
if (candidate.assetRef.disposition !== "proposed") {
|
|
1968
1984
|
throw new Error("ModelPromotionReceipt is only valid for a staged proposed candidate.");
|
|
1969
1985
|
}
|
|
1970
|
-
const
|
|
1971
|
-
assertAllowedKeys(
|
|
1986
|
+
const record2 = assertRecord(input, "ModelPromotionReceipt");
|
|
1987
|
+
assertAllowedKeys(record2, [
|
|
1972
1988
|
"contractVersion",
|
|
1973
1989
|
"promotionId",
|
|
1974
1990
|
"resolutionId",
|
|
@@ -1982,17 +1998,17 @@ function createModelPromotionReceipt(input, resolutionId, candidate, confirmatio
|
|
|
1982
1998
|
"promotedAt",
|
|
1983
1999
|
"publicationToken"
|
|
1984
2000
|
], "ModelPromotionReceipt");
|
|
1985
|
-
assertContractVersion(
|
|
1986
|
-
if (
|
|
2001
|
+
assertContractVersion(record2, "ModelPromotionReceipt");
|
|
2002
|
+
if (record2.resolutionId !== resolutionId || record2.candidateId !== candidate.candidateId || record2.proposalId !== candidate.assetRef.proposalId || record2.confirmationId !== confirmation.confirmationId || record2.processingManifestId !== candidate.processingManifest.manifestId || record2.processingContentHash !== candidate.processingManifest.contentHash || record2.closureHash !== candidate.processingManifest.closureHash) {
|
|
1987
2003
|
throw new Error("ModelPromotionReceipt must bind the exact proposal, confirmation, manifest, and closure.");
|
|
1988
2004
|
}
|
|
1989
|
-
const receiptAssetRef = createModelAssetRef(
|
|
2005
|
+
const receiptAssetRef = createModelAssetRef(record2.finalAssetRef);
|
|
1990
2006
|
if (JSON.stringify(receiptAssetRef) !== JSON.stringify(finalAssetRef)) {
|
|
1991
2007
|
throw new Error("ModelPromotionReceipt.finalAssetRef must match the completed finalAssetRef.");
|
|
1992
2008
|
}
|
|
1993
2009
|
return deepFreeze({
|
|
1994
2010
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1995
|
-
promotionId: requiredToken(
|
|
2011
|
+
promotionId: requiredToken(record2.promotionId, "ModelPromotionReceipt.promotionId"),
|
|
1996
2012
|
resolutionId,
|
|
1997
2013
|
candidateId: candidate.candidateId,
|
|
1998
2014
|
proposalId: candidate.assetRef.proposalId,
|
|
@@ -2001,9 +2017,9 @@ function createModelPromotionReceipt(input, resolutionId, candidate, confirmatio
|
|
|
2001
2017
|
processingContentHash: candidate.processingManifest.contentHash,
|
|
2002
2018
|
closureHash: candidate.processingManifest.closureHash,
|
|
2003
2019
|
finalAssetRef: receiptAssetRef,
|
|
2004
|
-
promotedAt: requiredTimestamp(
|
|
2020
|
+
promotedAt: requiredTimestamp(record2.promotedAt, "ModelPromotionReceipt.promotedAt"),
|
|
2005
2021
|
publicationToken: requiredAttestationToken(
|
|
2006
|
-
|
|
2022
|
+
record2.publicationToken,
|
|
2007
2023
|
"ModelPromotionReceipt.publicationToken"
|
|
2008
2024
|
)
|
|
2009
2025
|
});
|
|
@@ -2064,8 +2080,8 @@ function isModelResolutionState(value) {
|
|
|
2064
2080
|
return typeof value === "string" && MODEL_RESOLUTION_STATES.includes(value);
|
|
2065
2081
|
}
|
|
2066
2082
|
function createModelResolution(input) {
|
|
2067
|
-
const
|
|
2068
|
-
assertAllowedKeys(
|
|
2083
|
+
const record2 = assertRecord(input, "ModelResolution");
|
|
2084
|
+
assertAllowedKeys(record2, [
|
|
2069
2085
|
"contractVersion",
|
|
2070
2086
|
"resolutionId",
|
|
2071
2087
|
"request",
|
|
@@ -2081,19 +2097,19 @@ function createModelResolution(input) {
|
|
|
2081
2097
|
"createdAt",
|
|
2082
2098
|
"updatedAt"
|
|
2083
2099
|
], "ModelResolution");
|
|
2084
|
-
assertContractVersion(
|
|
2085
|
-
const resolutionId = requiredModelPathSegment(
|
|
2086
|
-
const request = createModelRequestSpec(
|
|
2087
|
-
const createdAt = requiredTimestamp(
|
|
2088
|
-
const updatedAt = requiredTimestamp(
|
|
2100
|
+
assertContractVersion(record2, "ModelResolution");
|
|
2101
|
+
const resolutionId = requiredModelPathSegment(record2.resolutionId, "ModelResolution.resolutionId");
|
|
2102
|
+
const request = createModelRequestSpec(record2.request);
|
|
2103
|
+
const createdAt = requiredTimestamp(record2.createdAt, "ModelResolution.createdAt");
|
|
2104
|
+
const updatedAt = requiredTimestamp(record2.updatedAt, "ModelResolution.updatedAt");
|
|
2089
2105
|
if (Date.parse(updatedAt) < Date.parse(createdAt)) {
|
|
2090
2106
|
throw new Error("ModelResolution.updatedAt must not precede createdAt.");
|
|
2091
2107
|
}
|
|
2092
|
-
const state = requiredEnum(
|
|
2093
|
-
if (!Array.isArray(
|
|
2108
|
+
const state = requiredEnum(record2.state, MODEL_RESOLUTION_STATES, "ModelResolution.state");
|
|
2109
|
+
if (!Array.isArray(record2.candidates) || record2.candidates.length > MAX_CANDIDATES) {
|
|
2094
2110
|
throw new Error(`ModelResolution.candidates must contain at most ${MAX_CANDIDATES} items.`);
|
|
2095
2111
|
}
|
|
2096
|
-
const candidates =
|
|
2112
|
+
const candidates = record2.candidates.map(createModelCandidate);
|
|
2097
2113
|
if (new Set(candidates.map((candidate) => candidate.candidateId)).size !== candidates.length) {
|
|
2098
2114
|
throw new Error("ModelResolution candidateId values must be unique.");
|
|
2099
2115
|
}
|
|
@@ -2127,8 +2143,8 @@ function createModelResolution(input) {
|
|
|
2127
2143
|
}
|
|
2128
2144
|
}
|
|
2129
2145
|
let bestCandidate;
|
|
2130
|
-
if (
|
|
2131
|
-
const requestedBest = createModelCandidate(
|
|
2146
|
+
if (record2.bestCandidate !== void 0) {
|
|
2147
|
+
const requestedBest = createModelCandidate(record2.bestCandidate);
|
|
2132
2148
|
bestCandidate = candidates.find((candidate) => candidate.candidateId === requestedBest.candidateId);
|
|
2133
2149
|
if (bestCandidate === void 0 || !modelCandidateEquivalent(bestCandidate, requestedBest)) {
|
|
2134
2150
|
throw new Error("ModelResolution.bestCandidate must exactly match a candidate.");
|
|
@@ -2143,11 +2159,11 @@ function createModelResolution(input) {
|
|
|
2143
2159
|
);
|
|
2144
2160
|
}
|
|
2145
2161
|
let confirmation;
|
|
2146
|
-
if (
|
|
2162
|
+
if (record2.confirmation !== void 0) {
|
|
2147
2163
|
if (bestCandidate === void 0) {
|
|
2148
2164
|
throw new Error("ModelResolution confirmation requires bestCandidate.");
|
|
2149
2165
|
}
|
|
2150
|
-
confirmation = createModelCandidateConfirmation(
|
|
2166
|
+
confirmation = createModelCandidateConfirmation(record2.confirmation, bestCandidate, resolutionId);
|
|
2151
2167
|
if (Date.parse(confirmation.confirmedAt) < Date.parse(createdAt) || Date.parse(confirmation.confirmedAt) > Date.parse(updatedAt)) {
|
|
2152
2168
|
throw new Error("ModelResolution confirmation.confirmedAt must fall between createdAt and updatedAt.");
|
|
2153
2169
|
}
|
|
@@ -2161,15 +2177,15 @@ function createModelResolution(input) {
|
|
|
2161
2177
|
if (confirmation !== void 0 && !["promoting", "completed", "failed", "cancelled"].includes(state)) {
|
|
2162
2178
|
throw new Error(`ModelResolution confirmation evidence is not allowed in ${state} state.`);
|
|
2163
2179
|
}
|
|
2164
|
-
const refinementQuestions =
|
|
2165
|
-
const stateReasonCode =
|
|
2180
|
+
const refinementQuestions = record2.refinementQuestions === void 0 ? [] : stringList(record2.refinementQuestions, "ModelResolution.refinementQuestions", { maxItems: 3 });
|
|
2181
|
+
const stateReasonCode = record2.stateReasonCode === void 0 ? void 0 : requiredToken(record2.stateReasonCode, "ModelResolution.stateReasonCode");
|
|
2166
2182
|
if (["failed", "cancelled", "unresolved"].includes(state) && stateReasonCode === void 0) {
|
|
2167
2183
|
throw new Error(`ModelResolution ${state} state requires stateReasonCode.`);
|
|
2168
2184
|
}
|
|
2169
2185
|
if (state === "awaiting-confirmation" && bestCandidate?.match.assurance === "low" && refinementQuestions.length === 0) {
|
|
2170
2186
|
throw new Error("ModelResolution low-assurance bestCandidate requires refinementQuestions.");
|
|
2171
2187
|
}
|
|
2172
|
-
const finalAssetRef =
|
|
2188
|
+
const finalAssetRef = record2.finalAssetRef === void 0 ? void 0 : createModelAssetRef(record2.finalAssetRef);
|
|
2173
2189
|
if (state === "completed" && finalAssetRef === void 0) {
|
|
2174
2190
|
throw new Error("ModelResolution completed state requires finalAssetRef.");
|
|
2175
2191
|
}
|
|
@@ -2180,12 +2196,12 @@ function createModelResolution(input) {
|
|
|
2180
2196
|
throw new Error("ModelResolution finalAssetRef must match bestCandidate kind, hash, and immutable identity.");
|
|
2181
2197
|
}
|
|
2182
2198
|
let promotionReceipt;
|
|
2183
|
-
if (
|
|
2199
|
+
if (record2.promotionReceipt !== void 0) {
|
|
2184
2200
|
if (state !== "completed" || bestCandidate === void 0 || confirmation === void 0 || finalAssetRef === void 0) {
|
|
2185
2201
|
throw new Error("ModelResolution promotionReceipt is only allowed for a completed staged promotion.");
|
|
2186
2202
|
}
|
|
2187
2203
|
promotionReceipt = createModelPromotionReceipt(
|
|
2188
|
-
|
|
2204
|
+
record2.promotionReceipt,
|
|
2189
2205
|
resolutionId,
|
|
2190
2206
|
bestCandidate,
|
|
2191
2207
|
confirmation,
|
|
@@ -2205,7 +2221,7 @@ function createModelResolution(input) {
|
|
|
2205
2221
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
2206
2222
|
resolutionId,
|
|
2207
2223
|
request,
|
|
2208
|
-
attempts: requiredInteger(
|
|
2224
|
+
attempts: requiredInteger(record2.attempts, "ModelResolution.attempts", 1, MAX_ATTEMPTS),
|
|
2209
2225
|
state,
|
|
2210
2226
|
candidates,
|
|
2211
2227
|
...bestCandidate === void 0 ? {} : { bestCandidate },
|
|
@@ -2227,15 +2243,15 @@ function createGeneratorSeed(value, fieldName) {
|
|
|
2227
2243
|
return seed;
|
|
2228
2244
|
}
|
|
2229
2245
|
function createModelGeneratorRequest(input) {
|
|
2230
|
-
const
|
|
2231
|
-
assertAllowedKeys(
|
|
2246
|
+
const record2 = assertRecord(input, "ModelGeneratorRequest");
|
|
2247
|
+
assertAllowedKeys(record2, [
|
|
2232
2248
|
"generationId",
|
|
2233
2249
|
"request",
|
|
2234
2250
|
"budgets",
|
|
2235
2251
|
"seed",
|
|
2236
2252
|
"deadline"
|
|
2237
2253
|
], "ModelGeneratorRequest");
|
|
2238
|
-
const budgets = assertRecord(
|
|
2254
|
+
const budgets = assertRecord(record2.budgets, "ModelGeneratorRequest.budgets");
|
|
2239
2255
|
assertAllowedKeys(budgets, [
|
|
2240
2256
|
"maxDurationMs",
|
|
2241
2257
|
"maxTriangles",
|
|
@@ -2243,7 +2259,7 @@ function createModelGeneratorRequest(input) {
|
|
|
2243
2259
|
"maxTextureBytes",
|
|
2244
2260
|
"maxTextureDimensionPx"
|
|
2245
2261
|
], "ModelGeneratorRequest.budgets");
|
|
2246
|
-
const seed = createGeneratorSeed(
|
|
2262
|
+
const seed = createGeneratorSeed(record2.seed, "ModelGeneratorRequest.seed");
|
|
2247
2263
|
const maxBytes = requiredInteger(
|
|
2248
2264
|
budgets.maxBytes,
|
|
2249
2265
|
"ModelGeneratorRequest budget maxBytes",
|
|
@@ -2260,8 +2276,8 @@ function createModelGeneratorRequest(input) {
|
|
|
2260
2276
|
throw new Error("ModelGeneratorRequest maxTextureBytes must not exceed maxBytes.");
|
|
2261
2277
|
}
|
|
2262
2278
|
return deepFreeze({
|
|
2263
|
-
generationId: requiredModelPathSegment(
|
|
2264
|
-
request: createModelRequestSpec(
|
|
2279
|
+
generationId: requiredModelPathSegment(record2.generationId, "ModelGeneratorRequest.generationId"),
|
|
2280
|
+
request: createModelRequestSpec(record2.request),
|
|
2265
2281
|
budgets: {
|
|
2266
2282
|
maxDurationMs: requiredInteger(budgets.maxDurationMs, "ModelGeneratorRequest budget maxDurationMs", 1, 3e5),
|
|
2267
2283
|
maxTriangles: requiredInteger(budgets.maxTriangles, "ModelGeneratorRequest budget maxTriangles", 1, 1e7),
|
|
@@ -2270,7 +2286,7 @@ function createModelGeneratorRequest(input) {
|
|
|
2270
2286
|
maxTextureDimensionPx: requiredInteger(budgets.maxTextureDimensionPx, "ModelGeneratorRequest budget maxTextureDimensionPx", 1, 32768)
|
|
2271
2287
|
},
|
|
2272
2288
|
seed,
|
|
2273
|
-
deadline: requiredTimestamp(
|
|
2289
|
+
deadline: requiredTimestamp(record2.deadline, "ModelGeneratorRequest.deadline")
|
|
2274
2290
|
});
|
|
2275
2291
|
}
|
|
2276
2292
|
var MODEL_GENERATOR_ARTIFACT_ROLES = [
|
|
@@ -2308,8 +2324,8 @@ function assertGeneratorRetryability(reasonCode2, retryable) {
|
|
|
2308
2324
|
}
|
|
2309
2325
|
function createModelGeneratorArtifact(value, index) {
|
|
2310
2326
|
const fieldName = `ModelGeneratorResult.sourceBundle.artifacts[${index}]`;
|
|
2311
|
-
const
|
|
2312
|
-
assertAllowedKeys(
|
|
2327
|
+
const record2 = assertRecord(value, fieldName);
|
|
2328
|
+
assertAllowedKeys(record2, [
|
|
2313
2329
|
"uri",
|
|
2314
2330
|
"byteLength",
|
|
2315
2331
|
"sha256",
|
|
@@ -2319,12 +2335,12 @@ function createModelGeneratorArtifact(value, index) {
|
|
|
2319
2335
|
"maxTextureDimensionPx"
|
|
2320
2336
|
], fieldName);
|
|
2321
2337
|
const resource = createModelResourceRef({
|
|
2322
|
-
uri:
|
|
2323
|
-
byteLength:
|
|
2324
|
-
sha256:
|
|
2325
|
-
contentType:
|
|
2338
|
+
uri: record2.uri,
|
|
2339
|
+
byteLength: record2.byteLength,
|
|
2340
|
+
sha256: record2.sha256,
|
|
2341
|
+
contentType: record2.contentType
|
|
2326
2342
|
});
|
|
2327
|
-
const role = requiredEnum(
|
|
2343
|
+
const role = requiredEnum(record2.role, MODEL_GENERATOR_ARTIFACT_ROLES, `${fieldName}.role`);
|
|
2328
2344
|
const compatibleContentTypes = {
|
|
2329
2345
|
"model-entrypoint": ["model/gltf-binary", "model/gltf+json"],
|
|
2330
2346
|
"model-dependency": ["model/gltf-binary", "model/gltf+json"],
|
|
@@ -2336,13 +2352,13 @@ function createModelGeneratorArtifact(value, index) {
|
|
|
2336
2352
|
throw new Error(`${fieldName}.contentType is not compatible with artifact role ${role}.`);
|
|
2337
2353
|
}
|
|
2338
2354
|
const textureByteLength = requiredInteger(
|
|
2339
|
-
|
|
2355
|
+
record2.textureByteLength,
|
|
2340
2356
|
`${fieldName}.textureByteLength`,
|
|
2341
2357
|
0,
|
|
2342
2358
|
resource.byteLength
|
|
2343
2359
|
);
|
|
2344
2360
|
const maxTextureDimensionPx = requiredInteger(
|
|
2345
|
-
|
|
2361
|
+
record2.maxTextureDimensionPx,
|
|
2346
2362
|
`${fieldName}.maxTextureDimensionPx`,
|
|
2347
2363
|
0,
|
|
2348
2364
|
32768
|
|
@@ -2359,8 +2375,8 @@ function createModelGeneratorArtifact(value, index) {
|
|
|
2359
2375
|
return { ...resource, role, textureByteLength, maxTextureDimensionPx };
|
|
2360
2376
|
}
|
|
2361
2377
|
function createModelGeneratorUsage(value) {
|
|
2362
|
-
const
|
|
2363
|
-
assertAllowedKeys(
|
|
2378
|
+
const record2 = assertRecord(value, "ModelGeneratorResult.sourceBundle.usage");
|
|
2379
|
+
assertAllowedKeys(record2, [
|
|
2364
2380
|
"durationMs",
|
|
2365
2381
|
"triangleCount",
|
|
2366
2382
|
"byteLength",
|
|
@@ -2368,13 +2384,13 @@ function createModelGeneratorUsage(value) {
|
|
|
2368
2384
|
"maxTextureDimensionPx"
|
|
2369
2385
|
], "ModelGeneratorResult.sourceBundle.usage");
|
|
2370
2386
|
const textureByteLength = requiredInteger(
|
|
2371
|
-
|
|
2387
|
+
record2.textureByteLength,
|
|
2372
2388
|
"ModelGeneratorResult.sourceBundle.usage.textureByteLength",
|
|
2373
2389
|
0,
|
|
2374
2390
|
Number.MAX_SAFE_INTEGER
|
|
2375
2391
|
);
|
|
2376
2392
|
const maxTextureDimensionPx = requiredInteger(
|
|
2377
|
-
|
|
2393
|
+
record2.maxTextureDimensionPx,
|
|
2378
2394
|
"ModelGeneratorResult.sourceBundle.usage.maxTextureDimensionPx",
|
|
2379
2395
|
0,
|
|
2380
2396
|
32768
|
|
@@ -2383,30 +2399,30 @@ function createModelGeneratorUsage(value) {
|
|
|
2383
2399
|
throw new Error("ModelGeneratorResult texture usage bytes and dimensions must both be zero or both positive.");
|
|
2384
2400
|
}
|
|
2385
2401
|
return {
|
|
2386
|
-
durationMs: requiredInteger(
|
|
2387
|
-
triangleCount: requiredInteger(
|
|
2388
|
-
byteLength: requiredInteger(
|
|
2402
|
+
durationMs: requiredInteger(record2.durationMs, "ModelGeneratorResult.sourceBundle.usage.durationMs", 0, 3e5),
|
|
2403
|
+
triangleCount: requiredInteger(record2.triangleCount, "ModelGeneratorResult.sourceBundle.usage.triangleCount", 1, 1e7),
|
|
2404
|
+
byteLength: requiredInteger(record2.byteLength, "ModelGeneratorResult.sourceBundle.usage.byteLength", 1, Number.MAX_SAFE_INTEGER),
|
|
2389
2405
|
textureByteLength,
|
|
2390
2406
|
maxTextureDimensionPx
|
|
2391
2407
|
};
|
|
2392
2408
|
}
|
|
2393
|
-
function createGeneratorTerminalEvidence(
|
|
2394
|
-
const generationId = requiredModelPathSegment(
|
|
2409
|
+
function createGeneratorTerminalEvidence(record2, expected) {
|
|
2410
|
+
const generationId = requiredModelPathSegment(record2.generationId, "ModelGeneratorResult.generationId");
|
|
2395
2411
|
if (generationId !== expected.generationId) {
|
|
2396
2412
|
throw new Error("ModelGeneratorResult.generationId must match the generator request.");
|
|
2397
2413
|
}
|
|
2398
|
-
const diagnosticId =
|
|
2414
|
+
const diagnosticId = record2.diagnosticId === void 0 ? void 0 : requiredToken(record2.diagnosticId, "ModelGeneratorResult.diagnosticId");
|
|
2399
2415
|
return {
|
|
2400
2416
|
generationId,
|
|
2401
|
-
retryable: requiredBoolean(
|
|
2402
|
-
occurredAt: requiredTimestamp(
|
|
2417
|
+
retryable: requiredBoolean(record2.retryable, "ModelGeneratorResult.retryable"),
|
|
2418
|
+
occurredAt: requiredTimestamp(record2.occurredAt, "ModelGeneratorResult.occurredAt"),
|
|
2403
2419
|
...diagnosticId === void 0 ? {} : { diagnosticId }
|
|
2404
2420
|
};
|
|
2405
2421
|
}
|
|
2406
2422
|
function createModelGeneratorResult(input, expectedRequest) {
|
|
2407
2423
|
const expected = createModelGeneratorRequest(expectedRequest);
|
|
2408
|
-
const
|
|
2409
|
-
const status = requiredEnum(
|
|
2424
|
+
const record2 = assertRecord(input, "ModelGeneratorResult");
|
|
2425
|
+
const status = requiredEnum(record2.status, [
|
|
2410
2426
|
"disabled",
|
|
2411
2427
|
"generated",
|
|
2412
2428
|
"unavailable",
|
|
@@ -2414,13 +2430,13 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2414
2430
|
"cancelled",
|
|
2415
2431
|
"budget-exceeded"
|
|
2416
2432
|
], "ModelGeneratorResult.status");
|
|
2417
|
-
const generationId = requiredModelPathSegment(
|
|
2433
|
+
const generationId = requiredModelPathSegment(record2.generationId, "ModelGeneratorResult.generationId");
|
|
2418
2434
|
if (generationId !== expected.generationId) {
|
|
2419
2435
|
throw new Error("ModelGeneratorResult.generationId must match the generator request.");
|
|
2420
2436
|
}
|
|
2421
2437
|
if (status === "disabled") {
|
|
2422
|
-
assertAllowedKeys(
|
|
2423
|
-
if (
|
|
2438
|
+
assertAllowedKeys(record2, ["status", "generationId", "reasonCode"], "ModelGeneratorResult");
|
|
2439
|
+
if (record2.reasonCode !== MODEL_GENERATOR_DISABLED_REASON_CODE) {
|
|
2424
2440
|
throw new Error(
|
|
2425
2441
|
`ModelGeneratorResult.reasonCode must be ${MODEL_GENERATOR_DISABLED_REASON_CODE}.`
|
|
2426
2442
|
);
|
|
@@ -2432,8 +2448,8 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2432
2448
|
});
|
|
2433
2449
|
}
|
|
2434
2450
|
if (status === "generated") {
|
|
2435
|
-
assertAllowedKeys(
|
|
2436
|
-
const sourceBundle = assertRecord(
|
|
2451
|
+
assertAllowedKeys(record2, ["status", "generationId", "sourceBundle"], "ModelGeneratorResult");
|
|
2452
|
+
const sourceBundle = assertRecord(record2.sourceBundle, "ModelGeneratorResult.sourceBundle");
|
|
2437
2453
|
assertAllowedKeys(sourceBundle, [
|
|
2438
2454
|
"bundleId",
|
|
2439
2455
|
"generationId",
|
|
@@ -2533,14 +2549,14 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2533
2549
|
"diagnosticId"
|
|
2534
2550
|
];
|
|
2535
2551
|
if (status === "budget-exceeded") {
|
|
2536
|
-
assertAllowedKeys(
|
|
2537
|
-
if (
|
|
2552
|
+
assertAllowedKeys(record2, [...terminalAllowedKeys, "violations"], "ModelGeneratorResult");
|
|
2553
|
+
if (record2.reasonCode !== "generator-budget-exceeded" || record2.retryable !== false) {
|
|
2538
2554
|
throw new Error("ModelGeneratorResult budget-exceeded must use its fixed reasonCode and retryable false.");
|
|
2539
2555
|
}
|
|
2540
|
-
if (!Array.isArray(
|
|
2556
|
+
if (!Array.isArray(record2.violations) || record2.violations.length < 1 || record2.violations.length > 5) {
|
|
2541
2557
|
throw new Error("ModelGeneratorResult budget violations must contain one to five entries.");
|
|
2542
2558
|
}
|
|
2543
|
-
const violations =
|
|
2559
|
+
const violations = record2.violations.map((value, index) => {
|
|
2544
2560
|
const fieldName = `ModelGeneratorResult.violations[${index}]`;
|
|
2545
2561
|
const violation = assertRecord(value, fieldName);
|
|
2546
2562
|
assertAllowedKeys(violation, ["budget", "limit", "observed"], fieldName);
|
|
@@ -2555,7 +2571,7 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2555
2571
|
if (new Set(violations.map((violation) => violation.budget)).size !== violations.length) {
|
|
2556
2572
|
throw new Error("ModelGeneratorResult budget violation names must be unique.");
|
|
2557
2573
|
}
|
|
2558
|
-
const terminal2 = createGeneratorTerminalEvidence(
|
|
2574
|
+
const terminal2 = createGeneratorTerminalEvidence(record2, expected);
|
|
2559
2575
|
return deepFreeze({
|
|
2560
2576
|
status,
|
|
2561
2577
|
...terminal2,
|
|
@@ -2564,10 +2580,10 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2564
2580
|
violations
|
|
2565
2581
|
});
|
|
2566
2582
|
}
|
|
2567
|
-
assertAllowedKeys(
|
|
2568
|
-
const terminal = createGeneratorTerminalEvidence(
|
|
2583
|
+
assertAllowedKeys(record2, terminalAllowedKeys, "ModelGeneratorResult");
|
|
2584
|
+
const terminal = createGeneratorTerminalEvidence(record2, expected);
|
|
2569
2585
|
if (status === "unavailable") {
|
|
2570
|
-
const reasonCode3 = requiredEnum(
|
|
2586
|
+
const reasonCode3 = requiredEnum(record2.reasonCode, [
|
|
2571
2587
|
"capacity-unavailable",
|
|
2572
2588
|
"provider-unavailable",
|
|
2573
2589
|
"authentication-required",
|
|
@@ -2578,7 +2594,7 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2578
2594
|
return deepFreeze({ status, ...terminal, reasonCode: reasonCode3 });
|
|
2579
2595
|
}
|
|
2580
2596
|
if (status === "failed") {
|
|
2581
|
-
const reasonCode3 = requiredEnum(
|
|
2597
|
+
const reasonCode3 = requiredEnum(record2.reasonCode, [
|
|
2582
2598
|
"provider-failed",
|
|
2583
2599
|
"invalid-generator-output",
|
|
2584
2600
|
"internal-failure"
|
|
@@ -2586,7 +2602,7 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2586
2602
|
assertGeneratorRetryability(reasonCode3, terminal.retryable);
|
|
2587
2603
|
return deepFreeze({ status, ...terminal, reasonCode: reasonCode3 });
|
|
2588
2604
|
}
|
|
2589
|
-
const reasonCode2 = requiredEnum(
|
|
2605
|
+
const reasonCode2 = requiredEnum(record2.reasonCode, [
|
|
2590
2606
|
"caller-aborted",
|
|
2591
2607
|
"deadline-exceeded",
|
|
2592
2608
|
"superseded",
|
|
@@ -2935,9 +2951,9 @@ function asRecord(value, fieldName) {
|
|
|
2935
2951
|
}
|
|
2936
2952
|
return value;
|
|
2937
2953
|
}
|
|
2938
|
-
function exactKeys(
|
|
2954
|
+
function exactKeys(record2, allowed, fieldName) {
|
|
2939
2955
|
const set = new Set(allowed);
|
|
2940
|
-
const unsupported = Object.keys(
|
|
2956
|
+
const unsupported = Object.keys(record2).find((key) => !set.has(key));
|
|
2941
2957
|
if (unsupported !== void 0) throw new Error(`${fieldName} contains an unsupported field.`);
|
|
2942
2958
|
}
|
|
2943
2959
|
function asDenseArray(value, fieldName, minimum, maximum) {
|
|
@@ -2968,9 +2984,9 @@ function assertDataOnlyTree(value, fieldName, seen = /* @__PURE__ */ new WeakSet
|
|
|
2968
2984
|
assertDataOnlyTree(descriptor.value, `${fieldName}[${index}]`, seen, depth + 1);
|
|
2969
2985
|
}
|
|
2970
2986
|
} else {
|
|
2971
|
-
const
|
|
2972
|
-
for (const key of Object.keys(
|
|
2973
|
-
const descriptor = Object.getOwnPropertyDescriptor(
|
|
2987
|
+
const record2 = asRecord(value, fieldName);
|
|
2988
|
+
for (const key of Object.keys(record2)) {
|
|
2989
|
+
const descriptor = Object.getOwnPropertyDescriptor(record2, key);
|
|
2974
2990
|
assertDataOnlyTree(descriptor.value, `${fieldName}.${key}`, seen, depth + 1);
|
|
2975
2991
|
}
|
|
2976
2992
|
}
|
|
@@ -3055,10 +3071,10 @@ function requireVector(value, fieldName) {
|
|
|
3055
3071
|
];
|
|
3056
3072
|
}
|
|
3057
3073
|
function createBounds2(value, fieldName) {
|
|
3058
|
-
const
|
|
3059
|
-
exactKeys(
|
|
3060
|
-
const min = requireVector(
|
|
3061
|
-
const max = requireVector(
|
|
3074
|
+
const record2 = asRecord(value, fieldName);
|
|
3075
|
+
exactKeys(record2, ["min", "max"], fieldName);
|
|
3076
|
+
const min = requireVector(record2.min, `${fieldName}.min`);
|
|
3077
|
+
const max = requireVector(record2.max, `${fieldName}.max`);
|
|
3062
3078
|
if (min.some((coordinate, index) => coordinate >= max[index])) throw new Error(`${fieldName} minimum values must be below maximum values.`);
|
|
3063
3079
|
return { min, max };
|
|
3064
3080
|
}
|
|
@@ -3066,18 +3082,18 @@ function approximatelyEqual(left, right) {
|
|
|
3066
3082
|
return Math.abs(left - right) <= Math.max(1e-6, Math.abs(left) * 1e-6, Math.abs(right) * 1e-6);
|
|
3067
3083
|
}
|
|
3068
3084
|
function createHashAttestation(value, expectedDomain, expectedDigest, fieldName) {
|
|
3069
|
-
const
|
|
3070
|
-
exactKeys(
|
|
3071
|
-
if (
|
|
3085
|
+
const record2 = asRecord(value, fieldName);
|
|
3086
|
+
exactKeys(record2, ["algorithm", "domain", "digest", "attestationToken"], fieldName);
|
|
3087
|
+
if (record2.algorithm !== "sha256" || record2.domain !== expectedDomain) {
|
|
3072
3088
|
throw new Error(`${fieldName} must use the governed SHA-256 domain.`);
|
|
3073
3089
|
}
|
|
3074
|
-
const digest = requireSha256(
|
|
3090
|
+
const digest = requireSha256(record2.digest, `${fieldName}.digest`);
|
|
3075
3091
|
if (digest !== expectedDigest) throw new Error(`${fieldName}.digest must match its bound contract hash.`);
|
|
3076
3092
|
return {
|
|
3077
3093
|
algorithm: "sha256",
|
|
3078
3094
|
domain: expectedDomain,
|
|
3079
3095
|
digest,
|
|
3080
|
-
attestationToken: requireAttestation(
|
|
3096
|
+
attestationToken: requireAttestation(record2.attestationToken, `${fieldName}.attestationToken`)
|
|
3081
3097
|
};
|
|
3082
3098
|
}
|
|
3083
3099
|
function canonicalizePvoxHashProjectionV1(value) {
|
|
@@ -3134,19 +3150,19 @@ function assertFloorCentredBounds(bounds, fieldName) {
|
|
|
3134
3150
|
}
|
|
3135
3151
|
}
|
|
3136
3152
|
function createDimensions2(value, fieldName) {
|
|
3137
|
-
const
|
|
3138
|
-
exactKeys(
|
|
3153
|
+
const record2 = asRecord(value, fieldName);
|
|
3154
|
+
exactKeys(record2, ["width", "height", "depth"], fieldName);
|
|
3139
3155
|
return {
|
|
3140
|
-
width: requireNumber(
|
|
3141
|
-
height: requireNumber(
|
|
3142
|
-
depth: requireNumber(
|
|
3156
|
+
width: requireNumber(record2.width, `${fieldName}.width`, Number.EPSILON, 1e9),
|
|
3157
|
+
height: requireNumber(record2.height, `${fieldName}.height`, Number.EPSILON, 1e9),
|
|
3158
|
+
depth: requireNumber(record2.depth, `${fieldName}.depth`, Number.EPSILON, 1e9)
|
|
3143
3159
|
};
|
|
3144
3160
|
}
|
|
3145
3161
|
function assertCoordinateSystem2(value, fieldName) {
|
|
3146
|
-
const
|
|
3147
|
-
exactKeys(
|
|
3162
|
+
const record2 = asRecord(value, fieldName);
|
|
3163
|
+
exactKeys(record2, ["unit", "upAxis", "forwardAxis", "origin", "outwardFaceWinding"], fieldName);
|
|
3148
3164
|
for (const [key, expected] of Object.entries(CANONICAL_MODEL_COORDINATE_SYSTEM)) {
|
|
3149
|
-
if (
|
|
3165
|
+
if (record2[key] !== expected) throw new Error(`${fieldName} must use the canonical model coordinate system.`);
|
|
3150
3166
|
}
|
|
3151
3167
|
}
|
|
3152
3168
|
function parsePvoxResourceScope(uri) {
|
|
@@ -3171,20 +3187,20 @@ function parsePvoxResourceScope(uri) {
|
|
|
3171
3187
|
throw new Error("PVOX artifact URI must be an authenticated canonical PVOX MCP resource.");
|
|
3172
3188
|
}
|
|
3173
3189
|
function createResource(value) {
|
|
3174
|
-
const
|
|
3175
|
-
exactKeys(
|
|
3176
|
-
const uri = requireString(
|
|
3190
|
+
const record2 = asRecord(value, "PvoxAssetManifestV1.artifact");
|
|
3191
|
+
exactKeys(record2, ["uri", "byteLength", "sha256", "contentType", "authenticated"], "PvoxAssetManifestV1.artifact");
|
|
3192
|
+
const uri = requireString(record2.uri, "PvoxAssetManifestV1.artifact.uri", 512);
|
|
3177
3193
|
if (uri.includes("%") || uri.includes("?") || uri.includes("#") || uri.includes("..") || DIRECT_URL.test(uri)) {
|
|
3178
3194
|
throw new Error("PvoxAssetManifestV1.artifact.uri must be an authenticated canonical MCP resource.");
|
|
3179
3195
|
}
|
|
3180
3196
|
const scope = parsePvoxResourceScope(uri);
|
|
3181
|
-
const sha2562 = requireSha256(
|
|
3197
|
+
const sha2562 = requireSha256(record2.sha256, "PvoxAssetManifestV1.artifact.sha256");
|
|
3182
3198
|
if (scope.sha256 !== sha2562) throw new Error("PvoxAssetManifestV1.artifact URI hash must match sha256.");
|
|
3183
|
-
if (
|
|
3184
|
-
if (
|
|
3199
|
+
if (record2.contentType !== PVOX_CONTENT_TYPE) throw new Error("PvoxAssetManifestV1.artifact.contentType must be the PVOX content type.");
|
|
3200
|
+
if (record2.authenticated !== true) throw new Error("PvoxAssetManifestV1.artifact must be authenticated.");
|
|
3185
3201
|
return {
|
|
3186
3202
|
uri,
|
|
3187
|
-
byteLength: requireInteger(
|
|
3203
|
+
byteLength: requireInteger(record2.byteLength, "PvoxAssetManifestV1.artifact.byteLength", 1, PVOX_DEFAULT_LIMITS.maximumArtifactBytes),
|
|
3188
3204
|
sha256: sha2562,
|
|
3189
3205
|
contentType: PVOX_CONTENT_TYPE,
|
|
3190
3206
|
authenticated: true
|
|
@@ -3192,15 +3208,15 @@ function createResource(value) {
|
|
|
3192
3208
|
}
|
|
3193
3209
|
function createPage(value, index) {
|
|
3194
3210
|
const fieldName = `PvoxAssetManifestV1.pages[${index}]`;
|
|
3195
|
-
const
|
|
3196
|
-
const pageKind = requireEnum(
|
|
3197
|
-
exactKeys(
|
|
3198
|
-
const byteLength = requireInteger(
|
|
3199
|
-
const decodedByteLength = requireInteger(
|
|
3211
|
+
const record2 = asRecord(value, fieldName);
|
|
3212
|
+
const pageKind = requireEnum(record2.pageKind, PVOX_PAGE_KINDS, `${fieldName}.pageKind`);
|
|
3213
|
+
exactKeys(record2, ["pageIndex", "pageKind", "byteOffset", "byteLength", "decodedByteLength", "sha256", "spatialKey"], fieldName);
|
|
3214
|
+
const byteLength = requireInteger(record2.byteLength, `${fieldName}.byteLength`, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES);
|
|
3215
|
+
const decodedByteLength = requireInteger(record2.decodedByteLength, `${fieldName}.decodedByteLength`, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES);
|
|
3200
3216
|
if (decodedByteLength !== byteLength) throw new Error(`${fieldName} decodedByteLength must equal byteLength because PVOX pages are never compressed.`);
|
|
3201
3217
|
let spatialKey;
|
|
3202
|
-
if (
|
|
3203
|
-
const spatialRecord = asRecord(
|
|
3218
|
+
if (record2.spatialKey !== void 0) {
|
|
3219
|
+
const spatialRecord = asRecord(record2.spatialKey, `${fieldName}.spatialKey`);
|
|
3204
3220
|
exactKeys(spatialRecord, ["lodLevel", "partitionIndex", "hierarchyDepth", "minimumMortonCode", "maximumMortonCode"], `${fieldName}.spatialKey`);
|
|
3205
3221
|
const minimumMortonCode = requireString(spatialRecord.minimumMortonCode, `${fieldName}.spatialKey.minimumMortonCode`, 16);
|
|
3206
3222
|
const maximumMortonCode = requireString(spatialRecord.maximumMortonCode, `${fieldName}.spatialKey.maximumMortonCode`, 16);
|
|
@@ -3220,19 +3236,19 @@ function createPage(value, index) {
|
|
|
3220
3236
|
throw new Error(`${fieldName} render/collision field pages require spatialKey; metadata/LOD-structure pages forbid it.`);
|
|
3221
3237
|
}
|
|
3222
3238
|
return {
|
|
3223
|
-
pageIndex: requireInteger(
|
|
3239
|
+
pageIndex: requireInteger(record2.pageIndex, `${fieldName}.pageIndex`, 0, PVOX_DEFAULT_LIMITS.maximumPages - 1),
|
|
3224
3240
|
pageKind,
|
|
3225
|
-
byteOffset: requireInteger(
|
|
3241
|
+
byteOffset: requireInteger(record2.byteOffset, `${fieldName}.byteOffset`, 0, PVOX_DEFAULT_LIMITS.maximumArtifactBytes - 1),
|
|
3226
3242
|
byteLength,
|
|
3227
3243
|
decodedByteLength,
|
|
3228
|
-
sha256: requireSha256(
|
|
3244
|
+
sha256: requireSha256(record2.sha256, `${fieldName}.sha256`),
|
|
3229
3245
|
...spatialKey === void 0 ? {} : { spatialKey }
|
|
3230
3246
|
};
|
|
3231
3247
|
}
|
|
3232
3248
|
function createLod(value, index, pageCount) {
|
|
3233
3249
|
const fieldName = `PvoxAssetManifestV1.lods[${index}]`;
|
|
3234
|
-
const
|
|
3235
|
-
exactKeys(
|
|
3250
|
+
const record2 = asRecord(value, fieldName);
|
|
3251
|
+
exactKeys(record2, [
|
|
3236
3252
|
"level",
|
|
3237
3253
|
"firstPageIndex",
|
|
3238
3254
|
"pageCount",
|
|
@@ -3246,30 +3262,30 @@ function createLod(value, index, pageCount) {
|
|
|
3246
3262
|
"p95DeltaE2000",
|
|
3247
3263
|
"normalizedMaterialError"
|
|
3248
3264
|
], fieldName);
|
|
3249
|
-
const firstPageIndex = requireInteger(
|
|
3250
|
-
const lodPageCount = requireInteger(
|
|
3265
|
+
const firstPageIndex = requireInteger(record2.firstPageIndex, `${fieldName}.firstPageIndex`, 0, pageCount - 1);
|
|
3266
|
+
const lodPageCount = requireInteger(record2.pageCount, `${fieldName}.pageCount`, 1, pageCount);
|
|
3251
3267
|
if (firstPageIndex + lodPageCount > pageCount) throw new Error(`${fieldName} page range exceeds the PVOX page set.`);
|
|
3252
|
-
const maximumSurfaceErrorMetres = requireNumber(
|
|
3253
|
-
const p99SurfaceErrorMetres = requireNumber(
|
|
3268
|
+
const maximumSurfaceErrorMetres = requireNumber(record2.maximumSurfaceErrorMetres, `${fieldName}.maximumSurfaceErrorMetres`, 0, 1e6);
|
|
3269
|
+
const p99SurfaceErrorMetres = requireNumber(record2.p99SurfaceErrorMetres, `${fieldName}.p99SurfaceErrorMetres`, 0, maximumSurfaceErrorMetres);
|
|
3254
3270
|
return {
|
|
3255
|
-
level: requireInteger(
|
|
3271
|
+
level: requireInteger(record2.level, `${fieldName}.level`, 0, 3),
|
|
3256
3272
|
firstPageIndex,
|
|
3257
3273
|
pageCount: lodPageCount,
|
|
3258
|
-
brickCount: requireInteger(
|
|
3259
|
-
cellSizeMetres: requireNumber(
|
|
3274
|
+
brickCount: requireInteger(record2.brickCount, `${fieldName}.brickCount`, 1, PVOX_DEFAULT_LIMITS.maximumBricks),
|
|
3275
|
+
cellSizeMetres: requireNumber(record2.cellSizeMetres, `${fieldName}.cellSizeMetres`, Number.EPSILON, 1e6),
|
|
3260
3276
|
maximumSurfaceErrorMetres,
|
|
3261
3277
|
p99SurfaceErrorMetres,
|
|
3262
|
-
silhouetteIou: requireNumber(
|
|
3263
|
-
maximumContourDisplacementPx: requireNumber(
|
|
3264
|
-
renderedSsim: requireNumber(
|
|
3265
|
-
p95DeltaE2000: requireNumber(
|
|
3266
|
-
normalizedMaterialError: requireNumber(
|
|
3278
|
+
silhouetteIou: requireNumber(record2.silhouetteIou, `${fieldName}.silhouetteIou`, 0, 1),
|
|
3279
|
+
maximumContourDisplacementPx: requireNumber(record2.maximumContourDisplacementPx, `${fieldName}.maximumContourDisplacementPx`, 0, 1e6),
|
|
3280
|
+
renderedSsim: requireNumber(record2.renderedSsim, `${fieldName}.renderedSsim`, 0, 1),
|
|
3281
|
+
p95DeltaE2000: requireNumber(record2.p95DeltaE2000, `${fieldName}.p95DeltaE2000`, 0, 100),
|
|
3282
|
+
normalizedMaterialError: requireNumber(record2.normalizedMaterialError, `${fieldName}.normalizedMaterialError`, 0, 1)
|
|
3267
3283
|
};
|
|
3268
3284
|
}
|
|
3269
3285
|
function createSourceIngestionLimitsV2(value) {
|
|
3270
3286
|
const fieldName = "ModelRequestSpecV2.sourceIngestionLimits";
|
|
3271
|
-
const
|
|
3272
|
-
exactKeys(
|
|
3287
|
+
const record2 = asRecord(value, fieldName);
|
|
3288
|
+
exactKeys(record2, [
|
|
3273
3289
|
"maximumDownloadBytes",
|
|
3274
3290
|
"maximumExpandedBytes",
|
|
3275
3291
|
"maximumArchiveEntries",
|
|
@@ -3278,12 +3294,12 @@ function createSourceIngestionLimitsV2(value) {
|
|
|
3278
3294
|
"maximumTextureDimensionPx"
|
|
3279
3295
|
], fieldName);
|
|
3280
3296
|
const limits = {
|
|
3281
|
-
maximumDownloadBytes: requireInteger(
|
|
3282
|
-
maximumExpandedBytes: requireInteger(
|
|
3283
|
-
maximumArchiveEntries: requireInteger(
|
|
3284
|
-
maximumSourceFiles: requireInteger(
|
|
3285
|
-
maximumDecodedTextureBytes: requireInteger(
|
|
3286
|
-
maximumTextureDimensionPx: requireInteger(
|
|
3297
|
+
maximumDownloadBytes: requireInteger(record2.maximumDownloadBytes, `${fieldName}.maximumDownloadBytes`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumDownloadBytes),
|
|
3298
|
+
maximumExpandedBytes: requireInteger(record2.maximumExpandedBytes, `${fieldName}.maximumExpandedBytes`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumExpandedBytes),
|
|
3299
|
+
maximumArchiveEntries: requireInteger(record2.maximumArchiveEntries, `${fieldName}.maximumArchiveEntries`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumArchiveEntries),
|
|
3300
|
+
maximumSourceFiles: requireInteger(record2.maximumSourceFiles, `${fieldName}.maximumSourceFiles`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumSourceFiles),
|
|
3301
|
+
maximumDecodedTextureBytes: requireInteger(record2.maximumDecodedTextureBytes, `${fieldName}.maximumDecodedTextureBytes`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumDecodedTextureBytes),
|
|
3302
|
+
maximumTextureDimensionPx: requireInteger(record2.maximumTextureDimensionPx, `${fieldName}.maximumTextureDimensionPx`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumTextureDimensionPx)
|
|
3287
3303
|
};
|
|
3288
3304
|
if (limits.maximumExpandedBytes < limits.maximumDownloadBytes) {
|
|
3289
3305
|
throw new Error(`${fieldName}.maximumExpandedBytes must cover maximumDownloadBytes.`);
|
|
@@ -3298,8 +3314,8 @@ function createSourceIngestionLimitsV2(value) {
|
|
|
3298
3314
|
}
|
|
3299
3315
|
function createPvoxRuntimeLimitsV1(value) {
|
|
3300
3316
|
const fieldName = "ModelRequestSpecV2.pvoxRuntimeProfile.limits";
|
|
3301
|
-
const
|
|
3302
|
-
exactKeys(
|
|
3317
|
+
const record2 = asRecord(value, fieldName);
|
|
3318
|
+
exactKeys(record2, [
|
|
3303
3319
|
"maximumArtifactBytes",
|
|
3304
3320
|
"maximumPages",
|
|
3305
3321
|
"maximumHierarchyDepth",
|
|
@@ -3320,24 +3336,24 @@ function createPvoxRuntimeLimitsV1(value) {
|
|
|
3320
3336
|
"maximumGpuResidentBytes"
|
|
3321
3337
|
], fieldName);
|
|
3322
3338
|
const limits = {
|
|
3323
|
-
maximumArtifactBytes: requireInteger(
|
|
3324
|
-
maximumPages: requireInteger(
|
|
3325
|
-
maximumHierarchyDepth: requireInteger(
|
|
3326
|
-
maximumHierarchyNodes: requireInteger(
|
|
3327
|
-
maximumBricks: requireInteger(
|
|
3328
|
-
maximumLogicalVoxels: requireInteger(
|
|
3329
|
-
maximumEncodedSurfaceSamples: requireInteger(
|
|
3330
|
-
maximumSurfaceProperties: requireInteger(
|
|
3331
|
-
maximumPhysicalPaletteRecords: requireInteger(
|
|
3332
|
-
maximumPhysicalEvidenceEntries: requireInteger(
|
|
3333
|
-
maximumMaterialRegions: requireInteger(
|
|
3334
|
-
maximumInteriorLayers: requireInteger(
|
|
3335
|
-
maximumMassPropertyRecords: requireInteger(
|
|
3336
|
-
maximumBondRecords: requireInteger(
|
|
3337
|
-
maximumPartitions: requireInteger(
|
|
3338
|
-
maximumLodCount: requireInteger(
|
|
3339
|
-
maximumCpuResidentBytes: requireInteger(
|
|
3340
|
-
maximumGpuResidentBytes: requireInteger(
|
|
3339
|
+
maximumArtifactBytes: requireInteger(record2.maximumArtifactBytes, `${fieldName}.maximumArtifactBytes`, 1, PVOX_DEFAULT_LIMITS.maximumArtifactBytes),
|
|
3340
|
+
maximumPages: requireInteger(record2.maximumPages, `${fieldName}.maximumPages`, 1, PVOX_DEFAULT_LIMITS.maximumPages),
|
|
3341
|
+
maximumHierarchyDepth: requireInteger(record2.maximumHierarchyDepth, `${fieldName}.maximumHierarchyDepth`, 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth),
|
|
3342
|
+
maximumHierarchyNodes: requireInteger(record2.maximumHierarchyNodes, `${fieldName}.maximumHierarchyNodes`, 1, PVOX_DEFAULT_LIMITS.maximumHierarchyNodes),
|
|
3343
|
+
maximumBricks: requireInteger(record2.maximumBricks, `${fieldName}.maximumBricks`, 1, PVOX_DEFAULT_LIMITS.maximumBricks),
|
|
3344
|
+
maximumLogicalVoxels: requireInteger(record2.maximumLogicalVoxels, `${fieldName}.maximumLogicalVoxels`, 1, PVOX_DEFAULT_LIMITS.maximumLogicalVoxels),
|
|
3345
|
+
maximumEncodedSurfaceSamples: requireInteger(record2.maximumEncodedSurfaceSamples, `${fieldName}.maximumEncodedSurfaceSamples`, 1, PVOX_DEFAULT_LIMITS.maximumEncodedSurfaceSamples),
|
|
3346
|
+
maximumSurfaceProperties: requireInteger(record2.maximumSurfaceProperties, `${fieldName}.maximumSurfaceProperties`, 1, PVOX_DEFAULT_LIMITS.maximumSurfaceProperties),
|
|
3347
|
+
maximumPhysicalPaletteRecords: requireInteger(record2.maximumPhysicalPaletteRecords, `${fieldName}.maximumPhysicalPaletteRecords`, 1, PVOX_DEFAULT_LIMITS.maximumPhysicalProperties),
|
|
3348
|
+
maximumPhysicalEvidenceEntries: requireInteger(record2.maximumPhysicalEvidenceEntries, `${fieldName}.maximumPhysicalEvidenceEntries`, 1, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries),
|
|
3349
|
+
maximumMaterialRegions: requireInteger(record2.maximumMaterialRegions, `${fieldName}.maximumMaterialRegions`, 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions),
|
|
3350
|
+
maximumInteriorLayers: requireInteger(record2.maximumInteriorLayers, `${fieldName}.maximumInteriorLayers`, 1, PVOX_DEFAULT_LIMITS.maximumInteriorLayers),
|
|
3351
|
+
maximumMassPropertyRecords: requireInteger(record2.maximumMassPropertyRecords, `${fieldName}.maximumMassPropertyRecords`, 1, PVOX_DEFAULT_LIMITS.maximumMassPropertyRecords),
|
|
3352
|
+
maximumBondRecords: requireInteger(record2.maximumBondRecords, `${fieldName}.maximumBondRecords`, 0, PVOX_DEFAULT_LIMITS.maximumBondRecords),
|
|
3353
|
+
maximumPartitions: requireInteger(record2.maximumPartitions, `${fieldName}.maximumPartitions`, 1, PVOX_DEFAULT_LIMITS.maximumPartitions),
|
|
3354
|
+
maximumLodCount: requireInteger(record2.maximumLodCount, `${fieldName}.maximumLodCount`, 1, PVOX_DEFAULT_LIMITS.maximumLodCount),
|
|
3355
|
+
maximumCpuResidentBytes: requireInteger(record2.maximumCpuResidentBytes, `${fieldName}.maximumCpuResidentBytes`, 1, PVOX_DEFAULT_LIMITS.maximumCpuResidentBytes),
|
|
3356
|
+
maximumGpuResidentBytes: requireInteger(record2.maximumGpuResidentBytes, `${fieldName}.maximumGpuResidentBytes`, 1, PVOX_DEFAULT_LIMITS.maximumGpuResidentBytes)
|
|
3341
3357
|
};
|
|
3342
3358
|
if (limits.maximumArtifactBytes > limits.maximumPages * PVOX_PAGE_SIZE_BYTES) {
|
|
3343
3359
|
throw new Error(`${fieldName}.maximumPages cannot contain maximumArtifactBytes.`);
|
|
@@ -3352,9 +3368,9 @@ function createPvoxRuntimeLimitsV1(value) {
|
|
|
3352
3368
|
}
|
|
3353
3369
|
function createPvoxRuntimeRequestProfileV1(value) {
|
|
3354
3370
|
const fieldName = "ModelRequestSpecV2.pvoxRuntimeProfile";
|
|
3355
|
-
const
|
|
3356
|
-
exactKeys(
|
|
3357
|
-
const requestedCapabilityEntries = asDenseArray(
|
|
3371
|
+
const record2 = asRecord(value, fieldName);
|
|
3372
|
+
exactKeys(record2, ["profileId", "fidelityProfileId", "capabilityProfileId", "geometryMode", "requiredCapabilities", "limits"], fieldName);
|
|
3373
|
+
const requestedCapabilityEntries = asDenseArray(record2.requiredCapabilities, `${fieldName}.requiredCapabilities`, 1, PVOX_CAPABILITIES.length);
|
|
3358
3374
|
const requiredCapabilities = requestedCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `${fieldName}.requiredCapabilities[${index}]`));
|
|
3359
3375
|
if (new Set(requiredCapabilities).size !== requiredCapabilities.length) {
|
|
3360
3376
|
throw new Error(`${fieldName}.requiredCapabilities must not contain duplicates.`);
|
|
@@ -3362,7 +3378,7 @@ function createPvoxRuntimeRequestProfileV1(value) {
|
|
|
3362
3378
|
if (!requiredCapabilities.includes("rendering")) {
|
|
3363
3379
|
throw new Error(`${fieldName}.requiredCapabilities must include rendering.`);
|
|
3364
3380
|
}
|
|
3365
|
-
const capabilityProfileId = requireEnum(
|
|
3381
|
+
const capabilityProfileId = requireEnum(record2.capabilityProfileId, PVOX_RUNTIME_CAPABILITY_PROFILE_IDS, `${fieldName}.capabilityProfileId`);
|
|
3366
3382
|
const profileRequirements = {
|
|
3367
3383
|
"static-render-v1": ["rendering"],
|
|
3368
3384
|
"world-editable-v1": ["rendering", "collision", "destruction", "thermal", "moisture", "fluid-boundary"],
|
|
@@ -3371,24 +3387,24 @@ function createPvoxRuntimeRequestProfileV1(value) {
|
|
|
3371
3387
|
if (profileRequirements[capabilityProfileId].some((capability) => !requiredCapabilities.includes(capability))) {
|
|
3372
3388
|
throw new Error(`${fieldName}.requiredCapabilities do not satisfy capabilityProfileId.`);
|
|
3373
3389
|
}
|
|
3374
|
-
const limits = createPvoxRuntimeLimitsV1(
|
|
3390
|
+
const limits = createPvoxRuntimeLimitsV1(record2.limits);
|
|
3375
3391
|
if (capabilityProfileId === "world-editable-v1" && limits.maximumPhysicalEvidenceEntries < limits.maximumMaterialRegions * PVOX_PHYSICAL_PROPERTIES.length) {
|
|
3376
3392
|
throw new Error(`${fieldName}.limits must budget a complete physical-property matrix for every allowed material region.`);
|
|
3377
3393
|
}
|
|
3378
3394
|
return {
|
|
3379
|
-
profileId: requireToken(
|
|
3380
|
-
fidelityProfileId: requireEnum(
|
|
3395
|
+
profileId: requireToken(record2.profileId, `${fieldName}.profileId`),
|
|
3396
|
+
fidelityProfileId: requireEnum(record2.fidelityProfileId, PVOX_FIDELITY_PROFILE_IDS, `${fieldName}.fidelityProfileId`),
|
|
3381
3397
|
capabilityProfileId,
|
|
3382
|
-
geometryMode: requireEnum(
|
|
3398
|
+
geometryMode: requireEnum(record2.geometryMode, ["solid", "shell", "mixed", "auto"], `${fieldName}.geometryMode`),
|
|
3383
3399
|
requiredCapabilities,
|
|
3384
3400
|
limits
|
|
3385
3401
|
};
|
|
3386
3402
|
}
|
|
3387
3403
|
function createModelRequestSpecV2(input) {
|
|
3388
3404
|
const fieldName = "ModelRequestSpecV2";
|
|
3389
|
-
const
|
|
3390
|
-
assertDataOnlyTree(
|
|
3391
|
-
exactKeys(
|
|
3405
|
+
const record2 = asRecord(input, fieldName);
|
|
3406
|
+
assertDataOnlyTree(record2, fieldName);
|
|
3407
|
+
exactKeys(record2, [
|
|
3392
3408
|
"contractVersion",
|
|
3393
3409
|
"policyProfileId",
|
|
3394
3410
|
"requestSemanticProfileHash",
|
|
@@ -3402,26 +3418,26 @@ function createModelRequestSpecV2(input) {
|
|
|
3402
3418
|
"sourceIngestionLimits",
|
|
3403
3419
|
"pvoxRuntimeProfile"
|
|
3404
3420
|
], fieldName);
|
|
3405
|
-
if (
|
|
3421
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) {
|
|
3406
3422
|
throw new Error(`${fieldName}.contractVersion is unsupported.`);
|
|
3407
3423
|
}
|
|
3408
|
-
if (
|
|
3424
|
+
if (record2.policyProfileId !== PVOX_MODEL_REQUEST_POLICY_ID) {
|
|
3409
3425
|
throw new Error(`${fieldName}.policyProfileId must be ${PVOX_MODEL_REQUEST_POLICY_ID}.`);
|
|
3410
3426
|
}
|
|
3411
|
-
const hardConstraintsRecord = asRecord(
|
|
3427
|
+
const hardConstraintsRecord = asRecord(record2.hardConstraints ?? {}, `${fieldName}.hardConstraints`);
|
|
3412
3428
|
exactKeys(hardConstraintsRecord, ["boundsMetres", "dimensionsMetres", "collision", "partition"], `${fieldName}.hardConstraints`);
|
|
3413
|
-
const softPreferencesRecord = asRecord(
|
|
3429
|
+
const softPreferencesRecord = asRecord(record2.softPreferences ?? {}, `${fieldName}.softPreferences`);
|
|
3414
3430
|
for (const field of ["materials", "colors", "tags"]) {
|
|
3415
3431
|
if (softPreferencesRecord[field] !== void 0) {
|
|
3416
3432
|
asDenseArray(softPreferencesRecord[field], `${fieldName}.softPreferences.${field}`, 0, 32);
|
|
3417
3433
|
}
|
|
3418
3434
|
}
|
|
3419
|
-
const exclusions = asDenseArray(
|
|
3435
|
+
const exclusions = asDenseArray(record2.exclusions ?? [], `${fieldName}.exclusions`, 0, 32);
|
|
3420
3436
|
const normalizedV1 = createModelRequestSpec({
|
|
3421
|
-
query:
|
|
3422
|
-
revision:
|
|
3423
|
-
...
|
|
3424
|
-
...
|
|
3437
|
+
query: record2.query,
|
|
3438
|
+
revision: record2.revision,
|
|
3439
|
+
...record2.locale === void 0 ? {} : { locale: record2.locale },
|
|
3440
|
+
...record2.rankerId === void 0 ? {} : { rankerId: record2.rankerId },
|
|
3425
3441
|
hardConstraints: hardConstraintsRecord,
|
|
3426
3442
|
softPreferences: softPreferencesRecord,
|
|
3427
3443
|
exclusions
|
|
@@ -3435,7 +3451,7 @@ function createModelRequestSpecV2(input) {
|
|
|
3435
3451
|
return deepFreeze2({
|
|
3436
3452
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
3437
3453
|
policyProfileId: PVOX_MODEL_REQUEST_POLICY_ID,
|
|
3438
|
-
requestSemanticProfileHash: requireSha256(
|
|
3454
|
+
requestSemanticProfileHash: requireSha256(record2.requestSemanticProfileHash, `${fieldName}.requestSemanticProfileHash`),
|
|
3439
3455
|
query: normalizedV1.query,
|
|
3440
3456
|
revision: normalizedV1.revision,
|
|
3441
3457
|
...normalizedV1.locale === void 0 ? {} : { locale: normalizedV1.locale },
|
|
@@ -3443,13 +3459,13 @@ function createModelRequestSpecV2(input) {
|
|
|
3443
3459
|
hardConstraints,
|
|
3444
3460
|
softPreferences: normalizedV1.softPreferences,
|
|
3445
3461
|
exclusions: normalizedV1.exclusions,
|
|
3446
|
-
sourceIngestionLimits: createSourceIngestionLimitsV2(
|
|
3447
|
-
pvoxRuntimeProfile: createPvoxRuntimeRequestProfileV1(
|
|
3462
|
+
sourceIngestionLimits: createSourceIngestionLimitsV2(record2.sourceIngestionLimits),
|
|
3463
|
+
pvoxRuntimeProfile: createPvoxRuntimeRequestProfileV1(record2.pvoxRuntimeProfile)
|
|
3448
3464
|
});
|
|
3449
3465
|
}
|
|
3450
3466
|
function createPvoxAssetManifestV1(input) {
|
|
3451
|
-
const
|
|
3452
|
-
exactKeys(
|
|
3467
|
+
const record2 = asRecord(input, "PvoxAssetManifestV1");
|
|
3468
|
+
exactKeys(record2, [
|
|
3453
3469
|
"manifestVersion",
|
|
3454
3470
|
"representation",
|
|
3455
3471
|
"formatVersion",
|
|
@@ -3480,23 +3496,23 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3480
3496
|
"pages",
|
|
3481
3497
|
"lods"
|
|
3482
3498
|
], "PvoxAssetManifestV1");
|
|
3483
|
-
if (
|
|
3484
|
-
if (
|
|
3485
|
-
const formatVersion = asRecord(
|
|
3499
|
+
if (record2.manifestVersion !== PVOX_ASSET_MANIFEST_VERSION) throw new Error("PvoxAssetManifestV1.manifestVersion is unsupported.");
|
|
3500
|
+
if (record2.representation !== "pvox") throw new Error("PvoxAssetManifestV1.representation must be pvox.");
|
|
3501
|
+
const formatVersion = asRecord(record2.formatVersion, "PvoxAssetManifestV1.formatVersion");
|
|
3486
3502
|
exactKeys(formatVersion, ["major", "minor"], "PvoxAssetManifestV1.formatVersion");
|
|
3487
3503
|
if (formatVersion.major !== PVOX_FORMAT_VERSION.major || formatVersion.minor !== PVOX_FORMAT_VERSION.minor) throw new Error("PvoxAssetManifestV1.formatVersion is unsupported.");
|
|
3488
|
-
if (
|
|
3504
|
+
if (record2.contentType !== PVOX_CONTENT_TYPE || record2.fileExtension !== PVOX_FILE_EXTENSION || record2.magic !== PVOX_MAGIC) {
|
|
3489
3505
|
throw new Error("PvoxAssetManifestV1 must declare the canonical PVOX content type, extension, and magic.");
|
|
3490
3506
|
}
|
|
3491
|
-
if (
|
|
3507
|
+
if (record2.brickEdgeVoxels !== PVOX_BRICK_EDGE_VOXELS || record2.pageSizeBytes !== PVOX_PAGE_SIZE_BYTES) {
|
|
3492
3508
|
throw new Error("PvoxAssetManifestV1 must use fixed 8-cubed bricks and 64-KiB pages.");
|
|
3493
3509
|
}
|
|
3494
|
-
assertCoordinateSystem2(
|
|
3495
|
-
const boundsMetres = createBounds2(
|
|
3510
|
+
assertCoordinateSystem2(record2.coordinateSystem, "PvoxAssetManifestV1.coordinateSystem");
|
|
3511
|
+
const boundsMetres = createBounds2(record2.boundsMetres, "PvoxAssetManifestV1.boundsMetres");
|
|
3496
3512
|
assertFloorCentredBounds(boundsMetres, "PvoxAssetManifestV1.boundsMetres");
|
|
3497
|
-
const artifact = createResource(
|
|
3513
|
+
const artifact = createResource(record2.artifact);
|
|
3498
3514
|
if (artifact.byteLength % PVOX_PAGE_SIZE_BYTES !== 0) throw new Error("PvoxAssetManifestV1 artifact byteLength must contain whole 64-KiB pages.");
|
|
3499
|
-
const pageEntries = asDenseArray(
|
|
3515
|
+
const pageEntries = asDenseArray(record2.pages, "PvoxAssetManifestV1.pages", 1, PVOX_DEFAULT_LIMITS.maximumPages);
|
|
3500
3516
|
const pages = pageEntries.map(createPage);
|
|
3501
3517
|
let expectedOffset = 0;
|
|
3502
3518
|
const previousSpatialMaximumByScope = /* @__PURE__ */ new Map();
|
|
@@ -3517,7 +3533,7 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3517
3533
|
expectedOffset += current.byteLength;
|
|
3518
3534
|
}
|
|
3519
3535
|
if (expectedOffset !== artifact.byteLength) throw new Error("PvoxAssetManifestV1 pages must exactly cover the artifact bytes.");
|
|
3520
|
-
const lodEntries = asDenseArray(
|
|
3536
|
+
const lodEntries = asDenseArray(record2.lods, "PvoxAssetManifestV1.lods", 1, 4);
|
|
3521
3537
|
const lods = lodEntries.map((entry, index) => createLod(entry, index, pages.length));
|
|
3522
3538
|
for (const [index, current] of lods.entries()) {
|
|
3523
3539
|
if (current.level !== index) throw new Error("PvoxAssetManifestV1 LOD levels must be contiguous from LOD0.");
|
|
@@ -3527,14 +3543,14 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3527
3543
|
throw new Error("PvoxAssetManifestV1 LOD page ranges must reference render-field pages in the matching LOD scope.");
|
|
3528
3544
|
}
|
|
3529
3545
|
}
|
|
3530
|
-
const sourceContentHash = requireSha256(
|
|
3531
|
-
const canonicalDocumentHash = requireSha256(
|
|
3532
|
-
const compilationInputHash = requireSha256(
|
|
3533
|
-
const runtimeRequestProfileHash = requireSha256(
|
|
3546
|
+
const sourceContentHash = requireSha256(record2.sourceContentHash, "PvoxAssetManifestV1.sourceContentHash");
|
|
3547
|
+
const canonicalDocumentHash = requireSha256(record2.canonicalDocumentHash, "PvoxAssetManifestV1.canonicalDocumentHash");
|
|
3548
|
+
const compilationInputHash = requireSha256(record2.compilationInputHash, "PvoxAssetManifestV1.compilationInputHash");
|
|
3549
|
+
const runtimeRequestProfileHash = requireSha256(record2.runtimeRequestProfileHash, "PvoxAssetManifestV1.runtimeRequestProfileHash");
|
|
3534
3550
|
if ((/* @__PURE__ */ new Set([sourceContentHash, canonicalDocumentHash, compilationInputHash, runtimeRequestProfileHash])).size !== 4) {
|
|
3535
3551
|
throw new Error("PvoxAssetManifestV1 source, canonical document, compilation input, and runtime request profile hashes must be distinct attestations.");
|
|
3536
3552
|
}
|
|
3537
|
-
if (
|
|
3553
|
+
if (record2.headerByteLength !== PVOX_HEADER_BYTE_LENGTH || record2.directoryEntryByteLength !== PVOX_DIRECTORY_ENTRY_BYTE_LENGTH || record2.maximumRunsPerBrick !== PVOX_MAX_RUNS_PER_BRICK || record2.maximumLocalSamplesPerBrick !== PVOX_MAX_LOCAL_SAMPLES_PER_BRICK || record2.maximumEncodedBrickPayloadBytes !== PVOX_MAX_ENCODED_BRICK_PAYLOAD_BYTES) {
|
|
3538
3554
|
throw new Error("PvoxAssetManifestV1 must declare the fixed PVOX 1.0 layout and brick codec limits.");
|
|
3539
3555
|
}
|
|
3540
3556
|
return deepFreeze2({
|
|
@@ -3546,19 +3562,19 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3546
3562
|
magic: PVOX_MAGIC,
|
|
3547
3563
|
brickEdgeVoxels: PVOX_BRICK_EDGE_VOXELS,
|
|
3548
3564
|
pageSizeBytes: PVOX_PAGE_SIZE_BYTES,
|
|
3549
|
-
geometryMode: requireEnum(
|
|
3565
|
+
geometryMode: requireEnum(record2.geometryMode, ["solid", "shell", "mixed"], "PvoxAssetManifestV1.geometryMode"),
|
|
3550
3566
|
sourceContentHash,
|
|
3551
3567
|
canonicalDocumentHash,
|
|
3552
|
-
rootHash: requireSha256(
|
|
3553
|
-
directoryHash: requireSha256(
|
|
3554
|
-
pageSetHash: requireSha256(
|
|
3555
|
-
binaryClosureHash: requireSha256(
|
|
3556
|
-
validationEvidenceHash: requireSha256(
|
|
3568
|
+
rootHash: requireSha256(record2.rootHash, "PvoxAssetManifestV1.rootHash"),
|
|
3569
|
+
directoryHash: requireSha256(record2.directoryHash, "PvoxAssetManifestV1.directoryHash"),
|
|
3570
|
+
pageSetHash: requireSha256(record2.pageSetHash, "PvoxAssetManifestV1.pageSetHash"),
|
|
3571
|
+
binaryClosureHash: requireSha256(record2.binaryClosureHash, "PvoxAssetManifestV1.binaryClosureHash"),
|
|
3572
|
+
validationEvidenceHash: requireSha256(record2.validationEvidenceHash, "PvoxAssetManifestV1.validationEvidenceHash"),
|
|
3557
3573
|
compilationInputHash,
|
|
3558
3574
|
runtimeRequestProfileHash,
|
|
3559
3575
|
headerByteLength: PVOX_HEADER_BYTE_LENGTH,
|
|
3560
3576
|
directoryEntryByteLength: PVOX_DIRECTORY_ENTRY_BYTE_LENGTH,
|
|
3561
|
-
sectionCount: requireInteger(
|
|
3577
|
+
sectionCount: requireInteger(record2.sectionCount, "PvoxAssetManifestV1.sectionCount", PVOX_MIN_STATIC_SECTION_COUNT, PVOX_MAX_SECTIONS),
|
|
3562
3578
|
maximumRunsPerBrick: PVOX_MAX_RUNS_PER_BRICK,
|
|
3563
3579
|
maximumLocalSamplesPerBrick: PVOX_MAX_LOCAL_SAMPLES_PER_BRICK,
|
|
3564
3580
|
maximumEncodedBrickPayloadBytes: PVOX_MAX_ENCODED_BRICK_PAYLOAD_BYTES,
|
|
@@ -3573,8 +3589,8 @@ function createPvoxAssetManifest(input) {
|
|
|
3573
3589
|
return createPvoxAssetManifestV1(input);
|
|
3574
3590
|
}
|
|
3575
3591
|
function createVoxelTechnicalProfile(input) {
|
|
3576
|
-
const
|
|
3577
|
-
exactKeys(
|
|
3592
|
+
const record2 = asRecord(input, "VoxelTechnicalProfile");
|
|
3593
|
+
exactKeys(record2, [
|
|
3578
3594
|
"boundsMetres",
|
|
3579
3595
|
"dimensionsMetres",
|
|
3580
3596
|
"geometryMode",
|
|
@@ -3606,49 +3622,49 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3606
3622
|
"maximumPartitionExtentMetres",
|
|
3607
3623
|
"maximumPartitionDiagonalMetres"
|
|
3608
3624
|
], "VoxelTechnicalProfile");
|
|
3609
|
-
const boundsMetres = createBounds2(
|
|
3625
|
+
const boundsMetres = createBounds2(record2.boundsMetres, "VoxelTechnicalProfile.boundsMetres");
|
|
3610
3626
|
assertFloorCentredBounds(boundsMetres, "VoxelTechnicalProfile.boundsMetres");
|
|
3611
|
-
const dimensionsMetres = createDimensions2(
|
|
3627
|
+
const dimensionsMetres = createDimensions2(record2.dimensionsMetres, "VoxelTechnicalProfile.dimensionsMetres");
|
|
3612
3628
|
const derived = boundsMetres.max.map((coordinate, index) => coordinate - boundsMetres.min[index]);
|
|
3613
3629
|
const supplied = [dimensionsMetres.width, dimensionsMetres.height, dimensionsMetres.depth];
|
|
3614
3630
|
if (derived.some((coordinate, index) => Math.abs(coordinate - supplied[index]) > Math.max(1e-6, Math.abs(coordinate) * 1e-6))) {
|
|
3615
3631
|
throw new Error("VoxelTechnicalProfile dimensionsMetres must match boundsMetres.");
|
|
3616
3632
|
}
|
|
3617
|
-
const artifactByteLength = requireInteger(
|
|
3618
|
-
const pageCount = requireInteger(
|
|
3633
|
+
const artifactByteLength = requireInteger(record2.artifactByteLength, "VoxelTechnicalProfile.artifactByteLength", 1, PVOX_DEFAULT_LIMITS.maximumArtifactBytes);
|
|
3634
|
+
const pageCount = requireInteger(record2.pageCount, "VoxelTechnicalProfile.pageCount", 1, PVOX_DEFAULT_LIMITS.maximumPages);
|
|
3619
3635
|
if (artifactByteLength !== pageCount * PVOX_PAGE_SIZE_BYTES) {
|
|
3620
3636
|
throw new Error("VoxelTechnicalProfile artifactByteLength must equal pageCount multiplied by the fixed PVOX page size.");
|
|
3621
3637
|
}
|
|
3622
|
-
const brickCount = requireInteger(
|
|
3623
|
-
const logicalVoxelCapacity = requireInteger(
|
|
3638
|
+
const brickCount = requireInteger(record2.brickCount, "VoxelTechnicalProfile.brickCount", 1, PVOX_DEFAULT_LIMITS.maximumBricks);
|
|
3639
|
+
const logicalVoxelCapacity = requireInteger(record2.logicalVoxelCapacity, "VoxelTechnicalProfile.logicalVoxelCapacity", 1, PVOX_DEFAULT_LIMITS.maximumLogicalVoxels);
|
|
3624
3640
|
if (logicalVoxelCapacity !== brickCount * PVOX_BRICK_EDGE_VOXELS ** 3) {
|
|
3625
3641
|
throw new Error("VoxelTechnicalProfile logicalVoxelCapacity must equal brickCount multiplied by 512 voxels per brick.");
|
|
3626
3642
|
}
|
|
3627
|
-
const encodedSurfaceSampleCount = requireInteger(
|
|
3643
|
+
const encodedSurfaceSampleCount = requireInteger(record2.encodedSurfaceSampleCount, "VoxelTechnicalProfile.encodedSurfaceSampleCount", 1, PVOX_DEFAULT_LIMITS.maximumEncodedSurfaceSamples);
|
|
3628
3644
|
if (encodedSurfaceSampleCount > logicalVoxelCapacity) {
|
|
3629
3645
|
throw new Error("VoxelTechnicalProfile encodedSurfaceSampleCount cannot exceed logicalVoxelCapacity.");
|
|
3630
3646
|
}
|
|
3631
|
-
const surfacePropertyCount = requireInteger(
|
|
3632
|
-
const sectionCount = requireInteger(
|
|
3633
|
-
const hierarchyDepth = requireInteger(
|
|
3634
|
-
const hierarchyNodeCount = requireInteger(
|
|
3635
|
-
const lodCount = requireInteger(
|
|
3636
|
-
const partitionCount = requireInteger(
|
|
3637
|
-
const levelSpanRecordCount = requireInteger(
|
|
3638
|
-
const physicalPaletteRecordCount = requireInteger(
|
|
3639
|
-
const physicalEvidenceRecordCount = requireInteger(
|
|
3640
|
-
const materialRegionCount = requireInteger(
|
|
3641
|
-
const interiorLayerRecordCount = requireInteger(
|
|
3642
|
-
const massPropertyRecordCount = requireInteger(
|
|
3643
|
-
const bondRecordCount = requireInteger(
|
|
3644
|
-
const hasCollision = requireBoolean(
|
|
3645
|
-
const collisionHierarchyNodeCount = requireInteger(
|
|
3646
|
-
const collisionBrickCount = requireInteger(
|
|
3647
|
-
const collisionLogicalVoxelCapacity = requireInteger(
|
|
3647
|
+
const surfacePropertyCount = requireInteger(record2.surfacePropertyCount, "VoxelTechnicalProfile.surfacePropertyCount", 1, PVOX_DEFAULT_LIMITS.maximumSurfaceProperties);
|
|
3648
|
+
const sectionCount = requireInteger(record2.sectionCount, "VoxelTechnicalProfile.sectionCount", PVOX_MIN_STATIC_SECTION_COUNT, PVOX_MAX_SECTIONS);
|
|
3649
|
+
const hierarchyDepth = requireInteger(record2.hierarchyDepth, "VoxelTechnicalProfile.hierarchyDepth", 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth);
|
|
3650
|
+
const hierarchyNodeCount = requireInteger(record2.hierarchyNodeCount, "VoxelTechnicalProfile.hierarchyNodeCount", 1, PVOX_DEFAULT_LIMITS.maximumHierarchyNodes);
|
|
3651
|
+
const lodCount = requireInteger(record2.lodCount, "VoxelTechnicalProfile.lodCount", 1, PVOX_DEFAULT_LIMITS.maximumLodCount);
|
|
3652
|
+
const partitionCount = requireInteger(record2.partitionCount, "VoxelTechnicalProfile.partitionCount", 1, PVOX_DEFAULT_LIMITS.maximumPartitions);
|
|
3653
|
+
const levelSpanRecordCount = requireInteger(record2.levelSpanRecordCount, "VoxelTechnicalProfile.levelSpanRecordCount", partitionCount * lodCount, partitionCount * lodCount * (hierarchyDepth + 1));
|
|
3654
|
+
const physicalPaletteRecordCount = requireInteger(record2.physicalPaletteRecordCount, "VoxelTechnicalProfile.physicalPaletteRecordCount", 1, PVOX_DEFAULT_LIMITS.maximumPhysicalProperties);
|
|
3655
|
+
const physicalEvidenceRecordCount = requireInteger(record2.physicalEvidenceRecordCount, "VoxelTechnicalProfile.physicalEvidenceRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries);
|
|
3656
|
+
const materialRegionCount = requireInteger(record2.materialRegionCount, "VoxelTechnicalProfile.materialRegionCount", 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions);
|
|
3657
|
+
const interiorLayerRecordCount = requireInteger(record2.interiorLayerRecordCount, "VoxelTechnicalProfile.interiorLayerRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumInteriorLayers);
|
|
3658
|
+
const massPropertyRecordCount = requireInteger(record2.massPropertyRecordCount, "VoxelTechnicalProfile.massPropertyRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumMassPropertyRecords);
|
|
3659
|
+
const bondRecordCount = requireInteger(record2.bondRecordCount, "VoxelTechnicalProfile.bondRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumBondRecords);
|
|
3660
|
+
const hasCollision = requireBoolean(record2.hasCollision, "VoxelTechnicalProfile.hasCollision");
|
|
3661
|
+
const collisionHierarchyNodeCount = requireInteger(record2.collisionHierarchyNodeCount, "VoxelTechnicalProfile.collisionHierarchyNodeCount", hasCollision ? 1 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumHierarchyNodes : 0);
|
|
3662
|
+
const collisionBrickCount = requireInteger(record2.collisionBrickCount, "VoxelTechnicalProfile.collisionBrickCount", hasCollision ? 1 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumBricks : 0);
|
|
3663
|
+
const collisionLogicalVoxelCapacity = requireInteger(record2.collisionLogicalVoxelCapacity, "VoxelTechnicalProfile.collisionLogicalVoxelCapacity", hasCollision ? PVOX_BRICK_EDGE_VOXELS ** 3 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumLogicalVoxels : 0);
|
|
3648
3664
|
if (collisionLogicalVoxelCapacity !== collisionBrickCount * PVOX_BRICK_EDGE_VOXELS ** 3) {
|
|
3649
3665
|
throw new Error("VoxelTechnicalProfile collisionLogicalVoxelCapacity must equal collisionBrickCount multiplied by 512.");
|
|
3650
3666
|
}
|
|
3651
|
-
const collisionEncodedSurfaceSampleCount = requireInteger(
|
|
3667
|
+
const collisionEncodedSurfaceSampleCount = requireInteger(record2.collisionEncodedSurfaceSampleCount, "VoxelTechnicalProfile.collisionEncodedSurfaceSampleCount", hasCollision ? 1 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumEncodedSurfaceSamples : 0);
|
|
3652
3668
|
if (collisionEncodedSurfaceSampleCount > collisionLogicalVoxelCapacity) {
|
|
3653
3669
|
throw new Error("VoxelTechnicalProfile collision encoded sample count cannot exceed its logical voxel capacity.");
|
|
3654
3670
|
}
|
|
@@ -3658,15 +3674,15 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3658
3674
|
}
|
|
3659
3675
|
const maximumDimensionMetres = Math.max(...supplied);
|
|
3660
3676
|
const fullDiagonalMetres = Math.hypot(...supplied);
|
|
3661
|
-
const maximumPartitionExtentMetres = requireNumber(
|
|
3662
|
-
const maximumPartitionDiagonalMetres = requireNumber(
|
|
3677
|
+
const maximumPartitionExtentMetres = requireNumber(record2.maximumPartitionExtentMetres, "VoxelTechnicalProfile.maximumPartitionExtentMetres", Number.EPSILON, maximumDimensionMetres + Math.max(1e-9, maximumDimensionMetres * 1e-9));
|
|
3678
|
+
const maximumPartitionDiagonalMetres = requireNumber(record2.maximumPartitionDiagonalMetres, "VoxelTechnicalProfile.maximumPartitionDiagonalMetres", maximumPartitionExtentMetres, fullDiagonalMetres + Math.max(1e-9, fullDiagonalMetres * 1e-9));
|
|
3663
3679
|
if (partitionCount === 1 && (!approximatelyEqual(maximumPartitionExtentMetres, maximumDimensionMetres) || !approximatelyEqual(maximumPartitionDiagonalMetres, fullDiagonalMetres))) {
|
|
3664
3680
|
throw new Error("VoxelTechnicalProfile single-partition extent and diagonal must cover the complete bounds.");
|
|
3665
3681
|
}
|
|
3666
3682
|
const profile = {
|
|
3667
3683
|
boundsMetres,
|
|
3668
3684
|
dimensionsMetres,
|
|
3669
|
-
geometryMode: requireEnum(
|
|
3685
|
+
geometryMode: requireEnum(record2.geometryMode, ["solid", "shell", "mixed"], "VoxelTechnicalProfile.geometryMode"),
|
|
3670
3686
|
artifactByteLength,
|
|
3671
3687
|
pageCount,
|
|
3672
3688
|
brickCount,
|
|
@@ -3688,8 +3704,8 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3688
3704
|
collisionBrickCount,
|
|
3689
3705
|
collisionLogicalVoxelCapacity,
|
|
3690
3706
|
collisionEncodedSurfaceSampleCount,
|
|
3691
|
-
cpuResidentByteLength: requireInteger(
|
|
3692
|
-
gpuResidentByteLength: requireInteger(
|
|
3707
|
+
cpuResidentByteLength: requireInteger(record2.cpuResidentByteLength, "VoxelTechnicalProfile.cpuResidentByteLength", 1, PVOX_DEFAULT_LIMITS.maximumCpuResidentBytes),
|
|
3708
|
+
gpuResidentByteLength: requireInteger(record2.gpuResidentByteLength, "VoxelTechnicalProfile.gpuResidentByteLength", 1, PVOX_DEFAULT_LIMITS.maximumGpuResidentBytes),
|
|
3693
3709
|
hasCollision,
|
|
3694
3710
|
partitionCount,
|
|
3695
3711
|
maximumPartitionExtentMetres,
|
|
@@ -3702,21 +3718,21 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3702
3718
|
return deepFreeze2(profile);
|
|
3703
3719
|
}
|
|
3704
3720
|
function createVoxelCapabilityAssessment(input) {
|
|
3705
|
-
const
|
|
3706
|
-
exactKeys(
|
|
3707
|
-
const status = requireEnum(
|
|
3708
|
-
const reasonCodes2 = requireReasonCodes(
|
|
3721
|
+
const record2 = asRecord(input, "VoxelCapabilityAssessment");
|
|
3722
|
+
exactKeys(record2, ["capability", "status", "evaluatorId", "evaluatorVersion", "subjectContentHash", "evidenceHash", "reasonCodes", "evaluatedAt"], "VoxelCapabilityAssessment");
|
|
3723
|
+
const status = requireEnum(record2.status, ["supported", "unsupported", "not-applicable"], "VoxelCapabilityAssessment.status");
|
|
3724
|
+
const reasonCodes2 = requireReasonCodes(record2.reasonCodes, "VoxelCapabilityAssessment.reasonCodes");
|
|
3709
3725
|
if (status === "supported" && reasonCodes2.length !== 0) throw new Error("Supported capability evidence must not contain failure reason codes.");
|
|
3710
3726
|
if (status !== "supported" && reasonCodes2.length === 0) throw new Error("Unavailable capability evidence requires a reason code.");
|
|
3711
3727
|
return deepFreeze2({
|
|
3712
|
-
capability: requireEnum(
|
|
3728
|
+
capability: requireEnum(record2.capability, PVOX_CAPABILITIES, "VoxelCapabilityAssessment.capability"),
|
|
3713
3729
|
status,
|
|
3714
|
-
evaluatorId: requireToken(
|
|
3715
|
-
evaluatorVersion: assertImmutableAssetVersion(
|
|
3716
|
-
subjectContentHash: requireSha256(
|
|
3717
|
-
evidenceHash: requireSha256(
|
|
3730
|
+
evaluatorId: requireToken(record2.evaluatorId, "VoxelCapabilityAssessment.evaluatorId"),
|
|
3731
|
+
evaluatorVersion: assertImmutableAssetVersion(record2.evaluatorVersion),
|
|
3732
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, "VoxelCapabilityAssessment.subjectContentHash"),
|
|
3733
|
+
evidenceHash: requireSha256(record2.evidenceHash, "VoxelCapabilityAssessment.evidenceHash"),
|
|
3718
3734
|
reasonCodes: reasonCodes2,
|
|
3719
|
-
evaluatedAt: requireTimestamp(
|
|
3735
|
+
evaluatedAt: requireTimestamp(record2.evaluatedAt, "VoxelCapabilityAssessment.evaluatedAt")
|
|
3720
3736
|
});
|
|
3721
3737
|
}
|
|
3722
3738
|
var PVOX_PHYSICAL_PROPERTY_POLICIES = deepFreeze2({
|
|
@@ -3741,42 +3757,42 @@ var PVOX_PHYSICAL_PROPERTY_POLICIES = deepFreeze2({
|
|
|
3741
3757
|
"interior-thickness": { unit: "m", minimum: Number.EPSILON, maximum: 1e6 }
|
|
3742
3758
|
});
|
|
3743
3759
|
function createPhysicalPropertyEvidence(input) {
|
|
3744
|
-
const
|
|
3745
|
-
exactKeys(
|
|
3746
|
-
const property = requireEnum(
|
|
3760
|
+
const record2 = asRecord(input, "PhysicalPropertyEvidence");
|
|
3761
|
+
exactKeys(record2, ["property", "value", "unit", "provenance", "confidence", "subjectContentHash", "regionId", "materialId", "policyId", "policyVersion", "evidenceHash", "reviewedBy", "reviewedAt", "reviewToken"], "PhysicalPropertyEvidence");
|
|
3762
|
+
const property = requireEnum(record2.property, PVOX_PHYSICAL_PROPERTIES, "PhysicalPropertyEvidence.property");
|
|
3747
3763
|
const policy = PVOX_PHYSICAL_PROPERTY_POLICIES[property];
|
|
3748
|
-
if (
|
|
3764
|
+
if (record2.policyId !== PVOX_PHYSICAL_PROPERTY_POLICY_ID || record2.policyVersion !== PVOX_PHYSICAL_PROPERTY_POLICY_VERSION) {
|
|
3749
3765
|
throw new Error("PhysicalPropertyEvidence must use the governed immutable physical-property policy.");
|
|
3750
3766
|
}
|
|
3751
|
-
assertImmutableAssetVersion(
|
|
3752
|
-
if (
|
|
3753
|
-
const provenance = requireEnum(
|
|
3754
|
-
const reviewedBy =
|
|
3755
|
-
const reviewedAt =
|
|
3756
|
-
const reviewToken =
|
|
3767
|
+
assertImmutableAssetVersion(record2.policyVersion);
|
|
3768
|
+
if (record2.unit !== policy.unit) throw new Error("PhysicalPropertyEvidence unit must match its governed property policy.");
|
|
3769
|
+
const provenance = requireEnum(record2.provenance, ["source", "authored", "derived", "inferred", "default"], "PhysicalPropertyEvidence.provenance");
|
|
3770
|
+
const reviewedBy = record2.reviewedBy === void 0 ? void 0 : requireToken(record2.reviewedBy, "PhysicalPropertyEvidence.reviewedBy");
|
|
3771
|
+
const reviewedAt = record2.reviewedAt === void 0 ? void 0 : requireTimestamp(record2.reviewedAt, "PhysicalPropertyEvidence.reviewedAt");
|
|
3772
|
+
const reviewToken = record2.reviewToken === void 0 ? void 0 : requireAttestation(record2.reviewToken, "PhysicalPropertyEvidence.reviewToken");
|
|
3757
3773
|
if (new Set([reviewedBy, reviewedAt, reviewToken].map((entry) => entry === void 0)).size !== 1) throw new Error("PhysicalPropertyEvidence signed review identity, timestamp, and token must be supplied together.");
|
|
3758
3774
|
if (provenance === "inferred" && reviewedBy === void 0) throw new Error("Inferred PhysicalPropertyEvidence requires human review.");
|
|
3759
3775
|
return deepFreeze2({
|
|
3760
3776
|
property,
|
|
3761
|
-
value: requireNumber(
|
|
3777
|
+
value: requireNumber(record2.value, "PhysicalPropertyEvidence.value", policy.minimum, policy.maximum),
|
|
3762
3778
|
unit: policy.unit,
|
|
3763
3779
|
provenance,
|
|
3764
|
-
confidence: requireNumber(
|
|
3765
|
-
subjectContentHash: requireSha256(
|
|
3766
|
-
regionId: requireToken(
|
|
3767
|
-
materialId: requireToken(
|
|
3780
|
+
confidence: requireNumber(record2.confidence, "PhysicalPropertyEvidence.confidence", 0, 1),
|
|
3781
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, "PhysicalPropertyEvidence.subjectContentHash"),
|
|
3782
|
+
regionId: requireToken(record2.regionId, "PhysicalPropertyEvidence.regionId"),
|
|
3783
|
+
materialId: requireToken(record2.materialId, "PhysicalPropertyEvidence.materialId"),
|
|
3768
3784
|
policyId: PVOX_PHYSICAL_PROPERTY_POLICY_ID,
|
|
3769
3785
|
policyVersion: PVOX_PHYSICAL_PROPERTY_POLICY_VERSION,
|
|
3770
|
-
evidenceHash: requireSha256(
|
|
3786
|
+
evidenceHash: requireSha256(record2.evidenceHash, "PhysicalPropertyEvidence.evidenceHash"),
|
|
3771
3787
|
...reviewedBy === void 0 ? {} : { reviewedBy, reviewedAt, reviewToken }
|
|
3772
3788
|
});
|
|
3773
3789
|
}
|
|
3774
3790
|
function createPvoxPhysicalRegionInventoryV1(input) {
|
|
3775
3791
|
const fieldName = "PvoxPhysicalRegionInventoryV1";
|
|
3776
|
-
const
|
|
3777
|
-
exactKeys(
|
|
3778
|
-
if (
|
|
3779
|
-
const entriesInput = asDenseArray(
|
|
3792
|
+
const record2 = asRecord(input, fieldName);
|
|
3793
|
+
exactKeys(record2, ["inventoryVersion", "subjectContentHash", "inventoryHash", "inventoryHashAttestation", "validationEvidenceHash", "entries"], fieldName);
|
|
3794
|
+
if (record2.inventoryVersion !== PVOX_PHYSICAL_REGION_INVENTORY_VERSION) throw new Error(`${fieldName}.inventoryVersion is unsupported.`);
|
|
3795
|
+
const entriesInput = asDenseArray(record2.entries, `${fieldName}.entries`, 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions);
|
|
3780
3796
|
const entries = entriesInput.map((entry, index) => {
|
|
3781
3797
|
const entryName = `${fieldName}.entries[${index}]`;
|
|
3782
3798
|
const item = asRecord(entry, entryName);
|
|
@@ -3791,23 +3807,23 @@ function createPvoxPhysicalRegionInventoryV1(input) {
|
|
|
3791
3807
|
if (entries.some((entry, index) => entry.regionIndex !== index)) throw new Error(`${fieldName} regionIndex values must be contiguous and canonically ordered.`);
|
|
3792
3808
|
const pairs = entries.map(({ regionId, materialId }) => `${regionId}\0${materialId}`);
|
|
3793
3809
|
if (new Set(pairs).size !== pairs.length) throw new Error(`${fieldName} region/material pairs must be unique.`);
|
|
3794
|
-
const inventoryHash = requireSha256(
|
|
3810
|
+
const inventoryHash = requireSha256(record2.inventoryHash, `${fieldName}.inventoryHash`);
|
|
3795
3811
|
return deepFreeze2({
|
|
3796
3812
|
inventoryVersion: PVOX_PHYSICAL_REGION_INVENTORY_VERSION,
|
|
3797
|
-
subjectContentHash: requireSha256(
|
|
3813
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, `${fieldName}.subjectContentHash`),
|
|
3798
3814
|
inventoryHash,
|
|
3799
|
-
inventoryHashAttestation: createHashAttestation(
|
|
3800
|
-
validationEvidenceHash: requireSha256(
|
|
3815
|
+
inventoryHashAttestation: createHashAttestation(record2.inventoryHashAttestation, PVOX_PHYSICAL_INVENTORY_HASH_DOMAIN, inventoryHash, `${fieldName}.inventoryHashAttestation`),
|
|
3816
|
+
validationEvidenceHash: requireSha256(record2.validationEvidenceHash, `${fieldName}.validationEvidenceHash`),
|
|
3801
3817
|
entries
|
|
3802
3818
|
});
|
|
3803
3819
|
}
|
|
3804
3820
|
function createReviewedStructuralEvidence(input, expectedKind, expectedSubjectHash, expectedInventoryHash, expectedRecordCount, fieldName) {
|
|
3805
|
-
const
|
|
3806
|
-
exactKeys(
|
|
3807
|
-
if (
|
|
3808
|
-
const subjectContentHash = requireSha256(
|
|
3809
|
-
const physicalInventoryHash = requireSha256(
|
|
3810
|
-
const recordCount = requireInteger(
|
|
3821
|
+
const record2 = asRecord(input, fieldName);
|
|
3822
|
+
exactKeys(record2, ["kind", "subjectContentHash", "physicalInventoryHash", "recordCount", "evidenceHash", "reviewerId", "reviewedAt", "reviewToken"], fieldName);
|
|
3823
|
+
if (record2.kind !== expectedKind) throw new Error(`${fieldName}.kind must be ${expectedKind}.`);
|
|
3824
|
+
const subjectContentHash = requireSha256(record2.subjectContentHash, `${fieldName}.subjectContentHash`);
|
|
3825
|
+
const physicalInventoryHash = requireSha256(record2.physicalInventoryHash, `${fieldName}.physicalInventoryHash`);
|
|
3826
|
+
const recordCount = requireInteger(record2.recordCount, `${fieldName}.recordCount`, 0, PVOX_DEFAULT_LIMITS.maximumBondRecords);
|
|
3811
3827
|
if (subjectContentHash !== expectedSubjectHash || physicalInventoryHash !== expectedInventoryHash || recordCount !== expectedRecordCount) {
|
|
3812
3828
|
throw new Error(`${fieldName} must bind the exact PVOX closure, physical inventory, and binary record count.`);
|
|
3813
3829
|
}
|
|
@@ -3816,15 +3832,15 @@ function createReviewedStructuralEvidence(input, expectedKind, expectedSubjectHa
|
|
|
3816
3832
|
subjectContentHash,
|
|
3817
3833
|
physicalInventoryHash,
|
|
3818
3834
|
recordCount,
|
|
3819
|
-
evidenceHash: requireSha256(
|
|
3820
|
-
reviewerId: requireToken(
|
|
3821
|
-
reviewedAt: requireTimestamp(
|
|
3822
|
-
reviewToken: requireAttestation(
|
|
3835
|
+
evidenceHash: requireSha256(record2.evidenceHash, `${fieldName}.evidenceHash`),
|
|
3836
|
+
reviewerId: requireToken(record2.reviewerId, `${fieldName}.reviewerId`),
|
|
3837
|
+
reviewedAt: requireTimestamp(record2.reviewedAt, `${fieldName}.reviewedAt`),
|
|
3838
|
+
reviewToken: requireAttestation(record2.reviewToken, `${fieldName}.reviewToken`)
|
|
3823
3839
|
});
|
|
3824
3840
|
}
|
|
3825
3841
|
function createPvoxFidelityEvidence(input) {
|
|
3826
|
-
const
|
|
3827
|
-
exactKeys(
|
|
3842
|
+
const record2 = asRecord(input, "PvoxFidelityEvidence");
|
|
3843
|
+
exactKeys(record2, [
|
|
3828
3844
|
"profileId",
|
|
3829
3845
|
"surfaceProfileId",
|
|
3830
3846
|
"profileVersion",
|
|
@@ -3851,8 +3867,8 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3851
3867
|
"evaluatedAt",
|
|
3852
3868
|
"decisionToken"
|
|
3853
3869
|
], "PvoxFidelityEvidence");
|
|
3854
|
-
const profileId = requireEnum(
|
|
3855
|
-
const surfaceProfileId = requireEnum(
|
|
3870
|
+
const profileId = requireEnum(record2.profileId, PVOX_FIDELITY_PROFILE_IDS, "PvoxFidelityEvidence.profileId");
|
|
3871
|
+
const surfaceProfileId = requireEnum(record2.surfaceProfileId, [
|
|
3856
3872
|
"precision-hero-v1",
|
|
3857
3873
|
"props-furniture-v1",
|
|
3858
3874
|
"rocks-organic-shells-v1",
|
|
@@ -3861,12 +3877,12 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3861
3877
|
if (profileId !== "deformable-v1" && profileId !== surfaceProfileId) {
|
|
3862
3878
|
throw new Error("PvoxFidelityEvidence surfaceProfileId must match its governed profileId.");
|
|
3863
3879
|
}
|
|
3864
|
-
if (
|
|
3880
|
+
if (record2.profileVersion !== PVOX_FIDELITY_PROFILE_VERSION) {
|
|
3865
3881
|
throw new Error("PvoxFidelityEvidence.profileVersion must use the governed profile version.");
|
|
3866
3882
|
}
|
|
3867
|
-
assertImmutableAssetVersion(
|
|
3868
|
-
const evaluatedDiagonalMetres = requireNumber(
|
|
3869
|
-
const lod0CellSizeMetres = requireNumber(
|
|
3883
|
+
assertImmutableAssetVersion(record2.profileVersion);
|
|
3884
|
+
const evaluatedDiagonalMetres = requireNumber(record2.evaluatedDiagonalMetres, "PvoxFidelityEvidence.evaluatedDiagonalMetres", Number.EPSILON, 1e9);
|
|
3885
|
+
const lod0CellSizeMetres = requireNumber(record2.lod0CellSizeMetres, "PvoxFidelityEvidence.lod0CellSizeMetres", Number.EPSILON, 1e6);
|
|
3870
3886
|
const profileFormula = {
|
|
3871
3887
|
"precision-hero-v1": [25e-5, 1e-4],
|
|
3872
3888
|
"props-furniture-v1": [5e-4, 25e-5],
|
|
@@ -3876,16 +3892,16 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3876
3892
|
const [absoluteFloor, diagonalFactor] = profileFormula[surfaceProfileId];
|
|
3877
3893
|
const governedP99Ceiling = Math.max(absoluteFloor, evaluatedDiagonalMetres * diagonalFactor);
|
|
3878
3894
|
const governedMaximumCeiling = Math.min(3 * governedP99Ceiling, 1.5 * lod0CellSizeMetres);
|
|
3879
|
-
const suppliedP99Ceiling = requireNumber(
|
|
3880
|
-
const suppliedMaximumCeiling = requireNumber(
|
|
3895
|
+
const suppliedP99Ceiling = requireNumber(record2.p99AllowedSurfaceErrorMetres, "PvoxFidelityEvidence.p99AllowedSurfaceErrorMetres", 0, 1e6);
|
|
3896
|
+
const suppliedMaximumCeiling = requireNumber(record2.maximumAllowedSurfaceErrorMetres, "PvoxFidelityEvidence.maximumAllowedSurfaceErrorMetres", 0, 1e6);
|
|
3881
3897
|
if (!approximatelyEqual(suppliedP99Ceiling, governedP99Ceiling) || !approximatelyEqual(suppliedMaximumCeiling, governedMaximumCeiling)) {
|
|
3882
3898
|
throw new Error("PvoxFidelityEvidence surface ceilings must match the governed profile formula and LOD0 cell limit.");
|
|
3883
3899
|
}
|
|
3884
|
-
const minimumSilhouetteIou = requireNumber(
|
|
3885
|
-
const maximumAllowedContourDisplacementPx = requireNumber(
|
|
3886
|
-
const minimumRenderedSsim = requireNumber(
|
|
3887
|
-
const maximumP95DeltaE2000 = requireNumber(
|
|
3888
|
-
const maximumNormalizedMaterialError = requireNumber(
|
|
3900
|
+
const minimumSilhouetteIou = requireNumber(record2.minimumSilhouetteIou, "PvoxFidelityEvidence.minimumSilhouetteIou", 0, 1);
|
|
3901
|
+
const maximumAllowedContourDisplacementPx = requireNumber(record2.maximumAllowedContourDisplacementPx, "PvoxFidelityEvidence.maximumAllowedContourDisplacementPx", 0, 1e6);
|
|
3902
|
+
const minimumRenderedSsim = requireNumber(record2.minimumRenderedSsim, "PvoxFidelityEvidence.minimumRenderedSsim", 0, 1);
|
|
3903
|
+
const maximumP95DeltaE2000 = requireNumber(record2.maximumP95DeltaE2000, "PvoxFidelityEvidence.maximumP95DeltaE2000", 0, 100);
|
|
3904
|
+
const maximumNormalizedMaterialError = requireNumber(record2.maximumNormalizedMaterialError, "PvoxFidelityEvidence.maximumNormalizedMaterialError", 0, 1);
|
|
3889
3905
|
if (!approximatelyEqual(minimumSilhouetteIou, 0.995) || !approximatelyEqual(maximumAllowedContourDisplacementPx, 0.5) || !approximatelyEqual(minimumRenderedSsim, 0.98) || !approximatelyEqual(maximumP95DeltaE2000, 3) || !approximatelyEqual(maximumNormalizedMaterialError, 1 / 255)) {
|
|
3890
3906
|
throw new Error("PvoxFidelityEvidence must use the governed silhouette, contour, render, colour, and material thresholds.");
|
|
3891
3907
|
}
|
|
@@ -3893,38 +3909,38 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3893
3909
|
profileId,
|
|
3894
3910
|
surfaceProfileId,
|
|
3895
3911
|
profileVersion: PVOX_FIDELITY_PROFILE_VERSION,
|
|
3896
|
-
canonicalDocumentHash: requireSha256(
|
|
3897
|
-
pvoxBinaryClosureHash: requireSha256(
|
|
3912
|
+
canonicalDocumentHash: requireSha256(record2.canonicalDocumentHash, "PvoxFidelityEvidence.canonicalDocumentHash"),
|
|
3913
|
+
pvoxBinaryClosureHash: requireSha256(record2.pvoxBinaryClosureHash, "PvoxFidelityEvidence.pvoxBinaryClosureHash"),
|
|
3898
3914
|
evaluatedDiagonalMetres,
|
|
3899
3915
|
lod0CellSizeMetres,
|
|
3900
3916
|
maximumAllowedSurfaceErrorMetres: governedMaximumCeiling,
|
|
3901
|
-
maximumSurfaceErrorMetres: requireNumber(
|
|
3917
|
+
maximumSurfaceErrorMetres: requireNumber(record2.maximumSurfaceErrorMetres, "PvoxFidelityEvidence.maximumSurfaceErrorMetres", 0, 1e6),
|
|
3902
3918
|
p99AllowedSurfaceErrorMetres: governedP99Ceiling,
|
|
3903
|
-
p99SurfaceErrorMetres: requireNumber(
|
|
3919
|
+
p99SurfaceErrorMetres: requireNumber(record2.p99SurfaceErrorMetres, "PvoxFidelityEvidence.p99SurfaceErrorMetres", 0, 1e6),
|
|
3904
3920
|
minimumSilhouetteIou,
|
|
3905
|
-
silhouetteIou: requireNumber(
|
|
3921
|
+
silhouetteIou: requireNumber(record2.silhouetteIou, "PvoxFidelityEvidence.silhouetteIou", 0, 1),
|
|
3906
3922
|
maximumAllowedContourDisplacementPx,
|
|
3907
|
-
maximumContourDisplacementPx: requireNumber(
|
|
3923
|
+
maximumContourDisplacementPx: requireNumber(record2.maximumContourDisplacementPx, "PvoxFidelityEvidence.maximumContourDisplacementPx", 0, 1e6),
|
|
3908
3924
|
minimumRenderedSsim,
|
|
3909
|
-
renderedSsim: requireNumber(
|
|
3925
|
+
renderedSsim: requireNumber(record2.renderedSsim, "PvoxFidelityEvidence.renderedSsim", 0, 1),
|
|
3910
3926
|
maximumP95DeltaE2000,
|
|
3911
|
-
p95DeltaE2000: requireNumber(
|
|
3927
|
+
p95DeltaE2000: requireNumber(record2.p95DeltaE2000, "PvoxFidelityEvidence.p95DeltaE2000", 0, 100),
|
|
3912
3928
|
maximumNormalizedMaterialError,
|
|
3913
|
-
normalizedMaterialError: requireNumber(
|
|
3914
|
-
evidenceHash: requireSha256(
|
|
3915
|
-
evaluatedAt: requireTimestamp(
|
|
3916
|
-
decisionToken: requireAttestation(
|
|
3929
|
+
normalizedMaterialError: requireNumber(record2.normalizedMaterialError, "PvoxFidelityEvidence.normalizedMaterialError", 0, 1),
|
|
3930
|
+
evidenceHash: requireSha256(record2.evidenceHash, "PvoxFidelityEvidence.evidenceHash"),
|
|
3931
|
+
evaluatedAt: requireTimestamp(record2.evaluatedAt, "PvoxFidelityEvidence.evaluatedAt"),
|
|
3932
|
+
decisionToken: requireAttestation(record2.decisionToken, "PvoxFidelityEvidence.decisionToken")
|
|
3917
3933
|
};
|
|
3918
3934
|
const passed = output.maximumSurfaceErrorMetres <= output.maximumAllowedSurfaceErrorMetres && output.p99SurfaceErrorMetres <= output.p99AllowedSurfaceErrorMetres && output.silhouetteIou >= output.minimumSilhouetteIou && output.maximumContourDisplacementPx <= output.maximumAllowedContourDisplacementPx && output.renderedSsim >= output.minimumRenderedSsim && output.p95DeltaE2000 <= output.maximumP95DeltaE2000 && output.normalizedMaterialError <= output.maximumNormalizedMaterialError;
|
|
3919
|
-
const outcome = requireEnum(
|
|
3935
|
+
const outcome = requireEnum(record2.outcome, ["passed", "blocked"], "PvoxFidelityEvidence.outcome");
|
|
3920
3936
|
if (outcome !== (passed ? "passed" : "blocked")) throw new Error("PvoxFidelityEvidence outcome must match its measured fidelity thresholds.");
|
|
3921
3937
|
return deepFreeze2({ ...output, outcome });
|
|
3922
3938
|
}
|
|
3923
3939
|
function createPvoxEditJournalCurrentState(input, baseManifest) {
|
|
3924
3940
|
const fieldName = "PvoxEditJournalCurrentState";
|
|
3925
|
-
const
|
|
3926
|
-
exactKeys(
|
|
3927
|
-
const pageEntries = asDenseArray(
|
|
3941
|
+
const record2 = asRecord(input, fieldName);
|
|
3942
|
+
exactKeys(record2, ["baseContentHash", "basePageSetHash", "placementId", "gridVersion", "revision", "rootHash", "latestJournalHash", "pageHashes"], fieldName);
|
|
3943
|
+
const pageEntries = asDenseArray(record2.pageHashes, `${fieldName}.pageHashes`, baseManifest.pages.length, baseManifest.pages.length);
|
|
3928
3944
|
const pageHashes = pageEntries.map((entry, index) => {
|
|
3929
3945
|
const entryName = `${fieldName}.pageHashes[${index}]`;
|
|
3930
3946
|
const page = asRecord(entry, entryName);
|
|
@@ -3934,38 +3950,38 @@ function createPvoxEditJournalCurrentState(input, baseManifest) {
|
|
|
3934
3950
|
return { pageIndex, sha256: requireSha256(page.sha256, `${entryName}.sha256`) };
|
|
3935
3951
|
});
|
|
3936
3952
|
return deepFreeze2({
|
|
3937
|
-
baseContentHash: requireSha256(
|
|
3938
|
-
basePageSetHash: requireSha256(
|
|
3939
|
-
placementId: requireToken(
|
|
3940
|
-
gridVersion: requireToken(
|
|
3941
|
-
revision: requireInteger(
|
|
3942
|
-
rootHash: requireSha256(
|
|
3943
|
-
latestJournalHash: requireSha256(
|
|
3953
|
+
baseContentHash: requireSha256(record2.baseContentHash, `${fieldName}.baseContentHash`),
|
|
3954
|
+
basePageSetHash: requireSha256(record2.basePageSetHash, `${fieldName}.basePageSetHash`),
|
|
3955
|
+
placementId: requireToken(record2.placementId, `${fieldName}.placementId`),
|
|
3956
|
+
gridVersion: requireToken(record2.gridVersion, `${fieldName}.gridVersion`),
|
|
3957
|
+
revision: requireInteger(record2.revision, `${fieldName}.revision`, 1, Number.MAX_SAFE_INTEGER),
|
|
3958
|
+
rootHash: requireSha256(record2.rootHash, `${fieldName}.rootHash`),
|
|
3959
|
+
latestJournalHash: requireSha256(record2.latestJournalHash, `${fieldName}.latestJournalHash`),
|
|
3944
3960
|
pageHashes
|
|
3945
3961
|
});
|
|
3946
3962
|
}
|
|
3947
3963
|
function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
3948
|
-
const
|
|
3949
|
-
exactKeys(
|
|
3950
|
-
if (
|
|
3964
|
+
const record2 = asRecord(input, "PvoxEditJournal");
|
|
3965
|
+
exactKeys(record2, ["journalVersion", "baseContentHash", "basePageSetHash", "placementId", "gridVersion", "operationId", "expectedRevision", "resultingRevision", "previousJournalHash", "expectedRootHash", "resultingRootHash", "resultingRootHashAttestation", "patches", "dirtyBoundsMetres", "massDeltaKg", "journalHash", "journalHashAttestation", "recordedAt"], "PvoxEditJournal");
|
|
3966
|
+
if (record2.journalVersion !== PVOX_EDIT_JOURNAL_VERSION) throw new Error("PvoxEditJournal.journalVersion is unsupported.");
|
|
3951
3967
|
const baseManifest = createPvoxAssetManifestV1(baseManifestInput);
|
|
3952
|
-
const baseContentHash = requireSha256(
|
|
3953
|
-
const basePageSetHash = requireSha256(
|
|
3968
|
+
const baseContentHash = requireSha256(record2.baseContentHash, "PvoxEditJournal.baseContentHash");
|
|
3969
|
+
const basePageSetHash = requireSha256(record2.basePageSetHash, "PvoxEditJournal.basePageSetHash");
|
|
3954
3970
|
if (baseContentHash !== baseManifest.artifact.sha256 || basePageSetHash !== baseManifest.pageSetHash) {
|
|
3955
3971
|
throw new Error("PvoxEditJournal must bind the exact base PVOX artifact and page inventory.");
|
|
3956
3972
|
}
|
|
3957
|
-
const expectedRevision = requireInteger(
|
|
3958
|
-
const resultingRevision = requireInteger(
|
|
3973
|
+
const expectedRevision = requireInteger(record2.expectedRevision, "PvoxEditJournal.expectedRevision", 0, Number.MAX_SAFE_INTEGER - 1);
|
|
3974
|
+
const resultingRevision = requireInteger(record2.resultingRevision, "PvoxEditJournal.resultingRevision", 1, Number.MAX_SAFE_INTEGER);
|
|
3959
3975
|
if (resultingRevision !== expectedRevision + 1) throw new Error("PvoxEditJournal revisions must be sequential.");
|
|
3960
|
-
const previousJournalHash = requireSha256(
|
|
3976
|
+
const previousJournalHash = requireSha256(record2.previousJournalHash, "PvoxEditJournal.previousJournalHash");
|
|
3961
3977
|
if (expectedRevision === 0 !== (previousJournalHash === PVOX_EDIT_JOURNAL_GENESIS_HASH)) {
|
|
3962
3978
|
throw new Error("PvoxEditJournal genesis revision must use only the canonical genesis previousJournalHash.");
|
|
3963
3979
|
}
|
|
3964
|
-
const expectedRootHash = requireSha256(
|
|
3965
|
-
const resultingRootHash = requireSha256(
|
|
3980
|
+
const expectedRootHash = requireSha256(record2.expectedRootHash, "PvoxEditJournal.expectedRootHash");
|
|
3981
|
+
const resultingRootHash = requireSha256(record2.resultingRootHash, "PvoxEditJournal.resultingRootHash");
|
|
3966
3982
|
if (expectedRootHash === resultingRootHash) throw new Error("PvoxEditJournal copy-on-write operation must change the overlay root hash.");
|
|
3967
|
-
const placementId = requireToken(
|
|
3968
|
-
const gridVersion = requireToken(
|
|
3983
|
+
const placementId = requireToken(record2.placementId, "PvoxEditJournal.placementId");
|
|
3984
|
+
const gridVersion = requireToken(record2.gridVersion, "PvoxEditJournal.gridVersion");
|
|
3969
3985
|
let currentState;
|
|
3970
3986
|
if (expectedRevision === 0) {
|
|
3971
3987
|
if (currentStateInput !== void 0) throw new Error("PvoxEditJournal genesis must not supply a non-genesis current state.");
|
|
@@ -3977,7 +3993,7 @@ function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
|
3977
3993
|
throw new Error("PvoxEditJournal non-genesis CAS subject must match the authenticated current placement state.");
|
|
3978
3994
|
}
|
|
3979
3995
|
}
|
|
3980
|
-
const patchEntries = asDenseArray(
|
|
3996
|
+
const patchEntries = asDenseArray(record2.patches, "PvoxEditJournal.patches", 1, PVOX_MAX_EDIT_PATCHES);
|
|
3981
3997
|
const patches = patchEntries.map((entry, index) => {
|
|
3982
3998
|
const fieldName = `PvoxEditJournal.patches[${index}]`;
|
|
3983
3999
|
const patch = asRecord(entry, fieldName);
|
|
@@ -4029,26 +4045,26 @@ function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
|
4029
4045
|
if (new Set(patches.map(({ expectedPageIndex }) => expectedPageIndex)).size !== patches.length) {
|
|
4030
4046
|
throw new Error("PvoxEditJournal permits only one coherent patch transition per page.");
|
|
4031
4047
|
}
|
|
4032
|
-
const journalHash = requireSha256(
|
|
4048
|
+
const journalHash = requireSha256(record2.journalHash, "PvoxEditJournal.journalHash");
|
|
4033
4049
|
return deepFreeze2({
|
|
4034
4050
|
journalVersion: PVOX_EDIT_JOURNAL_VERSION,
|
|
4035
4051
|
baseContentHash,
|
|
4036
4052
|
basePageSetHash,
|
|
4037
4053
|
placementId,
|
|
4038
4054
|
gridVersion,
|
|
4039
|
-
operationId: requireToken(
|
|
4055
|
+
operationId: requireToken(record2.operationId, "PvoxEditJournal.operationId"),
|
|
4040
4056
|
expectedRevision,
|
|
4041
4057
|
resultingRevision,
|
|
4042
4058
|
previousJournalHash,
|
|
4043
4059
|
expectedRootHash,
|
|
4044
4060
|
resultingRootHash,
|
|
4045
|
-
resultingRootHashAttestation: createHashAttestation(
|
|
4061
|
+
resultingRootHashAttestation: createHashAttestation(record2.resultingRootHashAttestation, PVOX_EDIT_OVERLAY_ROOT_HASH_DOMAIN, resultingRootHash, "PvoxEditJournal.resultingRootHashAttestation"),
|
|
4046
4062
|
patches,
|
|
4047
|
-
dirtyBoundsMetres: createBounds2(
|
|
4048
|
-
massDeltaKg: requireNumber(
|
|
4063
|
+
dirtyBoundsMetres: createBounds2(record2.dirtyBoundsMetres, "PvoxEditJournal.dirtyBoundsMetres"),
|
|
4064
|
+
massDeltaKg: requireNumber(record2.massDeltaKg, "PvoxEditJournal.massDeltaKg", -Number.MAX_VALUE, Number.MAX_VALUE),
|
|
4049
4065
|
journalHash,
|
|
4050
|
-
journalHashAttestation: createHashAttestation(
|
|
4051
|
-
recordedAt: requireTimestamp(
|
|
4066
|
+
journalHashAttestation: createHashAttestation(record2.journalHashAttestation, PVOX_EDIT_JOURNAL_HASH_DOMAIN, journalHash, "PvoxEditJournal.journalHashAttestation"),
|
|
4067
|
+
recordedAt: requireTimestamp(record2.recordedAt, "PvoxEditJournal.recordedAt")
|
|
4052
4068
|
});
|
|
4053
4069
|
}
|
|
4054
4070
|
function createPvoxEditJournalChain(input, baseManifestInput) {
|
|
@@ -4089,55 +4105,55 @@ function createPvoxEditJournalChain(input, baseManifestInput) {
|
|
|
4089
4105
|
return deepFreeze2(journals);
|
|
4090
4106
|
}
|
|
4091
4107
|
function createCompilerEvidence(value) {
|
|
4092
|
-
const
|
|
4093
|
-
exactKeys(
|
|
4108
|
+
const record2 = asRecord(value, "ModelProcessingManifestV2.converter");
|
|
4109
|
+
exactKeys(record2, ["compilerId", "compilerVersion", "sourceFormat", "sourceContentHash", "canonicalDocumentHash", "compilationInputHash", "runtimeRequestProfileHash", "outputContentHash", "evidenceHash", "compiledAt"], "ModelProcessingManifestV2.converter");
|
|
4094
4110
|
return {
|
|
4095
|
-
compilerId: requireToken(
|
|
4096
|
-
compilerVersion: assertImmutableAssetVersion(
|
|
4097
|
-
sourceFormat: requireToken(
|
|
4098
|
-
sourceContentHash: requireSha256(
|
|
4099
|
-
canonicalDocumentHash: requireSha256(
|
|
4100
|
-
compilationInputHash: requireSha256(
|
|
4101
|
-
runtimeRequestProfileHash: requireSha256(
|
|
4102
|
-
outputContentHash: requireSha256(
|
|
4103
|
-
evidenceHash: requireSha256(
|
|
4104
|
-
compiledAt: requireTimestamp(
|
|
4111
|
+
compilerId: requireToken(record2.compilerId, "ModelProcessingManifestV2.converter.compilerId"),
|
|
4112
|
+
compilerVersion: assertImmutableAssetVersion(record2.compilerVersion),
|
|
4113
|
+
sourceFormat: requireToken(record2.sourceFormat, "ModelProcessingManifestV2.converter.sourceFormat"),
|
|
4114
|
+
sourceContentHash: requireSha256(record2.sourceContentHash, "ModelProcessingManifestV2.converter.sourceContentHash"),
|
|
4115
|
+
canonicalDocumentHash: requireSha256(record2.canonicalDocumentHash, "ModelProcessingManifestV2.converter.canonicalDocumentHash"),
|
|
4116
|
+
compilationInputHash: requireSha256(record2.compilationInputHash, "ModelProcessingManifestV2.converter.compilationInputHash"),
|
|
4117
|
+
runtimeRequestProfileHash: requireSha256(record2.runtimeRequestProfileHash, "ModelProcessingManifestV2.converter.runtimeRequestProfileHash"),
|
|
4118
|
+
outputContentHash: requireSha256(record2.outputContentHash, "ModelProcessingManifestV2.converter.outputContentHash"),
|
|
4119
|
+
evidenceHash: requireSha256(record2.evidenceHash, "ModelProcessingManifestV2.converter.evidenceHash"),
|
|
4120
|
+
compiledAt: requireTimestamp(record2.compiledAt, "ModelProcessingManifestV2.converter.compiledAt")
|
|
4105
4121
|
};
|
|
4106
4122
|
}
|
|
4107
4123
|
function createPvoxGateEvidence(input) {
|
|
4108
|
-
const
|
|
4109
|
-
exactKeys(
|
|
4110
|
-
const outcome = requireEnum(
|
|
4111
|
-
const reasonCodes2 = requireReasonCodes(
|
|
4124
|
+
const record2 = asRecord(input, "PvoxGateEvidence");
|
|
4125
|
+
exactKeys(record2, ["kind", "outcome", "validatorId", "validatorVersion", "subjectContentHash", "evidenceHash", "reasonCodes", "evaluatedAt", "attestationToken"], "PvoxGateEvidence");
|
|
4126
|
+
const outcome = requireEnum(record2.outcome, ["passed", "blocked"], "PvoxGateEvidence.outcome");
|
|
4127
|
+
const reasonCodes2 = requireReasonCodes(record2.reasonCodes, "PvoxGateEvidence.reasonCodes");
|
|
4112
4128
|
if (outcome === "blocked" && reasonCodes2.length === 0) throw new Error("Blocked PvoxGateEvidence requires a reason code.");
|
|
4113
4129
|
return deepFreeze2({
|
|
4114
|
-
kind: requireEnum(
|
|
4130
|
+
kind: requireEnum(record2.kind, PVOX_NON_OVERRIDABLE_GATE_KINDS, "PvoxGateEvidence.kind"),
|
|
4115
4131
|
outcome,
|
|
4116
|
-
validatorId: requireToken(
|
|
4117
|
-
validatorVersion: assertImmutableAssetVersion(
|
|
4118
|
-
subjectContentHash: requireSha256(
|
|
4119
|
-
evidenceHash: requireSha256(
|
|
4132
|
+
validatorId: requireToken(record2.validatorId, "PvoxGateEvidence.validatorId"),
|
|
4133
|
+
validatorVersion: assertImmutableAssetVersion(record2.validatorVersion),
|
|
4134
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, "PvoxGateEvidence.subjectContentHash"),
|
|
4135
|
+
evidenceHash: requireSha256(record2.evidenceHash, "PvoxGateEvidence.evidenceHash"),
|
|
4120
4136
|
reasonCodes: reasonCodes2,
|
|
4121
|
-
evaluatedAt: requireTimestamp(
|
|
4122
|
-
attestationToken: requireAttestation(
|
|
4137
|
+
evaluatedAt: requireTimestamp(record2.evaluatedAt, "PvoxGateEvidence.evaluatedAt"),
|
|
4138
|
+
attestationToken: requireAttestation(record2.attestationToken, "PvoxGateEvidence.attestationToken")
|
|
4123
4139
|
});
|
|
4124
4140
|
}
|
|
4125
4141
|
function createTransform2(value, fieldName) {
|
|
4126
|
-
const
|
|
4127
|
-
exactKeys(
|
|
4128
|
-
const translationMetres = requireVector(
|
|
4129
|
-
const rotationEntries = asDenseArray(
|
|
4142
|
+
const record2 = asRecord(value, fieldName);
|
|
4143
|
+
exactKeys(record2, ["translationMetres", "rotationQuaternion", "scale"], fieldName);
|
|
4144
|
+
const translationMetres = requireVector(record2.translationMetres, `${fieldName}.translationMetres`);
|
|
4145
|
+
const rotationEntries = asDenseArray(record2.rotationQuaternion, `${fieldName}.rotationQuaternion`, 4, 4);
|
|
4130
4146
|
const rotationQuaternion = rotationEntries.map((entry, index) => requireNumber(entry, `${fieldName}.rotationQuaternion[${index}]`, -1, 1));
|
|
4131
4147
|
if (Math.abs(Math.hypot(...rotationQuaternion) - 1) > 1e-4) throw new Error(`${fieldName}.rotationQuaternion must be normalized.`);
|
|
4132
|
-
const scale = requireVector(
|
|
4148
|
+
const scale = requireVector(record2.scale, `${fieldName}.scale`);
|
|
4133
4149
|
if (scale.some((entry) => entry <= 0)) throw new Error(`${fieldName}.scale values must be positive.`);
|
|
4134
4150
|
return { translationMetres, rotationQuaternion, scale };
|
|
4135
4151
|
}
|
|
4136
4152
|
function createAssemblyChild2(value, index, resolutionId, candidateId) {
|
|
4137
4153
|
const fieldName = `ModelProcessingManifestV2.children[${index}]`;
|
|
4138
|
-
const
|
|
4139
|
-
exactKeys(
|
|
4140
|
-
const childRef = asRecord(
|
|
4154
|
+
const record2 = asRecord(value, fieldName);
|
|
4155
|
+
exactKeys(record2, ["instanceId", "parentInstanceId", "assetRef", "transform"], fieldName);
|
|
4156
|
+
const childRef = asRecord(record2.assetRef, `${fieldName}.assetRef`);
|
|
4141
4157
|
let assetRef;
|
|
4142
4158
|
if (childRef.disposition === "staged-derived") {
|
|
4143
4159
|
exactKeys(childRef, ["disposition", "representation", "derivedId", "kind", "contentHash", "binaryClosureHash", "processingClosureHash", "processingManifestId", "processingManifestUri"], `${fieldName}.assetRef`);
|
|
@@ -4174,12 +4190,12 @@ function createAssemblyChild2(value, index, resolutionId, candidateId) {
|
|
|
4174
4190
|
processingManifestUri: expectedUri
|
|
4175
4191
|
};
|
|
4176
4192
|
}
|
|
4177
|
-
const parentInstanceId =
|
|
4193
|
+
const parentInstanceId = record2.parentInstanceId === void 0 ? void 0 : requireToken(record2.parentInstanceId, `${fieldName}.parentInstanceId`);
|
|
4178
4194
|
return {
|
|
4179
|
-
instanceId: requireToken(
|
|
4195
|
+
instanceId: requireToken(record2.instanceId, `${fieldName}.instanceId`),
|
|
4180
4196
|
...parentInstanceId === void 0 ? {} : { parentInstanceId },
|
|
4181
4197
|
assetRef,
|
|
4182
|
-
transform: createTransform2(
|
|
4198
|
+
transform: createTransform2(record2.transform, `${fieldName}.transform`)
|
|
4183
4199
|
};
|
|
4184
4200
|
}
|
|
4185
4201
|
function assertAssembly(children) {
|
|
@@ -4221,8 +4237,8 @@ var NON_DEFAULT_CRITICAL_PROPERTIES = /* @__PURE__ */ new Set([
|
|
|
4221
4237
|
"melting-temperature"
|
|
4222
4238
|
]);
|
|
4223
4239
|
function createModelProcessingManifestV2(input) {
|
|
4224
|
-
const
|
|
4225
|
-
exactKeys(
|
|
4240
|
+
const record2 = asRecord(input, "ModelProcessingManifestV2");
|
|
4241
|
+
exactKeys(record2, [
|
|
4226
4242
|
"contractVersion",
|
|
4227
4243
|
"representation",
|
|
4228
4244
|
"manifestId",
|
|
@@ -4259,24 +4275,24 @@ function createModelProcessingManifestV2(input) {
|
|
|
4259
4275
|
"children",
|
|
4260
4276
|
"processedAt"
|
|
4261
4277
|
], "ModelProcessingManifestV2");
|
|
4262
|
-
if (
|
|
4263
|
-
if (
|
|
4264
|
-
const resolutionId = requirePathSegment(
|
|
4265
|
-
const candidateId = requirePathSegment(
|
|
4266
|
-
assertCoordinateSystem2(
|
|
4267
|
-
const pvox = createPvoxAssetManifestV1(
|
|
4278
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelProcessingManifestV2.contractVersion is unsupported.");
|
|
4279
|
+
if (record2.representation !== "pvox") throw new Error("ModelProcessingManifestV2.representation must be pvox.");
|
|
4280
|
+
const resolutionId = requirePathSegment(record2.resolutionId, "ModelProcessingManifestV2.resolutionId");
|
|
4281
|
+
const candidateId = requirePathSegment(record2.candidateId, "ModelProcessingManifestV2.candidateId");
|
|
4282
|
+
assertCoordinateSystem2(record2.coordinateSystem, "ModelProcessingManifestV2.coordinateSystem");
|
|
4283
|
+
const pvox = createPvoxAssetManifestV1(record2.pvox);
|
|
4268
4284
|
const resourceScope = parsePvoxResourceScope(pvox.artifact.uri);
|
|
4269
4285
|
if (resourceScope.kind === "resolution" && (resourceScope.resolutionId !== resolutionId || resourceScope.candidateId !== candidateId)) {
|
|
4270
4286
|
throw new Error("ModelProcessingManifestV2 PVOX resource namespace must match resolutionId and candidateId.");
|
|
4271
4287
|
}
|
|
4272
|
-
const technicalProfile = createVoxelTechnicalProfile(
|
|
4273
|
-
const contentHash = requireSha256(
|
|
4274
|
-
const binaryClosureHash = requireSha256(
|
|
4275
|
-
const processingClosureHash = requireSha256(
|
|
4276
|
-
const assemblyClosureHash = requireSha256(
|
|
4277
|
-
const requestSemanticProfileHash = requireSha256(
|
|
4278
|
-
const compilationInputHash = requireSha256(
|
|
4279
|
-
const runtimeRequestProfileHash = requireSha256(
|
|
4288
|
+
const technicalProfile = createVoxelTechnicalProfile(record2.technicalProfile);
|
|
4289
|
+
const contentHash = requireSha256(record2.contentHash, "ModelProcessingManifestV2.contentHash");
|
|
4290
|
+
const binaryClosureHash = requireSha256(record2.binaryClosureHash, "ModelProcessingManifestV2.binaryClosureHash");
|
|
4291
|
+
const processingClosureHash = requireSha256(record2.processingClosureHash, "ModelProcessingManifestV2.processingClosureHash");
|
|
4292
|
+
const assemblyClosureHash = requireSha256(record2.assemblyClosureHash, "ModelProcessingManifestV2.assemblyClosureHash");
|
|
4293
|
+
const requestSemanticProfileHash = requireSha256(record2.requestSemanticProfileHash, "ModelProcessingManifestV2.requestSemanticProfileHash");
|
|
4294
|
+
const compilationInputHash = requireSha256(record2.compilationInputHash, "ModelProcessingManifestV2.compilationInputHash");
|
|
4295
|
+
const runtimeRequestProfileHash = requireSha256(record2.runtimeRequestProfileHash, "ModelProcessingManifestV2.runtimeRequestProfileHash");
|
|
4280
4296
|
if (contentHash !== pvox.artifact.sha256) throw new Error("ModelProcessingManifestV2 contentHash must match the PVOX artifact.");
|
|
4281
4297
|
if (binaryClosureHash !== pvox.binaryClosureHash) throw new Error("ModelProcessingManifestV2 binaryClosureHash must match the PVOX binary closure.");
|
|
4282
4298
|
if (compilationInputHash !== pvox.compilationInputHash || runtimeRequestProfileHash !== pvox.runtimeRequestProfileHash) {
|
|
@@ -4296,20 +4312,20 @@ function createModelProcessingManifestV2(input) {
|
|
|
4296
4312
|
if (technicalProfile.maximumPartitionExtentMetres > maximumGridExtentMetres && !approximatelyEqual(technicalProfile.maximumPartitionExtentMetres, maximumGridExtentMetres)) {
|
|
4297
4313
|
throw new Error("ModelProcessingManifestV2 partition extent exceeds the LOD0 cell and hierarchy grid capacity; use smaller fidelity-driven partitions.");
|
|
4298
4314
|
}
|
|
4299
|
-
const capabilityEntries = asDenseArray(
|
|
4315
|
+
const capabilityEntries = asDenseArray(record2.capabilities, "ModelProcessingManifestV2.capabilities", PVOX_CAPABILITIES.length, PVOX_CAPABILITIES.length);
|
|
4300
4316
|
const capabilities = capabilityEntries.map(createVoxelCapabilityAssessment);
|
|
4301
4317
|
const byCapability = new Map(capabilities.map((entry) => [entry.capability, entry]));
|
|
4302
4318
|
if (byCapability.size !== PVOX_CAPABILITIES.length || PVOX_CAPABILITIES.some((name) => !byCapability.has(name))) throw new Error("ModelProcessingManifestV2 must include every PVOX capability exactly once.");
|
|
4303
4319
|
const orderedCapabilities = PVOX_CAPABILITIES.map((name) => byCapability.get(name));
|
|
4304
4320
|
if (orderedCapabilities.some((entry) => entry.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 capability evidence must bind the binaryClosureHash.");
|
|
4305
|
-
const capabilityEvidenceSetHash = requireSha256(
|
|
4321
|
+
const capabilityEvidenceSetHash = requireSha256(record2.capabilityEvidenceSetHash, "ModelProcessingManifestV2.capabilityEvidenceSetHash");
|
|
4306
4322
|
const capabilityEvidenceSetAttestation = createHashAttestation(
|
|
4307
|
-
|
|
4323
|
+
record2.capabilityEvidenceSetAttestation,
|
|
4308
4324
|
PVOX_CAPABILITY_SET_HASH_DOMAIN,
|
|
4309
4325
|
capabilityEvidenceSetHash,
|
|
4310
4326
|
"ModelProcessingManifestV2.capabilityEvidenceSetAttestation"
|
|
4311
4327
|
);
|
|
4312
|
-
const requiredCapabilityEntries = asDenseArray(
|
|
4328
|
+
const requiredCapabilityEntries = asDenseArray(record2.requiredCapabilities, "ModelProcessingManifestV2.requiredCapabilities", 1, PVOX_CAPABILITIES.length);
|
|
4313
4329
|
const requiredCapabilities = requiredCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `ModelProcessingManifestV2.requiredCapabilities[${index}]`));
|
|
4314
4330
|
if (new Set(requiredCapabilities).size !== requiredCapabilities.length) throw new Error("ModelProcessingManifestV2.requiredCapabilities must be unique.");
|
|
4315
4331
|
if (!requiredCapabilities.includes("rendering")) throw new Error("ModelProcessingManifestV2 rendering capability is always required.");
|
|
@@ -4317,11 +4333,11 @@ function createModelProcessingManifestV2(input) {
|
|
|
4317
4333
|
if (technicalProfile.hasCollision && collisionPages.every(({ spatialKey }) => spatialKey?.lodLevel !== 0)) {
|
|
4318
4334
|
throw new Error("ModelProcessingManifestV2 supported collision requires LOD0 collision-field evidence.");
|
|
4319
4335
|
}
|
|
4320
|
-
const physicalRegionInventory = createPvoxPhysicalRegionInventoryV1(
|
|
4336
|
+
const physicalRegionInventory = createPvoxPhysicalRegionInventoryV1(record2.physicalRegionInventory);
|
|
4321
4337
|
if (physicalRegionInventory.subjectContentHash !== binaryClosureHash || physicalRegionInventory.validationEvidenceHash !== pvox.validationEvidenceHash || physicalRegionInventory.entries.length !== technicalProfile.materialRegionCount || physicalRegionInventory.entries.some(({ physicalPaletteIndex }) => physicalPaletteIndex >= technicalProfile.physicalPaletteRecordCount)) {
|
|
4322
4338
|
throw new Error("ModelProcessingManifestV2 authoritative physical region inventory must bind the PVOX validator, closure, region count, and palette records.");
|
|
4323
4339
|
}
|
|
4324
|
-
const physicalEntries = asDenseArray(
|
|
4340
|
+
const physicalEntries = asDenseArray(record2.physicalProperties, "ModelProcessingManifestV2.physicalProperties", 0, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries);
|
|
4325
4341
|
const physicalProperties = physicalEntries.map(createPhysicalPropertyEvidence);
|
|
4326
4342
|
const byRegionProperty = new Map(physicalProperties.map((entry) => [`${entry.regionId}\0${entry.materialId}\0${entry.property}`, entry]));
|
|
4327
4343
|
if (byRegionProperty.size !== physicalProperties.length) throw new Error("ModelProcessingManifestV2 physical properties must be unique per region, material, and property.");
|
|
@@ -4361,15 +4377,15 @@ function createModelProcessingManifestV2(input) {
|
|
|
4361
4377
|
if (physicalProperties.length !== technicalProfile.physicalEvidenceRecordCount) {
|
|
4362
4378
|
throw new Error("ModelProcessingManifestV2 authoritative material-region evidence count must match the PVOX PEVI record count.");
|
|
4363
4379
|
}
|
|
4364
|
-
const physicalEvidenceHash = requireSha256(
|
|
4380
|
+
const physicalEvidenceHash = requireSha256(record2.physicalEvidenceHash, "ModelProcessingManifestV2.physicalEvidenceHash");
|
|
4365
4381
|
const physicalEvidenceAttestation = createHashAttestation(
|
|
4366
|
-
|
|
4382
|
+
record2.physicalEvidenceAttestation,
|
|
4367
4383
|
PVOX_PHYSICAL_EVIDENCE_HASH_DOMAIN,
|
|
4368
4384
|
physicalEvidenceHash,
|
|
4369
4385
|
"ModelProcessingManifestV2.physicalEvidenceAttestation"
|
|
4370
4386
|
);
|
|
4371
4387
|
const massPropertiesEvidence = createReviewedStructuralEvidence(
|
|
4372
|
-
|
|
4388
|
+
record2.massPropertiesEvidence,
|
|
4373
4389
|
"mass-properties",
|
|
4374
4390
|
binaryClosureHash,
|
|
4375
4391
|
physicalRegionInventory.inventoryHash,
|
|
@@ -4377,7 +4393,7 @@ function createModelProcessingManifestV2(input) {
|
|
|
4377
4393
|
"ModelProcessingManifestV2.massPropertiesEvidence"
|
|
4378
4394
|
);
|
|
4379
4395
|
const bondGraphEvidence = createReviewedStructuralEvidence(
|
|
4380
|
-
|
|
4396
|
+
record2.bondGraphEvidence,
|
|
4381
4397
|
"bond-graph",
|
|
4382
4398
|
binaryClosureHash,
|
|
4383
4399
|
physicalRegionInventory.inventoryHash,
|
|
@@ -4385,7 +4401,7 @@ function createModelProcessingManifestV2(input) {
|
|
|
4385
4401
|
"ModelProcessingManifestV2.bondGraphEvidence"
|
|
4386
4402
|
);
|
|
4387
4403
|
const interiorLayerEvidence = createReviewedStructuralEvidence(
|
|
4388
|
-
|
|
4404
|
+
record2.interiorLayerEvidence,
|
|
4389
4405
|
"interior-layers",
|
|
4390
4406
|
binaryClosureHash,
|
|
4391
4407
|
physicalRegionInventory.inventoryHash,
|
|
@@ -4395,9 +4411,9 @@ function createModelProcessingManifestV2(input) {
|
|
|
4395
4411
|
if (byCapability.get("destruction")?.status === "supported" && (technicalProfile.massPropertyRecordCount === 0 || technicalProfile.bondRecordCount === 0 || technicalProfile.interiorLayerRecordCount === 0)) {
|
|
4396
4412
|
throw new Error("ModelProcessingManifestV2 destruction capability requires reviewed mass, bond-graph, and interior-layer records.");
|
|
4397
4413
|
}
|
|
4398
|
-
const converter = createCompilerEvidence(
|
|
4414
|
+
const converter = createCompilerEvidence(record2.converter);
|
|
4399
4415
|
if (converter.sourceContentHash !== pvox.sourceContentHash || converter.canonicalDocumentHash !== pvox.canonicalDocumentHash || converter.compilationInputHash !== compilationInputHash || converter.runtimeRequestProfileHash !== runtimeRequestProfileHash || converter.outputContentHash !== contentHash) throw new Error("ModelProcessingManifestV2 converter hashes must match source, canonical document, compilation input, runtime request profile, and PVOX outputContentHash.");
|
|
4400
|
-
const fidelity = createPvoxFidelityEvidence(
|
|
4416
|
+
const fidelity = createPvoxFidelityEvidence(record2.fidelity);
|
|
4401
4417
|
const diagonalMetres = technicalProfile.maximumPartitionDiagonalMetres;
|
|
4402
4418
|
if (fidelity.canonicalDocumentHash !== pvox.canonicalDocumentHash || fidelity.pvoxBinaryClosureHash !== binaryClosureHash || !approximatelyEqual(fidelity.evaluatedDiagonalMetres, diagonalMetres) || !approximatelyEqual(fidelity.lod0CellSizeMetres, pvox.lods[0].cellSizeMetres)) {
|
|
4403
4419
|
throw new Error("ModelProcessingManifestV2 fidelity evidence must bind the canonical document, PVOX closure, evaluated bounds, and LOD0 cell size.");
|
|
@@ -4406,22 +4422,22 @@ function createModelProcessingManifestV2(input) {
|
|
|
4406
4422
|
if (!approximatelyEqual(fidelity.maximumSurfaceErrorMetres, lod0.maximumSurfaceErrorMetres) || !approximatelyEqual(fidelity.p99SurfaceErrorMetres, lod0.p99SurfaceErrorMetres) || !approximatelyEqual(fidelity.silhouetteIou, lod0.silhouetteIou) || !approximatelyEqual(fidelity.maximumContourDisplacementPx, lod0.maximumContourDisplacementPx) || !approximatelyEqual(fidelity.renderedSsim, lod0.renderedSsim) || !approximatelyEqual(fidelity.p95DeltaE2000, lod0.p95DeltaE2000) || !approximatelyEqual(fidelity.normalizedMaterialError, lod0.normalizedMaterialError)) {
|
|
4407
4423
|
throw new Error("ModelProcessingManifestV2 fidelity evidence must exactly match every governed PVOX LOD0 metric.");
|
|
4408
4424
|
}
|
|
4409
|
-
const childEntries = asDenseArray(
|
|
4425
|
+
const childEntries = asDenseArray(record2.children, "ModelProcessingManifestV2.children", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
|
|
4410
4426
|
const children = childEntries.map((entry, index) => createAssemblyChild2(entry, index, resolutionId, candidateId));
|
|
4411
|
-
const kind = requireEnum(
|
|
4427
|
+
const kind = requireEnum(record2.kind, ["leaf", "assembly"], "ModelProcessingManifestV2.kind");
|
|
4412
4428
|
if (kind === "leaf" && children.length !== 0) throw new Error("A leaf ModelProcessingManifestV2 cannot contain children.");
|
|
4413
4429
|
if (kind === "leaf" && technicalProfile.partitionCount !== 1) throw new Error("A leaf ModelProcessingManifestV2 must have one partition.");
|
|
4414
4430
|
if (kind === "leaf" && assemblyClosureHash === binaryClosureHash) throw new Error("A leaf ModelProcessingManifestV2 requires a distinct domain-separated empty-child assemblyClosureHash.");
|
|
4415
4431
|
if (kind === "assembly" && children.length === 0) throw new Error("An assembly ModelProcessingManifestV2 requires children.");
|
|
4416
4432
|
if (kind === "assembly") assertAssembly(children);
|
|
4417
|
-
const assemblyClosureAttestation = createHashAttestation(
|
|
4433
|
+
const assemblyClosureAttestation = createHashAttestation(record2.assemblyClosureAttestation, PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN, assemblyClosureHash, "ModelProcessingManifestV2.assemblyClosureAttestation");
|
|
4418
4434
|
const processingClosureAttestation = createHashAttestation(
|
|
4419
|
-
|
|
4435
|
+
record2.processingClosureAttestation,
|
|
4420
4436
|
PVOX_PROCESSING_CLOSURE_HASH_DOMAIN,
|
|
4421
4437
|
processingClosureHash,
|
|
4422
4438
|
"ModelProcessingManifestV2.processingClosureAttestation"
|
|
4423
4439
|
);
|
|
4424
|
-
const processedAt = requireTimestamp(
|
|
4440
|
+
const processedAt = requireTimestamp(record2.processedAt, "ModelProcessingManifestV2.processedAt");
|
|
4425
4441
|
const latestEvidence = Math.max(
|
|
4426
4442
|
Date.parse(converter.compiledAt),
|
|
4427
4443
|
Date.parse(fidelity.evaluatedAt),
|
|
@@ -4435,13 +4451,13 @@ function createModelProcessingManifestV2(input) {
|
|
|
4435
4451
|
return deepFreeze2({
|
|
4436
4452
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
4437
4453
|
representation: "pvox",
|
|
4438
|
-
manifestId: requireToken(
|
|
4454
|
+
manifestId: requireToken(record2.manifestId, "ModelProcessingManifestV2.manifestId"),
|
|
4439
4455
|
resolutionId,
|
|
4440
4456
|
candidateId,
|
|
4441
4457
|
kind,
|
|
4442
|
-
requestProfileId: requireToken(
|
|
4458
|
+
requestProfileId: requireToken(record2.requestProfileId, "ModelProcessingManifestV2.requestProfileId"),
|
|
4443
4459
|
requestSemanticProfileHash,
|
|
4444
|
-
capabilityProfileId: requireEnum(
|
|
4460
|
+
capabilityProfileId: requireEnum(record2.capabilityProfileId, PVOX_RUNTIME_CAPABILITY_PROFILE_IDS, "ModelProcessingManifestV2.capabilityProfileId"),
|
|
4445
4461
|
contentHash,
|
|
4446
4462
|
binaryClosureHash,
|
|
4447
4463
|
processingClosureHash,
|
|
@@ -4471,76 +4487,76 @@ function createModelProcessingManifestV2(input) {
|
|
|
4471
4487
|
});
|
|
4472
4488
|
}
|
|
4473
4489
|
function createCandidateAssetRef2(value) {
|
|
4474
|
-
const
|
|
4475
|
-
if (
|
|
4476
|
-
exactKeys(
|
|
4477
|
-
assertDataOnlyTree(
|
|
4478
|
-
const asset = createModelAssetRef(
|
|
4479
|
-
if (
|
|
4490
|
+
const record2 = asRecord(value, "ModelCandidateV2.assetRef");
|
|
4491
|
+
if (record2.disposition === "existing") {
|
|
4492
|
+
exactKeys(record2, ["disposition", "kind", "contentHash", "asset"], "ModelCandidateV2.assetRef");
|
|
4493
|
+
assertDataOnlyTree(record2.asset, "ModelCandidateV2.assetRef.asset");
|
|
4494
|
+
const asset = createModelAssetRef(record2.asset);
|
|
4495
|
+
if (record2.kind !== asset.kind || record2.contentHash !== asset.contentHash) throw new Error("ModelCandidateV2 existing assetRef must match the immutable asset.");
|
|
4480
4496
|
return { disposition: "existing", kind: asset.kind, contentHash: asset.contentHash, asset };
|
|
4481
4497
|
}
|
|
4482
|
-
exactKeys(
|
|
4483
|
-
if (
|
|
4498
|
+
exactKeys(record2, ["disposition", "proposalId", "kind", "contentHash"], "ModelCandidateV2.assetRef");
|
|
4499
|
+
if (record2.disposition !== "proposed") throw new Error("ModelCandidateV2.assetRef disposition is unsupported.");
|
|
4484
4500
|
return {
|
|
4485
4501
|
disposition: "proposed",
|
|
4486
|
-
proposalId: requireToken(
|
|
4487
|
-
kind: requireEnum(
|
|
4488
|
-
contentHash: requireSha256(
|
|
4502
|
+
proposalId: requireToken(record2.proposalId, "ModelCandidateV2.assetRef.proposalId"),
|
|
4503
|
+
kind: requireEnum(record2.kind, ["leaf", "assembly"], "ModelCandidateV2.assetRef.kind"),
|
|
4504
|
+
contentHash: requireSha256(record2.contentHash, "ModelCandidateV2.assetRef.contentHash")
|
|
4489
4505
|
};
|
|
4490
4506
|
}
|
|
4491
4507
|
function createViews(value, resolutionId, candidateId) {
|
|
4492
4508
|
const entries = asDenseArray(value, "ModelCandidateV2.views", 4, 4);
|
|
4493
4509
|
const parsed = entries.map((entry, index) => {
|
|
4494
4510
|
const fieldName = `ModelCandidateV2.views[${index}]`;
|
|
4495
|
-
const
|
|
4496
|
-
exactKeys(
|
|
4497
|
-
const kind = requireEnum(
|
|
4511
|
+
const record2 = asRecord(entry, fieldName);
|
|
4512
|
+
exactKeys(record2, ["kind", "imageUri", "sha256", "contentType", "width", "height"], fieldName);
|
|
4513
|
+
const kind = requireEnum(record2.kind, MODEL_CONFIRMATION_VIEW_KINDS, `${fieldName}.kind`);
|
|
4498
4514
|
const expectedUri = `mcp://models/resolutions/${resolutionId}/candidates/${candidateId}/${kind}.png`;
|
|
4499
|
-
if (
|
|
4500
|
-
if (
|
|
4501
|
-
return { kind, imageUri: expectedUri, sha256: requireSha256(
|
|
4515
|
+
if (record2.imageUri !== expectedUri) throw new Error(`${fieldName}.imageUri must match the candidate view identity.`);
|
|
4516
|
+
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
|
+
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 };
|
|
4502
4518
|
});
|
|
4503
4519
|
if (parsed.some((entry, index) => entry.kind !== MODEL_CONFIRMATION_VIEW_KINDS[index])) throw new Error("ModelCandidateV2.views must be ordered front, left, top, isometric.");
|
|
4504
4520
|
return parsed;
|
|
4505
4521
|
}
|
|
4506
4522
|
function createRenderEvidence2(value) {
|
|
4507
|
-
const
|
|
4508
|
-
exactKeys(
|
|
4509
|
-
if (
|
|
4523
|
+
const record2 = asRecord(value, "ModelCandidateV2.renderEvidence");
|
|
4524
|
+
exactKeys(record2, ["renderId", "representation", "lodLevel", "traversalBackend", "rendererId", "rendererVersion", "rendererQualificationHash", "settingsId", "settingsVersion", "cameraQualificationHash", "processingManifestId", "processingClosureHash", "pvoxContentHash", "pvoxRootHash", "pvoxDirectoryHash", "pvoxPageSetHash", "viewSha256s", "renderedAt", "evidenceHash", "evidenceHashAttestation"], "ModelCandidateV2.renderEvidence");
|
|
4525
|
+
if (record2.representation !== "pvox" || record2.lodLevel !== 0 || record2.traversalBackend !== "native-pvox") {
|
|
4510
4526
|
throw new Error("ModelCandidateV2 render evidence must use native PVOX LOD0 traversal.");
|
|
4511
4527
|
}
|
|
4512
|
-
if (
|
|
4528
|
+
if (record2.rendererId !== PVOX_NATIVE_RENDERER_ID || record2.settingsId !== PVOX_NATIVE_REVIEW_SETTINGS_ID) {
|
|
4513
4529
|
throw new Error("ModelCandidateV2 render evidence must use the governed native renderer and review settings.");
|
|
4514
4530
|
}
|
|
4515
|
-
const evidenceHash = requireSha256(
|
|
4531
|
+
const evidenceHash = requireSha256(record2.evidenceHash, "ModelCandidateV2.renderEvidence.evidenceHash");
|
|
4516
4532
|
return {
|
|
4517
|
-
renderId: requireToken(
|
|
4533
|
+
renderId: requireToken(record2.renderId, "ModelCandidateV2.renderEvidence.renderId"),
|
|
4518
4534
|
representation: "pvox",
|
|
4519
4535
|
lodLevel: 0,
|
|
4520
4536
|
traversalBackend: "native-pvox",
|
|
4521
4537
|
rendererId: PVOX_NATIVE_RENDERER_ID,
|
|
4522
|
-
rendererVersion: assertImmutableAssetVersion(
|
|
4523
|
-
rendererQualificationHash: requireSha256(
|
|
4538
|
+
rendererVersion: assertImmutableAssetVersion(record2.rendererVersion),
|
|
4539
|
+
rendererQualificationHash: requireSha256(record2.rendererQualificationHash, "ModelCandidateV2.renderEvidence.rendererQualificationHash"),
|
|
4524
4540
|
settingsId: PVOX_NATIVE_REVIEW_SETTINGS_ID,
|
|
4525
|
-
settingsVersion: assertImmutableAssetVersion(
|
|
4526
|
-
cameraQualificationHash: requireSha256(
|
|
4527
|
-
processingManifestId: requireToken(
|
|
4528
|
-
processingClosureHash: requireSha256(
|
|
4529
|
-
pvoxContentHash: requireSha256(
|
|
4530
|
-
pvoxRootHash: requireSha256(
|
|
4531
|
-
pvoxDirectoryHash: requireSha256(
|
|
4532
|
-
pvoxPageSetHash: requireSha256(
|
|
4533
|
-
viewSha256s: requireShaTuple(
|
|
4534
|
-
renderedAt: requireTimestamp(
|
|
4541
|
+
settingsVersion: assertImmutableAssetVersion(record2.settingsVersion),
|
|
4542
|
+
cameraQualificationHash: requireSha256(record2.cameraQualificationHash, "ModelCandidateV2.renderEvidence.cameraQualificationHash"),
|
|
4543
|
+
processingManifestId: requireToken(record2.processingManifestId, "ModelCandidateV2.renderEvidence.processingManifestId"),
|
|
4544
|
+
processingClosureHash: requireSha256(record2.processingClosureHash, "ModelCandidateV2.renderEvidence.processingClosureHash"),
|
|
4545
|
+
pvoxContentHash: requireSha256(record2.pvoxContentHash, "ModelCandidateV2.renderEvidence.pvoxContentHash"),
|
|
4546
|
+
pvoxRootHash: requireSha256(record2.pvoxRootHash, "ModelCandidateV2.renderEvidence.pvoxRootHash"),
|
|
4547
|
+
pvoxDirectoryHash: requireSha256(record2.pvoxDirectoryHash, "ModelCandidateV2.renderEvidence.pvoxDirectoryHash"),
|
|
4548
|
+
pvoxPageSetHash: requireSha256(record2.pvoxPageSetHash, "ModelCandidateV2.renderEvidence.pvoxPageSetHash"),
|
|
4549
|
+
viewSha256s: requireShaTuple(record2.viewSha256s, "ModelCandidateV2.renderEvidence.viewSha256s"),
|
|
4550
|
+
renderedAt: requireTimestamp(record2.renderedAt, "ModelCandidateV2.renderEvidence.renderedAt"),
|
|
4535
4551
|
evidenceHash,
|
|
4536
|
-
evidenceHashAttestation: createHashAttestation(
|
|
4552
|
+
evidenceHashAttestation: createHashAttestation(record2.evidenceHashAttestation, PVOX_RENDER_EVIDENCE_HASH_DOMAIN, evidenceHash, "ModelCandidateV2.renderEvidence.evidenceHashAttestation")
|
|
4537
4553
|
};
|
|
4538
4554
|
}
|
|
4539
4555
|
function createPvoxModelMatchAssessmentV2(input) {
|
|
4540
4556
|
const fieldName = "PvoxModelMatchAssessmentV2";
|
|
4541
|
-
const
|
|
4542
|
-
assertDataOnlyTree(
|
|
4543
|
-
exactKeys(
|
|
4557
|
+
const record2 = asRecord(input, fieldName);
|
|
4558
|
+
assertDataOnlyTree(record2, fieldName);
|
|
4559
|
+
exactKeys(record2, [
|
|
4544
4560
|
"contractVersion",
|
|
4545
4561
|
"score",
|
|
4546
4562
|
"assurance",
|
|
@@ -4557,65 +4573,65 @@ function createPvoxModelMatchAssessmentV2(input) {
|
|
|
4557
4573
|
"evidenceHash"
|
|
4558
4574
|
], fieldName);
|
|
4559
4575
|
const assessment = createModelMatchAssessment({
|
|
4560
|
-
...
|
|
4561
|
-
score:
|
|
4562
|
-
...
|
|
4563
|
-
hardConstraintPass:
|
|
4564
|
-
exactMatch:
|
|
4565
|
-
reasonCodes:
|
|
4566
|
-
ranker:
|
|
4567
|
-
fidelityWarnings:
|
|
4568
|
-
request:
|
|
4569
|
-
candidateId:
|
|
4570
|
-
candidateContentHash:
|
|
4576
|
+
...record2.contractVersion === void 0 ? {} : { contractVersion: record2.contractVersion },
|
|
4577
|
+
score: record2.score,
|
|
4578
|
+
...record2.assurance === void 0 ? {} : { assurance: record2.assurance },
|
|
4579
|
+
hardConstraintPass: record2.hardConstraintPass,
|
|
4580
|
+
exactMatch: record2.exactMatch,
|
|
4581
|
+
reasonCodes: record2.reasonCodes,
|
|
4582
|
+
ranker: record2.ranker,
|
|
4583
|
+
fidelityWarnings: record2.fidelityWarnings,
|
|
4584
|
+
request: record2.request,
|
|
4585
|
+
candidateId: record2.candidateId,
|
|
4586
|
+
candidateContentHash: record2.candidateContentHash
|
|
4571
4587
|
});
|
|
4572
4588
|
return deepFreeze2({
|
|
4573
4589
|
...assessment,
|
|
4574
|
-
requestSemanticProfileHash: requireSha256(
|
|
4575
|
-
requestSemanticProfileCanonical: requireString(
|
|
4576
|
-
evidenceHash: requireSha256(
|
|
4590
|
+
requestSemanticProfileHash: requireSha256(record2.requestSemanticProfileHash, `${fieldName}.requestSemanticProfileHash`),
|
|
4591
|
+
requestSemanticProfileCanonical: requireString(record2.requestSemanticProfileCanonical, `${fieldName}.requestSemanticProfileCanonical`, 65536),
|
|
4592
|
+
evidenceHash: requireSha256(record2.evidenceHash, `${fieldName}.evidenceHash`)
|
|
4577
4593
|
});
|
|
4578
4594
|
}
|
|
4579
4595
|
function createConfirmationBinding(value) {
|
|
4580
|
-
const
|
|
4581
|
-
assertDataOnlyTree(
|
|
4582
|
-
exactKeys(
|
|
4583
|
-
const bindingHash = requireSha256(
|
|
4584
|
-
const hardGateEntries = asDenseArray(
|
|
4596
|
+
const record2 = asRecord(value, "ModelCandidateV2.confirmationBinding");
|
|
4597
|
+
assertDataOnlyTree(record2, "ModelCandidateV2.confirmationBinding");
|
|
4598
|
+
exactKeys(record2, ["bindingHash", "bindingHashAttestation", "resolutionId", "requesterId", "request", "requestSemanticProfileHash", "requestRevision", "candidateId", "candidateContentHash", "candidateAssetRef", "match", "provenance", "rights", "sourceContentHash", "binaryClosureHash", "pvoxRootHash", "pvoxDirectoryHash", "pvoxPageSetHash", "compilationInputHash", "runtimeRequestProfileHash", "processingClosureHash", "assemblyClosureHash", "evaluationClosureHash", "capabilityEvidenceSetHash", "hardGates", "viewSha256s", "rightsDecisionId", "fidelityEvidenceHash", "physicalEvidenceHash", "rendererId", "rendererVersion", "rendererEvidenceHash", "renderEvidence"], "ModelCandidateV2.confirmationBinding");
|
|
4599
|
+
const bindingHash = requireSha256(record2.bindingHash, "ModelCandidateV2.confirmationBinding.bindingHash");
|
|
4600
|
+
const hardGateEntries = asDenseArray(record2.hardGates, "ModelCandidateV2.confirmationBinding.hardGates", PVOX_NON_OVERRIDABLE_GATE_KINDS.length, PVOX_NON_OVERRIDABLE_GATE_KINDS.length);
|
|
4585
4601
|
return {
|
|
4586
4602
|
bindingHash,
|
|
4587
|
-
bindingHashAttestation: createHashAttestation(
|
|
4588
|
-
resolutionId: requirePathSegment(
|
|
4589
|
-
requesterId: requireToken(
|
|
4590
|
-
request: createModelRequestSpecV2(
|
|
4591
|
-
requestSemanticProfileHash: requireSha256(
|
|
4592
|
-
requestRevision: requireInteger(
|
|
4593
|
-
candidateId: requirePathSegment(
|
|
4594
|
-
candidateContentHash: requireSha256(
|
|
4595
|
-
candidateAssetRef: createCandidateAssetRef2(
|
|
4596
|
-
match: createPvoxModelMatchAssessmentV2(
|
|
4597
|
-
provenance: createModelProvenance(
|
|
4598
|
-
rights: createModelRightsAssessment(
|
|
4599
|
-
sourceContentHash: requireSha256(
|
|
4600
|
-
binaryClosureHash: requireSha256(
|
|
4601
|
-
pvoxRootHash: requireSha256(
|
|
4602
|
-
pvoxDirectoryHash: requireSha256(
|
|
4603
|
-
pvoxPageSetHash: requireSha256(
|
|
4604
|
-
compilationInputHash: requireSha256(
|
|
4605
|
-
runtimeRequestProfileHash: requireSha256(
|
|
4606
|
-
processingClosureHash: requireSha256(
|
|
4607
|
-
assemblyClosureHash: requireSha256(
|
|
4608
|
-
evaluationClosureHash: requireSha256(
|
|
4609
|
-
capabilityEvidenceSetHash: requireSha256(
|
|
4603
|
+
bindingHashAttestation: createHashAttestation(record2.bindingHashAttestation, PVOX_CONFIRMATION_BINDING_HASH_DOMAIN, bindingHash, "ModelCandidateV2.confirmationBinding.bindingHashAttestation"),
|
|
4604
|
+
resolutionId: requirePathSegment(record2.resolutionId, "ModelCandidateV2.confirmationBinding.resolutionId"),
|
|
4605
|
+
requesterId: requireToken(record2.requesterId, "ModelCandidateV2.confirmationBinding.requesterId"),
|
|
4606
|
+
request: createModelRequestSpecV2(record2.request),
|
|
4607
|
+
requestSemanticProfileHash: requireSha256(record2.requestSemanticProfileHash, "ModelCandidateV2.confirmationBinding.requestSemanticProfileHash"),
|
|
4608
|
+
requestRevision: requireInteger(record2.requestRevision, "ModelCandidateV2.confirmationBinding.requestRevision", 0, MODEL_REQUEST_MAX_REVISION),
|
|
4609
|
+
candidateId: requirePathSegment(record2.candidateId, "ModelCandidateV2.confirmationBinding.candidateId"),
|
|
4610
|
+
candidateContentHash: requireSha256(record2.candidateContentHash, "ModelCandidateV2.confirmationBinding.candidateContentHash"),
|
|
4611
|
+
candidateAssetRef: createCandidateAssetRef2(record2.candidateAssetRef),
|
|
4612
|
+
match: createPvoxModelMatchAssessmentV2(record2.match),
|
|
4613
|
+
provenance: createModelProvenance(record2.provenance),
|
|
4614
|
+
rights: createModelRightsAssessment(record2.rights),
|
|
4615
|
+
sourceContentHash: requireSha256(record2.sourceContentHash, "ModelCandidateV2.confirmationBinding.sourceContentHash"),
|
|
4616
|
+
binaryClosureHash: requireSha256(record2.binaryClosureHash, "ModelCandidateV2.confirmationBinding.binaryClosureHash"),
|
|
4617
|
+
pvoxRootHash: requireSha256(record2.pvoxRootHash, "ModelCandidateV2.confirmationBinding.pvoxRootHash"),
|
|
4618
|
+
pvoxDirectoryHash: requireSha256(record2.pvoxDirectoryHash, "ModelCandidateV2.confirmationBinding.pvoxDirectoryHash"),
|
|
4619
|
+
pvoxPageSetHash: requireSha256(record2.pvoxPageSetHash, "ModelCandidateV2.confirmationBinding.pvoxPageSetHash"),
|
|
4620
|
+
compilationInputHash: requireSha256(record2.compilationInputHash, "ModelCandidateV2.confirmationBinding.compilationInputHash"),
|
|
4621
|
+
runtimeRequestProfileHash: requireSha256(record2.runtimeRequestProfileHash, "ModelCandidateV2.confirmationBinding.runtimeRequestProfileHash"),
|
|
4622
|
+
processingClosureHash: requireSha256(record2.processingClosureHash, "ModelCandidateV2.confirmationBinding.processingClosureHash"),
|
|
4623
|
+
assemblyClosureHash: requireSha256(record2.assemblyClosureHash, "ModelCandidateV2.confirmationBinding.assemblyClosureHash"),
|
|
4624
|
+
evaluationClosureHash: requireSha256(record2.evaluationClosureHash, "ModelCandidateV2.confirmationBinding.evaluationClosureHash"),
|
|
4625
|
+
capabilityEvidenceSetHash: requireSha256(record2.capabilityEvidenceSetHash, "ModelCandidateV2.confirmationBinding.capabilityEvidenceSetHash"),
|
|
4610
4626
|
hardGates: hardGateEntries.map(createPvoxGateEvidence),
|
|
4611
|
-
viewSha256s: requireShaTuple(
|
|
4612
|
-
rightsDecisionId: requireToken(
|
|
4613
|
-
fidelityEvidenceHash: requireSha256(
|
|
4614
|
-
physicalEvidenceHash: requireSha256(
|
|
4615
|
-
rendererId: requireToken(
|
|
4616
|
-
rendererVersion: assertImmutableAssetVersion(
|
|
4617
|
-
rendererEvidenceHash: requireSha256(
|
|
4618
|
-
renderEvidence: createRenderEvidence2(
|
|
4627
|
+
viewSha256s: requireShaTuple(record2.viewSha256s, "ModelCandidateV2.confirmationBinding.viewSha256s"),
|
|
4628
|
+
rightsDecisionId: requireToken(record2.rightsDecisionId, "ModelCandidateV2.confirmationBinding.rightsDecisionId"),
|
|
4629
|
+
fidelityEvidenceHash: requireSha256(record2.fidelityEvidenceHash, "ModelCandidateV2.confirmationBinding.fidelityEvidenceHash"),
|
|
4630
|
+
physicalEvidenceHash: requireSha256(record2.physicalEvidenceHash, "ModelCandidateV2.confirmationBinding.physicalEvidenceHash"),
|
|
4631
|
+
rendererId: requireToken(record2.rendererId, "ModelCandidateV2.confirmationBinding.rendererId"),
|
|
4632
|
+
rendererVersion: assertImmutableAssetVersion(record2.rendererVersion),
|
|
4633
|
+
rendererEvidenceHash: requireSha256(record2.rendererEvidenceHash, "ModelCandidateV2.confirmationBinding.rendererEvidenceHash"),
|
|
4634
|
+
renderEvidence: createRenderEvidence2(record2.renderEvidence)
|
|
4619
4635
|
};
|
|
4620
4636
|
}
|
|
4621
4637
|
function assertRequestMatchesProcessing(request, processing) {
|
|
@@ -4650,27 +4666,27 @@ function assertRequestMatchesProcessing(request, processing) {
|
|
|
4650
4666
|
}
|
|
4651
4667
|
}
|
|
4652
4668
|
function createModelCandidateV2(input) {
|
|
4653
|
-
const
|
|
4654
|
-
assertDataOnlyTree(
|
|
4669
|
+
const record2 = asRecord(input, "ModelCandidateV2");
|
|
4670
|
+
assertDataOnlyTree(record2, "ModelCandidateV2");
|
|
4655
4671
|
const commonKeys = ["contractVersion", "resolutionId", "requesterId", "candidateId", "request", "admissionStatus", "assetRef", "match", "provenance", "rights", "processingManifest", "views", "renderEvidence", "evaluationClosureHash", "evaluationClosureAttestation", "hardGates", "confirmationRequired"];
|
|
4656
|
-
const admissionStatus = requireEnum(
|
|
4657
|
-
exactKeys(
|
|
4658
|
-
if (
|
|
4659
|
-
const resolutionId = requirePathSegment(
|
|
4660
|
-
const requesterId = requireToken(
|
|
4661
|
-
const candidateId = requirePathSegment(
|
|
4662
|
-
const request = createModelRequestSpecV2(
|
|
4663
|
-
const assetRef = createCandidateAssetRef2(
|
|
4664
|
-
assertDataOnlyTree(
|
|
4665
|
-
assertDataOnlyTree(
|
|
4666
|
-
assertDataOnlyTree(
|
|
4667
|
-
const match = createPvoxModelMatchAssessmentV2(
|
|
4672
|
+
const admissionStatus = requireEnum(record2.admissionStatus, ["confirmable", "diagnostic"], "ModelCandidateV2.admissionStatus");
|
|
4673
|
+
exactKeys(record2, admissionStatus === "confirmable" ? [...commonKeys, "confirmationBinding", "confirmationToken"] : [...commonKeys, "blockingReasonCodes"], "ModelCandidateV2");
|
|
4674
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelCandidateV2.contractVersion is unsupported.");
|
|
4675
|
+
const resolutionId = requirePathSegment(record2.resolutionId, "ModelCandidateV2.resolutionId");
|
|
4676
|
+
const requesterId = requireToken(record2.requesterId, "ModelCandidateV2.requesterId");
|
|
4677
|
+
const candidateId = requirePathSegment(record2.candidateId, "ModelCandidateV2.candidateId");
|
|
4678
|
+
const request = createModelRequestSpecV2(record2.request);
|
|
4679
|
+
const assetRef = createCandidateAssetRef2(record2.assetRef);
|
|
4680
|
+
assertDataOnlyTree(record2.match, "ModelCandidateV2.match");
|
|
4681
|
+
assertDataOnlyTree(record2.provenance, "ModelCandidateV2.provenance");
|
|
4682
|
+
assertDataOnlyTree(record2.rights, "ModelCandidateV2.rights");
|
|
4683
|
+
const match = createPvoxModelMatchAssessmentV2(record2.match);
|
|
4668
4684
|
assertImmutableAssetVersion(match.ranker.version);
|
|
4669
4685
|
assertImmutableAssetVersion(match.ranker.calibrationVersion);
|
|
4670
|
-
const provenance = createModelProvenance(
|
|
4671
|
-
const rights = createModelRightsAssessment(
|
|
4686
|
+
const provenance = createModelProvenance(record2.provenance);
|
|
4687
|
+
const rights = createModelRightsAssessment(record2.rights);
|
|
4672
4688
|
assertImmutableAssetVersion(rights.policyVersion);
|
|
4673
|
-
const processingManifest = createModelProcessingManifestV2(
|
|
4689
|
+
const processingManifest = createModelProcessingManifestV2(record2.processingManifest);
|
|
4674
4690
|
if (processingManifest.resolutionId !== resolutionId || processingManifest.candidateId !== candidateId || processingManifest.kind !== assetRef.kind || processingManifest.contentHash !== assetRef.contentHash) throw new Error("ModelCandidateV2 processing manifest must match candidate identity and contentHash.");
|
|
4675
4691
|
if (match.candidateId !== candidateId || match.candidateContentHash !== assetRef.contentHash) throw new Error("ModelCandidateV2 match must bind the exact candidate content.");
|
|
4676
4692
|
if (match.requestSemanticProfileHash !== request.requestSemanticProfileHash) throw new Error("ModelCandidateV2 semantic assessment must bind the complete normalized v2 request profile hash.");
|
|
@@ -4686,18 +4702,18 @@ function createModelCandidateV2(input) {
|
|
|
4686
4702
|
if (assetRef.disposition === "existing" && (resourceScope.kind !== "catalog" || resourceScope.assetId !== assetRef.asset.assetId || resourceScope.version !== assetRef.asset.version)) {
|
|
4687
4703
|
throw new Error("ModelCandidateV2 existing asset must prove the same immutable catalog PVOX representation.");
|
|
4688
4704
|
}
|
|
4689
|
-
const views = createViews(
|
|
4690
|
-
const renderEvidence = createRenderEvidence2(
|
|
4705
|
+
const views = createViews(record2.views, resolutionId, candidateId);
|
|
4706
|
+
const renderEvidence = createRenderEvidence2(record2.renderEvidence);
|
|
4691
4707
|
if (renderEvidence.processingManifestId !== processingManifest.manifestId || renderEvidence.processingClosureHash !== processingManifest.processingClosureHash || renderEvidence.pvoxContentHash !== processingManifest.contentHash || renderEvidence.pvoxRootHash !== processingManifest.pvox.rootHash || renderEvidence.pvoxDirectoryHash !== processingManifest.pvox.directoryHash || renderEvidence.pvoxPageSetHash !== processingManifest.pvox.pageSetHash || renderEvidence.viewSha256s.some((digest, index) => digest !== views[index].sha256)) throw new Error("ModelCandidateV2 renderer evidence must bind the PVOX manifest and ordered views.");
|
|
4692
4708
|
if (Date.parse(renderEvidence.renderedAt) < Date.parse(processingManifest.processedAt)) throw new Error("ModelCandidateV2 render evidence must follow processing.");
|
|
4693
|
-
const evaluationClosureHash = requireSha256(
|
|
4709
|
+
const evaluationClosureHash = requireSha256(record2.evaluationClosureHash, "ModelCandidateV2.evaluationClosureHash");
|
|
4694
4710
|
const evaluationClosureAttestation = createHashAttestation(
|
|
4695
|
-
|
|
4711
|
+
record2.evaluationClosureAttestation,
|
|
4696
4712
|
PVOX_EVALUATION_CLOSURE_HASH_DOMAIN,
|
|
4697
4713
|
evaluationClosureHash,
|
|
4698
4714
|
"ModelCandidateV2.evaluationClosureAttestation"
|
|
4699
4715
|
);
|
|
4700
|
-
const gateEntries = asDenseArray(
|
|
4716
|
+
const gateEntries = asDenseArray(record2.hardGates, "ModelCandidateV2.hardGates", PVOX_NON_OVERRIDABLE_GATE_KINDS.length, PVOX_NON_OVERRIDABLE_GATE_KINDS.length);
|
|
4701
4717
|
const gates = gateEntries.map(createPvoxGateEvidence);
|
|
4702
4718
|
const byGate = new Map(gates.map((entry) => [entry.kind, entry]));
|
|
4703
4719
|
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.");
|
|
@@ -4747,16 +4763,16 @@ function createModelCandidateV2(input) {
|
|
|
4747
4763
|
hardGates: orderedGates
|
|
4748
4764
|
};
|
|
4749
4765
|
if (admissionStatus === "diagnostic") {
|
|
4750
|
-
if (
|
|
4751
|
-
if (
|
|
4752
|
-
const suppliedEntries = asDenseArray(
|
|
4766
|
+
if (record2.confirmationRequired !== false) throw new Error("Diagnostic ModelCandidateV2 confirmationRequired must be false.");
|
|
4767
|
+
if (record2.blockingReasonCodes !== void 0) {
|
|
4768
|
+
const suppliedEntries = asDenseArray(record2.blockingReasonCodes, "ModelCandidateV2.blockingReasonCodes", 1, 256);
|
|
4753
4769
|
const suppliedBlockingReasons = suppliedEntries.map((entry, index) => requireReasonCode(entry, `ModelCandidateV2.blockingReasonCodes[${index}]`));
|
|
4754
4770
|
if (JSON.stringify(suppliedBlockingReasons) !== JSON.stringify(blockingReasonCodes)) throw new Error("Diagnostic ModelCandidateV2 blockingReasonCodes must be derived from its evidence.");
|
|
4755
4771
|
}
|
|
4756
4772
|
return deepFreeze2({ ...common, admissionStatus: "diagnostic", blockingReasonCodes, confirmationRequired: false });
|
|
4757
4773
|
}
|
|
4758
|
-
if (
|
|
4759
|
-
const confirmationBinding = createConfirmationBinding(
|
|
4774
|
+
if (record2.confirmationRequired !== true) throw new Error("Confirmable ModelCandidateV2 confirmationRequired must be true.");
|
|
4775
|
+
const confirmationBinding = createConfirmationBinding(record2.confirmationBinding);
|
|
4760
4776
|
const expectedBinding = {
|
|
4761
4777
|
resolutionId,
|
|
4762
4778
|
requesterId,
|
|
@@ -4793,7 +4809,7 @@ function createModelCandidateV2(input) {
|
|
|
4793
4809
|
if (JSON.stringify(confirmationBinding.renderEvidence) !== JSON.stringify(renderEvidence)) throw new Error("ModelCandidateV2 confirmationBinding must bind the complete native renderer evidence.");
|
|
4794
4810
|
if (JSON.stringify(confirmationBinding.hardGates) !== JSON.stringify(orderedGates)) throw new Error("ModelCandidateV2 confirmationBinding must contain the complete ordered hard-gate evidence set.");
|
|
4795
4811
|
if (confirmationBinding.viewSha256s.some((digest, index) => digest !== views[index].sha256)) throw new Error("ModelCandidateV2 confirmationBinding view hashes must match candidate views.");
|
|
4796
|
-
const confirmationToken = requireAttestation(
|
|
4812
|
+
const confirmationToken = requireAttestation(record2.confirmationToken, "ModelCandidateV2.confirmationToken");
|
|
4797
4813
|
if (confirmationBinding.bindingHashAttestation.attestationToken !== confirmationToken) throw new Error("ModelCandidateV2 confirmation token must attest the exact confirmation binding hash.");
|
|
4798
4814
|
return deepFreeze2({
|
|
4799
4815
|
...common,
|
|
@@ -4807,21 +4823,21 @@ function isModelCandidateV2Confirmable(candidate) {
|
|
|
4807
4823
|
return candidate.admissionStatus === "confirmable" && candidate.confirmationRequired && candidate.match.hardConstraintPass && candidate.match.assurance !== "none" && (candidate.rights.status === "allowed" || candidate.rights.status === "attribution-required") && candidate.processingManifest.fidelity.outcome === "passed" && candidate.hardGates.length === PVOX_NON_OVERRIDABLE_GATE_KINDS.length && candidate.hardGates.every(({ outcome }) => outcome === "passed") && candidate.processingManifest.requiredCapabilities.every((name) => candidate.processingManifest.capabilities.find((entry) => entry.capability === name)?.status === "supported");
|
|
4808
4824
|
}
|
|
4809
4825
|
function createModelCandidateConfirmationV2(input, expectedCandidate, expectedResolutionId) {
|
|
4810
|
-
const
|
|
4811
|
-
exactKeys(
|
|
4812
|
-
if (
|
|
4826
|
+
const record2 = asRecord(input, "ModelCandidateConfirmationV2");
|
|
4827
|
+
exactKeys(record2, ["contractVersion", "confirmationId", "resolutionId", "requesterId", "candidateId", "confirmationToken", "confirmationBindingHash", "viewSha256s", "confirmedBy", "confirmedAt", "semanticRiskAccepted"], "ModelCandidateConfirmationV2");
|
|
4828
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelCandidateConfirmationV2.contractVersion is unsupported.");
|
|
4813
4829
|
const candidate = createModelCandidateV2(expectedCandidate);
|
|
4814
4830
|
if (candidate.admissionStatus !== "confirmable") throw new Error("ModelCandidateConfirmationV2 cannot confirm a diagnostic candidate.");
|
|
4815
4831
|
const resolutionId = requirePathSegment(expectedResolutionId, "ModelCandidateConfirmationV2 expected resolutionId");
|
|
4816
|
-
if (candidate.resolutionId !== resolutionId ||
|
|
4817
|
-
if (
|
|
4818
|
-
if (
|
|
4819
|
-
const viewSha256s = requireShaTuple(
|
|
4832
|
+
if (candidate.resolutionId !== resolutionId || record2.resolutionId !== resolutionId || record2.requesterId !== candidate.requesterId || record2.candidateId !== candidate.candidateId) throw new Error("ModelCandidateConfirmationV2 must match the candidate, requester, and resolution.");
|
|
4833
|
+
if (record2.confirmationToken !== candidate.confirmationToken) throw new Error("ModelCandidateConfirmationV2 confirmationToken must match the candidate.");
|
|
4834
|
+
if (record2.confirmationBindingHash !== candidate.confirmationBinding.bindingHash) throw new Error("ModelCandidateConfirmationV2 confirmation binding must match the candidate.");
|
|
4835
|
+
const viewSha256s = requireShaTuple(record2.viewSha256s, "ModelCandidateConfirmationV2.viewSha256s");
|
|
4820
4836
|
if (viewSha256s.some((digest, index) => digest !== candidate.views[index].sha256)) throw new Error("ModelCandidateConfirmationV2 view hashes must match candidate evidence.");
|
|
4821
4837
|
if (!isModelCandidateV2Confirmable(candidate)) throw new Error("ModelCandidateConfirmationV2 candidate is not confirmable because a hard gate is blocked.");
|
|
4822
|
-
const semanticRiskAccepted = requireBoolean(
|
|
4838
|
+
const semanticRiskAccepted = requireBoolean(record2.semanticRiskAccepted, "ModelCandidateConfirmationV2.semanticRiskAccepted");
|
|
4823
4839
|
if (candidate.match.assurance === "low" !== semanticRiskAccepted) throw new Error("Only low semantic assurance requires and permits semantic risk acceptance.");
|
|
4824
|
-
const confirmedAt = requireTimestamp(
|
|
4840
|
+
const confirmedAt = requireTimestamp(record2.confirmedAt, "ModelCandidateConfirmationV2.confirmedAt");
|
|
4825
4841
|
const latestEvidence = Math.max(
|
|
4826
4842
|
Date.parse(candidate.provenance.capturedAt),
|
|
4827
4843
|
Date.parse(candidate.rights.reviewedAt),
|
|
@@ -4830,12 +4846,12 @@ function createModelCandidateConfirmationV2(input, expectedCandidate, expectedRe
|
|
|
4830
4846
|
...candidate.hardGates.map(({ evaluatedAt }) => Date.parse(evaluatedAt))
|
|
4831
4847
|
);
|
|
4832
4848
|
if (Date.parse(confirmedAt) < latestEvidence) throw new Error("ModelCandidateConfirmationV2 confirmation must follow all required evidence.");
|
|
4833
|
-
const confirmedBy = requireString(
|
|
4849
|
+
const confirmedBy = requireString(record2.confirmedBy, "ModelCandidateConfirmationV2.confirmedBy", 256);
|
|
4834
4850
|
if (DIRECT_URL.test(confirmedBy)) throw new Error("ModelCandidateConfirmationV2.confirmedBy must not expose a URL.");
|
|
4835
4851
|
if (confirmedBy !== candidate.requesterId) throw new Error("ModelCandidateConfirmationV2 confirmedBy must be the requester bound to the resolution.");
|
|
4836
4852
|
return deepFreeze2({
|
|
4837
4853
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
4838
|
-
confirmationId: requireToken(
|
|
4854
|
+
confirmationId: requireToken(record2.confirmationId, "ModelCandidateConfirmationV2.confirmationId"),
|
|
4839
4855
|
resolutionId,
|
|
4840
4856
|
requesterId: candidate.requesterId,
|
|
4841
4857
|
candidateId: candidate.candidateId,
|
|
@@ -4859,8 +4875,8 @@ function candidatesEquivalent(left, right) {
|
|
|
4859
4875
|
}
|
|
4860
4876
|
function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmation, finalAssetRef) {
|
|
4861
4877
|
if (candidate.assetRef.disposition !== "proposed") throw new Error("ModelPromotionReceiptV2 is only valid for a proposed candidate.");
|
|
4862
|
-
const
|
|
4863
|
-
exactKeys(
|
|
4878
|
+
const record2 = asRecord(input, "ModelPromotionReceiptV2");
|
|
4879
|
+
exactKeys(record2, [
|
|
4864
4880
|
"contractVersion",
|
|
4865
4881
|
"promotionId",
|
|
4866
4882
|
"resolutionId",
|
|
@@ -4891,38 +4907,38 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4891
4907
|
"promotedAt",
|
|
4892
4908
|
"publicationToken"
|
|
4893
4909
|
], "ModelPromotionReceiptV2");
|
|
4894
|
-
if (
|
|
4910
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION || record2.publicationState !== "pointer-last-complete") {
|
|
4895
4911
|
throw new Error("ModelPromotionReceiptV2 must use the v2 contract and prove pointer-last completion.");
|
|
4896
4912
|
}
|
|
4897
|
-
if (
|
|
4913
|
+
if (record2.resolutionId !== resolutionId || record2.requesterId !== candidate.requesterId || record2.candidateId !== candidate.candidateId || record2.proposalId !== candidate.assetRef.proposalId || record2.confirmationId !== confirmation.confirmationId || record2.confirmationBindingHash !== confirmation.confirmationBindingHash || record2.processingManifestId !== candidate.processingManifest.manifestId || record2.processingContentHash !== candidate.processingManifest.contentHash || record2.processingClosureHash !== candidate.processingManifest.processingClosureHash || record2.assemblyClosureHash !== candidate.processingManifest.assemblyClosureHash) {
|
|
4898
4914
|
throw new Error("ModelPromotionReceiptV2 must bind the exact proposal, confirmation, processing content, and PVOX assembly closure.");
|
|
4899
4915
|
}
|
|
4900
|
-
assertDataOnlyTree(
|
|
4901
|
-
const candidateAssetRef = createCandidateAssetRef2(
|
|
4916
|
+
assertDataOnlyTree(record2.candidateAssetRef, "ModelPromotionReceiptV2.candidateAssetRef");
|
|
4917
|
+
const candidateAssetRef = createCandidateAssetRef2(record2.candidateAssetRef);
|
|
4902
4918
|
if (JSON.stringify(candidateAssetRef) !== JSON.stringify(candidate.assetRef)) throw new Error("ModelPromotionReceiptV2 must bind the exact proposed candidate identity.");
|
|
4903
|
-
const childClosureEntries = asDenseArray(
|
|
4919
|
+
const childClosureEntries = asDenseArray(record2.assemblyChildClosureHashes, "ModelPromotionReceiptV2.assemblyChildClosureHashes", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
|
|
4904
4920
|
const assemblyChildClosureHashes = childClosureEntries.map((entry, index) => requireSha256(entry, `ModelPromotionReceiptV2.assemblyChildClosureHashes[${index}]`));
|
|
4905
4921
|
const expectedChildClosureHashes = candidate.processingManifest.children.map(({ assetRef }) => assetRef.binaryClosureHash);
|
|
4906
4922
|
if (JSON.stringify(assemblyChildClosureHashes) !== JSON.stringify(expectedChildClosureHashes)) throw new Error("ModelPromotionReceiptV2 child closure sequence must match the canonical assembly order.");
|
|
4907
|
-
assertDataOnlyTree(
|
|
4908
|
-
const receiptAssetRef = createModelAssetRef(
|
|
4923
|
+
assertDataOnlyTree(record2.finalAssetRef, "ModelPromotionReceiptV2.finalAssetRef");
|
|
4924
|
+
const receiptAssetRef = createModelAssetRef(record2.finalAssetRef);
|
|
4909
4925
|
if (JSON.stringify(receiptAssetRef) !== JSON.stringify(finalAssetRef)) throw new Error("ModelPromotionReceiptV2 finalAssetRef must match the completed resolution.");
|
|
4910
|
-
const creditsWrittenAt = requireTimestamp(
|
|
4911
|
-
const catalogRowWrittenAt = requireTimestamp(
|
|
4912
|
-
const indexSnapshotWrittenAt = requireTimestamp(
|
|
4913
|
-
const pointerPublishedAt = requireTimestamp(
|
|
4914
|
-
const promotedAt = requireTimestamp(
|
|
4926
|
+
const creditsWrittenAt = requireTimestamp(record2.creditsWrittenAt, "ModelPromotionReceiptV2.creditsWrittenAt");
|
|
4927
|
+
const catalogRowWrittenAt = requireTimestamp(record2.catalogRowWrittenAt, "ModelPromotionReceiptV2.catalogRowWrittenAt");
|
|
4928
|
+
const indexSnapshotWrittenAt = requireTimestamp(record2.indexSnapshotWrittenAt, "ModelPromotionReceiptV2.indexSnapshotWrittenAt");
|
|
4929
|
+
const pointerPublishedAt = requireTimestamp(record2.pointerPublishedAt, "ModelPromotionReceiptV2.pointerPublishedAt");
|
|
4930
|
+
const promotedAt = requireTimestamp(record2.promotedAt, "ModelPromotionReceiptV2.promotedAt");
|
|
4915
4931
|
const publicationSequence = [confirmation.confirmedAt, creditsWrittenAt, catalogRowWrittenAt, indexSnapshotWrittenAt, pointerPublishedAt, promotedAt].map(Date.parse);
|
|
4916
4932
|
if (publicationSequence.some((time, index) => index > 0 && time < publicationSequence[index - 1])) {
|
|
4917
4933
|
throw new Error("ModelPromotionReceiptV2 must prove credits, catalog row, index snapshot, and pointers were published in pointer-last order.");
|
|
4918
4934
|
}
|
|
4919
|
-
const publicationHash = requireSha256(
|
|
4920
|
-
const publicationHashAttestation = createHashAttestation(
|
|
4921
|
-
const publicationToken = requireAttestation(
|
|
4935
|
+
const publicationHash = requireSha256(record2.publicationHash, "ModelPromotionReceiptV2.publicationHash");
|
|
4936
|
+
const publicationHashAttestation = createHashAttestation(record2.publicationHashAttestation, PVOX_PUBLICATION_HASH_DOMAIN, publicationHash, "ModelPromotionReceiptV2.publicationHashAttestation");
|
|
4937
|
+
const publicationToken = requireAttestation(record2.publicationToken, "ModelPromotionReceiptV2.publicationToken");
|
|
4922
4938
|
if (publicationHashAttestation.attestationToken !== publicationToken) throw new Error("ModelPromotionReceiptV2 publication token must attest the exact publication hash.");
|
|
4923
4939
|
return deepFreeze2({
|
|
4924
4940
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
4925
|
-
promotionId: requireToken(
|
|
4941
|
+
promotionId: requireToken(record2.promotionId, "ModelPromotionReceiptV2.promotionId"),
|
|
4926
4942
|
resolutionId,
|
|
4927
4943
|
requesterId: candidate.requesterId,
|
|
4928
4944
|
candidateId: candidate.candidateId,
|
|
@@ -4936,11 +4952,11 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4936
4952
|
assemblyClosureHash: candidate.processingManifest.assemblyClosureHash,
|
|
4937
4953
|
assemblyChildClosureHashes,
|
|
4938
4954
|
finalAssetRef: receiptAssetRef,
|
|
4939
|
-
creditsRecordHash: requireSha256(
|
|
4940
|
-
catalogRowHash: requireSha256(
|
|
4941
|
-
indexSnapshotHash: requireSha256(
|
|
4942
|
-
catalogPointerEtag: requireToken(
|
|
4943
|
-
indexPointerEtag: requireToken(
|
|
4955
|
+
creditsRecordHash: requireSha256(record2.creditsRecordHash, "ModelPromotionReceiptV2.creditsRecordHash"),
|
|
4956
|
+
catalogRowHash: requireSha256(record2.catalogRowHash, "ModelPromotionReceiptV2.catalogRowHash"),
|
|
4957
|
+
indexSnapshotHash: requireSha256(record2.indexSnapshotHash, "ModelPromotionReceiptV2.indexSnapshotHash"),
|
|
4958
|
+
catalogPointerEtag: requireToken(record2.catalogPointerEtag, "ModelPromotionReceiptV2.catalogPointerEtag"),
|
|
4959
|
+
indexPointerEtag: requireToken(record2.indexPointerEtag, "ModelPromotionReceiptV2.indexPointerEtag"),
|
|
4944
4960
|
publicationState: "pointer-last-complete",
|
|
4945
4961
|
creditsWrittenAt,
|
|
4946
4962
|
catalogRowWrittenAt,
|
|
@@ -4954,43 +4970,43 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4954
4970
|
}
|
|
4955
4971
|
function createModelRefinementQuestionV2(input, index) {
|
|
4956
4972
|
const fieldName = `ModelResolutionV2.refinementQuestions[${index}]`;
|
|
4957
|
-
const
|
|
4958
|
-
exactKeys(
|
|
4973
|
+
const record2 = asRecord(input, fieldName);
|
|
4974
|
+
exactKeys(record2, ["questionId", "prompt", "reasonCodes"], fieldName);
|
|
4959
4975
|
return deepFreeze2({
|
|
4960
|
-
questionId: requireToken(
|
|
4961
|
-
prompt: requireString(
|
|
4962
|
-
reasonCodes: requireReasonCodes(
|
|
4976
|
+
questionId: requireToken(record2.questionId, `${fieldName}.questionId`),
|
|
4977
|
+
prompt: requireString(record2.prompt, `${fieldName}.prompt`, 500),
|
|
4978
|
+
reasonCodes: requireReasonCodes(record2.reasonCodes, `${fieldName}.reasonCodes`)
|
|
4963
4979
|
});
|
|
4964
4980
|
}
|
|
4965
4981
|
function createModelResolutionV2(input) {
|
|
4966
|
-
const
|
|
4967
|
-
exactKeys(
|
|
4968
|
-
if (
|
|
4969
|
-
const resolutionId = requirePathSegment(
|
|
4970
|
-
const requesterId = requireToken(
|
|
4971
|
-
const request = createModelRequestSpecV2(
|
|
4972
|
-
const state = requireEnum(
|
|
4982
|
+
const record2 = asRecord(input, "ModelResolutionV2");
|
|
4983
|
+
exactKeys(record2, ["contractVersion", "resolutionId", "requesterId", "request", "attempts", "state", "compatibilityState", "candidates", "bestCandidate", "confirmation", "promotionReceipt", "refinementQuestions", "finalAssetRef", "stateReasonCode", "createdAt", "updatedAt"], "ModelResolutionV2");
|
|
4984
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelResolutionV2.contractVersion is unsupported.");
|
|
4985
|
+
const resolutionId = requirePathSegment(record2.resolutionId, "ModelResolutionV2.resolutionId");
|
|
4986
|
+
const requesterId = requireToken(record2.requesterId, "ModelResolutionV2.requesterId");
|
|
4987
|
+
const request = createModelRequestSpecV2(record2.request);
|
|
4988
|
+
const state = requireEnum(record2.state, MODEL_RESOLUTION_V2_STATES, "ModelResolutionV2.state");
|
|
4973
4989
|
const compatibilityState = toLegacyModelResolutionState(state);
|
|
4974
|
-
if (
|
|
4975
|
-
const candidateEntries = asDenseArray(
|
|
4990
|
+
if (record2.compatibilityState !== void 0 && record2.compatibilityState !== compatibilityState) throw new Error("ModelResolutionV2 compatibilityState must match its deterministic legacy projection.");
|
|
4991
|
+
const candidateEntries = asDenseArray(record2.candidates, "ModelResolutionV2.candidates", 0, 20);
|
|
4976
4992
|
const candidates = candidateEntries.map(createModelCandidateV2);
|
|
4977
4993
|
if (new Set(candidates.map(({ candidateId }) => candidateId)).size !== candidates.length) throw new Error("ModelResolutionV2 candidate IDs must be unique.");
|
|
4978
4994
|
if (candidates.some((candidate) => candidate.resolutionId !== resolutionId || candidate.requesterId !== requesterId || JSON.stringify(candidate.request) !== JSON.stringify(request))) throw new Error("ModelResolutionV2 candidates must bind the exact requester, v2 resolution request, and profiles.");
|
|
4979
|
-
const bestCandidate =
|
|
4995
|
+
const bestCandidate = record2.bestCandidate === void 0 ? void 0 : createModelCandidateV2(record2.bestCandidate);
|
|
4980
4996
|
if (bestCandidate !== void 0 && !candidates.some((candidate) => candidatesEquivalent(candidate, bestCandidate))) throw new Error("ModelResolutionV2 bestCandidate must be one exact candidate in the resolution.");
|
|
4981
4997
|
if (state === "awaiting-confirmation" && (bestCandidate === void 0 || !isModelCandidateV2Confirmable(bestCandidate))) throw new Error("ModelResolutionV2 awaiting-confirmation requires one confirmable bestCandidate.");
|
|
4982
|
-
const refinementEntries = asDenseArray(
|
|
4998
|
+
const refinementEntries = asDenseArray(record2.refinementQuestions, "ModelResolutionV2.refinementQuestions", 0, 3);
|
|
4983
4999
|
const refinementQuestions = refinementEntries.map(createModelRefinementQuestionV2);
|
|
4984
5000
|
if (new Set(refinementQuestions.map(({ questionId }) => questionId)).size !== refinementQuestions.length) throw new Error("ModelResolutionV2 refinement question IDs must be unique.");
|
|
4985
5001
|
if (state === "awaiting-confirmation" && bestCandidate?.match.assurance === "low" && refinementQuestions.length === 0) throw new Error("Low-assurance ModelResolutionV2 requires refinement questions.");
|
|
4986
|
-
if (state === "completed" &&
|
|
4987
|
-
const confirmation =
|
|
5002
|
+
if (state === "completed" && record2.finalAssetRef === void 0) throw new Error("ModelResolutionV2 completed state requires finalAssetRef.");
|
|
5003
|
+
const confirmation = record2.confirmation === void 0 ? void 0 : bestCandidate === void 0 ? (() => {
|
|
4988
5004
|
throw new Error("ModelResolutionV2 confirmation requires bestCandidate.");
|
|
4989
|
-
})() : createModelCandidateConfirmationV2(
|
|
5005
|
+
})() : createModelCandidateConfirmationV2(record2.confirmation, bestCandidate, resolutionId);
|
|
4990
5006
|
if (["promoting", "completed"].includes(state) && confirmation === void 0) throw new Error(`ModelResolutionV2 ${state} requires confirmation.`);
|
|
4991
5007
|
if (!["promoting", "completed"].includes(state) && confirmation !== void 0) throw new Error("ModelResolutionV2 confirmation is only allowed while promoting or completed.");
|
|
4992
|
-
if (
|
|
4993
|
-
const finalAssetRef =
|
|
5008
|
+
if (record2.finalAssetRef !== void 0) assertDataOnlyTree(record2.finalAssetRef, "ModelResolutionV2.finalAssetRef");
|
|
5009
|
+
const finalAssetRef = record2.finalAssetRef === void 0 ? void 0 : createModelAssetRef(record2.finalAssetRef);
|
|
4994
5010
|
if (state === "completed" && finalAssetRef === void 0) throw new Error("ModelResolutionV2 completed state requires finalAssetRef.");
|
|
4995
5011
|
if (state !== "completed" && finalAssetRef !== void 0) throw new Error("ModelResolutionV2 finalAssetRef is only allowed for completed state.");
|
|
4996
5012
|
if (finalAssetRef !== void 0 && bestCandidate !== void 0) {
|
|
@@ -5005,21 +5021,21 @@ function createModelResolutionV2(input) {
|
|
|
5005
5021
|
}
|
|
5006
5022
|
}
|
|
5007
5023
|
let promotionReceipt;
|
|
5008
|
-
if (
|
|
5024
|
+
if (record2.promotionReceipt !== void 0) {
|
|
5009
5025
|
if (state !== "completed" || finalAssetRef === void 0 || confirmation === void 0 || bestCandidate?.admissionStatus !== "confirmable" || bestCandidate.assetRef.disposition !== "proposed") {
|
|
5010
5026
|
throw new Error("ModelResolutionV2 promotionReceipt is only allowed for a completed confirmed proposed candidate.");
|
|
5011
5027
|
}
|
|
5012
|
-
promotionReceipt = createModelPromotionReceiptV2(
|
|
5028
|
+
promotionReceipt = createModelPromotionReceiptV2(record2.promotionReceipt, resolutionId, bestCandidate, confirmation, finalAssetRef);
|
|
5013
5029
|
}
|
|
5014
5030
|
if (state === "completed" && bestCandidate?.assetRef.disposition === "proposed" && promotionReceipt === void 0) {
|
|
5015
5031
|
throw new Error("ModelResolutionV2 completed proposed candidates require a pointer-last promotionReceipt.");
|
|
5016
5032
|
}
|
|
5017
5033
|
if (bestCandidate?.assetRef.disposition === "existing" && promotionReceipt !== void 0) throw new Error("ModelResolutionV2 existing selections must not include a promotionReceipt.");
|
|
5018
|
-
const stateReasonCode =
|
|
5034
|
+
const stateReasonCode = record2.stateReasonCode === void 0 ? void 0 : requireReasonCode(record2.stateReasonCode, "ModelResolutionV2.stateReasonCode");
|
|
5019
5035
|
if (["unresolved", "failed", "cancelled"].includes(state) && stateReasonCode === void 0) throw new Error(`ModelResolutionV2 ${state} state requires stateReasonCode.`);
|
|
5020
5036
|
if (!["unresolved", "failed", "cancelled"].includes(state) && stateReasonCode !== void 0) throw new Error("ModelResolutionV2 stateReasonCode is reserved for terminal non-success states.");
|
|
5021
|
-
const createdAt = requireTimestamp(
|
|
5022
|
-
const updatedAt = requireTimestamp(
|
|
5037
|
+
const createdAt = requireTimestamp(record2.createdAt, "ModelResolutionV2.createdAt");
|
|
5038
|
+
const updatedAt = requireTimestamp(record2.updatedAt, "ModelResolutionV2.updatedAt");
|
|
5023
5039
|
if (Date.parse(updatedAt) < Date.parse(createdAt)) throw new Error("ModelResolutionV2.updatedAt must not precede createdAt.");
|
|
5024
5040
|
if (confirmation !== void 0 && Date.parse(confirmation.confirmedAt) > Date.parse(updatedAt)) {
|
|
5025
5041
|
throw new Error("ModelResolutionV2 confirmation confirmedAt must not exceed updatedAt.");
|
|
@@ -5032,7 +5048,7 @@ function createModelResolutionV2(input) {
|
|
|
5032
5048
|
resolutionId,
|
|
5033
5049
|
requesterId,
|
|
5034
5050
|
request,
|
|
5035
|
-
attempts: requireInteger(
|
|
5051
|
+
attempts: requireInteger(record2.attempts, "ModelResolutionV2.attempts", 1, 12),
|
|
5036
5052
|
state,
|
|
5037
5053
|
compatibilityState,
|
|
5038
5054
|
candidates,
|
|
@@ -5162,12 +5178,12 @@ function copyPvoxByteInput(value, fieldName, maximumByteLength) {
|
|
|
5162
5178
|
if (!(value instanceof Uint8Array)) throw new Error(`${fieldName} must be a Uint8Array.`);
|
|
5163
5179
|
let byteLength;
|
|
5164
5180
|
try {
|
|
5165
|
-
const
|
|
5166
|
-
const
|
|
5167
|
-
const
|
|
5168
|
-
if (
|
|
5169
|
-
byteLength =
|
|
5170
|
-
const buffer =
|
|
5181
|
+
const typedArrayPrototype2 = Object.getPrototypeOf(Uint8Array.prototype);
|
|
5182
|
+
const byteLengthGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "byteLength")?.get;
|
|
5183
|
+
const bufferGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "buffer")?.get;
|
|
5184
|
+
if (byteLengthGetter2 === void 0 || bufferGetter2 === void 0) throw new Error("Typed-array intrinsics are unavailable.");
|
|
5185
|
+
byteLength = byteLengthGetter2.call(value);
|
|
5186
|
+
const buffer = bufferGetter2.call(value);
|
|
5171
5187
|
if (typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer) {
|
|
5172
5188
|
throw new Error(`${fieldName} must not use shared mutable storage.`);
|
|
5173
5189
|
}
|
|
@@ -5373,18 +5389,18 @@ function encodePvoxPageSetHashPreimageV1(records) {
|
|
|
5373
5389
|
preflightPvoxArrayCardinality(records, 1, PVOX_DEFAULT_LIMITS.maximumPages, "PVOX page-set hash input");
|
|
5374
5390
|
assertPvoxPreimageDataOnly(records, "PVOX page-set hash input");
|
|
5375
5391
|
let previousEnd = 0n;
|
|
5376
|
-
const encoded = records.map((
|
|
5377
|
-
assertExactPvoxPreimageKeys(
|
|
5378
|
-
if (
|
|
5379
|
-
const offset = asUnsignedBigInt(
|
|
5380
|
-
const byteLength = assertSafeIntegerInRange(
|
|
5392
|
+
const encoded = records.map((record2, position) => {
|
|
5393
|
+
assertExactPvoxPreimageKeys(record2, ["pageIndex", "byteOffset", "byteLength", "pageSha256"], `records[${position}]`);
|
|
5394
|
+
if (record2.pageIndex !== position) throw new Error("Page-set records must have contiguous page indexes starting at zero.");
|
|
5395
|
+
const offset = asUnsignedBigInt(record2.byteOffset, `records[${position}].byteOffset`);
|
|
5396
|
+
const byteLength = assertSafeIntegerInRange(record2.byteLength, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES, `records[${position}].byteLength`);
|
|
5381
5397
|
if (offset !== previousEnd || offset !== BigInt(position) * BigInt(PVOX_PAGE_SIZE_BYTES)) throw new Error("Page-set records must exactly cover contiguous 64-KiB PVOX pages.");
|
|
5382
5398
|
previousEnd = offset + BigInt(byteLength);
|
|
5383
5399
|
return concatenateBytes([
|
|
5384
|
-
encodePvoxU32LeV1(
|
|
5400
|
+
encodePvoxU32LeV1(record2.pageIndex),
|
|
5385
5401
|
encodePvoxU64LeV1(offset),
|
|
5386
5402
|
encodePvoxU32LeV1(byteLength),
|
|
5387
|
-
decodePvoxSha256HexV1(
|
|
5403
|
+
decodePvoxSha256HexV1(record2.pageSha256, `records[${position}].pageSha256`)
|
|
5388
5404
|
]);
|
|
5389
5405
|
});
|
|
5390
5406
|
return concatenateBytes([
|
|
@@ -5405,12 +5421,12 @@ function encodeCanonicalJsonRecords(values) {
|
|
|
5405
5421
|
const encoded = [];
|
|
5406
5422
|
let aggregateByteLength = 0;
|
|
5407
5423
|
for (const value of normalized) {
|
|
5408
|
-
const
|
|
5409
|
-
aggregateByteLength +=
|
|
5424
|
+
const record2 = encodeCanonicalJsonRecord(value);
|
|
5425
|
+
aggregateByteLength += record2.byteLength;
|
|
5410
5426
|
if (aggregateByteLength > PVOX_MAX_CANONICAL_HASH_UTF8_BYTES_V1) {
|
|
5411
5427
|
throw new Error("Canonical JSON records exceed the governed PVOX hash-input byte limit.");
|
|
5412
5428
|
}
|
|
5413
|
-
encoded.push(
|
|
5429
|
+
encoded.push(record2);
|
|
5414
5430
|
}
|
|
5415
5431
|
return encoded;
|
|
5416
5432
|
}
|
|
@@ -5477,15 +5493,15 @@ function encodePvoxPhysicalInventoryHashPreimageV1(input) {
|
|
|
5477
5493
|
assertPvoxPreimageDataOnly(input, "PVOX physical-inventory hash input");
|
|
5478
5494
|
assertExactPvoxPreimageKeys(input, ["inventoryVersion", "subjectBinaryClosureHash", "validationEvidenceHash", "entries"], "PVOX physical-inventory hash input");
|
|
5479
5495
|
if (input.entries.length < 1 || input.entries.length > PVOX_DEFAULT_LIMITS.maximumMaterialRegions) throw new Error("Physical inventory must fit the governed material-region limit.");
|
|
5480
|
-
const encoded = input.entries.map((
|
|
5481
|
-
assertExactPvoxPreimageKeys(
|
|
5482
|
-
const regionIndex = assertSafeIntegerInRange(
|
|
5496
|
+
const encoded = input.entries.map((record2, index) => {
|
|
5497
|
+
assertExactPvoxPreimageKeys(record2, ["regionIndex", "physicalPaletteIndex", "regionId", "materialId"], `entries[${index}]`);
|
|
5498
|
+
const regionIndex = assertSafeIntegerInRange(record2.regionIndex, 0, U32_MAXIMUM, `entries[${index}].regionIndex`);
|
|
5483
5499
|
if (regionIndex !== index) throw new Error("Physical inventory records must have contiguous canonical region indexes starting at zero.");
|
|
5484
5500
|
return concatenateBytes([
|
|
5485
5501
|
encodePvoxU32LeV1(regionIndex),
|
|
5486
|
-
encodePvoxU32LeV1(
|
|
5487
|
-
encodePvoxCanonicalUtf8V1(
|
|
5488
|
-
encodePvoxCanonicalUtf8V1(
|
|
5502
|
+
encodePvoxU32LeV1(record2.physicalPaletteIndex),
|
|
5503
|
+
encodePvoxCanonicalUtf8V1(record2.regionId),
|
|
5504
|
+
encodePvoxCanonicalUtf8V1(record2.materialId)
|
|
5489
5505
|
]);
|
|
5490
5506
|
});
|
|
5491
5507
|
return concatenateBytes([
|
|
@@ -5533,12 +5549,12 @@ function encodePvoxPhysicalEvidenceHashPreimageV1(input) {
|
|
|
5533
5549
|
let previousOrder = -1;
|
|
5534
5550
|
for (const [index, evidence] of normalizedEvidence.entries()) {
|
|
5535
5551
|
if (evidence === null || typeof evidence !== "object" || Array.isArray(evidence)) throw new Error(`Physical evidence[${index}] must be an object.`);
|
|
5536
|
-
const
|
|
5537
|
-
if (typeof
|
|
5552
|
+
const record2 = evidence;
|
|
5553
|
+
if (typeof record2.regionId !== "string" || typeof record2.materialId !== "string" || typeof record2.property !== "string") {
|
|
5538
5554
|
throw new Error(`Physical evidence[${index}] must declare regionId, materialId, and property.`);
|
|
5539
5555
|
}
|
|
5540
|
-
const regionIndex = regionOrder.get(`${
|
|
5541
|
-
const propertyIndex = PVOX_PHYSICAL_PROPERTIES.indexOf(
|
|
5556
|
+
const regionIndex = regionOrder.get(`${record2.regionId}\0${record2.materialId}`);
|
|
5557
|
+
const propertyIndex = PVOX_PHYSICAL_PROPERTIES.indexOf(record2.property);
|
|
5542
5558
|
if (regionIndex === void 0 || propertyIndex < 0) throw new Error(`Physical evidence[${index}] is absent from the governed inventory/property registry.`);
|
|
5543
5559
|
const order = regionIndex * PVOX_PHYSICAL_PROPERTIES.length + propertyIndex;
|
|
5544
5560
|
if (order <= previousOrder) throw new Error("Physical evidence must use canonical inventory-region and governed-property order.");
|
|
@@ -5670,33 +5686,33 @@ function encodePvoxEditOverlayRootHashPreimageV1(input) {
|
|
|
5670
5686
|
const pageIndexes = /* @__PURE__ */ new Set();
|
|
5671
5687
|
normalizedPatches.forEach((patch, index) => {
|
|
5672
5688
|
if (patch === null || typeof patch !== "object" || Array.isArray(patch)) throw new Error(`Edit-overlay patch[${index}] must be an object.`);
|
|
5673
|
-
const
|
|
5674
|
-
if (Object.keys(
|
|
5675
|
-
if (
|
|
5676
|
-
const lodLevel = assertSafeIntegerInRange(
|
|
5677
|
-
const partitionIndex = assertSafeIntegerInRange(
|
|
5678
|
-
const hierarchyDepth = assertSafeIntegerInRange(
|
|
5679
|
-
if (typeof
|
|
5680
|
-
const expectedPageIndex = assertSafeIntegerInRange(
|
|
5689
|
+
const record2 = patch;
|
|
5690
|
+
if (Object.keys(record2).length !== requiredKeys.length || requiredKeys.some((key) => !(key in record2))) throw new Error(`Edit-overlay patch[${index}] must contain the complete fixed patch address and transition.`);
|
|
5691
|
+
if (record2.fieldKind !== "render-field" && record2.fieldKind !== "collision-field") throw new Error(`Edit-overlay patch[${index}].fieldKind is invalid.`);
|
|
5692
|
+
const lodLevel = assertSafeIntegerInRange(record2.lodLevel, 0, PVOX_DEFAULT_LIMITS.maximumLodCount - 1, `orderedPatches[${index}].lodLevel`);
|
|
5693
|
+
const partitionIndex = assertSafeIntegerInRange(record2.partitionIndex, 0, PVOX_DEFAULT_LIMITS.maximumPartitions - 1, `orderedPatches[${index}].partitionIndex`);
|
|
5694
|
+
const hierarchyDepth = assertSafeIntegerInRange(record2.hierarchyDepth, 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth, `orderedPatches[${index}].hierarchyDepth`);
|
|
5695
|
+
if (typeof record2.mortonCode !== "string" || !/^[0-9a-f]{16}$/u.test(record2.mortonCode)) throw new Error(`Edit-overlay patch[${index}].mortonCode is invalid.`);
|
|
5696
|
+
const expectedPageIndex = assertSafeIntegerInRange(record2.expectedPageIndex, 0, PVOX_DEFAULT_LIMITS.maximumPages - 1, `orderedPatches[${index}].expectedPageIndex`);
|
|
5681
5697
|
if (pageIndexes.has(expectedPageIndex)) throw new Error("Edit-overlay permits only one transition per page.");
|
|
5682
5698
|
pageIndexes.add(expectedPageIndex);
|
|
5683
|
-
const expectedPageHash = decodePvoxSha256HexV1(
|
|
5684
|
-
const resultingPageHash = decodePvoxSha256HexV1(
|
|
5699
|
+
const expectedPageHash = decodePvoxSha256HexV1(record2.expectedPageHash, `orderedPatches[${index}].expectedPageHash`);
|
|
5700
|
+
const resultingPageHash = decodePvoxSha256HexV1(record2.resultingPageHash, `orderedPatches[${index}].resultingPageHash`);
|
|
5685
5701
|
if (expectedPageHash.every((value, offset) => value === resultingPageHash[offset])) throw new Error("Edit-overlay page transitions must change the page hash.");
|
|
5686
|
-
const expectedBrickHash =
|
|
5687
|
-
const resultingBrickHash =
|
|
5702
|
+
const expectedBrickHash = record2.expectedBrickHash;
|
|
5703
|
+
const resultingBrickHash = record2.resultingBrickHash;
|
|
5688
5704
|
decodePvoxSha256HexV1(expectedBrickHash, `orderedPatches[${index}].expectedBrickHash`);
|
|
5689
5705
|
decodePvoxSha256HexV1(resultingBrickHash, `orderedPatches[${index}].resultingBrickHash`);
|
|
5690
|
-
if (
|
|
5706
|
+
if (record2.operation === "insert") {
|
|
5691
5707
|
if (expectedBrickHash !== PVOX_EDIT_JOURNAL_GENESIS_HASH || resultingBrickHash === PVOX_EDIT_JOURNAL_GENESIS_HASH) throw new Error("Edit-overlay insert must change an empty brick to a non-empty result.");
|
|
5692
|
-
} else if (
|
|
5708
|
+
} else if (record2.operation === "remove") {
|
|
5693
5709
|
if (expectedBrickHash === PVOX_EDIT_JOURNAL_GENESIS_HASH || resultingBrickHash !== PVOX_EDIT_JOURNAL_GENESIS_HASH) throw new Error("Edit-overlay remove must change a non-empty brick to the empty result.");
|
|
5694
|
-
} else if (
|
|
5710
|
+
} else if (record2.operation === "replace") {
|
|
5695
5711
|
if (expectedBrickHash === PVOX_EDIT_JOURNAL_GENESIS_HASH || resultingBrickHash === PVOX_EDIT_JOURNAL_GENESIS_HASH || expectedBrickHash === resultingBrickHash) throw new Error("Edit-overlay replace must change one non-empty brick to another.");
|
|
5696
5712
|
} else {
|
|
5697
5713
|
throw new Error(`Edit-overlay patch[${index}].operation is invalid.`);
|
|
5698
5714
|
}
|
|
5699
|
-
const address = [
|
|
5715
|
+
const address = [record2.fieldKind, String(lodLevel).padStart(2, "0"), String(partitionIndex).padStart(6, "0"), String(hierarchyDepth).padStart(2, "0"), record2.mortonCode].join("\0");
|
|
5700
5716
|
if (index > 0 && address <= previousAddress) throw new Error("Edit-overlay patches must be unique and canonically ordered by their complete spatial address.");
|
|
5701
5717
|
previousAddress = address;
|
|
5702
5718
|
});
|
|
@@ -5833,17 +5849,17 @@ function encodePvoxRootHashPreimageV1(exactHeader, orderedSectionHashes) {
|
|
|
5833
5849
|
const sectionCount = new DataView(header.buffer, header.byteOffset, header.byteLength).getUint16(PVOX_ROOT_HEADER_SECTION_COUNT_OFFSET, true);
|
|
5834
5850
|
if (orderedSectionHashes.length !== sectionCount) throw new Error("Root section hashes must match the header sectionCount.");
|
|
5835
5851
|
let previousKey = -1n;
|
|
5836
|
-
const sections = orderedSectionHashes.map((
|
|
5837
|
-
assertExactPvoxPreimageKeys(
|
|
5838
|
-
const sectionType = assertSafeIntegerInRange(
|
|
5839
|
-
const sectionVersion = assertSafeIntegerInRange(
|
|
5852
|
+
const sections = orderedSectionHashes.map((record2, index) => {
|
|
5853
|
+
assertExactPvoxPreimageKeys(record2, ["sectionType", "sectionVersion", "sectionHash"], `orderedSectionHashes[${index}]`);
|
|
5854
|
+
const sectionType = assertSafeIntegerInRange(record2.sectionType, 0, U32_MAXIMUM, `orderedSectionHashes[${index}].sectionType`);
|
|
5855
|
+
const sectionVersion = assertSafeIntegerInRange(record2.sectionVersion, 0, U16_MAXIMUM, `orderedSectionHashes[${index}].sectionVersion`);
|
|
5840
5856
|
const key = BigInt(sectionType) << 16n | BigInt(sectionVersion);
|
|
5841
5857
|
if (key <= previousKey) throw new Error("Root section hashes must be unique and strictly ordered by type then version.");
|
|
5842
5858
|
previousKey = key;
|
|
5843
5859
|
return concatenateBytes([
|
|
5844
5860
|
encodePvoxU32LeV1(sectionType),
|
|
5845
5861
|
encodePvoxU16LeV1(sectionVersion),
|
|
5846
|
-
decodePvoxSha256HexV1(
|
|
5862
|
+
decodePvoxSha256HexV1(record2.sectionHash, `orderedSectionHashes[${index}].sectionHash`)
|
|
5847
5863
|
]);
|
|
5848
5864
|
});
|
|
5849
5865
|
return concatenateBytes([TEXT_ENCODER.encode(PVOX_ROOT_HASH_DOMAIN), header, ...sections]);
|
|
@@ -6878,6 +6894,323 @@ var MODEL_RESOLUTION_V2_JSON_SCHEMAS = deepFreeze3({
|
|
|
6878
6894
|
modelResolution: standaloneSchema("modelResolution", "ModelResolutionV2")
|
|
6879
6895
|
});
|
|
6880
6896
|
|
|
6897
|
+
// src/model-resource-manifest.ts
|
|
6898
|
+
var import_gpu_shader3 = require("@plasius/gpu-shader");
|
|
6899
|
+
var MODEL_RESOURCE_MANIFEST_LIMITS = Object.freeze({
|
|
6900
|
+
files: 16384,
|
|
6901
|
+
fileBytes: 64 * 1024 * 1024,
|
|
6902
|
+
totalBytes: 128 * 1024 * 1024,
|
|
6903
|
+
metadataValues: 262144,
|
|
6904
|
+
metadataDepth: 16,
|
|
6905
|
+
timeoutMs: 6e4
|
|
6906
|
+
});
|
|
6907
|
+
var ModelResourceManifestError = class extends TypeError {
|
|
6908
|
+
constructor(code) {
|
|
6909
|
+
super(`Invalid model resource manifest: ${code}`);
|
|
6910
|
+
this.code = code;
|
|
6911
|
+
this.name = "ModelResourceManifestError";
|
|
6912
|
+
}
|
|
6913
|
+
code;
|
|
6914
|
+
};
|
|
6915
|
+
function fail(code = "invalid-input") {
|
|
6916
|
+
throw new ModelResourceManifestError(code);
|
|
6917
|
+
}
|
|
6918
|
+
function record(value) {
|
|
6919
|
+
if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail();
|
|
6920
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
6921
|
+
const keys = Reflect.ownKeys(value);
|
|
6922
|
+
if (keys.length > MODEL_RESOURCE_MANIFEST_LIMITS.files) fail("limit-exceeded");
|
|
6923
|
+
for (const key of keys) {
|
|
6924
|
+
if (typeof key !== "string" || ["__proto__", "constructor", "prototype"].includes(key)) fail();
|
|
6925
|
+
if (key.length > 128) fail("limit-exceeded");
|
|
6926
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
6927
|
+
if (!("value" in property) || !property.enumerable) fail();
|
|
6928
|
+
output[key] = property.value;
|
|
6929
|
+
}
|
|
6930
|
+
return output;
|
|
6931
|
+
}
|
|
6932
|
+
function list(value) {
|
|
6933
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) fail();
|
|
6934
|
+
if (value.length > MODEL_RESOURCE_MANIFEST_LIMITS.files) fail("limit-exceeded");
|
|
6935
|
+
if (Reflect.ownKeys(value).length !== value.length + 1) fail();
|
|
6936
|
+
const output = [];
|
|
6937
|
+
for (let i = 0; i < value.length; i++) {
|
|
6938
|
+
const property = Object.getOwnPropertyDescriptor(value, String(i));
|
|
6939
|
+
if (!property || !("value" in property) || !property.enumerable) fail();
|
|
6940
|
+
output.push(property.value);
|
|
6941
|
+
}
|
|
6942
|
+
return output;
|
|
6943
|
+
}
|
|
6944
|
+
function snapshot(value, budget, depth = 0) {
|
|
6945
|
+
if (--budget.remaining < 0 || depth > MODEL_RESOURCE_MANIFEST_LIMITS.metadataDepth) fail("limit-exceeded");
|
|
6946
|
+
if (value === null || value === void 0 || typeof value === "boolean") return value;
|
|
6947
|
+
if (typeof value === "string") {
|
|
6948
|
+
if (value.length > 2048) fail("limit-exceeded");
|
|
6949
|
+
return value;
|
|
6950
|
+
}
|
|
6951
|
+
if (typeof value === "number") {
|
|
6952
|
+
if (!Number.isFinite(value)) fail();
|
|
6953
|
+
return value;
|
|
6954
|
+
}
|
|
6955
|
+
if (Array.isArray(value)) return Object.freeze(list(value).map((item) => snapshot(item, budget, depth + 1)));
|
|
6956
|
+
const fields2 = record(value);
|
|
6957
|
+
for (const key of Object.keys(fields2)) fields2[key] = snapshot(fields2[key], budget, depth + 1);
|
|
6958
|
+
return Object.freeze(fields2);
|
|
6959
|
+
}
|
|
6960
|
+
function path(value) {
|
|
6961
|
+
if (typeof value !== "string" || value.length > 1024 || !value.length) fail();
|
|
6962
|
+
for (const part of value.split("/")) {
|
|
6963
|
+
if (part.length > 255 || !/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/u.test(part) || part.endsWith(".") || /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\.|$)/iu.test(part)) fail();
|
|
6964
|
+
}
|
|
6965
|
+
return value;
|
|
6966
|
+
}
|
|
6967
|
+
function contentType(value) {
|
|
6968
|
+
if (typeof value !== "string" || value.length > 128 || !/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+(?:; charset=utf-8)?$/u.test(value)) fail();
|
|
6969
|
+
return value;
|
|
6970
|
+
}
|
|
6971
|
+
function uniquePaths(paths) {
|
|
6972
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6973
|
+
for (const value of paths) {
|
|
6974
|
+
const normalized = path(value).toLowerCase();
|
|
6975
|
+
if (seen.has(normalized)) fail("path-conflict");
|
|
6976
|
+
seen.add(normalized);
|
|
6977
|
+
}
|
|
6978
|
+
for (const value of seen) {
|
|
6979
|
+
for (let offset = value.indexOf("/"); offset !== -1; offset = value.indexOf("/", offset + 1)) {
|
|
6980
|
+
if (seen.has(value.slice(0, offset))) fail("path-conflict");
|
|
6981
|
+
}
|
|
6982
|
+
}
|
|
6983
|
+
}
|
|
6984
|
+
var typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
|
|
6985
|
+
var byteLengthGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength").get;
|
|
6986
|
+
var bufferGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer").get;
|
|
6987
|
+
async function qualifyModelResourceManifest(input) {
|
|
6988
|
+
try {
|
|
6989
|
+
return await qualify(input);
|
|
6990
|
+
} catch (error) {
|
|
6991
|
+
if (error instanceof ModelResourceManifestError) throw error;
|
|
6992
|
+
return fail();
|
|
6993
|
+
}
|
|
6994
|
+
}
|
|
6995
|
+
async function qualify(input) {
|
|
6996
|
+
const started = performance.now();
|
|
6997
|
+
const root = record(input);
|
|
6998
|
+
if (Object.keys(root).some((key) => !["manifest", "packageManifest", "files", "signal", "timeoutMs"].includes(key))) fail();
|
|
6999
|
+
const timeout = root.timeoutMs ?? 1e4;
|
|
7000
|
+
if (typeof timeout !== "number" || !Number.isSafeInteger(timeout) || timeout < 1 || timeout > MODEL_RESOURCE_MANIFEST_LIMITS.timeoutMs) fail();
|
|
7001
|
+
const signal = root.signal;
|
|
7002
|
+
if (signal !== void 0 && !(signal instanceof AbortSignal)) fail();
|
|
7003
|
+
const check = () => {
|
|
7004
|
+
if (signal?.aborted) fail("aborted");
|
|
7005
|
+
if (performance.now() - started >= timeout) fail("deadline-exceeded");
|
|
7006
|
+
};
|
|
7007
|
+
check();
|
|
7008
|
+
const budget = { remaining: MODEL_RESOURCE_MANIFEST_LIMITS.metadataValues };
|
|
7009
|
+
const rawManifest = snapshot(root.manifest, budget);
|
|
7010
|
+
const inventory = record(snapshot(root.packageManifest, budget));
|
|
7011
|
+
if (Object.keys(inventory).some((key) => !["format", "entries"].includes(key)) || !["zip", "tar", "directory-manifest", "inline"].includes(inventory.format)) fail();
|
|
7012
|
+
let manifest;
|
|
7013
|
+
try {
|
|
7014
|
+
manifest = rawManifest.assetKind === "model" ? createModelAssetManifest(rawManifest) : createAssetManifest(rawManifest);
|
|
7015
|
+
} catch {
|
|
7016
|
+
return fail();
|
|
7017
|
+
}
|
|
7018
|
+
uniquePaths(manifest.files.map((file) => file.path));
|
|
7019
|
+
if (!manifest.files.some((file) => file.path === path(manifest.entrypoint))) fail("inventory-mismatch");
|
|
7020
|
+
const descriptors = new Map(manifest.files.map((file) => [file.path, file]));
|
|
7021
|
+
let total = 0;
|
|
7022
|
+
for (const file of manifest.files) {
|
|
7023
|
+
if (!Number.isSafeInteger(file.byteLength)) fail();
|
|
7024
|
+
if (file.byteLength > MODEL_RESOURCE_MANIFEST_LIMITS.fileBytes) fail("limit-exceeded");
|
|
7025
|
+
total += file.byteLength;
|
|
7026
|
+
if (total > MODEL_RESOURCE_MANIFEST_LIMITS.totalBytes) fail("limit-exceeded");
|
|
7027
|
+
contentType(file.contentType);
|
|
7028
|
+
}
|
|
7029
|
+
const entries = list(inventory.entries).map(record);
|
|
7030
|
+
uniquePaths(entries.map((entry) => path(entry.path)));
|
|
7031
|
+
const ids = /* @__PURE__ */ new Set();
|
|
7032
|
+
for (const entry of entries) {
|
|
7033
|
+
if (Object.keys(entry).some((key) => !["id", "path", "role", "contentType"].includes(key)) || typeof entry.id !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(entry.id) || ids.has(entry.id) || !["mesh", "material", "texture", "animation", "auxiliary"].includes(entry.role)) fail();
|
|
7034
|
+
ids.add(entry.id);
|
|
7035
|
+
const descriptor = descriptors.get(entry.path);
|
|
7036
|
+
if (!descriptor) fail("inventory-mismatch");
|
|
7037
|
+
if (entry.contentType !== void 0 && contentType(entry.contentType) !== descriptor.contentType) fail("content-mismatch");
|
|
7038
|
+
}
|
|
7039
|
+
if (entries.length !== descriptors.size) fail("inventory-mismatch");
|
|
7040
|
+
const payloads = list(root.files).map((value) => {
|
|
7041
|
+
const file = record(value);
|
|
7042
|
+
if (Object.keys(file).length !== 3 || Object.keys(file).some((key) => !["path", "bytes", "contentType"].includes(key))) fail();
|
|
7043
|
+
const filePath = path(file.path);
|
|
7044
|
+
if (!(file.bytes instanceof Uint8Array) || Object.getPrototypeOf(file.bytes) !== Uint8Array.prototype || !(bufferGetter.call(file.bytes) instanceof ArrayBuffer)) fail();
|
|
7045
|
+
const descriptor = descriptors.get(filePath);
|
|
7046
|
+
if (!descriptor) fail("inventory-mismatch");
|
|
7047
|
+
if (byteLengthGetter.call(file.bytes) !== descriptor.byteLength || contentType(file.contentType) !== descriptor.contentType) fail("content-mismatch");
|
|
7048
|
+
return { path: filePath, bytes: file.bytes, descriptor };
|
|
7049
|
+
});
|
|
7050
|
+
uniquePaths(payloads.map((file) => file.path));
|
|
7051
|
+
if (payloads.length !== descriptors.size) fail("inventory-mismatch");
|
|
7052
|
+
const copies = payloads.map((file) => ({ ...file, bytes: new Uint8Array(file.bytes) }));
|
|
7053
|
+
for (const file of copies) {
|
|
7054
|
+
check();
|
|
7055
|
+
let hash;
|
|
7056
|
+
try {
|
|
7057
|
+
hash = await (0, import_gpu_shader3.computeSha256)(file.bytes);
|
|
7058
|
+
} catch {
|
|
7059
|
+
return fail("content-mismatch");
|
|
7060
|
+
}
|
|
7061
|
+
check();
|
|
7062
|
+
if (hash !== file.descriptor.sha256) fail("content-mismatch");
|
|
7063
|
+
}
|
|
7064
|
+
return Object.freeze({ ...manifest, files: Object.freeze([...manifest.files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) });
|
|
7065
|
+
}
|
|
7066
|
+
|
|
7067
|
+
// src/model-conversion-diagnostics.ts
|
|
7068
|
+
var MODEL_CONVERSION_DIAGNOSTIC_LIMITS = Object.freeze({ issues: 100, resources: 100, values: 16384, depth: 12, string: 2048, characters: 262144, timeoutMs: 6e4 });
|
|
7069
|
+
var ModelConversionDiagnosticsError = class extends TypeError {
|
|
7070
|
+
constructor(code) {
|
|
7071
|
+
super(`Invalid model conversion diagnostics: ${code}`);
|
|
7072
|
+
this.code = code;
|
|
7073
|
+
this.name = "ModelConversionDiagnosticsError";
|
|
7074
|
+
}
|
|
7075
|
+
code;
|
|
7076
|
+
};
|
|
7077
|
+
function fail2(code = "invalid-input") {
|
|
7078
|
+
throw new ModelConversionDiagnosticsError(code);
|
|
7079
|
+
}
|
|
7080
|
+
function fields(value) {
|
|
7081
|
+
if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail2();
|
|
7082
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
7083
|
+
const keys = Reflect.ownKeys(value);
|
|
7084
|
+
if (keys.length > 1024) fail2("limit-exceeded");
|
|
7085
|
+
for (const key of keys) {
|
|
7086
|
+
if (typeof key !== "string" || key.length > 128 || ["__proto__", "prototype", "constructor"].includes(key)) fail2();
|
|
7087
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
7088
|
+
if (!("value" in descriptor) || !descriptor.enumerable) fail2();
|
|
7089
|
+
result[key] = descriptor.value;
|
|
7090
|
+
}
|
|
7091
|
+
return result;
|
|
7092
|
+
}
|
|
7093
|
+
function only(record2, keys) {
|
|
7094
|
+
if (Object.keys(record2).some((key) => !keys.includes(key))) fail2();
|
|
7095
|
+
}
|
|
7096
|
+
function snapshot2(value, budget, depth = 0) {
|
|
7097
|
+
if (--budget.values < 0 || depth > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.depth) fail2("limit-exceeded");
|
|
7098
|
+
if (value === null || typeof value === "boolean") return value;
|
|
7099
|
+
if (typeof value === "string") {
|
|
7100
|
+
budget.characters -= value.length;
|
|
7101
|
+
if (value.length > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.string || budget.characters < 0) fail2("limit-exceeded");
|
|
7102
|
+
return value;
|
|
7103
|
+
}
|
|
7104
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
7105
|
+
if (Array.isArray(value)) {
|
|
7106
|
+
if (Object.getPrototypeOf(value) !== Array.prototype || value.length > 1024) fail2("limit-exceeded");
|
|
7107
|
+
if (Reflect.ownKeys(value).length !== value.length + 1) fail2();
|
|
7108
|
+
const result = [];
|
|
7109
|
+
for (let i = 0; i < value.length; i++) {
|
|
7110
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(i));
|
|
7111
|
+
if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) fail2();
|
|
7112
|
+
result.push(snapshot2(descriptor.value, budget, depth + 1));
|
|
7113
|
+
}
|
|
7114
|
+
return Object.freeze(result);
|
|
7115
|
+
}
|
|
7116
|
+
const record2 = fields(value);
|
|
7117
|
+
for (const key of Object.keys(record2)) {
|
|
7118
|
+
budget.characters -= key.length;
|
|
7119
|
+
if (budget.characters < 0) fail2("limit-exceeded");
|
|
7120
|
+
record2[key] = snapshot2(record2[key], budget, depth + 1);
|
|
7121
|
+
}
|
|
7122
|
+
return Object.freeze(record2);
|
|
7123
|
+
}
|
|
7124
|
+
function legacy(value) {
|
|
7125
|
+
return Object.freeze({ severity: value.severity, code: value.code, message: value.message });
|
|
7126
|
+
}
|
|
7127
|
+
async function qualifyModelConversionDiagnostics(input, options = {}) {
|
|
7128
|
+
let timer;
|
|
7129
|
+
let signal;
|
|
7130
|
+
let onAbort;
|
|
7131
|
+
try {
|
|
7132
|
+
const start = performance.now();
|
|
7133
|
+
const controls = fields(options);
|
|
7134
|
+
only(controls, ["signal", "timeoutMs"]);
|
|
7135
|
+
const timeoutMs = controls.timeoutMs === void 0 ? 1e4 : controls.timeoutMs;
|
|
7136
|
+
if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.timeoutMs) fail2();
|
|
7137
|
+
signal = controls.signal;
|
|
7138
|
+
const aborted = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted").get;
|
|
7139
|
+
const check = () => {
|
|
7140
|
+
if (signal !== void 0 && aborted.call(signal)) fail2("aborted");
|
|
7141
|
+
if (performance.now() - start >= timeoutMs) fail2("deadline-exceeded");
|
|
7142
|
+
};
|
|
7143
|
+
check();
|
|
7144
|
+
const args = fields(snapshot2(input, { values: MODEL_CONVERSION_DIAGNOSTIC_LIMITS.values, characters: MODEL_CONVERSION_DIAGNOSTIC_LIMITS.characters }));
|
|
7145
|
+
only(args, ["outcome", "source", "report", "resources"]);
|
|
7146
|
+
if (!["completed", "unsupported", "failed"].includes(args.outcome)) fail2();
|
|
7147
|
+
const source = createModelProvenance(args.source);
|
|
7148
|
+
const reportInput = fields(args.report);
|
|
7149
|
+
const maximumIssues = MODEL_CONVERSION_DIAGNOSTIC_LIMITS.issues - (args.outcome === "completed" ? 0 : 1);
|
|
7150
|
+
if (!Array.isArray(reportInput.issues) || reportInput.issues.length > maximumIssues) fail2("limit-exceeded");
|
|
7151
|
+
if (!Array.isArray(args.resources) || args.resources.length > MODEL_CONVERSION_DIAGNOSTIC_LIMITS.resources) fail2("limit-exceeded");
|
|
7152
|
+
const resources = /* @__PURE__ */ new Map();
|
|
7153
|
+
const identities = /* @__PURE__ */ new Map();
|
|
7154
|
+
for (const value of args.resources) {
|
|
7155
|
+
const binding = fields(value);
|
|
7156
|
+
only(binding, ["path", "resource"]);
|
|
7157
|
+
if (typeof binding.path !== "string" || !binding.path.startsWith("/") || binding.path === "/" || resources.has(binding.path)) fail2();
|
|
7158
|
+
const resource = createModelResourceRef(binding.resource);
|
|
7159
|
+
const identity = JSON.stringify(resource);
|
|
7160
|
+
if (identities.has(resource.uri) && identities.get(resource.uri) !== identity) fail2();
|
|
7161
|
+
identities.set(resource.uri, identity);
|
|
7162
|
+
resources.set(binding.path, resource);
|
|
7163
|
+
}
|
|
7164
|
+
check();
|
|
7165
|
+
const interrupted = new Promise((_resolve, reject) => {
|
|
7166
|
+
timer = setTimeout(() => reject(new ModelConversionDiagnosticsError("deadline-exceeded")), Math.max(1, timeoutMs - (performance.now() - start)));
|
|
7167
|
+
onAbort = () => reject(new ModelConversionDiagnosticsError("aborted"));
|
|
7168
|
+
if (signal) AbortSignal.prototype.addEventListener.call(signal, "abort", onAbort, { once: true });
|
|
7169
|
+
});
|
|
7170
|
+
const core = await Promise.race([import("@plasius/gpu-model-core"), interrupted]);
|
|
7171
|
+
check();
|
|
7172
|
+
const report = core.evaluateGpuModelDiagnostics(reportInput);
|
|
7173
|
+
const diagnostics = report.diagnostics.map((diagnostic) => {
|
|
7174
|
+
const resource = resources.get(diagnostic.path);
|
|
7175
|
+
if (diagnostic.path !== "/" && !resource) fail2("unbound-resource");
|
|
7176
|
+
const base = resource ? { resource } : {};
|
|
7177
|
+
switch (diagnostic.kind) {
|
|
7178
|
+
case "warning":
|
|
7179
|
+
return Object.freeze({ ...base, severity: "warning", code: "conversion.warning", message: "Conversion completed with an advisory." });
|
|
7180
|
+
case "repair":
|
|
7181
|
+
return Object.freeze({ ...base, severity: "warning", code: "conversion.repair", message: "Conversion applied a canonical repair fallback.", fallback: diagnostic.ruleId });
|
|
7182
|
+
case "loss":
|
|
7183
|
+
return Object.freeze({ ...base, severity: reportInput.allowLoss === true ? "warning" : "blocking", code: `conversion.loss.${diagnostic.effect}`, message: diagnostic.effect === "dropped" ? "Conversion dropped source data." : "Conversion approximated source data.", effect: diagnostic.effect });
|
|
7184
|
+
case "unsupported":
|
|
7185
|
+
return Object.freeze({ ...base, severity: "blocking", code: "conversion.unsupported", message: "Conversion encountered unsupported data." });
|
|
7186
|
+
case "blocking-error":
|
|
7187
|
+
return Object.freeze({ ...base, severity: "blocking", code: "conversion.blocked", message: "Conversion did not satisfy canonical validation policy." });
|
|
7188
|
+
}
|
|
7189
|
+
});
|
|
7190
|
+
if (args.outcome !== "completed") diagnostics.push(Object.freeze({ severity: "blocking", code: args.outcome === "failed" ? "conversion.failed" : "conversion.unsupported", message: args.outcome === "failed" ? "Conversion failed." : "Conversion encountered unsupported data." }));
|
|
7191
|
+
const technicalAccepted = args.outcome === "completed" && report.accepted;
|
|
7192
|
+
const status = args.outcome === "failed" ? "failed" : args.outcome === "unsupported" || report.diagnostics.some((item) => item.kind === "unsupported") ? "unsupported" : !technicalAccepted ? "failed" : diagnostics.length ? "warning" : "succeeded";
|
|
7193
|
+
const losses = diagnostics.filter((item) => item.effect !== void 0).map(legacy);
|
|
7194
|
+
check();
|
|
7195
|
+
return Object.freeze({
|
|
7196
|
+
contractVersion: 1,
|
|
7197
|
+
status,
|
|
7198
|
+
technicalAccepted,
|
|
7199
|
+
standard: report.standard,
|
|
7200
|
+
mode: report.mode,
|
|
7201
|
+
source,
|
|
7202
|
+
diagnostics: Object.freeze(diagnostics),
|
|
7203
|
+
converter: Object.freeze({ diagnostics: Object.freeze(diagnostics.filter((item) => item.effect === void 0).map(legacy)), losses: Object.freeze(losses) })
|
|
7204
|
+
});
|
|
7205
|
+
} catch (error) {
|
|
7206
|
+
if (error instanceof ModelConversionDiagnosticsError) throw error;
|
|
7207
|
+
return fail2();
|
|
7208
|
+
} finally {
|
|
7209
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
7210
|
+
if (signal && onAbort) AbortSignal.prototype.removeEventListener.call(signal, "abort", onAbort);
|
|
7211
|
+
}
|
|
7212
|
+
}
|
|
7213
|
+
|
|
6881
7214
|
// src/index.ts
|
|
6882
7215
|
var ASSET_CONTRACTS_PACKAGE = "@plasius/asset-contracts";
|
|
6883
7216
|
var UNIFIED_ASSET_PIPELINE_FEATURE_FLAG_ID = "asset.pipeline.unified-ai-assets.enabled";
|
|
@@ -7058,7 +7391,7 @@ function createModelAssetManifest(input) {
|
|
|
7058
7391
|
"screenshot",
|
|
7059
7392
|
"metadata"
|
|
7060
7393
|
]);
|
|
7061
|
-
const descriptor = (0,
|
|
7394
|
+
const descriptor = (0, import_gpu_shader4.parseModelGpuCompatibilityDescriptor)({
|
|
7062
7395
|
modelId: base.assetId,
|
|
7063
7396
|
version: base.version,
|
|
7064
7397
|
gpuInterface: input.gpuInterface,
|
|
@@ -7088,7 +7421,7 @@ function createGpuInterfaceAssetManifest(input) {
|
|
|
7088
7421
|
);
|
|
7089
7422
|
assertAllowedFileRoles(base, ["gpu-interface-manifest"]);
|
|
7090
7423
|
assertRoleCount(base, "gpu-interface-manifest", 1);
|
|
7091
|
-
const gpuInterfaceManifest = (0,
|
|
7424
|
+
const gpuInterfaceManifest = (0, import_gpu_shader4.parseGpuInterfaceManifest)(input.gpuInterfaceManifest);
|
|
7092
7425
|
assertImmutableAssetVersion(gpuInterfaceManifest.interfaceVersion);
|
|
7093
7426
|
assertDomainIdentity(
|
|
7094
7427
|
base,
|
|
@@ -7111,7 +7444,7 @@ function createShaderAssetManifest(input) {
|
|
|
7111
7444
|
);
|
|
7112
7445
|
assertAllowedFileRoles(base, ["shader-manifest", "wgsl"]);
|
|
7113
7446
|
assertRoleCount(base, "shader-manifest", 1);
|
|
7114
|
-
const shaderManifest = (0,
|
|
7447
|
+
const shaderManifest = (0, import_gpu_shader4.parseShaderVersionManifest)(input.shaderManifest);
|
|
7115
7448
|
assertShaderManifestVersions(shaderManifest);
|
|
7116
7449
|
assertDomainIdentity(base, shaderManifest.shaderId, shaderManifest.version, "Shader");
|
|
7117
7450
|
assertShaderModuleFiles(base.files, shaderManifest);
|
|
@@ -7130,7 +7463,7 @@ function createShaderStyleProfileAssetManifest(input) {
|
|
|
7130
7463
|
);
|
|
7131
7464
|
assertAllowedFileRoles(base, ["shader-style-profile-manifest"]);
|
|
7132
7465
|
assertRoleCount(base, "shader-style-profile-manifest", 1);
|
|
7133
|
-
const styleProfileManifest = (0,
|
|
7466
|
+
const styleProfileManifest = (0, import_gpu_shader4.parseShaderStyleProfileManifest)(input.styleProfileManifest);
|
|
7134
7467
|
assertStyleProfileManifestVersions(styleProfileManifest);
|
|
7135
7468
|
assertDomainIdentity(base, styleProfileManifest.profileId, styleProfileManifest.version, "Style profile");
|
|
7136
7469
|
return Object.freeze({ ...base, styleProfileManifest });
|
|
@@ -7194,8 +7527,8 @@ function createShaderValidationEvidenceRef(input) {
|
|
|
7194
7527
|
sha256: assertSha256(attestation2.sha256, "Shader validation evidence attestation sha256")
|
|
7195
7528
|
})
|
|
7196
7529
|
};
|
|
7197
|
-
const supportedUniversal =
|
|
7198
|
-
const supportedAdditive =
|
|
7530
|
+
const supportedUniversal = import_gpu_shader4.SUPPORTED_STABLE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
|
|
7531
|
+
const supportedAdditive = import_gpu_shader4.SUPPORTED_ADDITIVE_WEBGPU_MATRIX_POLICIES.some((policy) => result.matrixId === policy.matrixId && result.matrixVersion === policy.matrixVersion && result.matrixSha256 === policy.matrixSha256);
|
|
7199
7532
|
if (!supportedUniversal && !supportedAdditive) {
|
|
7200
7533
|
throw new Error("Shader validation evidence must target a supported WebGPU matrix policy.");
|
|
7201
7534
|
}
|
|
@@ -7206,7 +7539,7 @@ function createShaderValidationEvidenceRef(input) {
|
|
|
7206
7539
|
}
|
|
7207
7540
|
async function createGpuInterfaceRef(input) {
|
|
7208
7541
|
assertImmutableAssetVersion(input.manifest.interfaceVersion);
|
|
7209
|
-
const manifest = (0,
|
|
7542
|
+
const manifest = (0, import_gpu_shader4.parseGpuInterfaceManifest)(input.manifest);
|
|
7210
7543
|
assertAssetId(manifest.interfaceId);
|
|
7211
7544
|
assertImmutableAssetVersion(manifest.interfaceVersion);
|
|
7212
7545
|
const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "GPU interface");
|
|
@@ -7221,7 +7554,7 @@ async function createGpuInterfaceRef(input) {
|
|
|
7221
7554
|
}
|
|
7222
7555
|
async function createShaderVersionRef(input) {
|
|
7223
7556
|
assertImmutableAssetVersion(input.manifest.version);
|
|
7224
|
-
const manifest = (0,
|
|
7557
|
+
const manifest = (0, import_gpu_shader4.parseShaderVersionManifest)(input.manifest);
|
|
7225
7558
|
assertAssetId(manifest.shaderId);
|
|
7226
7559
|
assertShaderManifestVersions(manifest);
|
|
7227
7560
|
const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Shader");
|
|
@@ -7234,7 +7567,7 @@ async function createShaderVersionRef(input) {
|
|
|
7234
7567
|
}
|
|
7235
7568
|
async function createShaderStyleProfileRef(input) {
|
|
7236
7569
|
assertImmutableAssetVersion(input.manifest.version);
|
|
7237
|
-
const manifest = (0,
|
|
7570
|
+
const manifest = (0, import_gpu_shader4.parseShaderStyleProfileManifest)(input.manifest);
|
|
7238
7571
|
assertAssetId(manifest.profileId);
|
|
7239
7572
|
assertStyleProfileManifestVersions(manifest);
|
|
7240
7573
|
const manifestSha256 = await bindDomainManifestBytes(input.manifestBytes, manifest, "Style profile");
|
|
@@ -7266,9 +7599,9 @@ async function validateGpuAssetFiles(input) {
|
|
|
7266
7599
|
if (input.files.size !== manifest.files.length) {
|
|
7267
7600
|
throw new Error("GPU asset validation requires exactly the declared file set.");
|
|
7268
7601
|
}
|
|
7269
|
-
for (const
|
|
7270
|
-
if (!manifest.files.some((file) => file.path ===
|
|
7271
|
-
throw new Error(`GPU asset validation received undeclared file ${
|
|
7602
|
+
for (const path2 of input.files.keys()) {
|
|
7603
|
+
if (!manifest.files.some((file) => file.path === path2)) {
|
|
7604
|
+
throw new Error(`GPU asset validation received undeclared file ${path2}.`);
|
|
7272
7605
|
}
|
|
7273
7606
|
}
|
|
7274
7607
|
await Promise.all(manifest.files.map(async (file) => {
|
|
@@ -7279,7 +7612,7 @@ async function validateGpuAssetFiles(input) {
|
|
|
7279
7612
|
if (bytes.byteLength !== file.byteLength) {
|
|
7280
7613
|
throw new Error(`GPU asset file ${file.path} byte length differs from its descriptor.`);
|
|
7281
7614
|
}
|
|
7282
|
-
if (await (0,
|
|
7615
|
+
if (await (0, import_gpu_shader4.computeSha256)(bytes) !== file.sha256) {
|
|
7283
7616
|
throw new Error(`GPU asset file ${file.path} digest differs from its descriptor.`);
|
|
7284
7617
|
}
|
|
7285
7618
|
if (file.contentType === ASSET_JSON_CONTENT_TYPE) {
|
|
@@ -7298,13 +7631,13 @@ async function validateGpuAssetFiles(input) {
|
|
|
7298
7631
|
}
|
|
7299
7632
|
const expectedDomainManifest = domainManifestForAsset(manifest);
|
|
7300
7633
|
if (expectedDomainManifest !== null) {
|
|
7301
|
-
const parsedEntrypoint = (0,
|
|
7302
|
-
if ((0,
|
|
7634
|
+
const parsedEntrypoint = (0, import_gpu_shader4.parseJsonBytes)(entrypointBytes, "GPU asset manifest entrypoint");
|
|
7635
|
+
if ((0, import_gpu_shader4.canonicalizeGpuContract)(parsedEntrypoint) !== (0, import_gpu_shader4.canonicalizeGpuContract)(expectedDomainManifest)) {
|
|
7303
7636
|
throw new Error("GPU asset entrypoint bytes differ from the validated domain manifest.");
|
|
7304
7637
|
}
|
|
7305
7638
|
} else if (manifest.assetKind === "shader-validation-evidence") {
|
|
7306
7639
|
assertEvidenceEntrypointIdentity(
|
|
7307
|
-
(0,
|
|
7640
|
+
(0, import_gpu_shader4.parseJsonBytes)(entrypointBytes, "Shader validation evidence entrypoint"),
|
|
7308
7641
|
manifest.validationEvidence
|
|
7309
7642
|
);
|
|
7310
7643
|
}
|
|
@@ -7528,7 +7861,7 @@ function assertTypedRuntimeManifestUri(manifest, value) {
|
|
|
7528
7861
|
}
|
|
7529
7862
|
function assertEvidenceEntrypointIdentity(value, ref2) {
|
|
7530
7863
|
const evidence = assertRecord2(value, "Shader validation evidence entrypoint");
|
|
7531
|
-
if (evidence.contractVersion !==
|
|
7864
|
+
if (evidence.contractVersion !== import_gpu_shader4.SHADER_VALIDATION_EVIDENCE_VERSION || evidence.evidenceId !== ref2.evidenceId || evidence.status !== "passed") {
|
|
7532
7865
|
throw new Error("Shader validation evidence bytes do not identify passing declared evidence.");
|
|
7533
7866
|
}
|
|
7534
7867
|
const matrixRef = assertRecord2(evidence.matrixRef, "Shader validation evidence matrixRef");
|
|
@@ -7544,9 +7877,9 @@ function assertEvidenceEntrypointIdentity(value, ref2) {
|
|
|
7544
7877
|
}
|
|
7545
7878
|
}
|
|
7546
7879
|
function assertCanonicalJsonBytes(bytes, fieldName) {
|
|
7547
|
-
const value = (0,
|
|
7880
|
+
const value = (0, import_gpu_shader4.parseJsonBytes)(bytes, fieldName);
|
|
7548
7881
|
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
7549
|
-
if ((0,
|
|
7882
|
+
if ((0, import_gpu_shader4.canonicalizeGpuContract)(value) !== text) {
|
|
7550
7883
|
throw new Error(`${fieldName} must use canonical JSON serialization.`);
|
|
7551
7884
|
}
|
|
7552
7885
|
}
|
|
@@ -7555,11 +7888,11 @@ async function bindDomainManifestBytes(bytes, manifest, fieldName) {
|
|
|
7555
7888
|
throw new Error(`${fieldName} manifest bytes must be a Uint8Array.`);
|
|
7556
7889
|
}
|
|
7557
7890
|
assertCanonicalJsonBytes(bytes, `${fieldName} manifest`);
|
|
7558
|
-
const parsed = (0,
|
|
7559
|
-
if ((0,
|
|
7891
|
+
const parsed = (0, import_gpu_shader4.parseJsonBytes)(bytes, `${fieldName} manifest`);
|
|
7892
|
+
if ((0, import_gpu_shader4.canonicalizeGpuContract)(parsed) !== (0, import_gpu_shader4.canonicalizeGpuContract)(manifest)) {
|
|
7560
7893
|
throw new Error(`${fieldName} manifest bytes differ from the supplied manifest.`);
|
|
7561
7894
|
}
|
|
7562
|
-
return (0,
|
|
7895
|
+
return (0, import_gpu_shader4.computeSha256)(bytes);
|
|
7563
7896
|
}
|
|
7564
7897
|
function assertRoleCount(manifest, role, expectedCount) {
|
|
7565
7898
|
if (manifest.files.filter((file) => file.role === role).length !== expectedCount) {
|
|
@@ -7666,6 +7999,7 @@ function assertRequiredString(value, fieldName) {
|
|
|
7666
7999
|
MODEL_CANDIDATE_HARD_GATE_KINDS,
|
|
7667
8000
|
MODEL_CONFIRMATION_VIEW_KINDS,
|
|
7668
8001
|
MODEL_CONFIRMATION_VIEW_SIZE_PX,
|
|
8002
|
+
MODEL_CONVERSION_DIAGNOSTIC_LIMITS,
|
|
7669
8003
|
MODEL_GENERATOR_DISABLED_REASON_CODE,
|
|
7670
8004
|
MODEL_MATCH_ASSURANCE_BANDS,
|
|
7671
8005
|
MODEL_MATCH_ASSURANCE_THRESHOLDS,
|
|
@@ -7679,8 +8013,11 @@ function assertRequiredString(value, fieldName) {
|
|
|
7679
8013
|
MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
7680
8014
|
MODEL_RESOLUTION_V2_JSON_SCHEMAS,
|
|
7681
8015
|
MODEL_RESOLUTION_V2_STATES,
|
|
8016
|
+
MODEL_RESOURCE_MANIFEST_LIMITS,
|
|
7682
8017
|
MODEL_RIGHTS_STATUSES,
|
|
7683
8018
|
MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
|
|
8019
|
+
ModelConversionDiagnosticsError,
|
|
8020
|
+
ModelResourceManifestError,
|
|
7684
8021
|
PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
|
|
7685
8022
|
PVOX_ASSET_MANIFEST_VERSION,
|
|
7686
8023
|
PVOX_BINARY_CLOSURE_HASH_DOMAIN,
|
|
@@ -7871,6 +8208,8 @@ function assertRequiredString(value, fieldName) {
|
|
|
7871
8208
|
normalizeAssetId,
|
|
7872
8209
|
normalizePvoxRootHeaderForHashV1,
|
|
7873
8210
|
projectPvoxNamedJsonClosureV1,
|
|
8211
|
+
qualifyModelConversionDiagnostics,
|
|
8212
|
+
qualifyModelResourceManifest,
|
|
7874
8213
|
toLegacyModelResolutionState,
|
|
7875
8214
|
validateGpuAssetFiles
|
|
7876
8215
|
});
|