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.
A BoardWeaver game is a tree of TypeScript files under /src, entered at /src/game.ts. The entry point imports from "boardweaver" and exports gameConfig plus the game-logic hooks; everything else (pieces, spaces, shared helpers) lives in sibling files it imports. See core-concepts for the full layout.
The build bundles /src as a real module graph from the entry point, so files import each other by relative path (./pieces/XToken) or absolute /src path (/src/pieces/XToken). The only off-tree import allowed is the "boardweaver" runtime — bare imports (there's no node_modules) fail the build.
// /src/pieces/XToken.ts
import { PieceDef } from "boardweaver";
export class XToken extends PieceDef { /* see `pieces` */ }
// /src/spaces/GridCell.ts
import { SpaceDef } from "boardweaver";
export class GridCell extends SpaceDef { /* see `spaces` */ }
// /src/game.ts — the required entry point
import {
ApplyActionsFn,
GameStateConfigFn,
GetPlayerScoresFn,
GetSelectableItemsFn,
IsGameOverFn,
} from "boardweaver";
import { XToken } from "./pieces/XToken";
import { GridCell } from "./spaces/GridCell";
export const gameConfig: GameStateConfigFn = () => ({
startingPlayerStrategy: "Random",
spaces: { "0": new GridCell({ x: 0, y: 0 }) },
pieces: {},
pieceDefs: { "x-token": new XToken({ order: 1 }) },
});
export const getSelectableItems: GetSelectableItemsFn = (state) => [];
export const applyActions: ApplyActionsFn = (state, action) => {};
export const getPlayerScores: GetPlayerScoresFn = (state) => ({});
export const isGameOver: IsGameOverFn = (state, scores) => false;
Pieces and spaces follow a strict file layout:
PieceDef subclass per file under /src/pieces/. One SpaceDef subclass per file under /src/spaces/. Do not bundle multiple defs into a single file.XToken → /src/pieces/XToken.ts, class GridCell → /src/spaces/GridCell.ts), as in the examples above./src/pieces/index.ts, /src/spaces/index.ts, or any other re-export aggregator. /src/game.ts and other files import each def directly by its path: import { XToken } from "./pieces/XToken".| Export | Type | Notes |
|---|---|---|
gameConfig |
GameStateConfigFn |
Returns the initial config. Called once. |
getSelectableItems |
GetSelectableItemsFn |
Returns the viewer-visible click targets. |
applyActions |
ApplyActionsFn |
Mutates state in place. |
getPlayerScores |
GetPlayerScoresFn |
Returns Scores map. |
isGameOver |
IsGameOverFn |
Returns a boolean. |
| Export | Type | Default |
|---|---|---|
preGameInitialization |
PreGameInitializationFn |
not run |
getButtons |
GetButtonsFn |
no buttons |
getLayout |
GetLayoutFn |
{ type: "radial" } |
renderOverlay |
RenderOverlayFn |
no overlay |
See game-logic for each hook's signature and contract; see layout-overlays for getLayout and renderOverlay.
gameConfig return shapeGameStateConfigFn is (params: { numPlayers: number }) => GameStateConfigObject. The returned object:
type GameStateConfigObject = {
startingPlayerStrategy: "Random" | "All";
pieces: Record<string, PieceDef>; // top-level pieces, instantiated at game start
spaces: Record<string, SpaceDef>; // top-level spaces, instantiated at game start
player?: {
spaces?: Record<string, SpaceDef>; // per-player spaces (one set per seat)
publicState?: Record<string, unknown>; // default per-player public state
privateState?: Record<string, unknown>; // default per-player private state
def?: PlayerDef; // optional per-player Def (rare)
};
pieceDefs?: Record<string, PieceDef>; // register kinds used only via state.addPiece
spaceDefs?: Record<string, SpaceDef>; // register kinds used only via state.addSpace
metaData?: Record<string, unknown>; // initial gameState.metaData
scoreLabels?: string[]; // labels for the score columns shown in UI
};
Map keys ("0", "1", ...) become the initial pieceId / spaceId. Per-player spaces synthesize ids ${playerId}/space/${key}.
pieces / spaces MUST be a PieceDef / SpaceDef instance. Raw object literals are rejected.kind used only at runtime (via state.addPiece(id, new Foo())) MUST also appear in pieceDefs or spaceDefs, otherwise addPiece throws "PieceDef kind ... is not registered"./src files (relative or absolute /src path) and the "boardweaver" runtime. There's no node_modules, so a bare import like import x from "pieces/Foo" fails the build — use "./pieces/Foo" or "/src/pieces/Foo".