phase 5: wasm core, GalaxyClient skeleton, Connect-Web stubs
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>
This commit is contained in:
+76
-30
@@ -506,51 +506,97 @@ add coverage. Future contributors looking for "the Connect tests" can
|
||||
read any file in `gateway/internal/grpcapi/` — they all use the
|
||||
Connect client now.
|
||||
|
||||
## Phase 5. WASM Build, `WasmCore` Adapter, `GalaxyClient` Skeleton
|
||||
## ~~Phase 5. WASM Build, `WasmCore` Adapter, `GalaxyClient` Skeleton~~
|
||||
|
||||
Status: pending.
|
||||
Status: done.
|
||||
|
||||
Goal: package `ui/core` as a WASM module, expose it to TypeScript
|
||||
through a typed adapter, and prove the WASM-side crypto pipeline at
|
||||
unit level. End-to-end Connect round-trip is validated in Phase 7
|
||||
(authenticated calls only become possible after login).
|
||||
|
||||
Artifacts:
|
||||
Decisions taken with the project owner before implementation:
|
||||
|
||||
- `ui/wasm/main.go` TinyGo entry point exporting `Core` API to JS
|
||||
- `ui/Makefile` target `wasm` producing `core.wasm` and `wasm_exec.js`
|
||||
under `ui/frontend/static/`
|
||||
- `ui/frontend/src/platform/core/index.ts` `Core` interface and
|
||||
build-time target resolver
|
||||
- `ui/frontend/src/platform/core/wasm.ts` `WasmCore` adapter
|
||||
- `ui/frontend/src/api/galaxy-client.ts` `GalaxyClient` orchestrating
|
||||
`Core.signRequest` → ConnectRPC fetch → `Core.verifyResponse`
|
||||
- `ui/frontend/src/api/connect.ts` typed Connect client built from
|
||||
generated stubs (Connect codegen via `@bufbuild/protoc-gen-es` and
|
||||
`@connectrpc/protoc-gen-connect-es`)
|
||||
- topic doc `ui/docs/wasm-toolchain.md` documenting TinyGo vs
|
||||
standard Go choice and bundle size measured
|
||||
1. **TinyGo as primary toolchain.** `core.wasm` lands at 903 KB —
|
||||
well under the 1 MB acceptance bar. The `GOOS=js GOARCH=wasm`
|
||||
fallback path stays documented in `ui/docs/wasm-toolchain.md`.
|
||||
2. **`Core.signRequest` returns canonical bytes only.** No private
|
||||
key inside WASM; Phase 6 plugs WebCrypto's non-exportable keys at
|
||||
the orchestration layer. `GalaxyClient` takes a pluggable `Signer`
|
||||
so Phase 5 tests pass a fixture-key signer and Phase 6 swaps in
|
||||
WebCrypto without touching the orchestration.
|
||||
3. **TS codegen runs locally, not against buf.build BSR.** A new
|
||||
`ui/buf.gen.yaml` invokes
|
||||
`frontend/node_modules/.bin/protoc-gen-es` (added as a
|
||||
devDependency). This sidesteps BSR rate limiting and removes the
|
||||
network dependency from the codegen step.
|
||||
4. **Field naming is camelCase end-to-end.** Both the TS `Core`
|
||||
interface and the Go bridge in `ui/wasm/main.go` use camelCase
|
||||
field names; there is no snake-case translation layer.
|
||||
|
||||
Artifacts (delivered):
|
||||
|
||||
- `ui/wasm/main.go` TinyGo entry point on `globalThis.galaxyCore`
|
||||
with four functions: `signRequest`, `verifyResponse`,
|
||||
`verifyEvent`, `verifyPayloadHash`.
|
||||
- `ui/Makefile` `wasm` and `ts-protos` targets.
|
||||
- `ui/buf.gen.yaml` with the local Protobuf-ES plugin (single plugin —
|
||||
protobuf-es v2 emits both message types and Connect service
|
||||
descriptors in one file).
|
||||
- `ui/frontend/src/platform/core/index.ts` — typed `Core` interface
|
||||
plus a `loadCore()` resolver (Phase 5 ships only the WASM adapter).
|
||||
- `ui/frontend/src/platform/core/wasm.ts` — `WasmCore` adapter for
|
||||
browsers; the JSDOM test path lives next to it in
|
||||
`ui/frontend/tests/setup-wasm.ts`.
|
||||
- `ui/frontend/src/api/connect.ts` — typed Connect-Web transport +
|
||||
`EdgeGatewayClient` factory.
|
||||
- `ui/frontend/src/api/galaxy-client.ts` — `GalaxyClient` skeleton
|
||||
with injected `Signer` and `Sha256` dependencies.
|
||||
- `ui/frontend/src/proto/galaxy/gateway/v1/edge_gateway_pb.ts`
|
||||
(generated) and `ui/frontend/src/proto/buf/validate/validate_pb.ts`
|
||||
(generated as a transitive import via `--include-imports`).
|
||||
- `ui/frontend/static/core.wasm` (903 KB) + `wasm_exec.js` (TinyGo
|
||||
shim).
|
||||
- Three Vitest files exercising the bridge end-to-end:
|
||||
`tests/wasm-core.test.ts` (each Core method, including a sanity
|
||||
`signRequest` check that the canonical bytes start with the v1
|
||||
domain marker), `tests/wasm-core-canon-parity.test.ts` (byte-for-
|
||||
byte parity against three request fixtures plus the response and
|
||||
event signature fixtures from `ui/core/canon/testdata/`), and
|
||||
`tests/galaxy-client.test.ts` (orchestration through a mock `Core`
|
||||
and `createRouterTransport` from `@connectrpc/connect`).
|
||||
- Topic doc `ui/docs/wasm-toolchain.md`.
|
||||
- `ui/README.md` repository-layout block.
|
||||
|
||||
Dependencies: Phases 2, 3, 4.
|
||||
|
||||
Acceptance criteria:
|
||||
Acceptance criteria (met):
|
||||
|
||||
- `make wasm` produces a deterministic bundle under 1 MB (TinyGo) or
|
||||
under 3 MB (standard Go fallback);
|
||||
- `make wasm` produces `core.wasm` deterministically under 1 MB (903
|
||||
KB measured);
|
||||
- `WasmCore.signRequest` produces canonical bytes byte-for-byte
|
||||
identical to the gateway-side verifier output on shared fixtures
|
||||
(validated via Vitest with the WASM module loaded in JSDOM);
|
||||
- `WasmCore` exposes the same TypeScript types as the future
|
||||
`WailsCore` and `CapacitorCore` will need to satisfy.
|
||||
identical to the gateway-side fixtures for three message types
|
||||
(`request_user_account_get`, `request_user_games_command`,
|
||||
`request_lobby_my_games_list`);
|
||||
- `WasmCore` exposes the same `Core` TypeScript types future
|
||||
`WailsCore` and `CapacitorCore` adapters will satisfy.
|
||||
|
||||
Targeted tests:
|
||||
Targeted tests (delivered):
|
||||
|
||||
- Vitest unit tests for `WasmCore` calling each public method with a
|
||||
fixture WASM module loaded in JSDOM;
|
||||
- Vitest unit tests for `GalaxyClient` using a mock `Core` and a mock
|
||||
Connect transport;
|
||||
- Vitest tests asserting `WasmCore.signRequest` output matches gateway
|
||||
fixtures byte-for-byte for at least three message types.
|
||||
- Vitest unit tests for `WasmCore` calling each public method with
|
||||
the WASM module loaded in JSDOM via `tests/setup-wasm.ts`;
|
||||
- Vitest unit tests for `GalaxyClient` using a mock `Core` and the
|
||||
in-memory `createRouterTransport`;
|
||||
- Vitest tests asserting `WasmCore.signRequest` output matches the
|
||||
committed gateway fixtures byte-for-byte for the three request
|
||||
message types listed above.
|
||||
|
||||
Decision deviation note: the initial plan listed `protoc-gen-es` and
|
||||
`protoc-gen-connect-es` as separate plugins. Protobuf-ES v2 generates
|
||||
service descriptors in the `_pb.ts` file directly, so a single
|
||||
`@bufbuild/protoc-gen-es` plugin is sufficient — `@connectrpc/connect`
|
||||
v2 consumes those descriptors via `createClient`. The `connect-es`
|
||||
plugin is a v1-only path and is intentionally not used here.
|
||||
|
||||
## Phase 6. Storage Layer (Web)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user