feat: adopt shared wasm wire schemas; resurrect collab e2e bundle (ysync 0008 A)

- standalone wasm/collab/types.ts: the CollabItem/CollabDelta data shapes now
  come from MIT @pcbjam/shared (collab-wire zod schemas) via re-export; the
  RUNTIME adapter interface (CollabBridge) stays here. emptyDelta/isEmptyDelta
  alias the shared helpers — reconciler untouched.
- tests/collab/browser-entry.ts: fix the import path stale since the repo
  restructure (web/apps/frontend → web/standalone) — the collab bundle had been
  silently unbuildable; its opts type now tracks startCollab's real signature.
- tests/kicad/*-collab.spec.ts: update KicadCollab.start calls from the
  pre-ysync-0004 { channel, settleMs } API to { provider: { kind:
  "broadcastchannel", settleMs }, room } — the drift the broken bundle hid.

Verified: pl_editor 2/2, eeschema 2 passed, pcbnew 7 passed (skips pre-existing);
shared 47 unit tests; both typechecks clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gergő Törcsvári 2026-06-11 10:48:02 +02:00
commit b69ba09ebd
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
7 changed files with 28 additions and 26 deletions

View file

@ -16,11 +16,12 @@ import {
function start(
mod: CollabModule,
win: CollabWindow,
opts: { channel: string; settleMs?: number },
// Same opts as startCollab itself (ysync 0004: { provider, room }).
opts: Parameters<typeof startCollab>[2],
): ReturnType<typeof startCollab> {
return startCollab(mod, win, {
provider: { kind: "broadcastchannel", settleMs: opts.settleMs },
room: opts.channel,
room: opts.room,
});
}

View file

@ -240,7 +240,7 @@ test.describe("eeschema collab bridge — two tabs (BroadcastChannel)", () => {
KicadCollab: { start: (m: unknown, win: unknown, o: unknown) => Promise<unknown> };
Module: unknown;
};
await w.KicadCollab.start(w.Module, window, { channel: ch, settleMs: 500 });
await w.KicadCollab.start(w.Module, window, { provider: { kind: "broadcastchannel", settleMs: 500 }, room: ch });
}, channel);
await startCollab(tabA);
await startCollab(tabB);

View file

@ -414,7 +414,7 @@ test.describe("pcbnew collab bridge — two tabs (BroadcastChannel)", () => {
KicadCollab: { start: (m: unknown, win: unknown, o: unknown) => Promise<unknown> };
Module: unknown;
};
await w.KicadCollab.start(w.Module, window, { channel: ch, settleMs: 500 });
await w.KicadCollab.start(w.Module, window, { provider: { kind: "broadcastchannel", settleMs: 500 }, room: ch });
}, channel);
await startCollab(tabA);
await startCollab(tabB);

View file

@ -195,7 +195,7 @@ test.describe("pl_editor collab bridge — two tabs (BroadcastChannel)", () => {
Module: unknown;
__collab?: unknown;
};
w.__collab = await w.KicadCollab.start(w.Module, window, { channel: ch, settleMs: 500 });
w.__collab = await w.KicadCollab.start(w.Module, window, { provider: { kind: "broadcastchannel", settleMs: 500 }, room: ch });
}, channel);
await startCollab(tabA);
await startCollab(tabB);

@ -1 +1 @@
Subproject commit 011962c0992c5c30eba5487033f0c3609712dd28
Subproject commit 26d2d7951ba6a9574ff5149fb1a29a2a69d9176c

3
web/pnpm-lock.yaml generated
View file

@ -45,6 +45,9 @@ importers:
'@ts-rest/core':
specifier: ^3.52.1
version: 3.52.1(@types/node@22.19.19)(zod@3.25.76)
yjs:
specifier: ^13.6.18
version: 13.6.31
zod:
specifier: ^3.24.1
version: 3.25.76

View file

@ -1,18 +1,21 @@
// Wire contract shared with the C++ bridge (wasm/bindings/pl_editor_embind.cpp).
// Schema-agnostic by design: an item is just { id, type, …arbitrary fields }. The
// reconciler hardcodes no field names — it diffs values keyed by field name.
// Wire contract shared with the C++ bridge (wasm/bindings/*_embind.cpp).
//
// The data shapes (CollabItem / CollabDelta) and their zod schemas now live in
// MIT `@pcbjam/shared` (collab-wire.ts, ysync 0008) so any consumer — this GPL
// editor, tests, a backend — can validate the wasm from-to JSON against one
// source of truth. This module re-exports them under the established local names
// and keeps the RUNTIME adapter interface (CollabBridge), which is about how this
// app talks to a live wasm Module, not about the data.
export type CollabItem = {
id: string;
type: string;
[field: string]: unknown;
};
import {
emptyCollabDelta,
isEmptyCollabDelta,
type CollabDelta,
type CollabItem,
} from "@pcbjam/shared";
export type CollabDelta = {
added: CollabItem[];
changed: CollabItem[];
removed: string[]; // uuids
};
export { collabDeltaSchema, collabItemSchema, parseCollabDelta } from "@pcbjam/shared";
export type { CollabDelta, CollabItem };
/**
* The two C++ bridge entry points + the emit hook, abstracted so the reconciler is
@ -27,10 +30,5 @@ export interface CollabBridge {
onDelta(cb: (deltaJson: string) => void): void;
}
export function emptyDelta(): CollabDelta {
return { added: [], changed: [], removed: [] };
}
export function isEmptyDelta(d: CollabDelta): boolean {
return d.added.length === 0 && d.changed.length === 0 && d.removed.length === 0;
}
export const emptyDelta = emptyCollabDelta;
export const isEmptyDelta = isEmptyCollabDelta;