A pnpm workspace of packages that turn notebooks into web pages that run in the browser. A
notebook is a Markdown file or an Observable notebook-kit
HTML document. @statewalker/notebook-build builds a tree of notebooks into a site of
executable pages, @statewalker/notebook-db backs SQL cells with any @statewalker/db-api
database, and @statewalker/notebook-site serves the built site from one fetch handler. All
storage goes through FilesApi (@statewalker/webrun-files), so the same code runs under
Node, in a Worker and behind a ServiceWorker.
packages/
notebook-build/ notebooks (FilesApi) -> pages + attachments + module closure (FilesApi)
notebook-db/ db-api Db -> notebook-kit SQL source; build-time SQL cache files
notebook-site/ built site (FilesApi) -> one SiteHandler (Request -> Response)
apps/
demo/ private: builds ./notebooks and serves them on localhost:8099
How the pieces connect:
notebooks/*.md, *.html ──► newNotebookBuild ──► output/ (pages, attachments,
(FilesApi) │ ▲ static: /_m/ closure)
│ └── moduleServer (@statewalker/webrun-modules)
▼
cache/ (serialized notebooks, incremental state)
Request ──► newNotebookSite ──► /_m/* moduleServer (hosted mode only)
├─► /_events/* PubSub (SSE) (optional)
└─► /* output/
| Package | What it does | Published |
|---|---|---|
| @statewalker/notebook-build | Builds a tree of notebooks on a FilesApi into a static site of executable pages, incrementally. |
npm |
| @statewalker/notebook-db | Backs notebook-kit SQL cells with any @statewalker/db-api database: a live client, and a build-time precompute that writes the JSON files notebook-kit's cached client fetches. |
npm |
| @statewalker/notebook-site | Serves a built notebook site as a SiteHandler: pages and attachments from files, modules from a module server (hosted mode) or from the export (static mode), plus the rebuild event stream. |
npm |
| @statewalker/notebook-demo | Builds the notebooks in apps/demo/notebooks and serves them. |
private |
Requirements: Node.js 24 and pnpm 10 through corepack. The pnpm version is pinned by
packageManager in package.json.
corepack enablepnpm installpnpm run build(each package builds with tsdown intodist/)pnpm run test- Optional, browser tests:
pnpm --filter @statewalker/notebook-build exec playwright install chromium, thenpnpm run test:browser. - Optional, the demo:
pnpm --filter @statewalker/notebook-demo start, then open http://localhost:8099/index.html.
- Every store is a
FilesApi. Sources, output, build cache and module cache are allFilesApiinstances, so the build and the server run anywhere aFilesApibackend exists: the Node filesystem, memory, OPFS. - Nothing reads a DOM global.
notebook-buildtakes adocumentand aDOMParseras options (jsdom under Node, the natives in a browser), andnotebook-siteis compiled without the DOM lib, so it can run in a Worker. - npm imports are served from the same origin. In hosted mode a live module server
resolves and transforms npm packages on demand under
/_m/; in static mode the build writes the whole dependency closure into the output. In both cases a page makes no third-party requests at run time. - Browser tests are a separate script.
pnpm run testnever launches a browser. The browser tests drive a real Chromium through Playwright from Node, because they have to execute built pages, a real DuckDB-WASM and a real ServiceWorker. - Integrations are peer dependencies.
@statewalker/webrun-files,webrun-builder,webrun-modules,webrun-site-builderand@statewalker/db-apiare peers, so an application and these packages share one copy of each interface.
- The first browser test run or demo start is slow. On a cold cache the module server downloads and transforms the npm dependency graph of the notebooks (Observable Plot, DuckDB). The browser tests allow up to 15 minutes for their setup hook for this reason.
pnpm exec playwrightat the root is the wrong Playwright. Playwright is a dependency of the packages, not of the root; run it throughpnpm --filter <package> execso the browser matches the version the tests import.- The build keeps its scanner state inside the notebooks tree. The build engine stores its
state in the
notebooksFilesApiunder.notebook-build/. The demo ignoresnotebooks/.notebook-build/in git; do the same in your own source tree. - There is no
formatscript.pnpm run lintrunsbiome check --write ., which lints and formats in one pass;pnpm run lint:checkchecks both without writing.
| Command | What it does |
|---|---|
pnpm run build |
tsdown in every package |
pnpm run test |
builds each package, then vitest run |
pnpm run test:browser |
Playwright browser tests in every package |
pnpm run typecheck |
tsc --noEmit for sources and browser tests |
pnpm run lint |
biome check --write . |
pnpm run lint:check |
biome check . |
pnpm changeset |
add a changeset (bump type and changelog text) to a pull request |
Packages are published to npm from CI with changesets.
A pull request may carry a changeset made with pnpm changeset; otherwise one is generated for
each package whose packed contents differ from the version on npm. Each package ships dist/
(JavaScript and .d.ts) and its TypeScript sources in src/; exports point at dist/.
MIT, see LICENSE.