Everything, in one page.
Build a config below, then reference the whole surface — install, the config schema, the
Vitest wiring, the baby CLI, the agent loop, and
what to do when something goes wrong. New here? The
Usage walkthrough is the friendlier tour.
Generate your babystack.config.ts.
Every option here is real — babystack orchestrates a live
mysql:8.4 and runs your own migrate + seed commands
to build the baseline. Tweak and copy.
More engines (Redis, S3/MinIO, DynamoDB, SQS, LocalStack) are on the
roadmap — the config union already reserves their shape,
but only mysql is built today.
Install
Add the Vitest wedge and the engine to your dev dependencies. The
baby CLI is optional — grab it if you or an
agent wants a persistent database outside the test run.
# the MySQL + Vitest wedge — one package (pulls the engine transitively) pnpm add -D @babystack/vitest # optional: the baby CLI (operator + agent surface) pnpm add -D babystack
Pre-alpha, but live on npm (v0.1.0). Requires Docker running and Node ≥ 22.
Config — babystack.config.ts
One file describes your backend. defineConfig is
a pure identity helper that gives your editor full typing and fails fast on
obviously invalid config. You define at least one service under
services, keyed by a name ([A-Za-z0-9_]{1,32}
— it becomes a cache path and a SQL identifier).
import { defineConfig } from '@babystack/core' export default defineConfig({ services: { db: { engine: 'mysql', // the only built engine (others reserved) image: 'mysql:8.4', // any real mysql tag database: 'app', // optional logical DB name baseline: { build: ['pnpm db:migrate', 'pnpm db:seed'], // change any matched file → rebuild the baseline invalidateWhenChanged: ['drizzle/**', 'db/seed.*'], }, test: { cleanup: 'destroy' }, // or 'keep' / 'keep-on-failure' }, }, })
Fields
| Field | Type | What it does |
|---|---|---|
engine |
required |
'mysql' today. The union reserves
redis,
minio,
dynamodb-local,
elasticmq,
localstack for the roadmap.
|
image |
optional |
The Docker image tag — any real
mysql:*. Defaults to a pinned tag.
|
database |
optional | Logical database name (MySQL service only). |
baseline.build |
string[] | Your own shell commands (migrate + seed), run once against a throwaway build DB. babystack never edits them. |
baseline.strategy |
optional |
'logical-dump' (the only value in Phase
0 — mysqldump → reload).
|
baseline.invalidateWhenChanged |
glob[] | File globs whose content feeds the invalidation hash — a change rebuilds the baseline. |
test.reset |
optional |
'snapshot' — a fresh per-worker DB
reloaded from the baseline per test file.
|
test.cleanup |
optional |
'destroy' (default) ·
'keep-on-failure' ·
'keep' (leave the container up to
inspect).
|
Vitest integration
Three lines in vitest.config.ts — the entire
test-infra change. No test file imports babystack or manages a database.
import { defineConfig } from 'vitest/config' export default defineConfig({ test: { globalSetup: ['@babystack/vitest/global-setup'], // provision + baseline, once setupFiles: ['@babystack/vitest/setup'], // fresh DB + DATABASE_URL, per file pool: 'forks', // per-worker DB via VITEST_POOL_ID }, })
globalSetup (main process) provisions the container and builds the
baseline once, then hands coordinates to workers. setupFiles (each
worker) opens a fresh lease and sets
DATABASE_URL via top-level await —
before your app imports — so the app reads it exactly like production.
CLI — baby
A persistent seeded MySQL outside the test run — for local dev and coding agents.
Every command speaks --json for CI and agents.
| Command | Alias | What it does |
|---|---|---|
baby doctor |
— |
Check Docker, Node, and your
babystack.config.ts (incl. an
import-time DATABASE_URL preflight).
|
baby wake |
up |
Provision + seed a real MySQL and leave it running. |
baby home |
env |
Print an eval-able DATABASE_URL for the
running stack (non-destructive).
|
baby reset |
— | Reload a pristine DB from the baseline — same URL, no re-provision. The agent's undo. |
baby sleep |
down |
Dispose this project's running stack (container + volume). |
The container is rediscovered across invocations by a stable project label; the
minted password is recovered from
docker inspect, never written to a file.
The agent loop
A coding agent gets a real backend it can break across many commands, then
reset to pristine between attempts — the
connection URL never changes.
baby wake # provision + seed a real MySQL, left running eval "$(baby home)" # export DATABASE_URL for this shell # … the agent writes / migrates / queries, checks the result … baby reset # back to the pristine baseline, same URL — try again baby sleep # done: dispose the container
Troubleshooting
babystack fails with typed error codes, not opaque strings. The common ones:
| Code | Meaning & fix |
|---|---|
DOCKER_UNAVAILABLE |
The Docker engine isn't reachable. Start Docker/Colima; confirm with
baby doctor.
|
WAIT_READY_TIMEOUT |
MySQL didn't accept authenticated connections in time — usually a slow first boot or an overloaded Docker VM. Retry; give the VM more CPU/RAM. |
BASELINE_BUILD_FAILED |
One of your baseline.build commands
failed. Run the migrate/seed commands by hand to see the real error.
|
BASELINE_CORRUPT |
A cached baseline failed its checksum — it will never silently load wrong
seed state. Delete .babystack/cache to
rebuild.
|
CONFIG_INVALID |
Bad babystack.config.ts — no services,
an unknown engine, or an illegal service name. The message says which.
|
ENV_READ_TOO_EARLY |
baby doctor found code reading
DATABASE_URL at import time — read it
lazily (inside a function) so the injected value is seen.
|
VITEST_POOL_ID missing |
The worker couldn't find its pool id — set
pool: 'forks' in
vitest.config.ts.
|
Still stuck? Open an issue on GitHub — pre-alpha, and feedback shapes the order.