fbc0260720
Compile `ui/core` to WebAssembly via TinyGo (903 KB) and expose four canonical-bytes / signature-verification functions on `globalThis.galaxyCore` from `ui/wasm/main.go`. The TypeScript-side `Core` interface plus a `WasmCore` adapter (browser + JSDOM loader) bridge those into a typed shape, and a `GalaxyClient` skeleton wires `Core.signRequest` → injected `Signer` → typed Connect client → `Core.verifyPayloadHash` / `verifyResponse`. Wire `ui/buf.gen.yaml` against the local `@bufbuild/protoc-gen-es` v2 binary (devDependency) so the codegen step does not depend on the buf.build BSR. Vitest covers the bridge end-to-end: per-method WasmCore tests under JSDOM, byte-for-byte canon parity against the gateway fixtures committed in Phase 3, and a `GalaxyClient` orchestration test using `createRouterTransport`. The committed `core.wasm` snapshot tracks TinyGo output so contributors run `make wasm` only when `ui/core/` changes; CI consumes the snapshot directly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
// Boots the TinyGo `core.wasm` module under Vitest/JSDOM by reading
|
|
// the artefact and the matching wasm_exec.js shim from disk, evaluating
|
|
// the shim into the test's global scope, then instantiating WebAssembly
|
|
// against `(new Go()).importObject`. Returns a `Core` ready to use in
|
|
// tests; subsequent calls return the cached instance so each Vitest
|
|
// file pays the boot cost once.
|
|
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { runInThisContext } from "node:vm";
|
|
import { adaptBridge, requireBridge } from "../src/platform/core/wasm";
|
|
import type { Core } from "../src/platform/core/index";
|
|
|
|
// Vitest is launched from ui/frontend, so the static artefacts produced
|
|
// by `make wasm` sit at <cwd>/static/. Resolving via process.cwd avoids
|
|
// the JSDOM-specific "URL must be of scheme file" error that
|
|
// import.meta.url triggers under the jsdom test environment.
|
|
const STATIC_DIR = resolve(process.cwd(), "static") + "/";
|
|
|
|
let cached: Promise<Core> | undefined;
|
|
|
|
export function loadWasmCoreForTest(): Promise<Core> {
|
|
if (!cached) {
|
|
cached = boot();
|
|
}
|
|
return cached;
|
|
}
|
|
|
|
async function boot(): Promise<Core> {
|
|
if (typeof globalThis.Go === "undefined") {
|
|
const shim = readFileSync(`${STATIC_DIR}wasm_exec.js`, "utf8");
|
|
runInThisContext(shim);
|
|
}
|
|
const Go = globalThis.Go;
|
|
if (!Go) {
|
|
throw new Error("setup-wasm: Go runtime missing after wasm_exec.js eval");
|
|
}
|
|
const go = new Go();
|
|
const wasm = readFileSync(`${STATIC_DIR}core.wasm`);
|
|
const { instance } = await WebAssembly.instantiate(
|
|
wasm,
|
|
go.importObject,
|
|
);
|
|
void go.run(instance);
|
|
// `go.run` is async but the Go side registers `globalThis.galaxyCore`
|
|
// and then blocks on `select {}`; the registration happens
|
|
// synchronously before the first await, so by the time the next
|
|
// microtask runs the bridge is already available.
|
|
await new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
return adaptBridge(requireBridge());
|
|
}
|