dánwòdánwò

Project

Environments

Starting the app and the emulators a worker's tests run against.

A config's environment() starts what a worker's tests run against: the app plus provider emulators behind danwo's egress proxy. Each provider's seed takes the shape listed under its SeedConfig.

appEnvironment

function

appEnvironment( options: AppEnvironmentOptions = {}, ): Promise<WorkerEnvironment>

A worker environment for any app: the services behind one proxy, the app started through it (so its calls to Stripe, Resend and the rest reach the emulators with no code change), and the lifecycle danwo matrix needs. The app keeps running between tests and matrix trials; resets put the services back to their seeds.

NangoEnvironment

type

OAuthEnvironment<"nango">

What nangoEnvironment() starts: Nango, and any backends, behind one proxy.

nangoEnvironment

function

nangoEnvironment( options: NangoOptions & Unrouted = {}, ): Promise<NangoEnvironment>

A worker-scoped Nango emulator behind its own proxy, with its backends on the same proxy.

nangoService

function

nangoService(options: NangoOptions = {}): OAuthService

Nango for startServices: integrations, connect sessions and the hosted Connect UI, connections whose tokens refresh on the emulator's clock, the proxy, actions, records and signed auth webhooks on api.nango.dev and connect.nango.dev; gives tests the nango fixture. Read it and revoke connections with nangoProject(nango). List the backends before it.

NylasEnvironment

type

OAuthEnvironment<"nylas">

What nylasEnvironment() starts: the Nylas emulator behind its own proxy.

nylasEnvironment

function

nylasEnvironment(options: NylasOptions & Unrouted = {}): Promise<NylasEnvironment>

A worker-scoped Nylas emulator behind its own proxy; combine services with startServices.

nylasService

function

nylasService(options: NylasOptions = {}): OAuthService

Nylas for startServices: hosted auth and grants, mail, calendars and events, contacts, the scheduler and signed webhooks on api.us.nylas.com and api.eu.nylas.com; gives tests the nylas fixture. Read and act on it with nylasApplication(nylas).

ResendEnvironment

type

OAuthEnvironment<"resend">

What resendEnvironment() starts: the Resend emulator behind its own proxy.

resendEnvironment

function

resendEnvironment( options: ResendOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<ResendEnvironment>

A worker-scoped Resend emulator behind its own proxy; combine services with startServices.

resendService

function

resendService(options: ResendOptions = {}): OAuthService

Resend for startServices: sending email, domains, API keys and signed webhooks on api.resend.com; gives tests the resend fixture and resendMail(resend) as a mail source.

UnipileEnvironment

type

OAuthEnvironment<"unipile">

What unipileEnvironment() starts: the Unipile emulator behind its own proxy.

unipileEnvironment

function

unipileEnvironment( options: UnipileOptions & Unrouted = {}, ): Promise<UnipileEnvironment>

A worker-scoped Unipile emulator behind its own proxy; combine services with startServices.

unipileService

function

unipileService(options: UnipileOptions = {}): OAuthService

Unipile for startServices: hosted auth links, connected LinkedIn, WhatsApp and mail accounts, chats, messages, mail and webhooks on every *.unipile.com DSN; gives tests the unipile fixture. Act as the other side with unipileWorkspace(unipile).

stackEnvironment

function

stackEnvironment( options: StackEnvironmentOptions, ): Promise<WorkerEnvironment>

A worker environment for a stack of apps: the services behind one proxy, every app started through it in dependsOn order, and the lifecycle danwo matrix needs. Apps keep running between tests; resets put the services back to their seeds.

UnroutedHosts

interface

What happens to calls for hosts no service answers.

MemberDescription
unmatched?: "passthrough" | "block"passthrough (default) sends them to the real host; block answers 502. This machine (localhost, 127.0.0.1), where the apps run, always passes, and so does the allow list.
blocked?: readonly string[]Hosts answered as blocked even when others pass through, e.g. ANDROID_BACKGROUND_HOSTS.
allow?: readonly string[]Hosts that always pass through, even under unmatched: "block".
telemetry?: "block" | "passthrough"Analytics and telemetry hosts: blocked by default; passthrough lets them go.
assets?: "passthrough" | "block"A page's images, fonts, stylesheets and media from unrouted hosts while unmatched is block: passthrough (default) lets them load, so the page looks right; block refuses them. Scripts and the page's fetch() calls stay blocked either way.

InngestService

interface

extends Service

What inngestService() returns: the service, and the environment the apps need.

MemberDescription
env: Readonly<Record<string, string>>Environment for every app that sends events or serves functions: the dev-mode flag and the routed base URL, e.g. env: { ...inngest.env }.

inngestService

function

inngestService(options: InngestOptions = {}): Promise<InngestService>

Inngest for startServices: events the apps send to inngest.danwo.test reach the dev server through the proxy, settle waits for the runs they triggered, and state lists those runs.

Auth0Environment

type

OAuthEnvironment<"auth0">

What auth0Environment() starts: the Auth0 emulator behind its own proxy.

auth0Environment

function

auth0Environment( options: Auth0Options & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<Auth0Environment>

A worker-scoped Auth0 emulator behind its own proxy; combine services with startServices.

auth0Service

function

auth0Service(options: Auth0Options = {}): OAuthService

Auth0 for startServices: Universal Login (the browser logs in, signs up and passes MFA on the tenant's domain through the same proxy), the authentication API's grants, OIDC discovery and keys, logout and the Management API v2; gives tests the auth0 fixture. Read users, logins and verification emails with auth0Tenant(auth0).

AwsEnvironment

interface

What awsEnvironment() starts: AWS hosts sent to MiniStack through its own proxy.

MemberDescription
endpoint: string
proxy: Proxy
travel: (ms: number) => Promise<void>Moves the app's clock and the emulator's time forward by ms; see time.travel.
reset(): Promise<void>MiniStack wiped (unless reset: false), proxy log and fault rules cleared.
fixtures: { readonly aws: AwsFixture readonly proxy: Proxy readonly settle: () => Promise<void> }Fixtures for tests: aws, proxy, settle.
close(): Promise<void>

awsEnvironment

function

awsEnvironment( options: AwsOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<AwsEnvironment>

A worker-scoped MiniStack behind its own danwo proxy. To combine it with emulators behind one proxy (e.g. Stripe and S3 emulators, MiniStack for SQS), use awsService with startServices.

AwsOptions

interface

Options of awsService() and awsEnvironment(): where MiniStack runs and what it answers.

MemberDescription
endpoint?: stringA MiniStack (or LocalStack) the user runs, e.g. http://127.0.0.1:4566. Ignored when docker is set.
docker?: boolean | { readonly image?: string }Start a MiniStack container for this service and remove it on close. Each worker then has its own, so resets never touch another worker's state. true uses MINISTACK_IMAGE.
hosts?: readonly string[]Hosts sent to MiniStack. Default *.amazonaws.com (every AWS service and region).
reset?: booleanWipe MiniStack's state between danwo matrix runs with POST /_ministack/reset (default true). The reset is global: with a shared endpoint and several workers, turn it off or use docker.

awsService

function

awsService(options: AwsOptions = {}): Promise<Service & AwsFixture>

AWS through MiniStack for startServices: AWS hosts are forwarded to a MiniStack the user runs (endpoint) or one started per worker (docker), with their original Host, so an unmodified app keeps calling sqs.us-east-1.amazonaws.com or <bucket>.s3.amazonaws.com.

EnvironmentContext

interface

What a config's environment() is called with, once per worker.

MemberDescription
workerIndex: number0-based worker slot; use it to keep ports and data apart between workers.

FlutterwaveEnvironment

type

OAuthEnvironment<"flutterwave">

What flutterwaveEnvironment() starts: the Flutterwave emulator behind its own proxy.

flutterwaveEnvironment

function

flutterwaveEnvironment( options: FlutterwaveOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<FlutterwaveEnvironment>

A worker-scoped Flutterwave emulator behind its own proxy; combine services with startServices.

flutterwaveService

function

flutterwaveService(options: FlutterwaveOptions = {}): OAuthService

Flutterwave for startServices: Standard payment links with their hosted checkout (the browser pays on checkout.flutterwave.com through the same proxy), verification, refunds, plans and subscriptions, transfers and virtual accounts, with webhooks and transfer settlements on a manual clock that settle plays to the end; gives tests the flutterwave fixture. Read the books with flutterwaveLedger(flutterwave).

GithubEnvironment

type

OAuthEnvironment<"github">

What githubEnvironment() starts: the GitHub emulator behind its own proxy.

githubEnvironment

function

githubEnvironment( options: GithubOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<GithubEnvironment>

A worker-scoped GitHub emulator behind its own proxy; combine services with startServices.

GithubOptions

interface

Options of githubService() and githubEnvironment().

MemberDescription
seed?: Seed<GithubSeedConfig>Users, orgs, repos, OAuth and GitHub Apps, installations and repository webhooks.

githubService

function

githubService(options: GithubOptions = {}): OAuthService

GitHub for startServices: sign-in, GitHub Apps, the REST API and signed webhooks; gives tests the github fixture.

ComposioEnvironment

type

OAuthEnvironment<"composio">

What composioEnvironment() starts: Composio, and any backends, behind one proxy.

composioEnvironment

function

composioEnvironment( options: ComposioOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<ComposioEnvironment>

A worker-scoped Composio emulator behind its own proxy, with its backends on the same proxy; combine services with startServices instead when the app talks to more.

composioService

function

composioService(options: ComposioOptions = {}): OAuthService

Composio for startServices: toolkits and tools, tool execution, auth configs, connected accounts with hosted connect links (the browser connects through the same proxy), triggers and the project's signed webhooks; gives tests the composio fixture. Read accounts and tool runs, and fire triggers, with composioProject(composio); answer tools with composioTools(composio). List the backends before it when starting the services together.

NovuEnvironment

type

OAuthEnvironment<"novu">

What novuEnvironment() starts: the Novu emulator behind its own proxy.

novuEnvironment

function

novuEnvironment(options: NovuOptions & Unrouted = {}): Promise<NovuEnvironment>

A worker-scoped Novu emulator behind its own proxy; combine services with startServices.

novuService

function

novuService(options: NovuOptions = {}): OAuthService

Novu for startServices: the server API the app's backend triggers workflows and manages subscribers with, code-first workflows run through the app's bridge (seed.bridgeUrl), and the Inbox's API and socket the browser uses; gives tests the novu fixture. settle waits for the workflow runs. Read runs and messages with novuProject(novu).

PusherEnvironment

type

OAuthEnvironment<"pusher">

What pusherEnvironment() starts: the Pusher emulator behind its own proxy.

pusherEnvironment

function

pusherEnvironment( options: PusherOptions & Unrouted = {}, ): Promise<PusherEnvironment>

A worker-scoped Pusher emulator behind its own proxy; combine services with startServices.

pusherService

function

pusherService(options: PusherOptions = {}): OAuthService

Pusher for startServices: the Channels API the app's server triggers events on, the socket pusher-js connects to from the browser, signed webhooks and Beams; gives tests the pusher fixture. Read what was published and who it reached with pusherChannels(pusher).

TiptapEnvironment

type

OAuthEnvironment<"tiptap">

What tiptapEnvironment() starts: the Tiptap emulator behind its own proxy.

tiptapEnvironment

function

tiptapEnvironment( options: TiptapOptions & Unrouted = {}, ): Promise<TiptapEnvironment>

A worker-scoped Tiptap emulator behind its own proxy; combine services with startServices.

tiptapService

function

tiptapService(options: TiptapOptions = {}): OAuthService

Tiptap Cloud collaboration for startServices: the socket editors sync documents over and the documents REST API on <appId>.collab.tiptap.cloud; gives tests the tiptap fixture. Read a document with tiptapCollab(tiptap).text(name).

GoogleEnvironment

type

OAuthEnvironment<"google">

What googleEnvironment() starts: the Google emulator behind its own proxy.

googleEnvironment

function

googleEnvironment( options: GoogleOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<GoogleEnvironment>

A worker-scoped Google emulator behind its own proxy; combine services with startServices.

GoogleOptions

interface

Options of googleService() and googleEnvironment().

MemberDescription
seed?: Seed<GoogleSeedConfig>OAuth clients, users, and their Gmail, Calendar, Drive and Pub/Sub push data.

googleService

function

googleService(options: GoogleOptions = {}): OAuthService

Google for startServices: sign-in, Gmail, Calendar and Drive on Google's real hosts; gives tests the google fixture.

LlmEnvironment

type

OAuthEnvironment<"llm">

What llmEnvironment() starts: the model API emulator behind its own proxy.

llmEnvironment

function

llmEnvironment( options: LlmOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<LlmEnvironment>

A worker-scoped model API emulator behind its own proxy; combine services with startServices.

llmService

function

llmService(options: LlmOptions = {}): OAuthService

Model APIs for startServices: api.openai.com, api.anthropic.com, ai-gateway.vercel.sh, generativelanguage.googleapis.com, api.perplexity.ai and bedrock-runtime.<region>.amazonaws.com answered from the seed's scripts, streamed when asked; gives tests the llm fixture. Read the calls back with llmCalls(llm) and add scripts with llmScripts(llm).

MicrosoftEnvironment

type

OAuthEnvironment<"microsoft">

What microsoftEnvironment() starts: the Microsoft emulator behind its own proxy.

microsoftEnvironment

function

microsoftEnvironment( options: MicrosoftOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<MicrosoftEnvironment>

A worker-scoped Microsoft emulator behind its own proxy; combine services with startServices.

MicrosoftOptions

interface

Options of microsoftService() and microsoftEnvironment().

MemberDescription
seed?: Seed<MicrosoftSeedConfig>Registrations, users, and their Outlook mail, calendar events and OneDrive files.

microsoftService

function

microsoftService(options: MicrosoftOptions = {}): OAuthService

Microsoft for startServices: sign-in and Graph mail, calendar, OneDrive and subscriptions; gives tests the microsoft fixture.

MINISTACK_IMAGE

value

"ministackorg/ministack:1.5.28"

The MiniStack image docker: true runs; MiniStack is MIT-licensed (ministack.org).

PaystackEnvironment

type

OAuthEnvironment<"paystack">

What paystackEnvironment() starts: the Paystack emulator behind its own proxy.

paystackEnvironment

function

paystackEnvironment( options: PaystackOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<PaystackEnvironment>

A worker-scoped Paystack emulator behind its own proxy; combine services with startServices.

paystackService

function

paystackService(options: PaystackOptions = {}): OAuthService

Paystack for startServices: the API and the hosted payment page the customer's browser opens, with events, transfers, refunds and subscription renewals on a manual clock that settle plays to the end; gives tests the paystack fixture. Read what was paid with paystackLedger(paystack).

S3Environment

interface

What s3Environment() starts: the S3 emulator behind its own proxy.

MemberDescription
emulator: Emulator
proxy: Proxy
env(options?: { readonly endpoint?: string }): Record<string, string>The S3 and Tigris variables for the app; see S3Service.env.
travel: (ms: number) => Promise<void>Moves the app's clock and the emulator's time forward by ms; see time.travel.
reset(): Promise<void>Emulator back to its seed, proxy log and fault rules cleared.
snapshot(slot?: string): Promise<void>Keeps the emulator's state now (right after a reset), for restore.
restore(slot?: string): Promise<void>Emulator back to the kept state, faster than a reset; proxy log and fault rules cleared.
snapshotSlots: booleanWhether snapshot and restore keep a state per slot.
fixtures: { readonly s3: Emulator readonly proxy: Proxy readonly settle: () => Promise<void> }Fixtures for tests: s3 (the emulator), proxy, settle.
close(): Promise<void>

s3Environment

function

s3Environment( options: S3Options & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<S3Environment>

A worker-scoped S3 emulator behind its own danwo proxy. To combine it with other services behind one proxy, use s3Service with startServices.

S3Options

interface

Options of s3Service() and s3Environment(): buckets to seed and endpoints to answer.

MemberDescription
seed?: S3SeedConfigBuckets (and objects) to create on start and after every reset.
endpoints?: readonly string[]S3 endpoints the emulator answers, path-style and virtual-hosted (<bucket>.<endpoint>). Default: Tigris (t3.storage.dev, fly.storage.tigris.dev), s3.amazonaws.com and s3.us-east-1.amazonaws.com; add s3.<region>.amazonaws.com for other regions.

s3Service

function

s3Service(options: S3Options = {}): S3Service

An S3 emulator for startServices, for apps on AWS S3 or Tigris; gives tests the s3 fixture.

Seed

type

Config | (() => Config | undefined)

Seed data applied on start and after every reset. A function runs each time, so it can use values only known once the app is up, such as its push or notification URL.

Services

interface

Services started together; its fields slot straight into a WorkerEnvironment.

MemberDescription
proxy: Proxy
webhooks: Readonly<Record<string, WebhookDispatcher>> | undefinedEvery webhook-delivering service's dispatcher, by service name; undefined when none deliver.
fixtures: Readonly<Record<string, unknown>>Every service's fixtures, plus proxy and settle.
mail?: readonly MailSource[]Every service's mail sources, so the mail fixture and the agent read what they send.
state(): Readonly<Record<string, unknown>>Every service's state(), by service name.
settle(): Promise<readonly string[]>Lets every service's background work finish, and says what each waited for and how long.
travel(ms: number): Promise<void>Moves the apps' clocks (through the proxy) and every service's time forward by ms.
reset(): Promise<void>Every service back to its seed; proxy log, fault rules and the apps' clock offset cleared.
snapshot(slot?: string): Promise<void>Keeps every service's state now, for restore, under slot when they keep slots.
restore(slot?: string): Promise<void>Every service back to its kept state (or its seed, when it keeps none); log and faults cleared.
snapshotSlots: booleanWhether every service that keeps a state keeps one per slot.
close(): Promise<void>

SlackEnvironment

type

OAuthEnvironment<"slack">

What slackEnvironment() starts: the Slack emulator behind its own proxy.

slackEnvironment

function

slackEnvironment( options: SlackOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<SlackEnvironment>

A worker-scoped Slack emulator behind its own proxy; combine services with startServices.

SlackOptions

interface

Options of slackService() and slackEnvironment().

MemberDescription
seed?: Seed<SlackSeedConfig>The team, users, channels, apps and installs; app URLs for events, commands and interactivity.

slackService

function

slackService(options: SlackOptions = {}): OAuthService

Slack for startServices: OAuth installs, the Web API, incoming webhooks and signed events; gives tests the slack fixture.

startServices

function

startServices( services: readonly Service[], options: UnroutedHosts = {}, ): Promise<Services>

Starts one proxy in front of services, e.g. a Stripe emulator, an S3 emulator and MiniStack for the rest of AWS. Other hosts pass through unless unmatched is block.

StripeEnvironment

interface

What stripeEnvironment() starts: the Stripe emulator behind its own proxy, on a manual clock.

MemberDescription
emulator: Emulator
proxy: Proxy
clock: ManualClock
settle: () => Promise<readonly string[]>Plays every pending webhook delivery and retry to the end on the manual clock.
travel: (ms: number) => Promise<void>Moves the app's clock and the emulator's time forward by ms; see time.travel.
reset(): Promise<void>Emulator back to its seed, proxy log and fault rules cleared.
snapshot(slot?: string): Promise<void>Keeps the emulator's state now (right after a reset), for restore.
restore(slot?: string): Promise<void>Emulator back to the kept state, faster than a reset; proxy log and fault rules cleared.
snapshotSlots: booleanWhether snapshot and restore keep a state per slot.
fixtures: { readonly stripe: Emulator readonly proxy: Proxy readonly settle: () => Promise<void> }Fixtures for tests: stripe (the emulator), proxy, settle.
close(): Promise<void>

stripeEnvironment

function

stripeEnvironment( options: StripeOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<StripeEnvironment>

A worker-scoped Stripe emulator behind its own danwo proxy: api.stripe.com and checkout.stripe.com route to it, other hosts pass through. To combine it with other services behind one proxy, use stripeService with startServices.

StripeOptions

interface

Options of stripeService() and stripeEnvironment().

MemberDescription
seed?: () => StripeSeedConfig | undefinedRuns on start and after every reset; a function, so it can use values only known later (the app's webhook URL).

stripeService

function

stripeService(options: StripeOptions = {}): StripeService

A Stripe emulator for startServices: answers api.stripe.com, checkout.stripe.com and js.stripe.com, delivers webhooks on a manual clock, and gives tests the stripe fixture.

TwilioEnvironment

type

OAuthEnvironment<"twilio">

What twilioEnvironment() starts: the Twilio emulator behind its own proxy.

twilioEnvironment

function

twilioEnvironment( options: TwilioOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<TwilioEnvironment>

A worker-scoped Twilio emulator behind its own proxy; combine services with startServices.

twilioService

function

twilioService(options: TwilioOptions = {}): OAuthService

Twilio for startServices: messages with status callbacks, texts to the app's numbers and Verify, with message statuses moving on a manual clock that settle plays to the end; gives tests the twilio fixture. Read texts and codes with twilioInbox(twilio), or with twilioSms(twilio) as a session's message source.

BachsEnvironment

type

OAuthEnvironment<"bachs">

What bachsEnvironment() starts: the Bachs emulator behind its own proxy.

bachsEnvironment

function

bachsEnvironment( options: BachsOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<BachsEnvironment>

A worker-scoped Bachs emulator behind its own proxy; combine services with startServices.

bachsService

function

bachsService(options: BachsOptions = {}): OAuthService

Bachs for startServices: the API on its live and sandbox hosts, the hosted checkout, overlay script, recovery page and customer portal, with payments settling, subscriptions renewing and payouts completing on a manual clock that settle plays to the end; gives tests the bachs fixture. Read what happened with bachsLedger(bachs).

WorkerEnvironment

interface

What a worker runs its tests against, set up once per worker by the config's environment hook: the app's URL, the proxy its browser and app go through, emulators, and any values tests receive as fixtures. Worker-scoped: it lives as long as the worker and is reset between matrix runs.

MemberDescription
baseUrl?: string | undefinedThe app's base URL; overrides the config's baseUrl.
proxy?: ProxyRoutes the browser (and the app, through its own env) to emulators. Required for danwo matrix.
webhooks?: RunWebhooks | undefinedWebhook delivery to fault in danwo matrix: one dispatcher, or several keyed by service ({ stripe, resend }). Without it the matrix plans outbound faults only.
data?: readonly DataProxy[] | undefinedProxies in front of the app's own database and cache, e.g. startPostgresProxy(…): the app connects through them, and danwo matrix faults the statements and commands its tests send (a lost COMMIT reply, a serialization failure, a cache that answers OOM).
device?: DeviceTargetA mobile device the worker's tests run on instead of a browser, e.g. androidDevice() from @danwo/device: each attempt resets and launches the app on it, and screen, app and expect act on the device.
fixtures?: Readonly<Record<string, unknown>>Extra fixtures every test in this worker receives, e.g. stripe, inbox, settle.
mail?: readonly MailSource[]Where mail the app sends lands, e.g. [resendMail(resend)]: tests read it through the mail fixture, and the agent reads codes and links from it that replays read again.
settle?(): Promise<void | readonly string[]>Lets pending background work finish (e.g. webhook deliveries on a manual clock) before a test's invariant or a matrix run's evidence is read. It may say what it waited for, one line each; the flight record shows that on the settle step.
travel?(ms: number): Promise<void>Moves time forward by ms for everything behind the app: the apps' clocks (through the proxy) and every emulator's clock, running renewals, retries and expiries as they fall due. The service environments (stripeEnvironment, startServices…) provide it; without it, time.travel moves only the apps' clocks and the browser's.
reset?(): Promise<void>Back to a clean state (emulators to their seeds, app state, fault rules), before the tests and matrix trials that need one; see the config's isolation.
snapshot?(slot?: string): Promise<void>Keeps the state it is in now for restore() to return to: e.g. emulator stores in memory, a Postgres template database, a copy of a data folder. Called right after a reset(), and again after setup the next tests start from (a file's beforeAll, a saved session's sign-in). Optional, and only used together with restore. With snapshotSlots, slot names which of several kept states this one is (pristine, file, pristine+<session>, ...); without it there is one, and slot is not passed.
restore?(slot?: string): Promise<void>Back to what snapshot() kept (in slot, with snapshotSlots), faster than reset(). A worker resets its environment once, snapshots it, and restores it before every later test and trial. Like reset, it also clears the proxy's log, fault rules and the apps' clock offset. The app's own memory is the environment's to reset here (e.g. by calling a test-reset endpoint). Without it, each test resets.
snapshotSlots?: booleanWhether snapshot(slot) and restore(slot) keep a separate state per slot. With it, a saved session's sign-in is kept beside the seeded state, so a test that names the session starts signed in and one that does not starts from the seeded state. Without it, there is one kept state: the seeded one, and a session signs in again for every test that names it.
notes?(): readonly string[]Facts to print next to a matrix run's verdict.
stopped?(): readonly string[]Apps the environment started that have since stopped on their own, e.g. api exited with code 1: a test that fails after one went down says so, rather than leaving only its symptoms (a refused connection, a page that never loads).
logs?(): Readonly<Record<string, ProcessLog>>What each app the environment started has written, by app: the flight record keeps the lines written during each run.
state?(): Readonly<Record<string, unknown>>State the flight recorder keeps when a run ends, by service (what an invariant reads from). Default: a snapshot of every emulator among fixtures.
close(): Promise<void>

postgresSnapshot

function

postgresSnapshot(options: PostgresSnapshotOptions): DatabaseSnapshot

A snapshot of a Postgres database for an environment's snapshot() and restore(). Needs a role that may create databases, and Postgres 14 or later.

ts const db = postgresSnapshot({ url: process.env.DATABASE_URL }) return { ..., snapshot: (slot) => db.snapshot(slot), restore: (slot) => db.restore(slot), snapshotSlots: true, close: () => db.close() }

postgresWorkerDatabase

function

postgresWorkerDatabase( url: string, workerIndex: number, options: WorkerDatabaseOptions = {}, ): Promise<string>

A database of its own for one worker, <base>_w<workerIndex>, copied fresh from base, so parallel workers never share rows. Resolves to its connection URL. Run migrations and seeds on base first: nobody may be connected to it while the copy is made.

ts async environment({ workerIndex }) { const url = await postgresWorkerDatabase(process.env.DATABASE_URL, workerIndex) const db = postgresSnapshot({ url }) // start the app with DATABASE_URL=url, then snapshot and restore `db` }

redisSnapshot

function

redisSnapshot(options: RedisSnapshotOptions): DatabaseSnapshot

A snapshot of one Redis or Valkey database for an environment's snapshot() and restore().

ts const cache = redisSnapshot({ url: "redis://127.0.0.1:6379/0" })

sqliteSnapshot

function

sqliteSnapshot(options: SqliteSnapshotOptions): DatabaseSnapshot

A snapshot of a SQLite database file for an environment's snapshot() and restore().

ts const db = sqliteSnapshot({ file: "data/app.db" }) return { ..., snapshot: (slot) => db.snapshot(slot), restore: (slot) => db.restore(slot), snapshotSlots: true, close: () => db.close() }

sqliteWorkerFile

function

sqliteWorkerFile(file: string, workerIndex: number): Promise<string>

A copy of a SQLite database for one worker, e.g. of a seeded database every worker starts from: await sqliteWorkerFile("seed.db", workerIndex) resolves to seed.w0.db. Made with SQLite's backup, so rows still in the write-ahead log are copied too.

ClerkEnvironment

type

OAuthEnvironment<"clerk">

What clerkEnvironment() starts: the Clerk emulator behind its own proxy.

clerkEnvironment

function

clerkEnvironment( options: ClerkOptions & { readonly unmatched?: "passthrough" | "block" } = {}, ): Promise<ClerkEnvironment>

A worker-scoped Clerk emulator behind its own proxy; combine services with startServices.

clerkService

function

clerkService(options: ClerkOptions = {}): OAuthService

Clerk for startServices: a development instance on its real hosts. The app's server reaches the Backend API on api.clerk.com; the browser loads ClerkJS and <SignIn/> from the Frontend API on <slug>.clerk.accounts.dev, or signs in on the Account Portal at <slug>.accounts.dev, through the same proxy. With social, its "Continue with Google" signs in on the Google emulator. Its webhooks go to the seeded endpoints, signed the Svix way. Gives tests the clerk fixture (read keys, sessions and codes with clerkInstance(clerk)), and the codes it emails reach the mail fixture and the agent.

StripeSeedConfig

type

Partial<{ readonly customers: readonly SeedCustomer[] readonly products: readonly SeedProduct[] readonly prices: readonly SeedPrice[] readonly webhookEndpoints: readonly SeedWebhookEndpoint[] readonly paymentMethodConfigurations: readonly SeedPaymentMethodConfiguration[] }>

Data to load into a Stripe emulator, on top of its defaults.

ResendSeedConfig

type

Partial<{ /** When set, only these addresses may receive mail, as on a sandboxed account. */ readonly allowlist: readonly string[] /** Sending domains, in `us-east-1` unless a `region` says otherwise. */ readonly domains: readonly { readonly name: string readonly region?: "us-east-1" | "eu-west-1" | "sa-east-1" | "ap-northeast-1" }[] /** * API keys the account has issued. Any `re_` key is accepte…

Data to load into a Resend emulator.

S3SeedConfig

type

Partial<{ readonly buckets: readonly { readonly name: string readonly region?: string readonly acl?: Acl readonly objects?: readonly SeedObject[] }[] /** * Access keys whose signatures are checked: a request signed with one of these must carry the * signature its secret makes, and once any is seeded an unknown key is refused * (InvalidAccessKeyId). Without any, every well-formed signature is acce…

Buckets (and objects in them) to load into an S3 emulator, and the access keys it knows.

GoogleSeedConfig

type

Partial<{ readonly users: readonly GoogleSeedUser[] readonly oauth_clients: readonly GoogleSeedClient[] /** Mailbox contents by account email. */ readonly gmail: Readonly<Record<string, readonly GmailSeedMessage[]>> /** Where Gmail's Pub/Sub notifications are pushed. */ readonly pubsub: readonly PubSubSeedSubscription[] /** Primary calendar events by account email. */ readonly calendar: Readonly<…

Accounts, OAuth clients and API data to load into a Google emulator.

MicrosoftSeedConfig

type

Partial<{ readonly users: readonly MicrosoftSeedUser[] readonly oauth_clients: readonly MicrosoftSeedClient[] readonly mail: readonly MicrosoftSeedMessage[] readonly events: readonly MicrosoftSeedEvent[] readonly files: readonly MicrosoftSeedFile[] }>

Accounts, app registrations and their mail, calendars and files, to load into an emulator.

SlackSeedConfig

type

Partial<{ readonly team: { readonly name: string } & Partial<Readonly<Record<"id" | "domain", string>>> readonly users: readonly SlackSeedUser[] readonly channels: readonly SlackSeedChannel[] readonly apps: readonly SlackSeedApp[] readonly installations: readonly SlackSeedInstall[] }>

A workspace to load into a Slack emulator.

GithubSeedConfig

type

Maybe<{ users: readonly GithubSeedUser[] orgs: readonly GithubSeedOrg[] repos: readonly GithubSeedRepo[] issues: readonly GithubSeedIssue[] oauthApps: readonly GithubSeedOAuthApp[] apps: readonly GithubSeedApp[] installations: readonly GithubSeedInstallation[] tokens: readonly GithubSeedToken[] hooks: readonly GithubSeedHook[] }>

People, organisations, repositories, apps and credentials to load into a GitHub emulator.

On this page

Run it on every pull request

A GitHub check and one comment per pull request, with the flight records attached.

Set up CI