@world-engines/scenario-review-snapshot 0.0.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/LICENSE +46 -0
- package/dist/artifact-digest.d.ts +15 -0
- package/dist/artifact-digest.js +831 -0
- package/dist/artifact.d.ts +225 -0
- package/dist/artifact.js +37 -0
- package/dist/bound-view-freeze.d.ts +43 -0
- package/dist/bound-view-freeze.js +114 -0
- package/dist/canonical.d.ts +10 -0
- package/dist/canonical.js +184 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +8 -0
- package/dist/neutral-artifact-assembler.d.ts +180 -0
- package/dist/neutral-artifact-assembler.js +1050 -0
- package/dist/publish-attempt.d.ts +118 -0
- package/dist/publish-attempt.js +33 -0
- package/dist/server-bound-view-compiler.d.ts +26 -0
- package/dist/server-bound-view-compiler.js +34 -0
- package/dist/snapshot.d.ts +5 -0
- package/dist/snapshot.js +701 -0
- package/dist/types.d.ts +112 -0
- package/dist/types.js +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
import { canonicalJson, compareUtf8, sha256Hex, toJsonValue } from "./canonical.js";
|
|
2
|
+
import { parseViewInteractionManifestV3, } from "@world-engines/tmw/view-interaction-manifest-v3";
|
|
3
|
+
import { parseMusicCandidateSourceV3, } from "@world-engines/tmw/music-candidate-set";
|
|
4
|
+
import { ARTIFACT_COMPILER_WITNESS_OBJECTS, ARTIFACT_COMPILER_WITNESS_PATHS, ARTIFACT_COMPILER_WITNESS_RESERVED_PREFIX, ARTIFACT_MANIFEST_VERSION_V5, ARTIFACT_PACKAGE_FORMAT_VERSION_V5, CAPTURED_REVISION_SCHEMA_VERSION, FROZEN_PUBLISH_INPUT_SCHEMA_VERSION, FROZEN_ARTIFACT_OBJECT_COUNT, FROZEN_DATABASE_DELIVERY_CODEC, FROZEN_DATABASE_DELIVERY_MEDIA_TYPE, FROZEN_DATABASE_DELIVERY_MAGIC, FROZEN_DATABASE_DELIVERY_RELATIVE_PATH, LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5, PUBLISHED_ARTIFACT_HEAD_SCHEMA_VERSION, } from "./artifact.js";
|
|
5
|
+
const encoder = new TextEncoder();
|
|
6
|
+
const strictDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
7
|
+
const SHA256_PATTERN = /^[0-9a-f]{64}$/u;
|
|
8
|
+
export function canonicalBytes(value) {
|
|
9
|
+
return encoder.encode(canonicalJson(value));
|
|
10
|
+
}
|
|
11
|
+
export function canonicalSha256(value) {
|
|
12
|
+
return sha256Hex(canonicalBytes(value));
|
|
13
|
+
}
|
|
14
|
+
export function buildArtifactV5(input) {
|
|
15
|
+
const normalized = normalizeFrozenPublishInput(input);
|
|
16
|
+
const metadata = jsonIdentity(normalized.metadata);
|
|
17
|
+
const protagonist = jsonIdentity(normalized.protagonist);
|
|
18
|
+
const promptsValue = toJsonValue(normalized.system_prompts);
|
|
19
|
+
const system_prompts = jsonIdentity(promptsValue);
|
|
20
|
+
const database = binaryIdentity(normalized.database_bytes);
|
|
21
|
+
const database_delivery = databaseDeliveryIdentity(normalized.database_delivery.bytes);
|
|
22
|
+
const trigger_document = jsonIdentity(normalized.trigger_document);
|
|
23
|
+
const compiled_plan = jsonIdentity(normalized.compiled_plan);
|
|
24
|
+
const source = binaryIdentity(normalized.view_source_bytes);
|
|
25
|
+
const publish_mobile = binaryIdentity(normalized.view_publish_mobile_bytes);
|
|
26
|
+
const publish_pc = binaryIdentity(normalized.view_publish_pc_bytes);
|
|
27
|
+
const viewInteractionMobileValue = toJsonValue(normalized.view_interaction_mobile);
|
|
28
|
+
const viewInteractionPcValue = toJsonValue(normalized.view_interaction_pc);
|
|
29
|
+
const musicCandidateSourceValue = toJsonValue(normalized.music_candidate_source);
|
|
30
|
+
const view_interaction = {
|
|
31
|
+
mobile: jsonIdentity(viewInteractionMobileValue),
|
|
32
|
+
pc: jsonIdentity(viewInteractionPcValue),
|
|
33
|
+
};
|
|
34
|
+
const music_candidate_source = jsonIdentity(musicCandidateSourceValue);
|
|
35
|
+
const retrieval = jsonIdentity(normalized.retrieval);
|
|
36
|
+
const resourceEntries = normalized.resources.map((resource) => ({
|
|
37
|
+
path: resource.path,
|
|
38
|
+
media_type: resource.media_type,
|
|
39
|
+
...binaryIdentity(resource.bytes),
|
|
40
|
+
}));
|
|
41
|
+
const resourceManifestValue = toJsonValue(resourceEntries);
|
|
42
|
+
const resourceManifest = jsonIdentity(resourceManifestValue);
|
|
43
|
+
const runtimeValue = toJsonValue(normalized.runtime_identity);
|
|
44
|
+
const runtimeCanonical = jsonIdentity(runtimeValue);
|
|
45
|
+
const archive = binaryIdentity(normalized.archive_bytes);
|
|
46
|
+
const frozenInputCore = toJsonValue({
|
|
47
|
+
input_schema_version: normalized.input_schema_version,
|
|
48
|
+
captured_revision: normalized.captured_revision,
|
|
49
|
+
runtime_contract_hash: normalized.runtime_contract_hash,
|
|
50
|
+
metadata,
|
|
51
|
+
protagonist,
|
|
52
|
+
system_prompts,
|
|
53
|
+
database,
|
|
54
|
+
database_delivery,
|
|
55
|
+
trigger_document,
|
|
56
|
+
compiled_plan,
|
|
57
|
+
views: { source, publish_mobile, publish_pc },
|
|
58
|
+
view_interaction,
|
|
59
|
+
music_candidate_source,
|
|
60
|
+
retrieval,
|
|
61
|
+
resources: { manifest: resourceManifest, entries: resourceEntries },
|
|
62
|
+
runtime: { identity: normalized.runtime_identity, canonical: runtimeCanonical },
|
|
63
|
+
archive,
|
|
64
|
+
});
|
|
65
|
+
const core = {
|
|
66
|
+
manifest_version: ARTIFACT_MANIFEST_VERSION_V5,
|
|
67
|
+
package_format_version: ARTIFACT_PACKAGE_FORMAT_VERSION_V5,
|
|
68
|
+
scenario_id: normalized.captured_revision.scenario_id,
|
|
69
|
+
epoch: normalized.captured_revision.epoch,
|
|
70
|
+
runtime_contract_hash: normalized.runtime_contract_hash,
|
|
71
|
+
captured_revision: normalized.captured_revision,
|
|
72
|
+
frozen_input_sha256: canonicalSha256(frozenInputCore),
|
|
73
|
+
metadata,
|
|
74
|
+
protagonist,
|
|
75
|
+
system_prompts,
|
|
76
|
+
database,
|
|
77
|
+
database_delivery,
|
|
78
|
+
trigger_document,
|
|
79
|
+
compiled_plan,
|
|
80
|
+
views: { source, publish_mobile, publish_pc },
|
|
81
|
+
view_interaction,
|
|
82
|
+
music_candidate_source,
|
|
83
|
+
retrieval,
|
|
84
|
+
resources: { manifest: resourceManifest, entries: resourceEntries },
|
|
85
|
+
runtime: { identity: normalized.runtime_identity, canonical: runtimeCanonical },
|
|
86
|
+
archive,
|
|
87
|
+
};
|
|
88
|
+
return deepFreeze({ ...core, canonical_digest: digestArtifactV5Core(core) });
|
|
89
|
+
}
|
|
90
|
+
export function buildFrozenArtifactBundle(input) {
|
|
91
|
+
const normalized = normalizeFrozenPublishInput(input);
|
|
92
|
+
const artifact = buildArtifactV5(normalized);
|
|
93
|
+
const compilerWitnesses = requireCompilerWitnessResources(normalized.resources);
|
|
94
|
+
const objects = [
|
|
95
|
+
frozenObject("database", FROZEN_DATABASE_DELIVERY_RELATIVE_PATH, FROZEN_DATABASE_DELIVERY_MEDIA_TYPE, normalized.database_delivery.bytes),
|
|
96
|
+
frozenObject("trigger_document", "trigger-document-v3.json", "application/json", canonicalBytes(normalized.trigger_document)),
|
|
97
|
+
frozenObject("compiled_plan", "compiled-plan-v3.json", "application/json", canonicalBytes(normalized.compiled_plan)),
|
|
98
|
+
frozenObject("view_interaction_mobile", "view-interaction-mobile-v3.json", "application/json", canonicalBytes(toJsonValue(normalized.view_interaction_mobile))),
|
|
99
|
+
frozenObject("view_interaction_pc", "view-interaction-pc-v3.json", "application/json", canonicalBytes(toJsonValue(normalized.view_interaction_pc))),
|
|
100
|
+
frozenObject("music_candidate_source", "music-candidate-source-v3.json", "application/json", canonicalBytes(toJsonValue(normalized.music_candidate_source))),
|
|
101
|
+
frozenObject(ARTIFACT_COMPILER_WITNESS_OBJECTS.source_manifest.kind, ARTIFACT_COMPILER_WITNESS_OBJECTS.source_manifest.relative_path, ARTIFACT_COMPILER_WITNESS_OBJECTS.source_manifest.media_type, compilerWitnesses.source_manifest.bytes),
|
|
102
|
+
frozenObject(ARTIFACT_COMPILER_WITNESS_OBJECTS.target_profile.kind, ARTIFACT_COMPILER_WITNESS_OBJECTS.target_profile.relative_path, ARTIFACT_COMPILER_WITNESS_OBJECTS.target_profile.media_type, compilerWitnesses.target_profile.bytes),
|
|
103
|
+
frozenObject(ARTIFACT_COMPILER_WITNESS_OBJECTS.compiler_receipt.kind, ARTIFACT_COMPILER_WITNESS_OBJECTS.compiler_receipt.relative_path, ARTIFACT_COMPILER_WITNESS_OBJECTS.compiler_receipt.media_type, compilerWitnesses.compiler_receipt.bytes),
|
|
104
|
+
].sort((left, right) => compareUtf8(left.relative_path, right.relative_path));
|
|
105
|
+
assertStrictlySortedUnique(objects.map((object) => object.relative_path), "frozen_artifact_object_path_duplicate");
|
|
106
|
+
assertBundleMatchesArtifact(artifact, objects);
|
|
107
|
+
return parseFrozenArtifactBundle({ artifact, objects });
|
|
108
|
+
}
|
|
109
|
+
export function parseFrozenArtifactBundle(value) {
|
|
110
|
+
const root = plainObject(value, "frozen_artifact_bundle_object_required");
|
|
111
|
+
exactKeys(root, ["artifact", "objects"], "frozen_artifact_bundle");
|
|
112
|
+
const artifact = parseArtifactV5(root.artifact);
|
|
113
|
+
if (!Array.isArray(root.objects))
|
|
114
|
+
throw new Error("frozen_artifact_bundle_objects_invalid");
|
|
115
|
+
const objects = root.objects.map(parseFrozenArtifactObject);
|
|
116
|
+
assertStrictlySortedUnique(objects.map((object) => object.relative_path), "frozen_artifact_object_order_invalid");
|
|
117
|
+
assertBundleMatchesArtifact(artifact, objects);
|
|
118
|
+
assertCanonicalBundlePayloads(artifact, objects);
|
|
119
|
+
return Object.freeze({ artifact, objects: Object.freeze(objects) });
|
|
120
|
+
}
|
|
121
|
+
function canonicalArtifactV5CoreBytes(core) {
|
|
122
|
+
return canonicalBytes(toJsonValue(core));
|
|
123
|
+
}
|
|
124
|
+
export function canonicalArtifactV5Bytes(artifact) {
|
|
125
|
+
const verified = parseArtifactV5(artifact);
|
|
126
|
+
return canonicalBytes(toJsonValue(verified));
|
|
127
|
+
}
|
|
128
|
+
export function artifactV5ManifestSha256(artifact) {
|
|
129
|
+
return sha256Hex(canonicalArtifactV5Bytes(artifact));
|
|
130
|
+
}
|
|
131
|
+
function digestArtifactV5Core(core) {
|
|
132
|
+
return sha256Hex(canonicalArtifactV5CoreBytes(core));
|
|
133
|
+
}
|
|
134
|
+
export function parseArtifactV5(value) {
|
|
135
|
+
const root = plainObject(value, "artifact_v5_object_required");
|
|
136
|
+
const manifestVersion = root.manifest_version;
|
|
137
|
+
if (manifestVersion !== ARTIFACT_MANIFEST_VERSION_V5) {
|
|
138
|
+
throw new Error("artifact_v5_required");
|
|
139
|
+
}
|
|
140
|
+
if (root.package_format_version === LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5) {
|
|
141
|
+
throw new Error("artifact_v5_legacy_package_reader_required");
|
|
142
|
+
}
|
|
143
|
+
exactKeys(root, [
|
|
144
|
+
"manifest_version", "package_format_version", "scenario_id", "captured_revision",
|
|
145
|
+
"frozen_input_sha256", "metadata", "protagonist", "system_prompts", "database",
|
|
146
|
+
"database_delivery",
|
|
147
|
+
"trigger_document", "compiled_plan", "views", "view_interaction", "music_candidate_source",
|
|
148
|
+
"retrieval", "resources", "runtime", "archive",
|
|
149
|
+
"epoch", "runtime_contract_hash",
|
|
150
|
+
"canonical_digest",
|
|
151
|
+
], "artifact_v5");
|
|
152
|
+
const capturedRevision = parseCapturedRevision(root.captured_revision);
|
|
153
|
+
const resources = plainObject(root.resources, "artifact_v5_resources_object_required");
|
|
154
|
+
exactKeys(resources, ["manifest", "entries"], "artifact_v5.resources");
|
|
155
|
+
if (!Array.isArray(resources.entries))
|
|
156
|
+
throw new Error("artifact_v5_resources_entries_invalid");
|
|
157
|
+
const resourceEntries = resources.entries.map(parseFrozenResourceIdentity);
|
|
158
|
+
assertStrictlySortedUnique(resourceEntries.map((entry) => entry.path), "artifact_v5_resource_order_invalid");
|
|
159
|
+
const views = plainObject(root.views, "artifact_v5_views_object_required");
|
|
160
|
+
exactKeys(views, ["source", "publish_mobile", "publish_pc"], "artifact_v5.views");
|
|
161
|
+
const viewInteraction = plainObject(root.view_interaction, "artifact_v5_view_interaction_object_required");
|
|
162
|
+
exactKeys(viewInteraction, ["mobile", "pc"], "artifact_v5.view_interaction");
|
|
163
|
+
const runtime = plainObject(root.runtime, "artifact_v5_runtime_object_required");
|
|
164
|
+
exactKeys(runtime, ["identity", "canonical"], "artifact_v5.runtime");
|
|
165
|
+
const artifact = {
|
|
166
|
+
manifest_version: ARTIFACT_MANIFEST_VERSION_V5,
|
|
167
|
+
package_format_version: requireExactInteger(root.package_format_version, ARTIFACT_PACKAGE_FORMAT_VERSION_V5, "artifact_v5_package_format_version_invalid"),
|
|
168
|
+
scenario_id: requireIdentifier(root.scenario_id, "artifact_v5_scenario_id_invalid"),
|
|
169
|
+
epoch: requirePositiveInteger(root.epoch, "artifact_v5_epoch_invalid"),
|
|
170
|
+
runtime_contract_hash: requireSha256(root.runtime_contract_hash, "artifact_v5_runtime_contract_hash_invalid"),
|
|
171
|
+
captured_revision: capturedRevision,
|
|
172
|
+
frozen_input_sha256: requireSha256(root.frozen_input_sha256, "artifact_v5_frozen_input_hash_invalid"),
|
|
173
|
+
metadata: parseJsonIdentity(root.metadata),
|
|
174
|
+
protagonist: parseJsonIdentity(root.protagonist),
|
|
175
|
+
system_prompts: parseJsonIdentity(root.system_prompts),
|
|
176
|
+
database: parseBinaryIdentity(root.database),
|
|
177
|
+
database_delivery: parseDatabaseDeliveryIdentity(root.database_delivery),
|
|
178
|
+
trigger_document: parseJsonIdentity(root.trigger_document),
|
|
179
|
+
compiled_plan: parseJsonIdentity(root.compiled_plan),
|
|
180
|
+
views: {
|
|
181
|
+
source: parseBinaryIdentity(views.source),
|
|
182
|
+
publish_mobile: parseBinaryIdentity(views.publish_mobile),
|
|
183
|
+
publish_pc: parseBinaryIdentity(views.publish_pc),
|
|
184
|
+
},
|
|
185
|
+
view_interaction: {
|
|
186
|
+
mobile: parseJsonIdentity(viewInteraction.mobile),
|
|
187
|
+
pc: parseJsonIdentity(viewInteraction.pc),
|
|
188
|
+
},
|
|
189
|
+
music_candidate_source: parseJsonIdentity(root.music_candidate_source),
|
|
190
|
+
retrieval: parseJsonIdentity(root.retrieval),
|
|
191
|
+
resources: {
|
|
192
|
+
manifest: parseJsonIdentity(resources.manifest),
|
|
193
|
+
entries: resourceEntries,
|
|
194
|
+
},
|
|
195
|
+
runtime: {
|
|
196
|
+
identity: parseRuntimeIdentity(runtime.identity),
|
|
197
|
+
canonical: parseJsonIdentity(runtime.canonical),
|
|
198
|
+
},
|
|
199
|
+
archive: parseBinaryIdentity(root.archive),
|
|
200
|
+
canonical_digest: requireSha256(root.canonical_digest, "artifact_v5_canonical_digest_invalid"),
|
|
201
|
+
};
|
|
202
|
+
if (artifact.scenario_id !== artifact.captured_revision.scenario_id) {
|
|
203
|
+
throw new Error("artifact_v5_capture_scenario_mismatch");
|
|
204
|
+
}
|
|
205
|
+
if (artifact.epoch !== artifact.captured_revision.epoch)
|
|
206
|
+
throw new Error("artifact_v5_capture_epoch_mismatch");
|
|
207
|
+
if (artifact.runtime_contract_hash !== artifact.runtime.identity.runtime_contract_hash) {
|
|
208
|
+
throw new Error("runtime_contract_hash_mismatch");
|
|
209
|
+
}
|
|
210
|
+
if (digestFrozenInputIdentity(artifact) !== artifact.frozen_input_sha256) {
|
|
211
|
+
throw new Error("artifact_v5_frozen_input_hash_mismatch");
|
|
212
|
+
}
|
|
213
|
+
const { canonical_digest: _digest, ...core } = artifact;
|
|
214
|
+
if (digestArtifactV5Core(core) !== artifact.canonical_digest) {
|
|
215
|
+
throw new Error("artifact_v5_canonical_digest_mismatch");
|
|
216
|
+
}
|
|
217
|
+
if (canonicalSha256(toJsonValue(artifact.resources.entries)) !== artifact.resources.manifest.sha256) {
|
|
218
|
+
throw new Error("artifact_v5_resource_manifest_hash_mismatch");
|
|
219
|
+
}
|
|
220
|
+
if (canonicalBytes(toJsonValue(artifact.resources.entries)).byteLength !== artifact.resources.manifest.size_bytes) {
|
|
221
|
+
throw new Error("artifact_v5_resource_manifest_size_mismatch");
|
|
222
|
+
}
|
|
223
|
+
const runtimeBytes = canonicalBytes(toJsonValue(artifact.runtime.identity));
|
|
224
|
+
if (sha256Hex(runtimeBytes) !== artifact.runtime.canonical.sha256
|
|
225
|
+
|| runtimeBytes.byteLength !== artifact.runtime.canonical.size_bytes) {
|
|
226
|
+
throw new Error("artifact_v5_runtime_identity_mismatch");
|
|
227
|
+
}
|
|
228
|
+
return deepFreeze(artifact);
|
|
229
|
+
}
|
|
230
|
+
export function parseLegacyArtifactV5Package2(value) {
|
|
231
|
+
const root = plainObject(value, "artifact_v5_legacy_object_required");
|
|
232
|
+
if (root.manifest_version !== ARTIFACT_MANIFEST_VERSION_V5
|
|
233
|
+
|| root.package_format_version !== LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5) {
|
|
234
|
+
throw new Error("artifact_v5_legacy_package2_required");
|
|
235
|
+
}
|
|
236
|
+
exactKeys(root, [
|
|
237
|
+
"manifest_version", "package_format_version", "scenario_id", "captured_revision",
|
|
238
|
+
"frozen_input_sha256", "metadata", "protagonist", "system_prompts", "database",
|
|
239
|
+
"trigger_document", "compiled_plan", "views", "view_interaction", "music_candidate_source",
|
|
240
|
+
"retrieval", "resources", "runtime", "archive", "epoch", "runtime_contract_hash",
|
|
241
|
+
"canonical_digest",
|
|
242
|
+
], "artifact_v5_legacy_package2");
|
|
243
|
+
const scenarioId = requireIdentifier(root.scenario_id, "artifact_v5_legacy_scenario_id_invalid");
|
|
244
|
+
const canonicalDigest = requireSha256(root.canonical_digest, "artifact_v5_legacy_canonical_digest_invalid");
|
|
245
|
+
const rawManifest = toJsonValue(root);
|
|
246
|
+
const rawObject = rawManifest;
|
|
247
|
+
const { canonical_digest: _canonicalDigest, ...core } = rawObject;
|
|
248
|
+
if (canonicalSha256(toJsonValue(core)) !== canonicalDigest) {
|
|
249
|
+
throw new Error("artifact_v5_legacy_canonical_digest_mismatch");
|
|
250
|
+
}
|
|
251
|
+
return deepFreeze({
|
|
252
|
+
verification: "legacy_unverified",
|
|
253
|
+
manifest_version: ARTIFACT_MANIFEST_VERSION_V5,
|
|
254
|
+
package_format_version: LEGACY_ARTIFACT_PACKAGE_FORMAT_VERSION_V5,
|
|
255
|
+
scenario_id: scenarioId,
|
|
256
|
+
canonical_digest: canonicalDigest,
|
|
257
|
+
raw_manifest: rawManifest,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
export function buildPublishedArtifactHead(core) {
|
|
261
|
+
const normalized = parsePublishedArtifactHeadCore(core);
|
|
262
|
+
return Object.freeze({ ...normalized, head_digest: digestPublishedArtifactHeadCore(normalized) });
|
|
263
|
+
}
|
|
264
|
+
export function canonicalPublishedArtifactHeadCoreBytes(core) {
|
|
265
|
+
return canonicalBytes(toJsonValue(parsePublishedArtifactHeadCore(core)));
|
|
266
|
+
}
|
|
267
|
+
export function digestPublishedArtifactHeadCore(core) {
|
|
268
|
+
return sha256Hex(canonicalPublishedArtifactHeadCoreBytes(core));
|
|
269
|
+
}
|
|
270
|
+
export function parsePublishedArtifactHead(value) {
|
|
271
|
+
const root = plainObject(value, "published_artifact_head_object_required");
|
|
272
|
+
exactKeys(root, [
|
|
273
|
+
"head_schema_version", "scenario_id", "epoch", "runtime_contract_hash", "source_revision_digest", "head_revision", "artifact_canonical_digest",
|
|
274
|
+
"artifact_manifest_sha256", "review_ticket_id", "review_snapshot_hash",
|
|
275
|
+
"review_coverage_hash", "published_at", "previous_head_digest", "head_digest",
|
|
276
|
+
], "published_artifact_head");
|
|
277
|
+
const core = parsePublishedArtifactHeadCore(root);
|
|
278
|
+
const head_digest = requireSha256(root.head_digest, "published_artifact_head_digest_invalid");
|
|
279
|
+
if (digestPublishedArtifactHeadCore(core) !== head_digest) {
|
|
280
|
+
throw new Error("published_artifact_head_digest_mismatch");
|
|
281
|
+
}
|
|
282
|
+
return Object.freeze({ ...core, head_digest });
|
|
283
|
+
}
|
|
284
|
+
function requireCompilerWitnessResources(resources) {
|
|
285
|
+
for (const resource of resources) {
|
|
286
|
+
if (resource.path.startsWith(ARTIFACT_COMPILER_WITNESS_RESERVED_PREFIX)
|
|
287
|
+
&& !Object.values(ARTIFACT_COMPILER_WITNESS_PATHS).includes(resource.path))
|
|
288
|
+
throw new Error("frozen_publish_compiler_witness_reserved_path_invalid");
|
|
289
|
+
}
|
|
290
|
+
const requireOne = (descriptor) => {
|
|
291
|
+
const matches = resources.filter((resource) => resource.path === descriptor.resource_path);
|
|
292
|
+
if (matches.length !== 1) {
|
|
293
|
+
throw new Error(`frozen_publish_compiler_witness_count_invalid:${descriptor.kind}`);
|
|
294
|
+
}
|
|
295
|
+
const resource = matches[0];
|
|
296
|
+
if (resource === undefined) {
|
|
297
|
+
throw new Error(`frozen_publish_compiler_witness_missing:${descriptor.kind}`);
|
|
298
|
+
}
|
|
299
|
+
if (resource.media_type !== descriptor.media_type) {
|
|
300
|
+
throw new Error(`frozen_publish_compiler_witness_media_type_invalid:${descriptor.kind}`);
|
|
301
|
+
}
|
|
302
|
+
return resource;
|
|
303
|
+
};
|
|
304
|
+
return Object.freeze({
|
|
305
|
+
source_manifest: requireOne(ARTIFACT_COMPILER_WITNESS_OBJECTS.source_manifest),
|
|
306
|
+
target_profile: requireOne(ARTIFACT_COMPILER_WITNESS_OBJECTS.target_profile),
|
|
307
|
+
compiler_receipt: requireOne(ARTIFACT_COMPILER_WITNESS_OBJECTS.compiler_receipt),
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
function normalizeFrozenPublishInput(input) {
|
|
311
|
+
if (input.input_schema_version !== FROZEN_PUBLISH_INPUT_SCHEMA_VERSION) {
|
|
312
|
+
throw new Error("frozen_publish_input_schema_version_invalid");
|
|
313
|
+
}
|
|
314
|
+
const captured_revision = parseCapturedRevision(input.captured_revision);
|
|
315
|
+
const runtimeContractHash = requireSha256(input.runtime_contract_hash, "frozen_publish_runtime_contract_hash_invalid");
|
|
316
|
+
const prompts = input.system_prompts.map(normalizePrompt).sort((left, right) => left.order - right.order || compareUtf8(left.prompt_id, right.prompt_id));
|
|
317
|
+
assertUnique(prompts.map((prompt) => prompt.prompt_id), "frozen_publish_prompt_id_duplicate");
|
|
318
|
+
const resources = input.resources.map((resource) => ({
|
|
319
|
+
path: normalizeResourcePath(resource.path),
|
|
320
|
+
media_type: requireIdentifier(resource.media_type, "frozen_publish_resource_media_type_invalid"),
|
|
321
|
+
bytes: requireBytes(resource.bytes, "frozen_publish_resource_bytes_invalid"),
|
|
322
|
+
})).sort((left, right) => compareUtf8(left.path, right.path));
|
|
323
|
+
assertStrictlySortedUnique(resources.map((resource) => resource.path), "frozen_publish_resource_path_duplicate");
|
|
324
|
+
requireCompilerWitnessResources(resources);
|
|
325
|
+
const runtimeIdentity = parseRuntimeIdentity(input.runtime_identity);
|
|
326
|
+
if (runtimeIdentity.runtime_contract_hash !== runtimeContractHash) {
|
|
327
|
+
throw new Error("runtime_contract_hash_mismatch");
|
|
328
|
+
}
|
|
329
|
+
const viewPublishMobileBytes = requireBytes(input.view_publish_mobile_bytes, "frozen_publish_view_mobile_bytes_invalid");
|
|
330
|
+
const viewPublishPcBytes = requireBytes(input.view_publish_pc_bytes, "frozen_publish_view_pc_bytes_invalid");
|
|
331
|
+
const viewInteractionMobile = normalizeViewInteractionManifest(input.view_interaction_mobile, captured_revision.epoch, runtimeContractHash, sha256Hex(viewPublishMobileBytes), "mobile");
|
|
332
|
+
const viewInteractionPc = normalizeViewInteractionManifest(input.view_interaction_pc, captured_revision.epoch, runtimeContractHash, sha256Hex(viewPublishPcBytes), "pc");
|
|
333
|
+
const musicCandidateSource = parseMusicCandidateSourceV3(input.music_candidate_source);
|
|
334
|
+
if (musicCandidateSource === null)
|
|
335
|
+
throw new Error("music_candidate_source_v3_invalid");
|
|
336
|
+
if (input.database_delivery?.codec !== FROZEN_DATABASE_DELIVERY_CODEC) {
|
|
337
|
+
throw new Error("frozen_publish_database_delivery_codec_invalid");
|
|
338
|
+
}
|
|
339
|
+
const databaseDeliveryBytes = requireBytes(input.database_delivery.bytes, "frozen_publish_database_delivery_bytes_invalid");
|
|
340
|
+
if (databaseDeliveryBytes.byteLength === 0
|
|
341
|
+
|| databaseDeliveryBytes[0] !== FROZEN_DATABASE_DELIVERY_MAGIC) {
|
|
342
|
+
throw new Error("frozen_publish_database_delivery_bytes_invalid");
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
input_schema_version: FROZEN_PUBLISH_INPUT_SCHEMA_VERSION,
|
|
346
|
+
captured_revision,
|
|
347
|
+
metadata: toJsonValue(input.metadata),
|
|
348
|
+
protagonist: toJsonValue(input.protagonist),
|
|
349
|
+
system_prompts: prompts,
|
|
350
|
+
database_bytes: requireBytes(input.database_bytes, "frozen_publish_database_bytes_invalid"),
|
|
351
|
+
database_delivery: Object.freeze({
|
|
352
|
+
codec: FROZEN_DATABASE_DELIVERY_CODEC,
|
|
353
|
+
bytes: databaseDeliveryBytes,
|
|
354
|
+
}),
|
|
355
|
+
runtime_contract_hash: runtimeContractHash,
|
|
356
|
+
trigger_document: toJsonValue(input.trigger_document),
|
|
357
|
+
compiled_plan: toJsonValue(input.compiled_plan),
|
|
358
|
+
view_source_bytes: requireBytes(input.view_source_bytes, "frozen_publish_view_source_bytes_invalid"),
|
|
359
|
+
view_publish_mobile_bytes: viewPublishMobileBytes,
|
|
360
|
+
view_publish_pc_bytes: viewPublishPcBytes,
|
|
361
|
+
view_interaction_mobile: viewInteractionMobile,
|
|
362
|
+
view_interaction_pc: viewInteractionPc,
|
|
363
|
+
music_candidate_source: deepFreeze(musicCandidateSource),
|
|
364
|
+
retrieval: toJsonValue(input.retrieval),
|
|
365
|
+
resources,
|
|
366
|
+
runtime_identity: runtimeIdentity,
|
|
367
|
+
archive_bytes: requireBytes(input.archive_bytes, "frozen_publish_archive_bytes_invalid"),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function normalizeViewInteractionManifest(value, expectedEpoch, expectedRuntimeContractHash, expectedViewArtifactHash, target) {
|
|
371
|
+
if (value === null)
|
|
372
|
+
return null;
|
|
373
|
+
const parsed = parseViewInteractionManifestV3(value);
|
|
374
|
+
if (parsed === null)
|
|
375
|
+
throw new Error(`view_interaction_${target}_v3_invalid`);
|
|
376
|
+
if (parsed.epoch !== expectedEpoch)
|
|
377
|
+
throw new Error("stale_content_epoch");
|
|
378
|
+
if (parsed.runtime_contract_hash !== expectedRuntimeContractHash) {
|
|
379
|
+
throw new Error("runtime_contract_hash_mismatch");
|
|
380
|
+
}
|
|
381
|
+
if (parsed.artifact_digest !== expectedViewArtifactHash) {
|
|
382
|
+
throw new Error(`view_interaction_${target}_artifact_mismatch`);
|
|
383
|
+
}
|
|
384
|
+
return deepFreeze(parsed);
|
|
385
|
+
}
|
|
386
|
+
function normalizePrompt(value) {
|
|
387
|
+
return {
|
|
388
|
+
prompt_id: requireIdentifier(value.prompt_id, "frozen_publish_prompt_id_invalid"),
|
|
389
|
+
order: requireNonNegativeInteger(value.order, "frozen_publish_prompt_order_invalid"),
|
|
390
|
+
content: toJsonValue(value.content),
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
function parseCapturedRevision(value) {
|
|
394
|
+
const object = plainObject(value, "captured_revision_object_required");
|
|
395
|
+
exactKeys(object, [
|
|
396
|
+
"schema_version", "owner_id", "scenario_id", "epoch", "revision_number", "revision_id",
|
|
397
|
+
"source_sha256", "revision_digest",
|
|
398
|
+
], "captured_revision");
|
|
399
|
+
return Object.freeze({
|
|
400
|
+
schema_version: requireExactInteger(object.schema_version, CAPTURED_REVISION_SCHEMA_VERSION, "captured_revision_schema_version_invalid"),
|
|
401
|
+
owner_id: requireIdentifier(object.owner_id, "captured_revision_owner_id_invalid"),
|
|
402
|
+
scenario_id: requireIdentifier(object.scenario_id, "captured_revision_scenario_id_invalid"),
|
|
403
|
+
epoch: requirePositiveInteger(object.epoch, "captured_revision_epoch_invalid"),
|
|
404
|
+
revision_number: requireNonNegativeInteger(object.revision_number, "captured_revision_number_invalid"),
|
|
405
|
+
revision_id: requireIdentifier(object.revision_id, "captured_revision_id_invalid"),
|
|
406
|
+
source_sha256: requireSha256(object.source_sha256, "captured_revision_source_hash_invalid"),
|
|
407
|
+
revision_digest: requireSha256(object.revision_digest, "captured_revision_digest_invalid"),
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
function parseRuntimeIdentity(value) {
|
|
411
|
+
const object = plainObject(value, "frozen_runtime_identity_object_required");
|
|
412
|
+
exactKeys(object, [
|
|
413
|
+
"runtime_generation", "runtime_contract_hash", "runtime_major", "runtime_patch",
|
|
414
|
+
"runtime_release_id", "protocol_version", "sdk_schema_version",
|
|
415
|
+
], "frozen_runtime_identity");
|
|
416
|
+
return Object.freeze({
|
|
417
|
+
runtime_generation: requireExactInteger(object.runtime_generation, 3, "frozen_runtime_generation_invalid"),
|
|
418
|
+
runtime_contract_hash: requireSha256(object.runtime_contract_hash, "frozen_runtime_contract_hash_invalid"),
|
|
419
|
+
runtime_major: requirePositiveInteger(object.runtime_major, "frozen_runtime_major_invalid"),
|
|
420
|
+
runtime_patch: requireNonNegativeInteger(object.runtime_patch, "frozen_runtime_patch_invalid"),
|
|
421
|
+
runtime_release_id: requireIdentifier(object.runtime_release_id, "frozen_runtime_release_id_invalid"),
|
|
422
|
+
protocol_version: requirePositiveInteger(object.protocol_version, "frozen_runtime_protocol_version_invalid"),
|
|
423
|
+
sdk_schema_version: requirePositiveInteger(object.sdk_schema_version, "frozen_runtime_sdk_schema_version_invalid"),
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
function parseJsonIdentity(value) {
|
|
427
|
+
const object = plainObject(value, "artifact_v5_json_identity_invalid");
|
|
428
|
+
exactKeys(object, ["encoding", "size_bytes", "sha256"], "artifact_v5_json_identity");
|
|
429
|
+
if (object.encoding !== "canonical-json-v1")
|
|
430
|
+
throw new Error("artifact_v5_json_encoding_invalid");
|
|
431
|
+
return Object.freeze({
|
|
432
|
+
encoding: "canonical-json-v1",
|
|
433
|
+
size_bytes: requireNonNegativeInteger(object.size_bytes, "artifact_v5_json_size_invalid"),
|
|
434
|
+
sha256: requireSha256(object.sha256, "artifact_v5_json_hash_invalid"),
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
function parseBinaryIdentity(value) {
|
|
438
|
+
const object = plainObject(value, "artifact_v5_binary_identity_invalid");
|
|
439
|
+
exactKeys(object, ["encoding", "size_bytes", "sha256"], "artifact_v5_binary_identity");
|
|
440
|
+
if (object.encoding !== "bytes")
|
|
441
|
+
throw new Error("artifact_v5_binary_encoding_invalid");
|
|
442
|
+
return Object.freeze({
|
|
443
|
+
encoding: "bytes",
|
|
444
|
+
size_bytes: requireNonNegativeInteger(object.size_bytes, "artifact_v5_binary_size_invalid"),
|
|
445
|
+
sha256: requireSha256(object.sha256, "artifact_v5_binary_hash_invalid"),
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
function parseDatabaseDeliveryIdentity(value) {
|
|
449
|
+
const object = plainObject(value, "artifact_v5_database_delivery_identity_invalid");
|
|
450
|
+
exactKeys(object, ["codec", "media_type", "encoding", "size_bytes", "sha256"], "artifact_v5_database_delivery");
|
|
451
|
+
if (object.codec !== FROZEN_DATABASE_DELIVERY_CODEC) {
|
|
452
|
+
throw new Error("artifact_v5_database_delivery_codec_invalid");
|
|
453
|
+
}
|
|
454
|
+
if (object.media_type !== FROZEN_DATABASE_DELIVERY_MEDIA_TYPE) {
|
|
455
|
+
throw new Error("artifact_v5_database_delivery_media_type_invalid");
|
|
456
|
+
}
|
|
457
|
+
const binary = parseBinaryIdentity({
|
|
458
|
+
encoding: object.encoding,
|
|
459
|
+
size_bytes: object.size_bytes,
|
|
460
|
+
sha256: object.sha256,
|
|
461
|
+
});
|
|
462
|
+
if (binary.size_bytes === 0)
|
|
463
|
+
throw new Error("artifact_v5_database_delivery_empty");
|
|
464
|
+
return Object.freeze({
|
|
465
|
+
codec: FROZEN_DATABASE_DELIVERY_CODEC,
|
|
466
|
+
media_type: FROZEN_DATABASE_DELIVERY_MEDIA_TYPE,
|
|
467
|
+
...binary,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
function parseFrozenResourceIdentity(value) {
|
|
471
|
+
const object = plainObject(value, "artifact_v5_resource_identity_invalid");
|
|
472
|
+
exactKeys(object, ["path", "media_type", "encoding", "size_bytes", "sha256"], "artifact_v5_resource");
|
|
473
|
+
const binary = parseBinaryIdentity({
|
|
474
|
+
encoding: object.encoding,
|
|
475
|
+
size_bytes: object.size_bytes,
|
|
476
|
+
sha256: object.sha256,
|
|
477
|
+
});
|
|
478
|
+
return Object.freeze({
|
|
479
|
+
path: normalizeResourcePath(object.path),
|
|
480
|
+
media_type: requireIdentifier(object.media_type, "artifact_v5_resource_media_type_invalid"),
|
|
481
|
+
...binary,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
function parsePublishedArtifactHeadCore(value) {
|
|
485
|
+
const object = plainObject(value, "published_artifact_head_core_object_required");
|
|
486
|
+
const allowed = [
|
|
487
|
+
"head_schema_version", "scenario_id", "epoch", "runtime_contract_hash", "source_revision_digest",
|
|
488
|
+
"head_revision", "artifact_canonical_digest",
|
|
489
|
+
"artifact_manifest_sha256", "review_ticket_id", "review_snapshot_hash",
|
|
490
|
+
"review_coverage_hash", "published_at", "previous_head_digest",
|
|
491
|
+
];
|
|
492
|
+
const keys = Object.keys(object).filter((key) => key !== "head_digest");
|
|
493
|
+
if (keys.length !== allowed.length || keys.sort(compareUtf8).some((key, index) => key !== [...allowed].sort(compareUtf8)[index])) {
|
|
494
|
+
throw new Error("published_artifact_head_core_fields_mismatch");
|
|
495
|
+
}
|
|
496
|
+
const previous = object.previous_head_digest;
|
|
497
|
+
return Object.freeze({
|
|
498
|
+
head_schema_version: requireExactInteger(object.head_schema_version, PUBLISHED_ARTIFACT_HEAD_SCHEMA_VERSION, "published_artifact_head_schema_version_invalid"),
|
|
499
|
+
scenario_id: requireIdentifier(object.scenario_id, "published_artifact_head_scenario_id_invalid"),
|
|
500
|
+
epoch: requirePositiveInteger(object.epoch, "published_artifact_head_epoch_invalid"),
|
|
501
|
+
runtime_contract_hash: requireSha256(object.runtime_contract_hash, "published_artifact_head_runtime_contract_hash_invalid"),
|
|
502
|
+
source_revision_digest: requireSha256(object.source_revision_digest, "published_artifact_head_source_revision_digest_invalid"),
|
|
503
|
+
head_revision: requirePositiveInteger(object.head_revision, "published_artifact_head_revision_invalid"),
|
|
504
|
+
artifact_canonical_digest: requireSha256(object.artifact_canonical_digest, "published_artifact_head_artifact_digest_invalid"),
|
|
505
|
+
artifact_manifest_sha256: requireSha256(object.artifact_manifest_sha256, "published_artifact_head_manifest_hash_invalid"),
|
|
506
|
+
review_ticket_id: requireIdentifier(object.review_ticket_id, "published_artifact_head_ticket_id_invalid"),
|
|
507
|
+
review_snapshot_hash: requireSha256(object.review_snapshot_hash, "published_artifact_head_snapshot_hash_invalid"),
|
|
508
|
+
review_coverage_hash: requireSha256(object.review_coverage_hash, "published_artifact_head_coverage_hash_invalid"),
|
|
509
|
+
published_at: requirePositiveInteger(object.published_at, "published_artifact_head_time_invalid"),
|
|
510
|
+
previous_head_digest: previous === null
|
|
511
|
+
? null
|
|
512
|
+
: requireSha256(previous, "published_artifact_head_previous_digest_invalid"),
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
function jsonIdentity(value) {
|
|
516
|
+
const bytes = canonicalBytes(value);
|
|
517
|
+
return Object.freeze({
|
|
518
|
+
encoding: "canonical-json-v1",
|
|
519
|
+
size_bytes: bytes.byteLength,
|
|
520
|
+
sha256: sha256Hex(bytes),
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
function digestFrozenInputIdentity(artifact) {
|
|
524
|
+
return canonicalSha256(toJsonValue({
|
|
525
|
+
input_schema_version: FROZEN_PUBLISH_INPUT_SCHEMA_VERSION,
|
|
526
|
+
captured_revision: artifact.captured_revision,
|
|
527
|
+
runtime_contract_hash: artifact.runtime_contract_hash,
|
|
528
|
+
metadata: artifact.metadata,
|
|
529
|
+
protagonist: artifact.protagonist,
|
|
530
|
+
system_prompts: artifact.system_prompts,
|
|
531
|
+
database: artifact.database,
|
|
532
|
+
database_delivery: artifact.database_delivery,
|
|
533
|
+
trigger_document: artifact.trigger_document,
|
|
534
|
+
compiled_plan: artifact.compiled_plan,
|
|
535
|
+
views: artifact.views,
|
|
536
|
+
view_interaction: artifact.view_interaction,
|
|
537
|
+
music_candidate_source: artifact.music_candidate_source,
|
|
538
|
+
retrieval: artifact.retrieval,
|
|
539
|
+
resources: artifact.resources,
|
|
540
|
+
runtime: artifact.runtime,
|
|
541
|
+
archive: artifact.archive,
|
|
542
|
+
}));
|
|
543
|
+
}
|
|
544
|
+
function binaryIdentity(value) {
|
|
545
|
+
return Object.freeze({ encoding: "bytes", size_bytes: value.byteLength, sha256: sha256Hex(value) });
|
|
546
|
+
}
|
|
547
|
+
function databaseDeliveryIdentity(value) {
|
|
548
|
+
return Object.freeze({
|
|
549
|
+
codec: FROZEN_DATABASE_DELIVERY_CODEC,
|
|
550
|
+
media_type: FROZEN_DATABASE_DELIVERY_MEDIA_TYPE,
|
|
551
|
+
...binaryIdentity(value),
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
function frozenObject(kind, relativePath, mediaType, value) {
|
|
555
|
+
const bytes = requireBytes(value, "frozen_artifact_object_bytes_invalid");
|
|
556
|
+
return Object.freeze({
|
|
557
|
+
kind,
|
|
558
|
+
relative_path: normalizeArtifactRelativePath(relativePath),
|
|
559
|
+
media_type: requireIdentifier(mediaType, "frozen_artifact_object_media_type_invalid"),
|
|
560
|
+
...binaryIdentity(bytes),
|
|
561
|
+
bytes,
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
function parseFrozenArtifactObject(value) {
|
|
565
|
+
const object = plainObject(value, "frozen_artifact_object_invalid");
|
|
566
|
+
exactKeys(object, [
|
|
567
|
+
"kind",
|
|
568
|
+
"relative_path",
|
|
569
|
+
"media_type",
|
|
570
|
+
"encoding",
|
|
571
|
+
"size_bytes",
|
|
572
|
+
"sha256",
|
|
573
|
+
"bytes",
|
|
574
|
+
], "frozen_artifact_object");
|
|
575
|
+
if (!FROZEN_ARTIFACT_OBJECT_KINDS.has(String(object.kind))) {
|
|
576
|
+
throw new Error("frozen_artifact_object_kind_invalid");
|
|
577
|
+
}
|
|
578
|
+
if (object.encoding !== "bytes")
|
|
579
|
+
throw new Error("frozen_artifact_object_encoding_invalid");
|
|
580
|
+
const bytes = requireBytes(object.bytes, "frozen_artifact_object_bytes_invalid");
|
|
581
|
+
const identity = binaryIdentity(bytes);
|
|
582
|
+
if (object.size_bytes !== identity.size_bytes
|
|
583
|
+
|| object.sha256 !== identity.sha256)
|
|
584
|
+
throw new Error("frozen_artifact_object_readback_mismatch");
|
|
585
|
+
return Object.freeze({
|
|
586
|
+
kind: object.kind,
|
|
587
|
+
relative_path: validateArtifactObjectPath(object.kind, object.relative_path),
|
|
588
|
+
media_type: validateArtifactObjectMediaType(object.kind, object.media_type),
|
|
589
|
+
...identity,
|
|
590
|
+
bytes,
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
const FROZEN_ARTIFACT_OBJECT_KINDS = new Set([
|
|
594
|
+
"database",
|
|
595
|
+
"trigger_document",
|
|
596
|
+
"compiled_plan",
|
|
597
|
+
"view_interaction_mobile",
|
|
598
|
+
"view_interaction_pc",
|
|
599
|
+
"music_candidate_source",
|
|
600
|
+
"source_manifest",
|
|
601
|
+
"compile_target_profile",
|
|
602
|
+
"compiler_receipt",
|
|
603
|
+
]);
|
|
604
|
+
const FROZEN_SINGLETON_PATHS = {
|
|
605
|
+
database: FROZEN_DATABASE_DELIVERY_RELATIVE_PATH,
|
|
606
|
+
trigger_document: "trigger-document-v3.json",
|
|
607
|
+
compiled_plan: "compiled-plan-v3.json",
|
|
608
|
+
view_interaction_mobile: "view-interaction-mobile-v3.json",
|
|
609
|
+
view_interaction_pc: "view-interaction-pc-v3.json",
|
|
610
|
+
music_candidate_source: "music-candidate-source-v3.json",
|
|
611
|
+
source_manifest: ARTIFACT_COMPILER_WITNESS_OBJECTS.source_manifest.relative_path,
|
|
612
|
+
compile_target_profile: ARTIFACT_COMPILER_WITNESS_OBJECTS.target_profile.relative_path,
|
|
613
|
+
compiler_receipt: ARTIFACT_COMPILER_WITNESS_OBJECTS.compiler_receipt.relative_path,
|
|
614
|
+
};
|
|
615
|
+
const FROZEN_SINGLETON_MEDIA_TYPES = {
|
|
616
|
+
database: FROZEN_DATABASE_DELIVERY_MEDIA_TYPE,
|
|
617
|
+
trigger_document: "application/json",
|
|
618
|
+
compiled_plan: "application/json",
|
|
619
|
+
view_interaction_mobile: "application/json",
|
|
620
|
+
view_interaction_pc: "application/json",
|
|
621
|
+
music_candidate_source: "application/json",
|
|
622
|
+
source_manifest: ARTIFACT_COMPILER_WITNESS_OBJECTS.source_manifest.media_type,
|
|
623
|
+
compile_target_profile: ARTIFACT_COMPILER_WITNESS_OBJECTS.target_profile.media_type,
|
|
624
|
+
compiler_receipt: ARTIFACT_COMPILER_WITNESS_OBJECTS.compiler_receipt.media_type,
|
|
625
|
+
};
|
|
626
|
+
function assertBundleMatchesArtifact(artifact, objects) {
|
|
627
|
+
const byKind = new Map();
|
|
628
|
+
for (const object of objects) {
|
|
629
|
+
const list = byKind.get(object.kind) ?? [];
|
|
630
|
+
list.push(object);
|
|
631
|
+
byKind.set(object.kind, list);
|
|
632
|
+
}
|
|
633
|
+
const requireOne = (kind) => {
|
|
634
|
+
const selected = byKind.get(kind) ?? [];
|
|
635
|
+
if (selected.length !== 1)
|
|
636
|
+
throw new Error(`frozen_artifact_object_kind_count_invalid:${kind}`);
|
|
637
|
+
const object = selected[0];
|
|
638
|
+
if (object === undefined) {
|
|
639
|
+
throw new Error(`frozen_artifact_object_kind_missing:${kind}`);
|
|
640
|
+
}
|
|
641
|
+
return object;
|
|
642
|
+
};
|
|
643
|
+
const assertIdentity = (object, identity, field) => {
|
|
644
|
+
if (object.sha256 !== identity.sha256 || object.size_bytes !== identity.size_bytes) {
|
|
645
|
+
throw new Error(`frozen_artifact_object_identity_mismatch:${field}`);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
assertIdentity(requireOne("database"), artifact.database_delivery, "database_delivery");
|
|
649
|
+
assertIdentity(requireOne("trigger_document"), artifact.trigger_document, "trigger_document");
|
|
650
|
+
assertIdentity(requireOne("compiled_plan"), artifact.compiled_plan, "compiled_plan");
|
|
651
|
+
assertIdentity(requireOne("view_interaction_mobile"), artifact.view_interaction.mobile, "view_interaction_mobile");
|
|
652
|
+
assertIdentity(requireOne("view_interaction_pc"), artifact.view_interaction.pc, "view_interaction_pc");
|
|
653
|
+
assertIdentity(requireOne("music_candidate_source"), artifact.music_candidate_source, "music_candidate_source");
|
|
654
|
+
const resourcesByPath = new Map(artifact.resources.entries.map((resource) => [resource.path, resource]));
|
|
655
|
+
const assertWitness = (descriptor) => {
|
|
656
|
+
const object = requireOne(descriptor.kind);
|
|
657
|
+
const resource = resourcesByPath.get(descriptor.resource_path);
|
|
658
|
+
if (resource === undefined
|
|
659
|
+
|| object.relative_path !== descriptor.relative_path
|
|
660
|
+
|| object.media_type !== descriptor.media_type
|
|
661
|
+
|| resource.media_type !== descriptor.media_type
|
|
662
|
+
|| object.sha256 !== resource.sha256
|
|
663
|
+
|| object.size_bytes !== resource.size_bytes) {
|
|
664
|
+
throw new Error(`frozen_artifact_compiler_witness_binding_mismatch:${descriptor.kind}`);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
assertWitness(ARTIFACT_COMPILER_WITNESS_OBJECTS.source_manifest);
|
|
668
|
+
assertWitness(ARTIFACT_COMPILER_WITNESS_OBJECTS.target_profile);
|
|
669
|
+
assertWitness(ARTIFACT_COMPILER_WITNESS_OBJECTS.compiler_receipt);
|
|
670
|
+
if (FROZEN_ARTIFACT_OBJECT_KINDS.size !== FROZEN_ARTIFACT_OBJECT_COUNT
|
|
671
|
+
|| objects.length !== FROZEN_ARTIFACT_OBJECT_COUNT) {
|
|
672
|
+
throw new Error("frozen_artifact_object_count_invalid");
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
function assertCanonicalBundlePayloads(artifact, objects) {
|
|
676
|
+
const one = (kind) => {
|
|
677
|
+
const selected = objects.filter((object) => object.kind === kind);
|
|
678
|
+
if (selected.length !== 1)
|
|
679
|
+
throw new Error(`frozen_artifact_object_kind_count_invalid:${kind}`);
|
|
680
|
+
const object = selected[0];
|
|
681
|
+
if (object === undefined) {
|
|
682
|
+
throw new Error(`frozen_artifact_object_kind_missing:${kind}`);
|
|
683
|
+
}
|
|
684
|
+
return object;
|
|
685
|
+
};
|
|
686
|
+
assertCanonicalJsonObject(one("trigger_document"));
|
|
687
|
+
assertCanonicalJsonObject(one("compiled_plan"));
|
|
688
|
+
const viewInteractionMobile = assertCanonicalJsonObject(one("view_interaction_mobile"));
|
|
689
|
+
const viewInteractionPc = assertCanonicalJsonObject(one("view_interaction_pc"));
|
|
690
|
+
const musicCandidateSource = assertCanonicalJsonObject(one("music_candidate_source"));
|
|
691
|
+
assertCanonicalJsonObject(one("compile_target_profile"));
|
|
692
|
+
assertCanonicalJsonObject(one("compiler_receipt"));
|
|
693
|
+
normalizeViewInteractionManifest(viewInteractionMobile, artifact.epoch, artifact.runtime_contract_hash, artifact.views.publish_mobile.sha256, "mobile");
|
|
694
|
+
normalizeViewInteractionManifest(viewInteractionPc, artifact.epoch, artifact.runtime_contract_hash, artifact.views.publish_pc.sha256, "pc");
|
|
695
|
+
if (parseMusicCandidateSourceV3(musicCandidateSource) === null) {
|
|
696
|
+
throw new Error("music_candidate_source_v3_invalid");
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
function assertCanonicalJsonObject(object) {
|
|
700
|
+
let text;
|
|
701
|
+
try {
|
|
702
|
+
text = strictDecoder.decode(object.bytes);
|
|
703
|
+
}
|
|
704
|
+
catch {
|
|
705
|
+
throw new Error(`frozen_artifact_canonical_json_utf8_invalid:${object.kind}`);
|
|
706
|
+
}
|
|
707
|
+
let decoded;
|
|
708
|
+
try {
|
|
709
|
+
decoded = JSON.parse(text);
|
|
710
|
+
}
|
|
711
|
+
catch {
|
|
712
|
+
throw new Error(`frozen_artifact_canonical_json_parse_invalid:${object.kind}`);
|
|
713
|
+
}
|
|
714
|
+
const normalized = toJsonValue(decoded);
|
|
715
|
+
if (!bytesEqual(object.bytes, canonicalBytes(normalized))) {
|
|
716
|
+
throw new Error(`frozen_artifact_canonical_json_bytes_mismatch:${object.kind}`);
|
|
717
|
+
}
|
|
718
|
+
return normalized;
|
|
719
|
+
}
|
|
720
|
+
function bytesEqual(left, right) {
|
|
721
|
+
if (left.byteLength !== right.byteLength)
|
|
722
|
+
return false;
|
|
723
|
+
let difference = 0;
|
|
724
|
+
for (let index = 0; index < left.byteLength; index += 1) {
|
|
725
|
+
difference |= (left[index] ?? 0) ^ (right[index] ?? 0);
|
|
726
|
+
}
|
|
727
|
+
return difference === 0;
|
|
728
|
+
}
|
|
729
|
+
function requireBytes(value, code) {
|
|
730
|
+
if (!(value instanceof Uint8Array))
|
|
731
|
+
throw new Error(code);
|
|
732
|
+
return value.slice();
|
|
733
|
+
}
|
|
734
|
+
function normalizeResourcePath(value) {
|
|
735
|
+
const path = requireIdentifier(value, "frozen_publish_resource_path_invalid").normalize("NFC");
|
|
736
|
+
if (path.startsWith("/")
|
|
737
|
+
|| path.endsWith("/")
|
|
738
|
+
|| path.includes("\\")
|
|
739
|
+
|| path.split("/").some((part) => part.length === 0 || part === "." || part === "..")) {
|
|
740
|
+
throw new Error("frozen_publish_resource_path_invalid");
|
|
741
|
+
}
|
|
742
|
+
return path;
|
|
743
|
+
}
|
|
744
|
+
function normalizeArtifactRelativePath(value) {
|
|
745
|
+
const path = requireIdentifier(value, "frozen_artifact_object_path_invalid");
|
|
746
|
+
if (path.startsWith("/")
|
|
747
|
+
|| path.endsWith("/")
|
|
748
|
+
|| path.includes("\\")
|
|
749
|
+
|| path.split("/").some((part) => part.length === 0 || part === "." || part === ".."))
|
|
750
|
+
throw new Error("frozen_artifact_object_path_invalid");
|
|
751
|
+
return path;
|
|
752
|
+
}
|
|
753
|
+
function validateArtifactObjectPath(kind, value) {
|
|
754
|
+
const path = normalizeArtifactRelativePath(value);
|
|
755
|
+
if (path !== FROZEN_SINGLETON_PATHS[kind]) {
|
|
756
|
+
throw new Error(`frozen_artifact_singleton_path_mismatch:${kind}`);
|
|
757
|
+
}
|
|
758
|
+
return path;
|
|
759
|
+
}
|
|
760
|
+
function validateArtifactObjectMediaType(kind, value) {
|
|
761
|
+
const mediaType = requireIdentifier(value, "frozen_artifact_object_media_type_invalid");
|
|
762
|
+
if (mediaType !== FROZEN_SINGLETON_MEDIA_TYPES[kind]) {
|
|
763
|
+
throw new Error(`frozen_artifact_singleton_media_type_mismatch:${kind}`);
|
|
764
|
+
}
|
|
765
|
+
return mediaType;
|
|
766
|
+
}
|
|
767
|
+
function requireIdentifier(value, code) {
|
|
768
|
+
if (typeof value !== "string")
|
|
769
|
+
throw new Error(code);
|
|
770
|
+
const normalized = value.trim().normalize("NFC");
|
|
771
|
+
if (normalized.length === 0 || normalized !== value)
|
|
772
|
+
throw new Error(code);
|
|
773
|
+
return normalized;
|
|
774
|
+
}
|
|
775
|
+
function requireSha256(value, code) {
|
|
776
|
+
if (typeof value !== "string" || !SHA256_PATTERN.test(value))
|
|
777
|
+
throw new Error(code);
|
|
778
|
+
return value;
|
|
779
|
+
}
|
|
780
|
+
function requireNonNegativeInteger(value, code) {
|
|
781
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
|
|
782
|
+
throw new Error(code);
|
|
783
|
+
return value;
|
|
784
|
+
}
|
|
785
|
+
function requirePositiveInteger(value, code) {
|
|
786
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0)
|
|
787
|
+
throw new Error(code);
|
|
788
|
+
return value;
|
|
789
|
+
}
|
|
790
|
+
function requireExactInteger(value, expected, code) {
|
|
791
|
+
if (value !== expected)
|
|
792
|
+
throw new Error(code);
|
|
793
|
+
return expected;
|
|
794
|
+
}
|
|
795
|
+
function plainObject(value, code) {
|
|
796
|
+
if (typeof value !== "object"
|
|
797
|
+
|| value === null
|
|
798
|
+
|| Array.isArray(value)
|
|
799
|
+
|| Object.getPrototypeOf(value) !== Object.prototype)
|
|
800
|
+
throw new Error(code);
|
|
801
|
+
return value;
|
|
802
|
+
}
|
|
803
|
+
function exactKeys(value, expected, field) {
|
|
804
|
+
const actual = Object.keys(value).sort(compareUtf8);
|
|
805
|
+
const canonical = [...expected].sort(compareUtf8);
|
|
806
|
+
if (actual.length !== canonical.length || actual.some((key, index) => key !== canonical[index])) {
|
|
807
|
+
throw new Error(`${field}_fields_mismatch`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function assertUnique(values, code) {
|
|
811
|
+
if (new Set(values).size !== values.length)
|
|
812
|
+
throw new Error(code);
|
|
813
|
+
}
|
|
814
|
+
function assertStrictlySortedUnique(values, code) {
|
|
815
|
+
for (let index = 1; index < values.length; index += 1) {
|
|
816
|
+
const previous = values[index - 1];
|
|
817
|
+
const current = values[index];
|
|
818
|
+
if (previous === undefined
|
|
819
|
+
|| current === undefined
|
|
820
|
+
|| compareUtf8(previous, current) >= 0)
|
|
821
|
+
throw new Error(code);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
function deepFreeze(value) {
|
|
825
|
+
if (typeof value !== "object" || value === null || Object.isFrozen(value))
|
|
826
|
+
return value;
|
|
827
|
+
Object.freeze(value);
|
|
828
|
+
for (const nested of Object.values(value))
|
|
829
|
+
deepFreeze(nested);
|
|
830
|
+
return value;
|
|
831
|
+
}
|