pcbjam/web
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Istvan Matejcsok c14e76651c findings(E-5,E-8,E-9): module-identity ngspice events + runWaitCompletion admission gate
E-8 (re-implemented for JSPI — the codex gate is entangled with the dropped
execution owner; under JSPI a fresh non-suspending JS→wasm entry while
another activation is suspended is structurally safe on its own stack, so
the admission boundary for worker completions is liveness + trap state, not
execution ownership):
- jspi-scheduler.js grows `terminal` (trapped instance; distinct from `dead`),
  canTouchNative(), _terminalizeNativeTrap() (WebAssembly.RuntimeError +
  cross-realm string classification), and runWaitCompletion(site, token,
  prepare, inertResult): prepare runs immediately and owns ALL native work;
  stale tokens and dead/terminal instances drop loudly without resolving
  (resolving would resume the parked frame inside the damaged module); a
  trap latches terminal; a plain JS bug resolves inertResult so the wait
  fails instead of stranding. beginWait refuses (token 0) when dead/terminal.
- all four delayed completion sites route their native work through the
  gate: 'OCC export completion' (exporter_step_stub), 'OCC model completion'
  (oce_plugin_stub — the MEMFS cache write moves inside the gate too),
  'ngspice request completion' and 'ngspice vector completion'
  (sharedspice_client — every HEAP32/HEAPF64/malloc write inside prepare,
  inertResult 1 = transport error). Every wxWasmBeginWait caller in the
  stubs bails on token <= 0.
- deliberately NOT ported from codex: ownerModule, enqueueNativeCompletion,
  executionBarrier, the byte-credit native-entry FIFO — completions are
  one-shot per wait token and stream volume is bounded at the E-6 transport
  credit window. Cross-refs logged for group M (M-2/M-6/M-8).

E-5 (re-implemented; codex shape kept, owner APIs replaced with the E-8
gate): js_ngspice_install_events binds the handler to the EXACT installing
module (handler.__pcbjamNgspiceOwnerModule stamp; presence is not identity),
re-installation replaces a foreign module's handler, a superseded handler
disarms itself, native entry goes through installingModule._malloc/
._pcbjam_ngspice_event (never lexical Module), each dispatch checks
canTouchNative() (loud drop on a dead/terminal module), and a trap on the
per-line entry latches the terminal gate.

Tests: scheduler-shim.test.ts +7 (gate happy/stale/dead/terminal/cross-realm/
js-bug/beginWait-refusal). e2e specs updated from the codex line: occ-export
decode-fault recovery (real onmessageerror transition via failDecode, J-4),
ngspice-probe direct-service coverage, eeschema-sim rewritten onto the E-7
applied-generation receipt (codex's executionBarrier await replaced with a
pendingWaits('ngspice') drain poll — the JSPI-line equivalent).

Also bumps the kicad submodule to the E-7/E-9 commit (dd5751038f7).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 18:18:21 +02:00
..
backend comments-ux: figma bubble pins, floating panel, seen/reactions/mentions UI, theme follow (0001 A–E + 0002) 2026-07-24 13:21:22 +02:00
pcbjam-shared@a4984c626d collab: paste-collision sync-delete fix — lenient sibling restage, forced removals for deleted dirty roots, eeschema child-blob lifting 2026-08-31 12:50:12 +02:00
standalone findings(E-5,E-8,E-9): module-identity ngspice events + runWaitCompletion admission gate 2026-08-31 18:18:21 +02:00
.env.example feat(editor): report uncaught errors to Better Stack 2026-08-03 12:24:30 +02:00
.gitignore feat(libs): eeschema symbol-chooser footprint selector + preview via publish-time fp-index 2026-07-07 21:09:21 +02:00
package.json chore: add dev:demo script (web) + refresh site lockfile 2026-06-30 09:54:49 +02:00
pnpm-lock.yaml feat: load-path §5 client — gateway transport behind the YjsProvider seam 2026-08-18 14:02:40 +02:00
pnpm-workspace.yaml feat(libs): r2-idb-sync bridge — sync-wire protocol, FE/BE packages, apps/server resolve+origin serving, GPL adapter 2026-06-17 09:59:59 +02:00
README.md feat: GPL backend self-provisions example libs + standalone port override 2026-06-16 16:28:21 +02:00
tsconfig.base.json feat(web): checkpoint web app init 2026-06-02 20:32:19 +02:00
turbo.json feat(editor): report uncaught errors to Better Stack 2026-08-03 12:24:30 +02:00

PCBJam Web — standalone editor (GPL)

A self-contained, GPL web app that opens KiCad projects in the WASM tools (pcbnew / eeschema / pl_editor / …) — from a local folder, or from any backend that implements the MIT @pcbjam/shared contract. It opens a tool by URL:

/p/<project>/<tool>/<file-path>      e.g. /p/demo/pcbnew/nyak.kicad_pcb

This workspace contains only the generic editor and a thin reference backend. All project-specific concerns (accounts, project management, uploads, auth) live in the separate closed application, which reuses this editor by hosting it standalone and redirecting to it (it must not link the GPL editor).

Layout

web/
├── standalone/      # @pcbjam/standalone — the GPL editor (Vite + React)
├── backend/         # @pcbjam/backend-example — thin reference @pcbjam/shared impl
└── pcbjam-shared/   # @pcbjam/shared — the FE↔BE contract (git submodule, MIT)
  • Editor: Vite + React + TypeScript. Boots a tool directly in the document (no iframe), syncs the project tree into MEMFS, drives File→Open, and runs same-tab collaboration over BroadcastChannel.
  • Example backend: Fastify + ts-rest serving a single project off the local filesystem (PROJECT_DIR). No DB, no auth, no uploads — the minimum the editor needs, and a worked example of the contract. Listens on :3060. On dev/start it self-provisions example libraries — a curated slice of upstream KiCad symbol + footprint libs is cloned + extracted into .libs (served as read-only origins), so a bare clone has libraries to browse with no closed repo present. See backend/src/extract/.

Quick start

cd web
pnpm install
git submodule update --init web/pcbjam-shared   # if not already populated

cp standalone/.env.example standalone/.env
cp backend/.env.example backend/.env            # PROJECT_DIR=../../tests/fixtures/demo

pnpm dev                                         # turbo: backend :3060 + editor :3048

Open http://localhost:3048 — either open a local folder (no backend needed) or open the backend's project. The editor can point at any conforming backend via VITE_API_BASE_URL.

WASM artifacts

The runtime artifacts (<tool>.js/.wasm, wx.js, images.tar.gz, <tool>.html) are build outputs, not committed. They are synced into tests/apps/kicad/ by tests/scripts/setup-kicad-wasm.sh (from repo-root output/).

They must be served same-origin as the app. Under the document's COEP / cross-origin-isolation (set by the Vite dev server), KiCad WASM refuses to load its glue/wasm from a different origin. pnpm dev runs scripts/link-wasm.mjs, which symlinks standalone/public/wasm → tests/apps/kicad; Vite serves them at /wasm. VITE_WASM_ASSET_BASE_URL defaults to /wasm.

  • Point the symlink elsewhere with WASM_SRC_DIR=/path pnpm --filter @pcbjam/standalone link-wasm.
  • If a tool won't load, the target dir is probably empty — run tests/scripts/setup-kicad-wasm.sh to populate tests/apps/kicad/.
  • prod: point VITE_WASM_ASSET_BASE_URL at a URL whose origin also satisfies the same-origin / COEP constraints.

Scripts

Command What
pnpm dev editor + example backend (turbo)
pnpm build build all packages
pnpm typecheck typecheck all packages

Contract (@pcbjam/shared, MIT)

The editor reads from a backend over the shared contract: GET /api/projects, GET /api/projects/:project, GET /api/projects/:project/files, and the streamed GET /api/projects/:project/files/* (raw bytes). Management/write operations and ownership are not part of this contract — they belong to the closed app.