CultLib/scripts/write-cultmesh-content-vectors.mjs
Meta Vens 3de72c6333 Content vectors: C#-written acceptance cases, trap inputs for keys, padded pack vector
CultMeshContentVectorTests writes content-acceptance-vectors.json (what the reference accepts, refuses, rewrites and
keys over nil, arity, trailing, integer/float, string-family and invalid-UTF-8 shapes), extends the record-key
vectors with the prefix-fold, fallback-hash traps, and the TS writer adds a whitespace-version, padded kind and
MIME pack vector. GameCult.Mesh exposes internals to its test project so the judge can call ValidateManifest.
The generated files follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 15:40:55 +02:00

63 lines
3.4 KiB
JavaScript

// The TypeScript -> C# direction of the CultMesh content parity check.
//
// `contracts/cultmesh/content-vectors.cs-written.json` and `.rs-written.json` are written by the reference and by Rust
// and decoded by TypeScript. This script writes the mirror file, `contracts/cultmesh/content-vectors.ts-written.json`:
// `packages/cultnet-ts/src/content.ts` packs each declared input and encodes the manifest, and the C# test
// `CultMeshContentVectorTests.TypeScriptManifestVectorsMatchTheReference` judges the committed file with the reference
// only (it decodes the bytes, packs the same input itself and compares, and derives the record key).
//
// Deliberately not the C# file's inputs: a multi-byte artifact id, no version, integer-like metadata keys that a JS object
// would reorder, a different body and chunk size. Agreeing on a copy of the other side's inputs would prove less.
//
// npm run build --workspace packages/cultnet-ts
// node scripts/write-cultmesh-content-vectors.mjs
//
// Nothing here is randomised: rerunning on an unchanged codec rewrites the same bytes.
import { existsSync, writeFileSync } from "node:fs";
import { createRequire } from "node:module";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const modulePath = join(repoRoot, "packages", "cultnet-ts", "dist", "content.js");
const outputPath = join(repoRoot, "contracts", "cultmesh", "content-vectors.ts-written.json");
if (!existsSync(modulePath)) {
throw new Error(`${modulePath} is missing; run 'npm run build --workspace packages/cultnet-ts' first.`);
}
const { packContent, encodeManifest, artifactRecordKey } = createRequire(import.meta.url)(modulePath);
const common = {
artifactId: "Pixel Heroes/H\u00fct \u2603",
kind: "asset",
version: "",
mimeType: "application/x-cultnet-ts-vector",
createdAtUtc: "2026-10-19T00:00:00.0000000+00:00",
chunkSizeBytes: 131072,
tags: ["a", "b"],
metadata: [["10", "x"], ["alpha", "y"], ["2", "z"]],
};
const inputs = [
["ts multi-chunk body", { ...common, body: { length: 777777, multiplier: 7, modulus: 256 } }],
["ts empty body", { ...common, body: { length: 0, multiplier: 7, modulus: 256 } }],
["ts single chunk, versioned", { ...common, version: "V1 Beta", chunkSizeBytes: 4194304, tags: [], metadata: [], body: { length: 1000, multiplier: 7, modulus: 256 } }],
// Padding the reference keeps: a whitespace version stays as written (the key reads it as unversioned), kind and MIME are
// only replaced when blank.
["ts whitespace version, padded kind and mime", { ...common, kind: " build ", version: " ", mimeType: " text/plain; charset=utf-8 ", chunkSizeBytes: 4194304, tags: [], metadata: [], body: { length: 1000, multiplier: 7, modulus: 256 } }],
];
const vectors = [];
for (const [label, input] of inputs) {
const { body, ...options } = input;
const bytes = Uint8Array.from({ length: body.length }, (_, index) => (index * body.multiplier) % body.modulus);
const { manifest } = await packContent(input.artifactId, bytes, options);
vectors.push({
label,
kind: "manifest",
input,
recordKey: await artifactRecordKey(manifest.artifactId, manifest.version, manifest.contentHash),
messagePackBase64: Buffer.from(encodeManifest(manifest)).toString("base64"),
});
}
writeFileSync(outputPath, `${JSON.stringify({ vectors }, null, 2)}\n`);
console.log(`wrote ${vectors.length} vectors to ${outputPath}`);