chat / client / test / vectors / generate.mjs
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
/**
 *   SUETA_TS=1 node test/vectors/generate.mjs
 *
 * Golden-vector extractor for everything chat owns above the vendored p2p lib.
 *
 * Run against the TypeScript implementation while it was still canon, per
 * dev/docs/CLJS.md: "splice the real modules into a harness rather than
 * transcribing their logic into a generator". harness/ts.mjs esbuild-bundles
 * `src/**.ts` once and hands the REAL classes over; hard-private members come in
 * through a mechanically injected `__t` bag, so not one line of logic is retyped.
 *
 *   node test/vectors/generate.mjs        # writes test/vectors/*.json
 *
 * The lib layer below `app/` is NOT re-extracted here: chat's
 * lib/{access,crypto,descriptor,encryption,protocol,turn}.ts are byte-identical
 * to rooms-kit's pre-CLJS ancestors and lib/{log,net,orbit-identity}.ts differ
 * only by the three sanctioned browser deltas, so those fixtures are reused
 * verbatim as `vectors/rk-*.json` and re-verified against THIS TypeScript by
 * `test/vectors.test.mjs` / `test/machines.test.mjs` in `SUETA_TS=1` mode.
 *
 * Not re-runnable after the TypeScript is retired (it needs esbuild, which the
 * CLJS install drops). Kept in the tree as the provenance record.
 */
import { writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { installDom, localStorageStub } from "../harness/dom.mjs";

installDom();

// the SAME normalised module map the tests use, so the generator and the
// checker drive identical call shapes
const { mods, priv, TS_MODE } = await import("../helpers/load.mjs");
if (!TS_MODE) throw new Error("run with SUETA_TS=1: fixtures come from the TypeScript, never from the port");
const M = await mods();
const { chatOpsScript, projectorScript, trustScript, roomsScript, migrateScript,
  storageScript, historyScript, mailboxScript, callScript } = await import("../helpers/app-fakes.mjs");

const OUT = dirname(fileURLToPath(import.meta.url));
const write = (name, data) => {
  writeFileSync(join(OUT, `${name}.json`), `${JSON.stringify(data, null, 2)}\n`);
  console.log(`wrote ${name}.json`);
};

/* ------------------------------------------------------------------ config -- */

write("config", {
  appSalt: M.APP_SALT,
  ns: M.NS,
  // Every persistent name the app ever writes. A change here orphans existing
  // users' identity seed, TOFU bindings, room list and block store.
  keys: ["id", "id-prev", "name", "notify", "trust", "rooms", "rooms-gone", "lang", "storage-migrated", "room"].map(
    (k) => [k, M.nsKey(k)],
  ),
  perRoomKeys: ["mbx-cursor", "mbx-queue", "mbx-sent", "mbx-retry", "mbx-ident", "migrated"].map((k) => [
    k,
    M.nsKey(`${k}:ROOMID`),
  ]),
  dbs: ["blocks", "data"].map((d) => [d, M.nsDb(d)]),
});

/* --------------------------------------------------------------- wire bytes -- */

write("logops", await chatOpsScript({ ...M, priv }));
write("call", await callScript({ ...M, priv }));

/* ---------------------------------------------------------------- projector -- */

write("projector", await projectorScript({ ...M, priv }));

/* ----------------------------------------------------------------- app state -- */

write("trust", await trustScript({ ...M, localStorageStub }));
write("rooms", await roomsScript({ ...M, localStorageStub }));
write("migrate", await migrateScript({ ...M, localStorageStub }));
write("storage", await storageScript({ ...M, localStorageStub }));
write("history", await historyScript({ ...M }));
write("mailbox", await mailboxScript({ ...M, priv, localStorageStub }));

/* ---------------------------------------------------------------------- i18n -- */

{
  const langs = ["en", "ro", "hu"];
  const out = { keys: {}, snapshot: {} };
  for (const lang of langs) {
    out.keys[lang] = M.catalogKeys(lang);
    // byte snapshot: the whole catalog, so a single changed character in a
    // Romanian or Hungarian string fails the suite (the transliteration gate)
    out.snapshot[lang] = Object.fromEntries(out.keys[lang].map((k) => [k, M.catalogRaw(lang, k)]));
  }
  // the plural sites, spelled out — Romanian's few/other split and Hungarian's
  // uninflected numerals must survive the port
  out.plurals = {};
  for (const k of out.keys.en) {
    const shapes = Object.fromEntries(langs.map((l) => [l, M.catalogMsg(l, k)]));
    if (langs.some((l) => Array.isArray(shapes[l]))) out.plurals[k] = shapes;
  }
  write("i18n", out);
}

console.log("done");
process.exit(0);

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/chat.git