Wallwise is a durable exhibition planning desk for independent galleries. It brings artist and artwork records, conflict-aware exhibition scheduling, hang placement, installation tasks, append-only activity, and portfolio analytics into one calm workspace.
The frontend is React 19 + Vite. The API is Elysia 1 with the Node adapter. SQLite is the durable source of truth, and an idempotent demo seed makes the first launch useful. The API contract is shared by browser and server code in TypeScript.
Prerequisites:
- Node.js 20.19 or newer (Node 22.12+ is also supported)
- npm 10 or newer
- A typical Linux/macOS build toolchain if a prebuilt
better-sqlite3binary is unavailable for your Node platform
Clone the repository, enter the project root, and run exactly these three commands:
npm install
npm run build
npm startOpen http://127.0.0.1:3000. The production server serves the built client and API. By default it creates
the durable database at data/wallwise.sqlite, applies the schema, and inserts the fixed demo seed once.
No external services, credentials, or seed command are required.
For a reproducibility-focused install, use npm ci instead of npm install. The committed
package-lock.json is generated by npm and is the CI install input.
- Review portfolio totals, status distribution, upcoming exhibitions, readiness, conflicts, overdue work, and recent activity on the dashboard.
- Create, search, edit, and archive artists while preserving their artwork history.
- Catalog artworks with dimensions, framing, availability, and an embedded artist summary.
- Create exhibitions with public and installation date windows, then track explicit lifecycle transitions.
- Hang artworks by wall and position with date/space and double-booking checks that fail atomically.
- Track installation tasks by category, assignee, due date, and status.
- Inspect append-only activity for changes and auditable exhibition status moves.
- Export the complete exhibition portfolio as a properly quoted CSV file.
All API reads and writes use typed envelopes in src/shared/types.ts. The full behavior contract,
acceptance criteria, schema decisions, and test strategy are in SPEC.md.
cp .env.example .env
npm run devThe web app is available at http://127.0.0.1:5173 and proxies /api to the Node API at
http://127.0.0.1:3000. The .env file is optional; defaults work without it. Node reads it with
--env-file-if-exists, and Vite exposes only variables that begin with VITE_.
The development scripts are:
npm run dev- run the API and Vite client together with restart on changenpm run dev:server- run only the API fromsrc/server/index.tswith Node watch andtsxnpm run dev:client- run only Vite
The implementation entry points are src/client (rendered through the root index.html) and
src/server/index.ts. Configuration expects npm run build:server to bundle that server entry.
| Variable | Default / example | Purpose |
|---|---|---|
NODE_ENV |
Set by scripts | Development/test modes are explicit; npm start forces production. |
HOST |
127.0.0.1 |
API bind address. Use 0.0.0.0 only when intentionally exposing it. |
PORT |
3000 |
Production/API port. E2E uses 4173. |
DATABASE_PATH |
./data/wallwise.sqlite |
Durable SQLite file. Parent directories are created by the server. |
CORS_ORIGIN |
http://localhost:5173,http://127.0.0.1:5173 |
Comma-separated development browser origins. |
VITE_DEV_PORT |
5173 |
Strict Vite dev port. |
VITE_API_PROXY_TARGET |
http://127.0.0.1:3000 |
Vite proxy target for /api. |
SQLite is the durable application database, not an in-memory store or a cache. Writes and their audit
entries commit together. The demo seed runs once in a transaction and records its marker and version in
app_meta. Restarting the server does not replace or reset existing gallery work.
To intentionally rebuild a local demo database, stop the server and remove all three database files:
rm -f data/wallwise.sqlite data/wallwise.sqlite-shm data/wallwise.sqlite-walThe next start recreates and seeds the database. Tests use disposable files under .tmp/ and never touch
the development database.
npm run build
npm startnpm run build performs a full TypeScript check, builds the Vite client into dist/, and bundles the
Node server to dist/server/index.js. npm start runs that production bundle. The server also serves the
built client and SPA fallback for non-API routes.
Build components are available separately with npm run build:client and npm run build:server. The
server bundle keeps runtime dependencies external, so deploy dist/, package.json, package-lock.json,
and the installed production dependencies together. Use Node 20.19+; Vite 7 and Node ESM are required.
| Command | What it does |
|---|---|
npm run lint |
ESLint with TypeScript and React rules; warnings fail. |
npm run format |
Write Prettier formatting across the repository. |
npm run format:check |
Verify formatting without writing. |
npm run typecheck |
Strict browser/shared and server TypeScript checks. |
npm test |
Vitest unit and component tests in jsdom. |
npm run test:integration |
Vitest integration tests against real SQLite at .tmp/wallwise-integration.sqlite. |
npm start |
Serve the build; CI smoke-checks GET http://127.0.0.1:3000/api/health. |
npm run test:e2e |
Build, then run Playwright Chromium against .tmp/wallwise-e2e.sqlite. |
Unit tests are discovered in src/**/*.test.*, src/**/*.spec.*, tests/unit, and tests/components.
Integration tests live in tests/integration or use the *.integration.test.ts suffix. Playwright specs
live in tests/e2e/*.spec.ts. If a shared jsdom setup file is added at src/test/setup.ts,
src/client/test/setup.ts, or tests/setup.ts, the unit configuration loads it automatically.
Install Playwright's Chromium once after installing npm packages:
npx playwright install chromiumThen run:
npm run test:e2eThis command builds the app first. Playwright owns the production server lifecycle, waits for
/api/health, and starts from a deterministic SQLite path. The start:e2e script clears stale
database/WAL/SHM files before the server starts. Browser tests fail on missing API readiness and do not
mock production data.
- JSON API base:
/api; readiness endpoint:/api/health. - List envelope:
{ data, pagination }; detail envelope:{ data }; mutation envelope:{ data, message, activity? }. - Failure envelope:
{ error: { code, message, field?, details? } }. - Dates are
YYYY-MM-DD; timestamps are ISO UTC strings. Date ranges are inclusive. - IDs are opaque strings. List pagination defaults to 25 and is bounded at 100.
- Exhibition status can only change through
POST /api/exhibitions/:id/transitionsusing the shared transition map. - Portfolio CSV is available from
GET /api/export/exhibitions.csv.
UI code should parse unknown failures through normalizeErrorResponse from src/shared/http.ts and use
the shared error/status constants instead of duplicating string values.
src/
shared/ Authoritative API types, constants, and HTTP helpers
client/ React 19 UI
server/ Elysia app, routes, validation, SQLite repository, and seed
index.html Vite browser entry document
tests/
unit/ Vitest unit/component tests
integration/ Real-SQLite API and storage tests
e2e/ Playwright user workflows
dist/
index.html Vite production client
assets/ Hashed browser assets
server/index.js Bundled Node server
The client never opens SQLite directly. It talks to /api; Vite proxies that path in development and the
production server owns it in deployment. The server owns validation, conflict decisions, transitions,
audit writes, and computed analytics. Shared modules contain no browser-only globals so both sides can
import them.
.github/workflows/ci.yml runs on pushes and pull requests with Node 20. It installs from the lockfile,
installs Playwright Chromium, and runs all gates in order: lint, format check, typecheck, unit tests,
integration tests, production build, production smoke check, and browser tests. The smoke step starts
npm start, polls GET http://127.0.0.1:3000/api/health, prints the server log if readiness fails, and
stops the production server before Playwright starts its own. CI uses the same npm scripts as local
development and sets deterministic .tmp/ database paths. No repository secrets or external services are
needed.
Wallwise is released under the MIT License. See LICENSE.