This documents framework v1, which is deprecated. Games already on it keep running and their authors can keep editing them, but new games are created on the current framework — start with React frontends.
BoardWeaver is a TypeScript platform for building turn-based multiplayer games.
A BoardWeaver game is a tree of TypeScript files under /src, entered at /src/game.ts, that exports gameConfig and a fixed set of hooks. The host runs this loop on every action:
// 1. Author module exports gameConfig + hooks (see `source-files`).
// 2. Engine calls gameConfig(params) once → initial GameState (pieces, spaces, players).
// 3. preGameInitialization?(state) // optional, runs once
// 4. Per turn:
// a. getSelectableItems(state) → SelectableItem[] // what the viewer may click
// b. user clicks → Action ("SpaceClick" | "PieceClick" | "ButtonClick")
// c. applyActions(state, action) // mutate state in place
// d. getPlayerScores(state) → Scores
// e. isGameOver(state, scores) → boolean
// 5. Engine publishes new state (privateState scrubbed per viewer) → clients re-render.
State mutation only happens inside applyActions and preGameInitialization. Every other hook (getSelectableItems, getPlayerScores, isGameOver, getButtons, getLayout, renderOverlay, every render()) is read-only — the framework deep-freezes the GameState it passes in. Writes from a read-only context throw a TypeError.
Authority is server-side. The client never decides what's legal — getSelectableItems gates input, applyActions runs server-side, the new state is broadcast.
PieceDef subclass. See pieces.SpaceDef subclass. See spaces.game-state.kind = the class identifier on a Def. Used everywhere to filter and narrow (state.pieces("x-token")). The platform types it via the kind registry — you don't wire that up.static — constants for a kind: card name, cost, ability text, artwork. Never serialized.publicState — per-instance, visible to everyone: tapped flag, damage, position counter.privateState — per-instance, owner-only. The framework nulls it on the wire for other viewers, so hidden info (a card's identity in hand) never leaves the server.render() returns: a small JSON tree of Box (flex container, children can be nodes or text strings), Image (a GameImage key), and Svg nodes, styled with a fixed, flexbox-like set of properties. Spaces add a Pieces node marking where laid-out piece children go.state.piece(...) / state.pieces(...) / state.space(...) / state.spaces(...) (by id or predicate, optionally kind-first for typing), state.currentPlayer, state.activePlayerIds (set this to pass the turn), state.metaData (free-form game-wide scratch state), and state.addPiece / state.addSpace to spawn entities mid-game. Move a piece by assigning piece.spaceId = targetSpace.spaceId.A game's source is an arbitrary tree of TypeScript files under /src. The build bundles it as a real module graph starting from the entry point, so files import each other by relative path (./pieces/Knight) or absolute /src path (/src/pieces/Knight). The only off-tree import allowed is the "boardweaver" runtime; bare imports (no node_modules) fail the build.
| Path | Holds |
|---|---|
/src/game.ts |
Required entry point. Exports gameConfig + the game-logic hooks. Small games can live entirely here. |
/src/pieces/ |
One PieceDef subclass per file by convention (e.g. /src/pieces/Knight.ts). See pieces. |
/src/spaces/ |
One SpaceDef subclass per file by convention (e.g. /src/spaces/Tile.ts). See spaces. |
/src/*.ts (anything else) |
Shared libraries — helpers, constants, type aliases. Ordinary modules with no special treatment; import them from anywhere in the tree. |
Keep gameConfig and the exported hooks in /src/game.ts; pull pieces, spaces, and reusable logic into their own files and import them. Non-code assets live outside /src in their own namespaces (image keys under /art/, game metadata in /manifest.json), so they don't go through the code bundler.
This summary is enough to start. For exact signatures and worked examples, pull the docs with the tools available to you — don't guess at an API:
get_doc_section — full reference for one section.get_example — complete source of a working game.