@plasius/asset-contracts 0.3.1 → 0.4.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.
@@ -0,0 +1,1074 @@
1
+ import { canonicalizeGpuContract } from "@plasius/gpu-shader";
2
+ import {
3
+ PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
4
+ PVOX_BINARY_CLOSURE_HASH_DOMAIN,
5
+ PVOX_BRICK_EDGE_VOXELS,
6
+ PVOX_CAPABILITIES,
7
+ PVOX_CAPABILITY_SET_HASH_DOMAIN,
8
+ PVOX_CONFIRMATION_BINDING_HASH_DOMAIN,
9
+ PVOX_DIRECTORY_ENTRY_BYTE_LENGTH,
10
+ PVOX_DIRECTORY_HASH_DOMAIN,
11
+ PVOX_DEFAULT_LIMITS,
12
+ PVOX_EDIT_JOURNAL_GENESIS_HASH,
13
+ PVOX_EDIT_JOURNAL_HASH_DOMAIN,
14
+ PVOX_MAX_ABSOLUTE_COORDINATE_METRES,
15
+ PVOX_MAX_EDIT_PATCHES,
16
+ PVOX_EDIT_OVERLAY_ROOT_HASH_DOMAIN,
17
+ PVOX_EVALUATION_CLOSURE_HASH_DOMAIN,
18
+ PVOX_FORMAT_VERSION,
19
+ PVOX_HEADER_BYTE_LENGTH,
20
+ PVOX_MAGIC,
21
+ PVOX_MAX_SECTIONS,
22
+ PVOX_PAGE_SET_HASH_DOMAIN,
23
+ PVOX_PAGE_SIZE_BYTES,
24
+ PVOX_PHYSICAL_EVIDENCE_HASH_DOMAIN,
25
+ PVOX_PHYSICAL_INVENTORY_HASH_DOMAIN,
26
+ PVOX_PHYSICAL_PROPERTIES,
27
+ PVOX_PROCESSING_CLOSURE_HASH_DOMAIN,
28
+ PVOX_PUBLICATION_HASH_DOMAIN,
29
+ PVOX_RENDER_EVIDENCE_HASH_DOMAIN,
30
+ PVOX_REQUEST_SEMANTIC_PROFILE_HASH_DOMAIN,
31
+ PVOX_ROOT_HASH_DOMAIN,
32
+ PVOX_RUNTIME_REQUEST_PROFILE_HASH_DOMAIN,
33
+ PVOX_SECTION_ALIGNMENT_BYTES,
34
+ PVOX_SECTION_HASH_DOMAIN,
35
+ } from "./pvox-model-resolution.js";
36
+
37
+ export const PVOX_SHA256_BYTE_LENGTH = 32 as const;
38
+ export const PVOX_SHA256_HEX_LENGTH = PVOX_SHA256_BYTE_LENGTH * 2;
39
+ export const PVOX_CANONICAL_UTF8_LENGTH_PREFIX_BYTES = 4 as const;
40
+ export const PVOX_ROOT_HEADER_DIRECTORY_HASH_OFFSET = 176 as const;
41
+ export const PVOX_ROOT_HEADER_COMPILATION_INPUT_HASH_OFFSET = 112 as const;
42
+ export const PVOX_ROOT_HEADER_RUNTIME_REQUEST_PROFILE_HASH_OFFSET = 144 as const;
43
+ export const PVOX_ROOT_HEADER_TRAILING_RESERVED_OFFSET = 208 as const;
44
+ export const PVOX_ROOT_HEADER_TRAILING_RESERVED_BYTE_LENGTH = 48 as const;
45
+ export const PVOX_ROOT_HEADER_INLINE_RESERVED_OFFSET = 58 as const;
46
+ export const PVOX_ROOT_HEADER_INLINE_RESERVED_BYTE_LENGTH = 2 as const;
47
+ export const PVOX_ROOT_HEADER_COORDINATE_BASIS_METRES_Y_UP_NEGATIVE_Z_FORWARD_CCW = 1 as const;
48
+ export const PVOX_ROOT_HEADER_FLAGS_V1 = 0 as const;
49
+ export const PVOX_ROOT_HEADER_MAXIMUM_HIERARCHY_DEPTH_V1 = 8 as const;
50
+ export const PVOX_ROOT_HEADER_MINIMUM_FIXED_POINT_FRACTION_BITS_V1 = 0 as const;
51
+ export const PVOX_ROOT_HEADER_MAXIMUM_FIXED_POINT_FRACTION_BITS_V1 = 32 as const;
52
+ /** Upper bound for one canonical JSON/text component in a governed PVOX hash preimage. */
53
+ export const PVOX_MAX_CANONICAL_HASH_UTF8_BYTES_V1 = PVOX_DEFAULT_LIMITS.maximumArtifactBytes;
54
+ /** Upper bound for a complete encoded hash preimage, including its domain and framing. */
55
+ export const PVOX_MAX_HASH_PREIMAGE_BYTES_V1 = PVOX_DEFAULT_LIMITS.maximumArtifactBytes + PVOX_PAGE_SIZE_BYTES;
56
+
57
+ const U16_MAXIMUM = 0xffff;
58
+ const U32_MAXIMUM = 0xffff_ffff;
59
+ const U64_MAXIMUM = 0xffff_ffff_ffff_ffffn;
60
+ const I64_MINIMUM = -0x8000_0000_0000_0000n;
61
+ const I64_MAXIMUM = 0x7fff_ffff_ffff_ffffn;
62
+ const PVOX_ROOT_HEADER_BOUND_COMPONENT_COUNT = 6 as const;
63
+ const PVOX_ROOT_HEADER_MAGIC_OFFSET = 0 as const;
64
+ const PVOX_ROOT_HEADER_FORMAT_MAJOR_OFFSET = 4 as const;
65
+ const PVOX_ROOT_HEADER_FORMAT_MINOR_OFFSET = 6 as const;
66
+ const PVOX_ROOT_HEADER_BYTE_LENGTH_OFFSET = 8 as const;
67
+ const PVOX_ROOT_HEADER_DIRECTORY_ENTRY_BYTE_LENGTH_OFFSET = 10 as const;
68
+ const PVOX_ROOT_HEADER_SECTION_COUNT_OFFSET = 12 as const;
69
+ const PVOX_ROOT_HEADER_FLAGS_OFFSET = 14 as const;
70
+ const PVOX_ROOT_HEADER_ARTIFACT_BYTE_LENGTH_OFFSET = 16 as const;
71
+ const PVOX_ROOT_HEADER_DIRECTORY_BYTE_OFFSET_OFFSET = 24 as const;
72
+ const PVOX_ROOT_HEADER_DIRECTORY_BYTE_LENGTH_OFFSET = 32 as const;
73
+ const PVOX_ROOT_HEADER_PAGE_SIZE_OFFSET = 40 as const;
74
+ const PVOX_ROOT_HEADER_PAGE_COUNT_OFFSET = 44 as const;
75
+ const PVOX_ROOT_HEADER_BRICK_EDGE_OFFSET = 48 as const;
76
+ const PVOX_ROOT_HEADER_MAXIMUM_HIERARCHY_DEPTH_OFFSET = 50 as const;
77
+ const PVOX_ROOT_HEADER_GEOMETRY_MODE_OFFSET = 51 as const;
78
+ const PVOX_ROOT_HEADER_COORDINATE_BASIS_OFFSET = 52 as const;
79
+ const PVOX_ROOT_HEADER_FIXED_POINT_FRACTION_BITS_OFFSET = 56 as const;
80
+ const PVOX_ROOT_HEADER_BOUNDS_OFFSET = 60 as const;
81
+ const PVOX_ROOT_HEADER_BOUND_COMPONENT_BYTE_LENGTH = 8 as const;
82
+ const PVOX_ROOT_HEADER_DIRECTORY_START_PAGE_OFFSET = 108 as const;
83
+ const PVOX_MAX_HASH_PREIMAGE_DATA_NODES_V1 = PVOX_DEFAULT_LIMITS.maximumHierarchyNodes;
84
+
85
+ const TEXT_ENCODER = new TextEncoder();
86
+ const PVOX_MAGIC_BYTES = TEXT_ENCODER.encode(PVOX_MAGIC);
87
+
88
+ function assertPvoxPreimageDataOnly(
89
+ value: unknown,
90
+ fieldName: string,
91
+ seen = new WeakSet<object>(),
92
+ depth = 0,
93
+ state = { nodes: 0 },
94
+ ): void {
95
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "bigint") return;
96
+ if (typeof value === "number") {
97
+ if (!Number.isFinite(value)) throw new Error(`${fieldName} must contain finite data values only.`);
98
+ return;
99
+ }
100
+ if (typeof value !== "object") throw new Error(`${fieldName} must contain JSON/fixed-width data values only.`);
101
+ state.nodes += 1;
102
+ if (depth > 64 || state.nodes > PVOX_MAX_HASH_PREIMAGE_DATA_NODES_V1 || seen.has(value)) throw new Error(`${fieldName} must be an acyclic bounded data tree.`);
103
+ seen.add(value);
104
+ try {
105
+ if (Array.isArray(value)) {
106
+ state.nodes += value.length;
107
+ if (state.nodes > PVOX_MAX_HASH_PREIMAGE_DATA_NODES_V1) throw new Error(`${fieldName} must be an acyclic bounded data tree.`);
108
+ const descriptors = Object.getOwnPropertyDescriptors(value);
109
+ const keys = Reflect.ownKeys(descriptors);
110
+ if (keys.some((key) => typeof key === "symbol" || (key !== "length" && !/^(0|[1-9][0-9]*)$/u.test(key)))) throw new Error(`${fieldName} arrays must contain indexed data properties only.`);
111
+ for (let index = 0; index < value.length; index += 1) {
112
+ const descriptor = descriptors[String(index)];
113
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new Error(`${fieldName} arrays must be dense enumerable data properties.`);
114
+ assertPvoxPreimageDataOnly(descriptor.value, `${fieldName}[${index}]`, seen, depth + 1, state);
115
+ }
116
+ return;
117
+ }
118
+ const prototype = Object.getPrototypeOf(value);
119
+ if (prototype !== Object.prototype && prototype !== null) throw new Error(`${fieldName} objects must have a plain or null prototype.`);
120
+ for (const key of Reflect.ownKeys(value)) {
121
+ if (typeof key === "symbol") throw new Error(`${fieldName} objects must contain string-keyed data properties only.`);
122
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
123
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new Error(`${fieldName} objects must contain enumerable data properties only.`);
124
+ assertPvoxPreimageDataOnly(descriptor.value, `${fieldName}.${key}`, seen, depth + 1, state);
125
+ }
126
+ } finally {
127
+ seen.delete(value);
128
+ }
129
+ }
130
+
131
+ function assertExactPvoxPreimageKeys(value: object, expectedKeys: readonly string[], fieldName: string): void {
132
+ const actualKeys = Object.keys(value);
133
+ if (actualKeys.length !== expectedKeys.length || expectedKeys.some((key) => !Object.hasOwn(value, key))) {
134
+ throw new Error(`${fieldName} must contain exactly: ${expectedKeys.join(", ")}.`);
135
+ }
136
+ }
137
+
138
+ function preflightPvoxFixedObject(value: unknown, expectedKeys: readonly string[], fieldName: string): Record<string, unknown> {
139
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`${fieldName} must be an object.`);
140
+ const prototype = Object.getPrototypeOf(value);
141
+ if (prototype !== Object.prototype && prototype !== null) throw new Error(`${fieldName} must have a plain or null prototype.`);
142
+ assertExactPvoxPreimageKeys(value, expectedKeys, fieldName);
143
+ for (const key of expectedKeys) {
144
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
145
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new Error(`${fieldName}.${key} must be an enumerable data property.`);
146
+ }
147
+ return value as Record<string, unknown>;
148
+ }
149
+
150
+ function getPreflightPvoxDataProperty(value: Record<string, unknown>, key: string): unknown {
151
+ return (Object.getOwnPropertyDescriptor(value, key) as PropertyDescriptor & { value: unknown }).value;
152
+ }
153
+
154
+ function preflightPvoxArrayCardinality(value: unknown, minimum: number, maximum: number, fieldName: string): readonly unknown[] {
155
+ if (!Array.isArray(value)) throw new Error(`${fieldName} must be an array.`);
156
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
157
+ if (!lengthDescriptor || !("value" in lengthDescriptor) || typeof lengthDescriptor.value !== "number" || !Number.isSafeInteger(lengthDescriptor.value)
158
+ || lengthDescriptor.value < minimum || lengthDescriptor.value > maximum) {
159
+ throw new Error(`${fieldName} must contain from ${minimum} through ${maximum} entries.`);
160
+ }
161
+ return value;
162
+ }
163
+
164
+ function copyPvoxByteInput(value: unknown, fieldName: string, maximumByteLength: number): Uint8Array {
165
+ if (!(value instanceof Uint8Array)) throw new Error(`${fieldName} must be a Uint8Array.`);
166
+ let byteLength: number;
167
+ try {
168
+ const typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype) as object;
169
+ const byteLengthGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength")?.get;
170
+ const bufferGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer")?.get;
171
+ if (byteLengthGetter === undefined || bufferGetter === undefined) throw new Error("Typed-array intrinsics are unavailable.");
172
+ byteLength = byteLengthGetter.call(value) as number;
173
+ const buffer = bufferGetter.call(value) as ArrayBufferLike;
174
+ if (typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer) {
175
+ throw new Error(`${fieldName} must not use shared mutable storage.`);
176
+ }
177
+ } catch (error) {
178
+ if (error instanceof Error && /shared mutable storage/u.test(error.message)) throw error;
179
+ throw new Error(`${fieldName} must be an intrinsic Uint8Array.`, { cause: error });
180
+ }
181
+ if (byteLength > maximumByteLength) throw new Error(`${fieldName} exceeds the governed PVOX byte limit.`);
182
+ const output = new Uint8Array(byteLength);
183
+ for (let index = 0; index < byteLength; index += 1) output[index] = value[index]!;
184
+ return output;
185
+ }
186
+
187
+ export const PVOX_ROOT_HEADER_GEOMETRY_MODE_CODES_V1 = Object.freeze({
188
+ solid: 1,
189
+ shell: 2,
190
+ mixed: 3,
191
+ } as const);
192
+
193
+ export type PvoxRootHeaderGeometryModeV1 = keyof typeof PVOX_ROOT_HEADER_GEOMETRY_MODE_CODES_V1;
194
+
195
+ /** Closed byte layout. Root and binary-closure digests are deliberately external to this header. */
196
+ export const PVOX_ROOT_HEADER_LAYOUT_V1 = Object.freeze({
197
+ magic: Object.freeze({ offset: PVOX_ROOT_HEADER_MAGIC_OFFSET, byteLength: PVOX_MAGIC_BYTES.byteLength }),
198
+ formatMajor: Object.freeze({ offset: PVOX_ROOT_HEADER_FORMAT_MAJOR_OFFSET, byteLength: 2 }),
199
+ formatMinor: Object.freeze({ offset: PVOX_ROOT_HEADER_FORMAT_MINOR_OFFSET, byteLength: 2 }),
200
+ headerByteLength: Object.freeze({ offset: PVOX_ROOT_HEADER_BYTE_LENGTH_OFFSET, byteLength: 2 }),
201
+ directoryEntryByteLength: Object.freeze({ offset: PVOX_ROOT_HEADER_DIRECTORY_ENTRY_BYTE_LENGTH_OFFSET, byteLength: 2 }),
202
+ sectionCount: Object.freeze({ offset: PVOX_ROOT_HEADER_SECTION_COUNT_OFFSET, byteLength: 2 }),
203
+ flags: Object.freeze({ offset: PVOX_ROOT_HEADER_FLAGS_OFFSET, byteLength: 2 }),
204
+ artifactByteLength: Object.freeze({ offset: PVOX_ROOT_HEADER_ARTIFACT_BYTE_LENGTH_OFFSET, byteLength: 8 }),
205
+ directoryByteOffset: Object.freeze({ offset: PVOX_ROOT_HEADER_DIRECTORY_BYTE_OFFSET_OFFSET, byteLength: 8 }),
206
+ directoryByteLength: Object.freeze({ offset: PVOX_ROOT_HEADER_DIRECTORY_BYTE_LENGTH_OFFSET, byteLength: 8 }),
207
+ pageSizeBytes: Object.freeze({ offset: PVOX_ROOT_HEADER_PAGE_SIZE_OFFSET, byteLength: 4 }),
208
+ pageCount: Object.freeze({ offset: PVOX_ROOT_HEADER_PAGE_COUNT_OFFSET, byteLength: 4 }),
209
+ brickEdgeVoxels: Object.freeze({ offset: PVOX_ROOT_HEADER_BRICK_EDGE_OFFSET, byteLength: 2 }),
210
+ maximumHierarchyDepth: Object.freeze({ offset: PVOX_ROOT_HEADER_MAXIMUM_HIERARCHY_DEPTH_OFFSET, byteLength: 1 }),
211
+ geometryMode: Object.freeze({ offset: PVOX_ROOT_HEADER_GEOMETRY_MODE_OFFSET, byteLength: 1 }),
212
+ coordinateBasis: Object.freeze({ offset: PVOX_ROOT_HEADER_COORDINATE_BASIS_OFFSET, byteLength: 4 }),
213
+ fixedPointFractionBits: Object.freeze({ offset: PVOX_ROOT_HEADER_FIXED_POINT_FRACTION_BITS_OFFSET, byteLength: 2 }),
214
+ inlineReserved: Object.freeze({ offset: PVOX_ROOT_HEADER_INLINE_RESERVED_OFFSET, byteLength: PVOX_ROOT_HEADER_INLINE_RESERVED_BYTE_LENGTH }),
215
+ quantizedBounds: Object.freeze({ offset: PVOX_ROOT_HEADER_BOUNDS_OFFSET, byteLength: PVOX_ROOT_HEADER_BOUND_COMPONENT_COUNT * PVOX_ROOT_HEADER_BOUND_COMPONENT_BYTE_LENGTH }),
216
+ directoryStartPage: Object.freeze({ offset: PVOX_ROOT_HEADER_DIRECTORY_START_PAGE_OFFSET, byteLength: 4 }),
217
+ compilationInputHash: Object.freeze({ offset: PVOX_ROOT_HEADER_COMPILATION_INPUT_HASH_OFFSET, byteLength: PVOX_SHA256_BYTE_LENGTH }),
218
+ runtimeRequestProfileHash: Object.freeze({ offset: PVOX_ROOT_HEADER_RUNTIME_REQUEST_PROFILE_HASH_OFFSET, byteLength: PVOX_SHA256_BYTE_LENGTH }),
219
+ directoryHash: Object.freeze({ offset: PVOX_ROOT_HEADER_DIRECTORY_HASH_OFFSET, byteLength: PVOX_SHA256_BYTE_LENGTH }),
220
+ trailingReserved: Object.freeze({ offset: PVOX_ROOT_HEADER_TRAILING_RESERVED_OFFSET, byteLength: PVOX_ROOT_HEADER_TRAILING_RESERVED_BYTE_LENGTH }),
221
+ rootHash: Object.freeze({ storage: "external" }),
222
+ binaryClosureHash: Object.freeze({ storage: "external" }),
223
+ } as const);
224
+
225
+ export interface PvoxRootHeaderInputV1 {
226
+ readonly sectionCount: number;
227
+ readonly artifactByteLength: number | bigint;
228
+ readonly directoryByteOffset: number | bigint;
229
+ readonly directoryByteLength: number | bigint;
230
+ readonly pageCount: number;
231
+ readonly maximumHierarchyDepth: number;
232
+ readonly geometryMode: PvoxRootHeaderGeometryModeV1;
233
+ readonly fixedPointFractionBits: number;
234
+ /** minX, minY, minZ, maxX, maxY, maxZ in the declared fixed-point scale. */
235
+ readonly quantizedBounds: readonly [bigint, bigint, bigint, bigint, bigint, bigint];
236
+ readonly directoryStartPage: number;
237
+ readonly compilationInputHash: string;
238
+ readonly runtimeRequestProfileHash: string;
239
+ readonly directoryHash: string;
240
+ }
241
+
242
+ export interface PvoxRootSectionHashRecordV1 {
243
+ readonly sectionType: number;
244
+ readonly sectionVersion: number;
245
+ readonly sectionHash: string;
246
+ }
247
+
248
+ export interface PvoxPageHashRecordV1 {
249
+ readonly pageIndex: number;
250
+ readonly byteOffset: number | bigint;
251
+ readonly byteLength: number;
252
+ readonly pageSha256: string;
253
+ }
254
+
255
+ export interface PvoxBinaryClosureHashInputV1 {
256
+ readonly sourceContentHash: string;
257
+ readonly canonicalDocumentHash: string;
258
+ readonly compilationInputHash: string;
259
+ readonly runtimeRequestProfileHash: string;
260
+ readonly artifactSha256: string;
261
+ readonly rootHash: string;
262
+ readonly directoryHash: string;
263
+ readonly pageSetHash: string;
264
+ }
265
+
266
+ export interface PvoxPhysicalInventoryHashRecordV1 {
267
+ readonly regionIndex: number;
268
+ readonly physicalPaletteIndex: number;
269
+ readonly regionId: string;
270
+ readonly materialId: string;
271
+ }
272
+
273
+ export interface PvoxPhysicalInventoryHashInputV1 {
274
+ readonly inventoryVersion: string;
275
+ readonly subjectBinaryClosureHash: string;
276
+ readonly validationEvidenceHash: string;
277
+ readonly entries: readonly PvoxPhysicalInventoryHashRecordV1[];
278
+ }
279
+
280
+ export interface PvoxEvaluationClosureHashInputV1 {
281
+ readonly fidelityEvidenceHash: string;
282
+ readonly physicalEvidenceHash: string;
283
+ readonly capabilitySetHash: string;
284
+ readonly renderEvidenceHash: string;
285
+ }
286
+
287
+ export interface PvoxPhysicalEvidenceRegionMaterialV1 {
288
+ readonly regionId: string;
289
+ readonly materialId: string;
290
+ }
291
+
292
+ /** `orderedRegionMaterials` is validation context derived from the attested inventory and is not encoded twice. */
293
+ export interface PvoxPhysicalEvidenceHashInputV1 {
294
+ readonly physicalInventoryHash: string;
295
+ readonly orderedRegionMaterials: readonly PvoxPhysicalEvidenceRegionMaterialV1[];
296
+ readonly orderedCanonicalEvidence: readonly unknown[];
297
+ }
298
+
299
+ export interface PvoxProcessingClosureHashInputV1 {
300
+ readonly binaryClosureHash: string;
301
+ readonly requestSemanticProfileHash: string;
302
+ readonly technicalProfile: unknown;
303
+ readonly pvoxValidationEvidenceHash: string;
304
+ readonly physicalInventoryValidationEvidenceHash: string;
305
+ readonly capabilitySetHash: string;
306
+ readonly physicalInventoryHash: string;
307
+ readonly physicalEvidenceHash: string;
308
+ readonly massPropertiesEvidenceHash: string;
309
+ readonly bondGraphEvidenceHash: string;
310
+ readonly interiorLayerEvidenceHash: string;
311
+ readonly compilerEvidenceHash: string;
312
+ readonly fidelityEvidenceHash: string;
313
+ }
314
+
315
+ export interface PvoxAssemblyClosureHashInputV1 {
316
+ readonly ownBinaryClosureHash: string;
317
+ readonly orderedChildren: readonly unknown[];
318
+ }
319
+
320
+ export interface PvoxEditOverlayRootHashInputV1 {
321
+ readonly baseContentHash: string;
322
+ readonly basePageSetHash: string;
323
+ readonly placementId: string;
324
+ readonly gridVersion: string;
325
+ readonly expectedRevision: number | bigint;
326
+ readonly resultingRevision: number | bigint;
327
+ readonly previousJournalHash: string;
328
+ readonly expectedRootHash: string;
329
+ readonly orderedPatches: readonly PvoxEditOverlayPatchHashInputV1[];
330
+ }
331
+
332
+ export interface PvoxEditOverlayPatchHashInputV1 {
333
+ readonly fieldKind: "render-field" | "collision-field";
334
+ readonly lodLevel: number;
335
+ readonly partitionIndex: number;
336
+ readonly hierarchyDepth: number;
337
+ readonly mortonCode: string;
338
+ readonly expectedPageIndex: number;
339
+ readonly expectedPageHash: string;
340
+ readonly operation: "insert" | "replace" | "remove";
341
+ readonly expectedBrickHash: string;
342
+ readonly resultingBrickHash: string;
343
+ readonly resultingPageHash: string;
344
+ }
345
+
346
+ export const PVOX_NAMED_JSON_CLOSURE_SPECS_V1 = Object.freeze({
347
+ "request-semantic-profile": Object.freeze({
348
+ domain: PVOX_REQUEST_SEMANTIC_PROFILE_HASH_DOMAIN,
349
+ omittedJsonPointers: Object.freeze(["/requestSemanticProfileHash"] as const),
350
+ }),
351
+ "runtime-request-profile": Object.freeze({
352
+ domain: PVOX_RUNTIME_REQUEST_PROFILE_HASH_DOMAIN,
353
+ omittedJsonPointers: Object.freeze(["/runtimeRequestProfileHash"] as const),
354
+ }),
355
+ "render-evidence": Object.freeze({
356
+ domain: PVOX_RENDER_EVIDENCE_HASH_DOMAIN,
357
+ omittedJsonPointers: Object.freeze(["/evidenceHash", "/evidenceHashAttestation"] as const),
358
+ }),
359
+ "confirmation-binding": Object.freeze({
360
+ domain: PVOX_CONFIRMATION_BINDING_HASH_DOMAIN,
361
+ omittedJsonPointers: Object.freeze(["/bindingHash", "/bindingHashAttestation"] as const),
362
+ }),
363
+ "edit-journal": Object.freeze({
364
+ domain: PVOX_EDIT_JOURNAL_HASH_DOMAIN,
365
+ omittedJsonPointers: Object.freeze(["/journalHash", "/journalHashAttestation", "/resultingRootHashAttestation"] as const),
366
+ }),
367
+ "publication-receipt": Object.freeze({
368
+ domain: PVOX_PUBLICATION_HASH_DOMAIN,
369
+ omittedJsonPointers: Object.freeze(["/publicationHash", "/publicationHashAttestation", "/publicationToken"] as const),
370
+ }),
371
+ } as const);
372
+
373
+ export type PvoxNamedJsonClosureV1 = keyof typeof PVOX_NAMED_JSON_CLOSURE_SPECS_V1;
374
+
375
+ function assertSafeIntegerInRange(value: number, minimum: number, maximum: number, fieldName: string): number {
376
+ if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
377
+ throw new Error(`${fieldName} must be a safe integer from ${minimum} through ${maximum}.`);
378
+ }
379
+ return value;
380
+ }
381
+
382
+ function asUnsignedBigInt(value: number | bigint, fieldName: string): bigint {
383
+ const result = typeof value === "bigint"
384
+ ? value
385
+ : BigInt(assertSafeIntegerInRange(value, 0, Number.MAX_SAFE_INTEGER, fieldName));
386
+ if (result < 0n || result > U64_MAXIMUM) throw new Error(`${fieldName} must fit an unsigned 64-bit integer.`);
387
+ return result;
388
+ }
389
+
390
+ function assertSignedBigInt(value: bigint, fieldName: string): bigint {
391
+ if (value < I64_MINIMUM || value > I64_MAXIMUM) throw new Error(`${fieldName} must fit a signed 64-bit integer.`);
392
+ return value;
393
+ }
394
+
395
+ function maximumQuantizedCoordinate(fractionBits: number): bigint {
396
+ return BigInt(PVOX_MAX_ABSOLUTE_COORDINATE_METRES) * (1n << BigInt(fractionBits));
397
+ }
398
+
399
+ function assertQuantizedCoordinate(value: bigint, fractionBits: number, fieldName: string): bigint {
400
+ const coordinate = assertSignedBigInt(value, fieldName);
401
+ const absolute = coordinate < 0n ? -coordinate : coordinate;
402
+ if (absolute > maximumQuantizedCoordinate(fractionBits)) throw new Error(`${fieldName} exceeds the governed absolute PVOX coordinate limit.`);
403
+ return coordinate;
404
+ }
405
+
406
+ function concatenateBytes(parts: readonly Uint8Array[]): Uint8Array {
407
+ const byteLength = parts.reduce((total, part) => total + part.byteLength, 0);
408
+ if (!Number.isSafeInteger(byteLength) || byteLength > PVOX_MAX_HASH_PREIMAGE_BYTES_V1) {
409
+ throw new Error("PVOX hash preimage exceeds the governed byte limit.");
410
+ }
411
+ const output = new Uint8Array(byteLength);
412
+ let offset = 0;
413
+ for (const part of parts) {
414
+ output.set(part, offset);
415
+ offset += part.byteLength;
416
+ }
417
+ return output;
418
+ }
419
+
420
+ function writeBytes(target: Uint8Array, offset: number, value: Uint8Array): void {
421
+ target.set(value, offset);
422
+ }
423
+
424
+ function isZeroRange(value: Uint8Array, offset: number, byteLength: number): boolean {
425
+ for (let index = offset; index < offset + byteLength; index += 1) {
426
+ if (value[index] !== 0) return false;
427
+ }
428
+ return true;
429
+ }
430
+
431
+ function readAscii(value: Uint8Array, offset: number, byteLength: number): string {
432
+ return String.fromCharCode(...value.slice(offset, offset + byteLength));
433
+ }
434
+
435
+ export function encodePvoxU16LeV1(value: number): Uint8Array {
436
+ const output = new Uint8Array(2);
437
+ new DataView(output.buffer).setUint16(0, assertSafeIntegerInRange(value, 0, U16_MAXIMUM, "u16"), true);
438
+ return output;
439
+ }
440
+
441
+ export function encodePvoxU32LeV1(value: number): Uint8Array {
442
+ const output = new Uint8Array(4);
443
+ new DataView(output.buffer).setUint32(0, assertSafeIntegerInRange(value, 0, U32_MAXIMUM, "u32"), true);
444
+ return output;
445
+ }
446
+
447
+ export function encodePvoxU64LeV1(value: number | bigint): Uint8Array {
448
+ const output = new Uint8Array(8);
449
+ new DataView(output.buffer).setBigUint64(0, asUnsignedBigInt(value, "u64"), true);
450
+ return output;
451
+ }
452
+
453
+ export function encodePvoxI64LeV1(value: bigint): Uint8Array {
454
+ const output = new Uint8Array(8);
455
+ new DataView(output.buffer).setBigInt64(0, assertSignedBigInt(value, "i64"), true);
456
+ return output;
457
+ }
458
+
459
+ export function decodePvoxSha256HexV1(value: string, fieldName = "sha256"): Uint8Array {
460
+ if (!new RegExp(`^[0-9a-f]{${PVOX_SHA256_HEX_LENGTH}}$`, "u").test(value)) {
461
+ throw new Error(`${fieldName} must be a canonical lowercase SHA-256 digest.`);
462
+ }
463
+ const output = new Uint8Array(PVOX_SHA256_BYTE_LENGTH);
464
+ for (let index = 0; index < output.byteLength; index += 1) {
465
+ output[index] = Number.parseInt(value.slice(index * 2, index * 2 + 2), 16);
466
+ }
467
+ return output;
468
+ }
469
+
470
+ function encodeCanonicalRawUtf8(value: string): Uint8Array {
471
+ if (!value.isWellFormed()) throw new Error("Canonical UTF-8 values cannot contain unpaired UTF-16 surrogates.");
472
+ let byteLength = 0;
473
+ for (let index = 0; index < value.length; index += 1) {
474
+ const codeUnit = value.charCodeAt(index);
475
+ if (codeUnit <= 0x7f) byteLength += 1;
476
+ else if (codeUnit <= 0x7ff) byteLength += 2;
477
+ else if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {
478
+ byteLength += 4;
479
+ index += 1;
480
+ } else byteLength += 3;
481
+ if (byteLength > PVOX_MAX_CANONICAL_HASH_UTF8_BYTES_V1) {
482
+ throw new Error("Canonical UTF-8 value exceeds the governed PVOX hash-input byte limit.");
483
+ }
484
+ }
485
+ const encoded = TEXT_ENCODER.encode(value);
486
+ if (encoded.byteLength !== byteLength) throw new Error("Canonical UTF-8 byte-length calculation failed.");
487
+ return encoded;
488
+ }
489
+
490
+ export function encodePvoxCanonicalUtf8V1(value: string): Uint8Array {
491
+ const utf8 = encodeCanonicalRawUtf8(value);
492
+ if (utf8.byteLength > U32_MAXIMUM) throw new Error("Canonical UTF-8 value exceeds the unsigned 32-bit length prefix.");
493
+ return concatenateBytes([encodePvoxU32LeV1(utf8.byteLength), utf8]);
494
+ }
495
+
496
+ export function encodePvoxSectionHashPreimageV1(
497
+ sectionType: number,
498
+ sectionVersion: number,
499
+ exactSectionBytes: Uint8Array,
500
+ ): Uint8Array {
501
+ const sectionBytes = copyPvoxByteInput(exactSectionBytes, "exactSectionBytes", PVOX_DEFAULT_LIMITS.maximumArtifactBytes);
502
+ return concatenateBytes([
503
+ TEXT_ENCODER.encode(PVOX_SECTION_HASH_DOMAIN),
504
+ encodePvoxU32LeV1(sectionType),
505
+ encodePvoxU16LeV1(sectionVersion),
506
+ encodePvoxU64LeV1(sectionBytes.byteLength),
507
+ sectionBytes,
508
+ ]);
509
+ }
510
+
511
+ export function encodePvoxDirectoryHashPreimageV1(
512
+ entryCount: number,
513
+ exactOrderedDirectoryBytes: Uint8Array,
514
+ ): Uint8Array {
515
+ const count = assertSafeIntegerInRange(entryCount, 1, PVOX_MAX_SECTIONS, "entryCount");
516
+ const directoryBytes = copyPvoxByteInput(
517
+ exactOrderedDirectoryBytes,
518
+ "exactOrderedDirectoryBytes",
519
+ PVOX_MAX_SECTIONS * PVOX_DIRECTORY_ENTRY_BYTE_LENGTH,
520
+ );
521
+ if (directoryBytes.byteLength !== count * PVOX_DIRECTORY_ENTRY_BYTE_LENGTH) {
522
+ throw new Error("Directory bytes must contain exactly entryCount fixed-width entries.");
523
+ }
524
+ return concatenateBytes([
525
+ TEXT_ENCODER.encode(PVOX_DIRECTORY_HASH_DOMAIN),
526
+ encodePvoxU16LeV1(count),
527
+ encodePvoxU16LeV1(PVOX_DIRECTORY_ENTRY_BYTE_LENGTH),
528
+ directoryBytes,
529
+ ]);
530
+ }
531
+
532
+ export function encodePvoxPageSetHashPreimageV1(records: readonly PvoxPageHashRecordV1[]): Uint8Array {
533
+ preflightPvoxArrayCardinality(records, 1, PVOX_DEFAULT_LIMITS.maximumPages, "PVOX page-set hash input");
534
+ assertPvoxPreimageDataOnly(records, "PVOX page-set hash input");
535
+ let previousEnd = 0n;
536
+ const encoded = records.map((record, position) => {
537
+ assertExactPvoxPreimageKeys(record, ["pageIndex", "byteOffset", "byteLength", "pageSha256"], `records[${position}]`);
538
+ if (record.pageIndex !== position) throw new Error("Page-set records must have contiguous page indexes starting at zero.");
539
+ const offset = asUnsignedBigInt(record.byteOffset, `records[${position}].byteOffset`);
540
+ const byteLength = assertSafeIntegerInRange(record.byteLength, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES, `records[${position}].byteLength`);
541
+ 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.");
542
+ previousEnd = offset + BigInt(byteLength);
543
+ return concatenateBytes([
544
+ encodePvoxU32LeV1(record.pageIndex),
545
+ encodePvoxU64LeV1(offset),
546
+ encodePvoxU32LeV1(byteLength),
547
+ decodePvoxSha256HexV1(record.pageSha256, `records[${position}].pageSha256`),
548
+ ]);
549
+ });
550
+ return concatenateBytes([
551
+ TEXT_ENCODER.encode(PVOX_PAGE_SET_HASH_DOMAIN),
552
+ encodePvoxU32LeV1(records.length),
553
+ ...encoded,
554
+ ]);
555
+ }
556
+
557
+ function normalizeJsonData(value: unknown): unknown {
558
+ return cloneProjectedData(value, "", new Set<string>(), new WeakSet<object>());
559
+ }
560
+
561
+ function encodeCanonicalJsonRecord(value: unknown): Uint8Array {
562
+ return encodePvoxCanonicalUtf8V1(canonicalizeGpuContract(normalizeJsonData(value)));
563
+ }
564
+
565
+ function encodeCanonicalJsonRecords(values: readonly unknown[]): readonly Uint8Array[] {
566
+ const normalized = normalizeJsonData(values);
567
+ if (!Array.isArray(normalized)) throw new Error("Canonical JSON record input must be an array.");
568
+ const encoded: Uint8Array[] = [];
569
+ let aggregateByteLength = 0;
570
+ for (const value of normalized) {
571
+ const record = encodeCanonicalJsonRecord(value);
572
+ aggregateByteLength += record.byteLength;
573
+ if (aggregateByteLength > PVOX_MAX_CANONICAL_HASH_UTF8_BYTES_V1) {
574
+ throw new Error("Canonical JSON records exceed the governed PVOX hash-input byte limit.");
575
+ }
576
+ encoded.push(record);
577
+ }
578
+ return encoded;
579
+ }
580
+
581
+ export function encodePvoxBinaryClosureHashPreimageV1(input: PvoxBinaryClosureHashInputV1): Uint8Array {
582
+ assertPvoxPreimageDataOnly(input, "PVOX binary-closure hash input");
583
+ assertExactPvoxPreimageKeys(input, [
584
+ "sourceContentHash", "canonicalDocumentHash", "compilationInputHash", "runtimeRequestProfileHash",
585
+ "artifactSha256", "rootHash", "directoryHash", "pageSetHash",
586
+ ], "PVOX binary-closure hash input");
587
+ return concatenateBytes([
588
+ TEXT_ENCODER.encode(PVOX_BINARY_CLOSURE_HASH_DOMAIN),
589
+ decodePvoxSha256HexV1(input.sourceContentHash, "sourceContentHash"),
590
+ decodePvoxSha256HexV1(input.canonicalDocumentHash, "canonicalDocumentHash"),
591
+ decodePvoxSha256HexV1(input.compilationInputHash, "compilationInputHash"),
592
+ decodePvoxSha256HexV1(input.runtimeRequestProfileHash, "runtimeRequestProfileHash"),
593
+ decodePvoxSha256HexV1(input.artifactSha256, "artifactSha256"),
594
+ decodePvoxSha256HexV1(input.rootHash, "rootHash"),
595
+ decodePvoxSha256HexV1(input.directoryHash, "directoryHash"),
596
+ decodePvoxSha256HexV1(input.pageSetHash, "pageSetHash"),
597
+ ]);
598
+ }
599
+
600
+ export function encodePvoxCapabilitySetHashPreimageV1(
601
+ subjectBinaryClosureHash: string,
602
+ orderedCanonicalAssessments: readonly unknown[],
603
+ ): Uint8Array {
604
+ preflightPvoxArrayCardinality(
605
+ orderedCanonicalAssessments,
606
+ PVOX_CAPABILITIES.length,
607
+ PVOX_CAPABILITIES.length,
608
+ "Capability hash input",
609
+ );
610
+ const assessments = normalizeJsonData(orderedCanonicalAssessments);
611
+ if (!Array.isArray(assessments) || assessments.length !== PVOX_CAPABILITIES.length) {
612
+ throw new Error("Capability hash input must contain every governed capability in canonical order.");
613
+ }
614
+ assessments.forEach((assessment, index) => {
615
+ if (assessment === null || typeof assessment !== "object" || Array.isArray(assessment)
616
+ || (assessment as Record<string, unknown>).capability !== PVOX_CAPABILITIES[index]) {
617
+ throw new Error("Capability hash input must contain every governed capability in canonical order.");
618
+ }
619
+ });
620
+ const encoded = encodeCanonicalJsonRecords(assessments);
621
+ return concatenateBytes([
622
+ TEXT_ENCODER.encode(PVOX_CAPABILITY_SET_HASH_DOMAIN),
623
+ decodePvoxSha256HexV1(subjectBinaryClosureHash, "subjectBinaryClosureHash"),
624
+ encodePvoxU32LeV1(encoded.length),
625
+ ...encoded,
626
+ ]);
627
+ }
628
+
629
+ export function encodePvoxPhysicalInventoryHashPreimageV1(input: PvoxPhysicalInventoryHashInputV1): Uint8Array {
630
+ const shallowInput = preflightPvoxFixedObject(
631
+ input,
632
+ ["inventoryVersion", "subjectBinaryClosureHash", "validationEvidenceHash", "entries"],
633
+ "PVOX physical-inventory hash input",
634
+ );
635
+ preflightPvoxArrayCardinality(
636
+ getPreflightPvoxDataProperty(shallowInput, "entries"),
637
+ 1,
638
+ PVOX_DEFAULT_LIMITS.maximumMaterialRegions,
639
+ "PVOX physical-inventory hash input.entries",
640
+ );
641
+ assertPvoxPreimageDataOnly(input, "PVOX physical-inventory hash input");
642
+ assertExactPvoxPreimageKeys(input, ["inventoryVersion", "subjectBinaryClosureHash", "validationEvidenceHash", "entries"], "PVOX physical-inventory hash input");
643
+ if (input.entries.length < 1 || input.entries.length > PVOX_DEFAULT_LIMITS.maximumMaterialRegions) throw new Error("Physical inventory must fit the governed material-region limit.");
644
+ const encoded = input.entries.map((record, index) => {
645
+ assertExactPvoxPreimageKeys(record, ["regionIndex", "physicalPaletteIndex", "regionId", "materialId"], `entries[${index}]`);
646
+ const regionIndex = assertSafeIntegerInRange(record.regionIndex, 0, U32_MAXIMUM, `entries[${index}].regionIndex`);
647
+ if (regionIndex !== index) throw new Error("Physical inventory records must have contiguous canonical region indexes starting at zero.");
648
+ return concatenateBytes([
649
+ encodePvoxU32LeV1(regionIndex),
650
+ encodePvoxU32LeV1(record.physicalPaletteIndex),
651
+ encodePvoxCanonicalUtf8V1(record.regionId),
652
+ encodePvoxCanonicalUtf8V1(record.materialId),
653
+ ]);
654
+ });
655
+ return concatenateBytes([
656
+ TEXT_ENCODER.encode(PVOX_PHYSICAL_INVENTORY_HASH_DOMAIN),
657
+ encodePvoxCanonicalUtf8V1(input.inventoryVersion),
658
+ decodePvoxSha256HexV1(input.subjectBinaryClosureHash, "subjectBinaryClosureHash"),
659
+ decodePvoxSha256HexV1(input.validationEvidenceHash, "validationEvidenceHash"),
660
+ encodePvoxU32LeV1(input.entries.length),
661
+ ...encoded,
662
+ ]);
663
+ }
664
+
665
+ export function encodePvoxPhysicalEvidenceHashPreimageV1(input: PvoxPhysicalEvidenceHashInputV1): Uint8Array {
666
+ const shallowInput = preflightPvoxFixedObject(
667
+ input,
668
+ ["physicalInventoryHash", "orderedRegionMaterials", "orderedCanonicalEvidence"],
669
+ "PVOX physical-evidence hash input",
670
+ );
671
+ preflightPvoxArrayCardinality(
672
+ getPreflightPvoxDataProperty(shallowInput, "orderedRegionMaterials"),
673
+ 1,
674
+ PVOX_DEFAULT_LIMITS.maximumMaterialRegions,
675
+ "PVOX physical-evidence hash input.orderedRegionMaterials",
676
+ );
677
+ preflightPvoxArrayCardinality(
678
+ getPreflightPvoxDataProperty(shallowInput, "orderedCanonicalEvidence"),
679
+ 0,
680
+ PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries,
681
+ "PVOX physical-evidence hash input.orderedCanonicalEvidence",
682
+ );
683
+ assertPvoxPreimageDataOnly(input, "PVOX physical-evidence hash input");
684
+ assertExactPvoxPreimageKeys(input, ["physicalInventoryHash", "orderedRegionMaterials", "orderedCanonicalEvidence"], "PVOX physical-evidence hash input");
685
+ if (input.orderedRegionMaterials.length < 1 || input.orderedRegionMaterials.length > PVOX_DEFAULT_LIMITS.maximumMaterialRegions) {
686
+ throw new Error("Physical evidence region/material validation context must fit the governed material-region limit.");
687
+ }
688
+ const regionOrder = new Map<string, number>();
689
+ input.orderedRegionMaterials.forEach((regionMaterial, index) => {
690
+ assertExactPvoxPreimageKeys(regionMaterial, ["regionId", "materialId"], `orderedRegionMaterials[${index}]`);
691
+ const { regionId, materialId } = regionMaterial;
692
+ const key = `${regionId}\u0000${materialId}`;
693
+ if (regionOrder.has(key)) throw new Error("Physical evidence region/material validation context must be unique and canonical.");
694
+ regionOrder.set(key, index);
695
+ });
696
+ const normalizedEvidence = normalizeJsonData(input.orderedCanonicalEvidence);
697
+ if (!Array.isArray(normalizedEvidence) || normalizedEvidence.length > PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries) throw new Error("Physical evidence must be a bounded canonical array.");
698
+ let previousOrder = -1;
699
+ for (const [index, evidence] of normalizedEvidence.entries()) {
700
+ if (evidence === null || typeof evidence !== "object" || Array.isArray(evidence)) throw new Error(`Physical evidence[${index}] must be an object.`);
701
+ const record = evidence as Record<string, unknown>;
702
+ if (typeof record.regionId !== "string" || typeof record.materialId !== "string" || typeof record.property !== "string") {
703
+ throw new Error(`Physical evidence[${index}] must declare regionId, materialId, and property.`);
704
+ }
705
+ const regionIndex = regionOrder.get(`${record.regionId}\u0000${record.materialId}`);
706
+ const propertyIndex = (PVOX_PHYSICAL_PROPERTIES as readonly string[]).indexOf(record.property);
707
+ if (regionIndex === undefined || propertyIndex < 0) throw new Error(`Physical evidence[${index}] is absent from the governed inventory/property registry.`);
708
+ const order = regionIndex * PVOX_PHYSICAL_PROPERTIES.length + propertyIndex;
709
+ if (order <= previousOrder) throw new Error("Physical evidence must use canonical inventory-region and governed-property order.");
710
+ previousOrder = order;
711
+ }
712
+ const encoded = encodeCanonicalJsonRecords(normalizedEvidence);
713
+ return concatenateBytes([
714
+ TEXT_ENCODER.encode(PVOX_PHYSICAL_EVIDENCE_HASH_DOMAIN),
715
+ decodePvoxSha256HexV1(input.physicalInventoryHash, "physicalInventoryHash"),
716
+ encodePvoxU32LeV1(encoded.length),
717
+ ...encoded,
718
+ ]);
719
+ }
720
+
721
+ export function encodePvoxEvaluationClosureHashPreimageV1(input: PvoxEvaluationClosureHashInputV1): Uint8Array {
722
+ assertPvoxPreimageDataOnly(input, "PVOX evaluation-closure hash input");
723
+ assertExactPvoxPreimageKeys(input, ["fidelityEvidenceHash", "physicalEvidenceHash", "capabilitySetHash", "renderEvidenceHash"], "PVOX evaluation-closure hash input");
724
+ return concatenateBytes([
725
+ TEXT_ENCODER.encode(PVOX_EVALUATION_CLOSURE_HASH_DOMAIN),
726
+ decodePvoxSha256HexV1(input.fidelityEvidenceHash, "fidelityEvidenceHash"),
727
+ decodePvoxSha256HexV1(input.physicalEvidenceHash, "physicalEvidenceHash"),
728
+ decodePvoxSha256HexV1(input.capabilitySetHash, "capabilitySetHash"),
729
+ decodePvoxSha256HexV1(input.renderEvidenceHash, "renderEvidenceHash"),
730
+ ]);
731
+ }
732
+
733
+ export function encodePvoxProcessingClosureHashPreimageV1(input: PvoxProcessingClosureHashInputV1): Uint8Array {
734
+ assertPvoxPreimageDataOnly(input, "PVOX processing-closure hash input");
735
+ assertExactPvoxPreimageKeys(input, [
736
+ "binaryClosureHash", "requestSemanticProfileHash", "technicalProfile", "pvoxValidationEvidenceHash",
737
+ "physicalInventoryValidationEvidenceHash", "capabilitySetHash", "physicalInventoryHash",
738
+ "physicalEvidenceHash", "massPropertiesEvidenceHash", "bondGraphEvidenceHash",
739
+ "interiorLayerEvidenceHash", "compilerEvidenceHash", "fidelityEvidenceHash",
740
+ ], "PVOX processing-closure hash input");
741
+ return concatenateBytes([
742
+ TEXT_ENCODER.encode(PVOX_PROCESSING_CLOSURE_HASH_DOMAIN),
743
+ decodePvoxSha256HexV1(input.binaryClosureHash, "binaryClosureHash"),
744
+ decodePvoxSha256HexV1(input.requestSemanticProfileHash, "requestSemanticProfileHash"),
745
+ encodeCanonicalJsonRecord(input.technicalProfile),
746
+ decodePvoxSha256HexV1(input.pvoxValidationEvidenceHash, "pvoxValidationEvidenceHash"),
747
+ decodePvoxSha256HexV1(input.physicalInventoryValidationEvidenceHash, "physicalInventoryValidationEvidenceHash"),
748
+ decodePvoxSha256HexV1(input.capabilitySetHash, "capabilitySetHash"),
749
+ decodePvoxSha256HexV1(input.physicalInventoryHash, "physicalInventoryHash"),
750
+ decodePvoxSha256HexV1(input.physicalEvidenceHash, "physicalEvidenceHash"),
751
+ decodePvoxSha256HexV1(input.massPropertiesEvidenceHash, "massPropertiesEvidenceHash"),
752
+ decodePvoxSha256HexV1(input.bondGraphEvidenceHash, "bondGraphEvidenceHash"),
753
+ decodePvoxSha256HexV1(input.interiorLayerEvidenceHash, "interiorLayerEvidenceHash"),
754
+ decodePvoxSha256HexV1(input.compilerEvidenceHash, "compilerEvidenceHash"),
755
+ decodePvoxSha256HexV1(input.fidelityEvidenceHash, "fidelityEvidenceHash"),
756
+ ]);
757
+ }
758
+
759
+ export function encodePvoxAssemblyClosureHashPreimageV1(input: PvoxAssemblyClosureHashInputV1): Uint8Array {
760
+ const shallowInput = preflightPvoxFixedObject(input, ["ownBinaryClosureHash", "orderedChildren"], "PVOX assembly-closure hash input");
761
+ preflightPvoxArrayCardinality(
762
+ getPreflightPvoxDataProperty(shallowInput, "orderedChildren"),
763
+ 0,
764
+ PVOX_DEFAULT_LIMITS.maximumAssemblyChildren,
765
+ "PVOX assembly-closure hash input.orderedChildren",
766
+ );
767
+ assertPvoxPreimageDataOnly(input, "PVOX assembly-closure hash input");
768
+ assertExactPvoxPreimageKeys(input, ["ownBinaryClosureHash", "orderedChildren"], "PVOX assembly-closure hash input");
769
+ const children = normalizeJsonData(input.orderedChildren);
770
+ if (!Array.isArray(children) || children.length > PVOX_DEFAULT_LIMITS.maximumAssemblyChildren) throw new Error("orderedChildren must be a canonical JSON array within the governed assembly-child limit.");
771
+ return concatenateBytes([
772
+ TEXT_ENCODER.encode(PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN),
773
+ decodePvoxSha256HexV1(input.ownBinaryClosureHash, "ownBinaryClosureHash"),
774
+ encodePvoxU32LeV1(children.length),
775
+ encodePvoxCanonicalUtf8V1(canonicalizeGpuContract(children)),
776
+ ]);
777
+ }
778
+
779
+ export function encodePvoxEditOverlayRootHashPreimageV1(input: PvoxEditOverlayRootHashInputV1): Uint8Array {
780
+ const shallowInput = preflightPvoxFixedObject(input, [
781
+ "baseContentHash", "basePageSetHash", "placementId", "gridVersion", "expectedRevision",
782
+ "resultingRevision", "previousJournalHash", "expectedRootHash", "orderedPatches",
783
+ ], "PVOX edit-overlay hash input");
784
+ preflightPvoxArrayCardinality(
785
+ getPreflightPvoxDataProperty(shallowInput, "orderedPatches"),
786
+ 1,
787
+ PVOX_MAX_EDIT_PATCHES,
788
+ "PVOX edit-overlay hash input.orderedPatches",
789
+ );
790
+ assertPvoxPreimageDataOnly(input, "PVOX edit-overlay hash input");
791
+ assertExactPvoxPreimageKeys(input, [
792
+ "baseContentHash", "basePageSetHash", "placementId", "gridVersion", "expectedRevision",
793
+ "resultingRevision", "previousJournalHash", "expectedRootHash", "orderedPatches",
794
+ ], "PVOX edit-overlay hash input");
795
+ const expectedRevision = asUnsignedBigInt(input.expectedRevision, "expectedRevision");
796
+ const resultingRevision = asUnsignedBigInt(input.resultingRevision, "resultingRevision");
797
+ if (resultingRevision !== expectedRevision + 1n) throw new Error("Edit-overlay revisions must be sequential.");
798
+ const normalizedPatches = normalizeJsonData(input.orderedPatches);
799
+ if (!Array.isArray(normalizedPatches) || normalizedPatches.length < 1 || normalizedPatches.length > PVOX_MAX_EDIT_PATCHES) {
800
+ throw new Error("Edit-overlay patches must be a bounded non-empty canonical array.");
801
+ }
802
+ const requiredKeys = [
803
+ "fieldKind", "lodLevel", "partitionIndex", "hierarchyDepth", "mortonCode", "expectedPageIndex",
804
+ "expectedPageHash", "operation", "expectedBrickHash", "resultingBrickHash", "resultingPageHash",
805
+ ] as const;
806
+ let previousAddress = "";
807
+ const pageIndexes = new Set<number>();
808
+ normalizedPatches.forEach((patch, index) => {
809
+ if (patch === null || typeof patch !== "object" || Array.isArray(patch)) throw new Error(`Edit-overlay patch[${index}] must be an object.`);
810
+ const record = patch as Record<string, unknown>;
811
+ if (Object.keys(record).length !== requiredKeys.length || requiredKeys.some((key) => !(key in record))) throw new Error(`Edit-overlay patch[${index}] must contain the complete fixed patch address and transition.`);
812
+ if (record.fieldKind !== "render-field" && record.fieldKind !== "collision-field") throw new Error(`Edit-overlay patch[${index}].fieldKind is invalid.`);
813
+ const lodLevel = assertSafeIntegerInRange(record.lodLevel as number, 0, PVOX_DEFAULT_LIMITS.maximumLodCount - 1, `orderedPatches[${index}].lodLevel`);
814
+ const partitionIndex = assertSafeIntegerInRange(record.partitionIndex as number, 0, PVOX_DEFAULT_LIMITS.maximumPartitions - 1, `orderedPatches[${index}].partitionIndex`);
815
+ const hierarchyDepth = assertSafeIntegerInRange(record.hierarchyDepth as number, 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth, `orderedPatches[${index}].hierarchyDepth`);
816
+ if (typeof record.mortonCode !== "string" || !/^[0-9a-f]{16}$/u.test(record.mortonCode)) throw new Error(`Edit-overlay patch[${index}].mortonCode is invalid.`);
817
+ const expectedPageIndex = assertSafeIntegerInRange(record.expectedPageIndex as number, 0, PVOX_DEFAULT_LIMITS.maximumPages - 1, `orderedPatches[${index}].expectedPageIndex`);
818
+ if (pageIndexes.has(expectedPageIndex)) throw new Error("Edit-overlay permits only one transition per page.");
819
+ pageIndexes.add(expectedPageIndex);
820
+ const expectedPageHash = decodePvoxSha256HexV1(record.expectedPageHash as string, `orderedPatches[${index}].expectedPageHash`);
821
+ const resultingPageHash = decodePvoxSha256HexV1(record.resultingPageHash as string, `orderedPatches[${index}].resultingPageHash`);
822
+ if (expectedPageHash.every((value, offset) => value === resultingPageHash[offset])) throw new Error("Edit-overlay page transitions must change the page hash.");
823
+ const expectedBrickHash = record.expectedBrickHash as string;
824
+ const resultingBrickHash = record.resultingBrickHash as string;
825
+ decodePvoxSha256HexV1(expectedBrickHash, `orderedPatches[${index}].expectedBrickHash`);
826
+ decodePvoxSha256HexV1(resultingBrickHash, `orderedPatches[${index}].resultingBrickHash`);
827
+ if (record.operation === "insert") {
828
+ 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.");
829
+ } else if (record.operation === "remove") {
830
+ 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.");
831
+ } else if (record.operation === "replace") {
832
+ 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.");
833
+ } else {
834
+ throw new Error(`Edit-overlay patch[${index}].operation is invalid.`);
835
+ }
836
+ const address = [record.fieldKind, String(lodLevel).padStart(2, "0"), String(partitionIndex).padStart(6, "0"), String(hierarchyDepth).padStart(2, "0"), record.mortonCode].join("\u0000");
837
+ if (index > 0 && address <= previousAddress) throw new Error("Edit-overlay patches must be unique and canonically ordered by their complete spatial address.");
838
+ previousAddress = address;
839
+ });
840
+ const patches = encodeCanonicalJsonRecords(normalizedPatches);
841
+ return concatenateBytes([
842
+ TEXT_ENCODER.encode(PVOX_EDIT_OVERLAY_ROOT_HASH_DOMAIN),
843
+ decodePvoxSha256HexV1(input.baseContentHash, "baseContentHash"),
844
+ decodePvoxSha256HexV1(input.basePageSetHash, "basePageSetHash"),
845
+ encodePvoxCanonicalUtf8V1(input.placementId),
846
+ encodePvoxCanonicalUtf8V1(input.gridVersion),
847
+ encodePvoxU64LeV1(expectedRevision),
848
+ encodePvoxU64LeV1(resultingRevision),
849
+ decodePvoxSha256HexV1(input.previousJournalHash, "previousJournalHash"),
850
+ decodePvoxSha256HexV1(input.expectedRootHash, "expectedRootHash"),
851
+ encodePvoxU32LeV1(patches.length),
852
+ ...patches,
853
+ ]);
854
+ }
855
+
856
+ export function encodePvoxRootHeaderV1(input: PvoxRootHeaderInputV1): Uint8Array {
857
+ const rootHeaderKeys = [
858
+ "sectionCount", "artifactByteLength", "directoryByteOffset", "directoryByteLength", "pageCount",
859
+ "maximumHierarchyDepth", "geometryMode", "fixedPointFractionBits", "quantizedBounds",
860
+ "directoryStartPage", "compilationInputHash", "runtimeRequestProfileHash", "directoryHash",
861
+ ] as const;
862
+ const shallowInput = preflightPvoxFixedObject(input, rootHeaderKeys, "PVOX root-header input");
863
+ preflightPvoxArrayCardinality(
864
+ getPreflightPvoxDataProperty(shallowInput, "quantizedBounds"),
865
+ PVOX_ROOT_HEADER_BOUND_COMPONENT_COUNT,
866
+ PVOX_ROOT_HEADER_BOUND_COMPONENT_COUNT,
867
+ "quantizedBounds",
868
+ );
869
+ assertPvoxPreimageDataOnly(input, "PVOX root-header input");
870
+ assertExactPvoxPreimageKeys(input, rootHeaderKeys, "PVOX root-header input");
871
+ if (!Array.isArray(input.quantizedBounds)
872
+ || input.quantizedBounds.length !== PVOX_ROOT_HEADER_BOUND_COMPONENT_COUNT
873
+ || input.quantizedBounds.some((component) => typeof component !== "bigint")) {
874
+ throw new Error("quantizedBounds must contain exactly six bigint components.");
875
+ }
876
+ const sectionCount = assertSafeIntegerInRange(input.sectionCount, 1, PVOX_MAX_SECTIONS, "sectionCount");
877
+ const artifactByteLength = asUnsignedBigInt(input.artifactByteLength, "artifactByteLength");
878
+ const directoryByteOffset = asUnsignedBigInt(input.directoryByteOffset, "directoryByteOffset");
879
+ const directoryByteLength = asUnsignedBigInt(input.directoryByteLength, "directoryByteLength");
880
+ const expectedDirectoryByteLength = BigInt(sectionCount * PVOX_DIRECTORY_ENTRY_BYTE_LENGTH);
881
+ if (directoryByteLength !== expectedDirectoryByteLength) throw new Error("directoryByteLength must match sectionCount fixed-width entries.");
882
+ if (directoryByteOffset < BigInt(PVOX_HEADER_BYTE_LENGTH)
883
+ || directoryByteOffset % BigInt(PVOX_SECTION_ALIGNMENT_BYTES) !== 0n) {
884
+ throw new Error("directoryByteOffset must follow the header and satisfy PVOX section alignment.");
885
+ }
886
+ if (artifactByteLength < directoryByteOffset + directoryByteLength) {
887
+ throw new Error("artifactByteLength cannot end before the directory.");
888
+ }
889
+ const pageCount = assertSafeIntegerInRange(input.pageCount, 1, PVOX_DEFAULT_LIMITS.maximumPages, "pageCount");
890
+ if (artifactByteLength > BigInt(PVOX_DEFAULT_LIMITS.maximumArtifactBytes)
891
+ || artifactByteLength !== BigInt(pageCount) * BigInt(PVOX_PAGE_SIZE_BYTES)) {
892
+ throw new Error("artifactByteLength must equal pageCount multiplied by the fixed PVOX page size and fit the governed ceiling.");
893
+ }
894
+ const maximumHierarchyDepth = assertSafeIntegerInRange(input.maximumHierarchyDepth, 0, PVOX_ROOT_HEADER_MAXIMUM_HIERARCHY_DEPTH_V1, "maximumHierarchyDepth");
895
+ const fixedPointFractionBits = assertSafeIntegerInRange(input.fixedPointFractionBits, PVOX_ROOT_HEADER_MINIMUM_FIXED_POINT_FRACTION_BITS_V1, PVOX_ROOT_HEADER_MAXIMUM_FIXED_POINT_FRACTION_BITS_V1, "fixedPointFractionBits");
896
+ const directoryStartPage = assertSafeIntegerInRange(input.directoryStartPage, 0, U32_MAXIMUM, "directoryStartPage");
897
+ const expectedDirectoryStartPage = Number(directoryByteOffset / BigInt(PVOX_PAGE_SIZE_BYTES));
898
+ if (directoryStartPage !== expectedDirectoryStartPage || directoryStartPage >= pageCount) throw new Error("directoryStartPage must identify a page containing directoryByteOffset.");
899
+ for (let axis = 0; axis < PVOX_ROOT_HEADER_BOUND_COMPONENT_COUNT / 2; axis += 1) {
900
+ if (input.quantizedBounds[axis]! >= input.quantizedBounds[axis + 3]!) {
901
+ throw new Error("quantizedBounds minima must be below their corresponding maxima.");
902
+ }
903
+ }
904
+
905
+ const output = new Uint8Array(PVOX_HEADER_BYTE_LENGTH);
906
+ const view = new DataView(output.buffer);
907
+ writeBytes(output, PVOX_ROOT_HEADER_MAGIC_OFFSET, PVOX_MAGIC_BYTES);
908
+ view.setUint16(PVOX_ROOT_HEADER_FORMAT_MAJOR_OFFSET, PVOX_FORMAT_VERSION.major, true);
909
+ view.setUint16(PVOX_ROOT_HEADER_FORMAT_MINOR_OFFSET, PVOX_FORMAT_VERSION.minor, true);
910
+ view.setUint16(PVOX_ROOT_HEADER_BYTE_LENGTH_OFFSET, PVOX_HEADER_BYTE_LENGTH, true);
911
+ view.setUint16(PVOX_ROOT_HEADER_DIRECTORY_ENTRY_BYTE_LENGTH_OFFSET, PVOX_DIRECTORY_ENTRY_BYTE_LENGTH, true);
912
+ view.setUint16(PVOX_ROOT_HEADER_SECTION_COUNT_OFFSET, sectionCount, true);
913
+ view.setUint16(PVOX_ROOT_HEADER_FLAGS_OFFSET, PVOX_ROOT_HEADER_FLAGS_V1, true);
914
+ view.setBigUint64(PVOX_ROOT_HEADER_ARTIFACT_BYTE_LENGTH_OFFSET, artifactByteLength, true);
915
+ view.setBigUint64(PVOX_ROOT_HEADER_DIRECTORY_BYTE_OFFSET_OFFSET, directoryByteOffset, true);
916
+ view.setBigUint64(PVOX_ROOT_HEADER_DIRECTORY_BYTE_LENGTH_OFFSET, directoryByteLength, true);
917
+ view.setUint32(PVOX_ROOT_HEADER_PAGE_SIZE_OFFSET, PVOX_PAGE_SIZE_BYTES, true);
918
+ view.setUint32(PVOX_ROOT_HEADER_PAGE_COUNT_OFFSET, pageCount, true);
919
+ view.setUint16(PVOX_ROOT_HEADER_BRICK_EDGE_OFFSET, PVOX_BRICK_EDGE_VOXELS, true);
920
+ view.setUint8(PVOX_ROOT_HEADER_MAXIMUM_HIERARCHY_DEPTH_OFFSET, maximumHierarchyDepth);
921
+ view.setUint8(PVOX_ROOT_HEADER_GEOMETRY_MODE_OFFSET, PVOX_ROOT_HEADER_GEOMETRY_MODE_CODES_V1[input.geometryMode]);
922
+ view.setUint32(PVOX_ROOT_HEADER_COORDINATE_BASIS_OFFSET, PVOX_ROOT_HEADER_COORDINATE_BASIS_METRES_Y_UP_NEGATIVE_Z_FORWARD_CCW, true);
923
+ view.setUint16(PVOX_ROOT_HEADER_FIXED_POINT_FRACTION_BITS_OFFSET, fixedPointFractionBits, true);
924
+ input.quantizedBounds.forEach((component, index) => {
925
+ view.setBigInt64(PVOX_ROOT_HEADER_BOUNDS_OFFSET + index * PVOX_ROOT_HEADER_BOUND_COMPONENT_BYTE_LENGTH, assertQuantizedCoordinate(component, fixedPointFractionBits, `quantizedBounds[${index}]`), true);
926
+ });
927
+ view.setUint32(PVOX_ROOT_HEADER_DIRECTORY_START_PAGE_OFFSET, directoryStartPage, true);
928
+ writeBytes(output, PVOX_ROOT_HEADER_COMPILATION_INPUT_HASH_OFFSET, decodePvoxSha256HexV1(input.compilationInputHash, "compilationInputHash"));
929
+ writeBytes(output, PVOX_ROOT_HEADER_RUNTIME_REQUEST_PROFILE_HASH_OFFSET, decodePvoxSha256HexV1(input.runtimeRequestProfileHash, "runtimeRequestProfileHash"));
930
+ writeBytes(output, PVOX_ROOT_HEADER_DIRECTORY_HASH_OFFSET, decodePvoxSha256HexV1(input.directoryHash, "directoryHash"));
931
+ return output;
932
+ }
933
+
934
+ /** Validates the exact header and returns a defensive copy; reserved bytes are rejected, never silently rewritten. */
935
+ export function normalizePvoxRootHeaderForHashV1(exactHeader: Uint8Array): Uint8Array {
936
+ const output = copyPvoxByteInput(exactHeader, "PVOX root header", PVOX_HEADER_BYTE_LENGTH);
937
+ if (output.byteLength !== PVOX_HEADER_BYTE_LENGTH) throw new Error(`PVOX root header must be exactly ${PVOX_HEADER_BYTE_LENGTH} bytes.`);
938
+ const view = new DataView(output.buffer, output.byteOffset, output.byteLength);
939
+ if (readAscii(output, PVOX_ROOT_HEADER_MAGIC_OFFSET, PVOX_MAGIC_BYTES.byteLength) !== PVOX_MAGIC) throw new Error("PVOX root header magic is invalid.");
940
+ if (view.getUint16(PVOX_ROOT_HEADER_FORMAT_MAJOR_OFFSET, true) !== PVOX_FORMAT_VERSION.major
941
+ || view.getUint16(PVOX_ROOT_HEADER_FORMAT_MINOR_OFFSET, true) !== PVOX_FORMAT_VERSION.minor) throw new Error("PVOX root header version is unsupported.");
942
+ if (view.getUint16(PVOX_ROOT_HEADER_BYTE_LENGTH_OFFSET, true) !== PVOX_HEADER_BYTE_LENGTH
943
+ || view.getUint16(PVOX_ROOT_HEADER_DIRECTORY_ENTRY_BYTE_LENGTH_OFFSET, true) !== PVOX_DIRECTORY_ENTRY_BYTE_LENGTH) throw new Error("PVOX root header fixed-width declarations are invalid.");
944
+ const sectionCount = view.getUint16(PVOX_ROOT_HEADER_SECTION_COUNT_OFFSET, true);
945
+ if (sectionCount < 1 || sectionCount > PVOX_MAX_SECTIONS) throw new Error("PVOX root header sectionCount is invalid.");
946
+ if (view.getUint16(PVOX_ROOT_HEADER_FLAGS_OFFSET, true) !== PVOX_ROOT_HEADER_FLAGS_V1) throw new Error("PVOX root header contains unknown flags.");
947
+ const directoryByteOffset = view.getBigUint64(PVOX_ROOT_HEADER_DIRECTORY_BYTE_OFFSET_OFFSET, true);
948
+ const directoryByteLength = view.getBigUint64(PVOX_ROOT_HEADER_DIRECTORY_BYTE_LENGTH_OFFSET, true);
949
+ if (directoryByteOffset < BigInt(PVOX_HEADER_BYTE_LENGTH)
950
+ || directoryByteOffset % BigInt(PVOX_SECTION_ALIGNMENT_BYTES) !== 0n
951
+ || directoryByteLength !== BigInt(sectionCount * PVOX_DIRECTORY_ENTRY_BYTE_LENGTH)) throw new Error("PVOX root header directory layout is invalid.");
952
+ const artifactByteLength = view.getBigUint64(PVOX_ROOT_HEADER_ARTIFACT_BYTE_LENGTH_OFFSET, true);
953
+ const pageCount = view.getUint32(PVOX_ROOT_HEADER_PAGE_COUNT_OFFSET, true);
954
+ if (artifactByteLength < directoryByteOffset + directoryByteLength
955
+ || artifactByteLength > BigInt(PVOX_DEFAULT_LIMITS.maximumArtifactBytes)
956
+ || pageCount < 1
957
+ || pageCount > PVOX_DEFAULT_LIMITS.maximumPages
958
+ || artifactByteLength !== BigInt(pageCount) * BigInt(PVOX_PAGE_SIZE_BYTES)) throw new Error("PVOX root header artifact/page accounting is invalid.");
959
+ if (view.getUint32(PVOX_ROOT_HEADER_PAGE_SIZE_OFFSET, true) !== PVOX_PAGE_SIZE_BYTES
960
+ || view.getUint16(PVOX_ROOT_HEADER_BRICK_EDGE_OFFSET, true) !== PVOX_BRICK_EDGE_VOXELS) throw new Error("PVOX root header runtime layout is invalid.");
961
+ if (view.getUint8(PVOX_ROOT_HEADER_MAXIMUM_HIERARCHY_DEPTH_OFFSET) > PVOX_ROOT_HEADER_MAXIMUM_HIERARCHY_DEPTH_V1
962
+ || !Object.values(PVOX_ROOT_HEADER_GEOMETRY_MODE_CODES_V1).includes(view.getUint8(PVOX_ROOT_HEADER_GEOMETRY_MODE_OFFSET) as 1 | 2 | 3)
963
+ || view.getUint32(PVOX_ROOT_HEADER_COORDINATE_BASIS_OFFSET, true) !== PVOX_ROOT_HEADER_COORDINATE_BASIS_METRES_Y_UP_NEGATIVE_Z_FORWARD_CCW
964
+ || view.getUint16(PVOX_ROOT_HEADER_FIXED_POINT_FRACTION_BITS_OFFSET, true) > PVOX_ROOT_HEADER_MAXIMUM_FIXED_POINT_FRACTION_BITS_V1) throw new Error("PVOX root header geometry layout is invalid.");
965
+ const expectedDirectoryStartPage = Number(directoryByteOffset / BigInt(PVOX_PAGE_SIZE_BYTES));
966
+ if (view.getUint32(PVOX_ROOT_HEADER_DIRECTORY_START_PAGE_OFFSET, true) !== expectedDirectoryStartPage || expectedDirectoryStartPage >= pageCount) throw new Error("PVOX root header directory page binding is invalid.");
967
+ const fractionBits = view.getUint16(PVOX_ROOT_HEADER_FIXED_POINT_FRACTION_BITS_OFFSET, true);
968
+ for (let axis = 0; axis < PVOX_ROOT_HEADER_BOUND_COMPONENT_COUNT / 2; axis += 1) {
969
+ const minimum = assertQuantizedCoordinate(view.getBigInt64(PVOX_ROOT_HEADER_BOUNDS_OFFSET + axis * PVOX_ROOT_HEADER_BOUND_COMPONENT_BYTE_LENGTH, true), fractionBits, `quantizedBounds[${axis}]`);
970
+ const maximum = assertQuantizedCoordinate(view.getBigInt64(PVOX_ROOT_HEADER_BOUNDS_OFFSET + (axis + 3) * PVOX_ROOT_HEADER_BOUND_COMPONENT_BYTE_LENGTH, true), fractionBits, `quantizedBounds[${axis + 3}]`);
971
+ if (minimum >= maximum) throw new Error("PVOX root header quantized bounds are invalid.");
972
+ }
973
+ if (!isZeroRange(output, PVOX_ROOT_HEADER_INLINE_RESERVED_OFFSET, PVOX_ROOT_HEADER_INLINE_RESERVED_BYTE_LENGTH)
974
+ || !isZeroRange(output, PVOX_ROOT_HEADER_TRAILING_RESERVED_OFFSET, PVOX_ROOT_HEADER_TRAILING_RESERVED_BYTE_LENGTH)) throw new Error("PVOX root header reserved bytes must be zero.");
975
+ return output;
976
+ }
977
+
978
+ export function encodePvoxRootHashPreimageV1(
979
+ exactHeader: Uint8Array,
980
+ orderedSectionHashes: readonly PvoxRootSectionHashRecordV1[],
981
+ ): Uint8Array {
982
+ preflightPvoxArrayCardinality(orderedSectionHashes, 1, PVOX_MAX_SECTIONS, "PVOX root section-hash input");
983
+ assertPvoxPreimageDataOnly(orderedSectionHashes, "PVOX root section-hash input");
984
+ const header = normalizePvoxRootHeaderForHashV1(exactHeader);
985
+ const sectionCount = new DataView(header.buffer, header.byteOffset, header.byteLength).getUint16(PVOX_ROOT_HEADER_SECTION_COUNT_OFFSET, true);
986
+ if (orderedSectionHashes.length !== sectionCount) throw new Error("Root section hashes must match the header sectionCount.");
987
+ let previousKey = -1n;
988
+ const sections = orderedSectionHashes.map((record, index) => {
989
+ assertExactPvoxPreimageKeys(record, ["sectionType", "sectionVersion", "sectionHash"], `orderedSectionHashes[${index}]`);
990
+ const sectionType = assertSafeIntegerInRange(record.sectionType, 0, U32_MAXIMUM, `orderedSectionHashes[${index}].sectionType`);
991
+ const sectionVersion = assertSafeIntegerInRange(record.sectionVersion, 0, U16_MAXIMUM, `orderedSectionHashes[${index}].sectionVersion`);
992
+ const key = (BigInt(sectionType) << 16n) | BigInt(sectionVersion);
993
+ if (key <= previousKey) throw new Error("Root section hashes must be unique and strictly ordered by type then version.");
994
+ previousKey = key;
995
+ return concatenateBytes([
996
+ encodePvoxU32LeV1(sectionType),
997
+ encodePvoxU16LeV1(sectionVersion),
998
+ decodePvoxSha256HexV1(record.sectionHash, `orderedSectionHashes[${index}].sectionHash`),
999
+ ]);
1000
+ });
1001
+ return concatenateBytes([TEXT_ENCODER.encode(PVOX_ROOT_HASH_DOMAIN), header, ...sections]);
1002
+ }
1003
+
1004
+ function escapeJsonPointerSegment(value: string): string {
1005
+ return value.replaceAll("~", "~0").replaceAll("/", "~1");
1006
+ }
1007
+
1008
+ function cloneProjectedData(
1009
+ value: unknown,
1010
+ pointer: string,
1011
+ omittedPointers: ReadonlySet<string>,
1012
+ seen: WeakSet<object>,
1013
+ depth = 0,
1014
+ state = { nodes: 0 },
1015
+ ): unknown {
1016
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
1017
+ if (typeof value === "number") {
1018
+ if (!Number.isFinite(value)) throw new Error("PVOX hash projections require finite JSON numbers.");
1019
+ return value;
1020
+ }
1021
+ if (typeof value !== "object") throw new Error("PVOX hash projections accept JSON data only.");
1022
+ state.nodes += 1;
1023
+ if (depth > 64 || state.nodes > PVOX_MAX_HASH_PREIMAGE_DATA_NODES_V1) throw new Error("PVOX hash projections must be bounded data trees.");
1024
+ if (seen.has(value)) throw new Error("PVOX hash projections cannot contain cycles or shared object references.");
1025
+ seen.add(value);
1026
+ try {
1027
+ if (Array.isArray(value)) {
1028
+ state.nodes += value.length;
1029
+ if (state.nodes > PVOX_MAX_HASH_PREIMAGE_DATA_NODES_V1) throw new Error("PVOX hash projections must be bounded data trees.");
1030
+ const descriptors = Object.getOwnPropertyDescriptors(value);
1031
+ const keys = Reflect.ownKeys(descriptors);
1032
+ if (keys.some((key) => typeof key === "symbol" || (key !== "length" && !/^(0|[1-9][0-9]*)$/u.test(key)))) throw new Error("PVOX hash projection arrays cannot contain named or symbol properties.");
1033
+ const output: unknown[] = [];
1034
+ for (let index = 0; index < value.length; index += 1) {
1035
+ const descriptor = descriptors[String(index)];
1036
+ if (!descriptor || descriptor.get || descriptor.set || !descriptor.enumerable) throw new Error("PVOX hash projection arrays must be dense enumerable data.");
1037
+ const childPointer = `${pointer}/${index}`;
1038
+ if (omittedPointers.has(childPointer)) throw new Error("PVOX named omissions cannot remove array positions.");
1039
+ output.push(cloneProjectedData(descriptor.value, childPointer, omittedPointers, seen, depth + 1, state));
1040
+ }
1041
+ return output;
1042
+ }
1043
+ const prototype = Object.getPrototypeOf(value);
1044
+ if (prototype !== Object.prototype && prototype !== null) throw new Error("PVOX hash projection objects must have a plain or null prototype.");
1045
+ const output = Object.create(null) as Record<string, unknown>;
1046
+ for (const key of Reflect.ownKeys(value)) {
1047
+ if (typeof key === "symbol") throw new Error("PVOX hash projection objects cannot contain symbol properties.");
1048
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
1049
+ if (!descriptor || descriptor.get || descriptor.set || !descriptor.enumerable) throw new Error("PVOX hash projection objects must contain enumerable data properties only.");
1050
+ const childPointer = `${pointer}/${escapeJsonPointerSegment(key)}`;
1051
+ if (!omittedPointers.has(childPointer)) output[key] = cloneProjectedData(descriptor.value, childPointer, omittedPointers, seen, depth + 1, state);
1052
+ }
1053
+ return output;
1054
+ } finally {
1055
+ seen.delete(value);
1056
+ }
1057
+ }
1058
+
1059
+ export function projectPvoxNamedJsonClosureV1(name: PvoxNamedJsonClosureV1, value: unknown): unknown {
1060
+ const spec = PVOX_NAMED_JSON_CLOSURE_SPECS_V1[name];
1061
+ return cloneProjectedData(value, "", new Set<string>(spec.omittedJsonPointers), new WeakSet<object>());
1062
+ }
1063
+
1064
+ export function canonicalizePvoxNamedJsonClosureV1(name: PvoxNamedJsonClosureV1, value: unknown): string {
1065
+ return canonicalizeGpuContract(projectPvoxNamedJsonClosureV1(name, value));
1066
+ }
1067
+
1068
+ export function encodePvoxNamedJsonClosurePreimageV1(name: PvoxNamedJsonClosureV1, value: unknown): Uint8Array {
1069
+ const spec = PVOX_NAMED_JSON_CLOSURE_SPECS_V1[name];
1070
+ return concatenateBytes([
1071
+ TEXT_ENCODER.encode(spec.domain),
1072
+ encodePvoxCanonicalUtf8V1(canonicalizePvoxNamedJsonClosureV1(name, value)),
1073
+ ]);
1074
+ }