docs

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.

config builder

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.

babystack.config.ts

            

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.