board / client / test / helpers / load.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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
 * Module loader for the black-box tests, in the shape rooms-kit established and
 * chat repeated.
 *
 * Default: the ClojureScript build (`test-dist/testlib.js`, produced by
 * `shadow-cljs release testlib`) — the tests run against compiled output, never
 * against src/.
 *
 * With `BOARD_TS=1` it instead loads the TypeScript implementation through
 * harness/ts.mjs. That mode is the suite's own regression harness: a test that
 * passes on the CLJS build but was never checked against the TS one cannot tell
 * "the port is right" from "the assertion is wrong". Every assertion in
 * test/*.test.mjs was run green in BOTH modes before the TypeScript was retired,
 * and the mode stays wired so the record is reproducible from git history.
 *
 * Both modes are normalised to one flat module map, so no test knows which
 * implementation it is driving.
 */
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";

const HERE = dirname(fileURLToPath(import.meta.url));
export const CLIENT = join(HERE, "..", "..");
export const TS_MODE = process.env.BOARD_TS === "1";

/**
 * Exposed names that are FIELDS, not methods: `priv(o, name)` always returns
 * something callable, so a field read is wrapped in a thunk on both sides.
 */
const FIELDS = new Set(["pending", "tombs", "editSeen", "queue", "pts", "prs", "penId", "certOk", "secrets"]);

/** CLJS keeps "private" members as `_name` string keys (peer-kit's idiom). */
export const priv = TS_MODE
  ? (o, n) => o.__t[n]
  : (o, n) => (FIELDS.has(n) ? () => o[`_${n}`] : o[`_${n}`].bind(o));

let cached = null;

export async function mods() {
  if (cached) return cached;
  if (TS_MODE) {
    const { loadTs } = await import("../harness/ts.mjs");
    const m = await loadTs();
    cached = {
      // config
      APP_SALT: m.APP_SALT,
      NS: m.NS,
      nsKey: m.nsKey,
      nsDb: m.nsDb,
      // lib/crypto
      RoomCrypto: m.RoomCrypto,
      Sealer: m.Sealer,
      AtRestCipher: m.AtRestCipher,
      toB64: m.toB64,
      fromB64: m.fromB64,
      toB64url: m.toB64url,
      fromB64url: m.fromB64url,
      randomBytes: m.randomBytes,
      newPeerId: m.newPeerId,
      newRoomSecret: m.newRoomSecret,
      // lib/encryption, lib/access, lib/orbit-identity
      makeLogEncryption: m.encryption.makeLogEncryption,
      signerMatchesIdentity: m.access.signerMatchesIdentity,
      HardenedIPFSAccessController: m.access.HardenedIPFSAccessController,
      SuetaIdentityProvider: m.orbitIdentity.SuetaIdentityProvider,
      registerSuetaProvider: m.orbitIdentity.registerSuetaProvider,
      // lib/descriptor, lib/turn
      resolveRelayConfig: m.descriptor.resolveRelayConfig,
      IceConfig: m.turn.IceConfig,
      // lib/log, lib/net
      openHelia: m.log.openHelia,
      RoomLog: m.log.RoomLog,
      Net: m.net.Net,
      newSessionKey: m.net.newSessionKey,
      // lib/mailbox
      MailboxAuth: m.mailbox.MailboxAuth,
      MailboxClient: m.mailbox.MailboxClient,
      MailboxSync: m.mailbox.MailboxSync,
      // lib/selftest
      runSelfTest: m.selftest.runSelfTest,
      // lib/epoch
      EpochKeyring: m.epoch.EpochKeyring,
      encodeGrant: m.epoch.encodeGrant,
      decodeGrant: m.epoch.decodeGrant,
      // lib/policy
      AccessFold: m.policy.AccessFold,
      isControlOp: m.policy.isControlOp,
      // lib/wrapbox
      deriveXKeyPair: m.wrapbox.deriveXKeyPair,
      signXCert: m.wrapbox.signXCert,
      verifyXCert: m.wrapbox.verifyXCert,
      wrapTo: m.wrapbox.wrapTo,
      unwrap: m.wrapbox.unwrap,
      // app/ops
      newElementId: m.ops.newElementId,
      clampTs: m.ops.clampTs,
      encodePts: m.ops.encodePts,
      decodePts: m.ops.decodePts,
      validId: m.ops.validId,
      sanitizeElement: m.ops.sanitizeElement,
      sanitizePatch: m.ops.sanitizePatch,
      elementBBox: m.ops.elementBBox,
      bboxUnion: m.ops.bboxUnion,
      bboxIntersects: m.ops.bboxIntersects,
      distToSegment: m.ops.distToSegment,
      MAX_PTS: m.ops.MAX_PTS,
      // app/state
      BoardStore: m.state.BoardStore,
      VAULT_CLOCK_OFFSET: m.state.VAULT_CLOCK_OFFSET,
      // app/trust
      trustObserve: m.trust.observe,
      trustSetVerified: m.trust.setVerified,
      trustIsVerified: m.trust.isVerified,
      // app/boards
      loadRooms: m.boards.loadRooms,
      touchRoom: m.boards.touchRoom,
      renameRoom: m.boards.renameRoom,
      forgetRoom: m.boards.forgetRoom,
      buildRoomSync: m.boards.buildRoomSync,
      mergeRooms: m.boards.mergeRooms,
      setOnRoomsChanged: m.boards.setOnRoomsChanged,
      // app/tools, app/canvas
      ToolController: m.tools.ToolController,
      Camera: m.canvas.Camera,
      themeFor: m.canvas.themeFor,
      resolveColor: m.canvas.resolveColor,
      layoutText: m.canvas.layoutText,
      // i18n
      catalogKeys: (lang) => Object.keys({ en: m.en, ro: m.ro, hu: m.hu }[lang]).sort(),
      // the FULL message under a key: a string, or the plural-variant object.
      // catalogMsg below reports only the variant NAMES, which would leave the
      // Romanian and Hungarian plural TEXT unsnapshotted.
      catalogRaw: (lang, k) => {
        const msg = { en: m.en, ro: m.ro, hu: m.hu }[lang][k];
        return typeof msg === "string" ? msg : msg === undefined ? undefined : { ...msg };
      },
      catalogMsg: (lang, k) => {
        const msg = { en: m.en, ro: m.ro, hu: m.hu }[lang][k];
        return typeof msg === "string" ? msg : msg === undefined ? undefined : Object.keys(msg).sort();
      },
      t: m.i18n.t,
      tn: m.i18n.tn,
    };
    return cached;
  }
  cached = await import(pathToFileURL(join(CLIENT, "test-dist", "testlib.js")).href);
  return cached;
}

export const readVector = (name) => JSON.parse(readFileSync(join(HERE, "..", "vectors", `${name}.json`), "utf8"));

export const hex = (u8) => [...u8].map((b) => b.toString(16).padStart(2, "0")).join("");

export const fromHex = (s) => {
  const out = new Uint8Array(s.length / 2);
  for (let i = 0; i < out.length; i++) out[i] = parseInt(s.slice(i * 2, i * 2 + 2), 16);
  return out;
};

export const json = (x) => JSON.parse(JSON.stringify(x));

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