All scripts run under Bun. Two conventions apply everywhere:
- Destructive CLIs are dry-run by default and only act with
--execute (dev:refresh, queue:admin, backfill-cfdi-encryption, enrich-viewports). There are no interactive prompts; each prints an env banner before acting.
- Env loading depends on cwd: root
scripts/* rely on Bun auto-loading .env.local from the repo root; package-local scripts are registered with --env-file=../../.env.local in their package.json, so always run them via bun run <script-name> from the package directory.
bun run dev:refresh (scripts/dev-refresh.ts) resets the entire dev environment in one command:
| Order | Step | What happens |
|---|
| 1 | stream | Full sweep — hard-deletes ALL GetStream messaging channels, then all users (app is dev-dedicated) |
| 2 | knock | Full sweep — deletes ALL Knock users in the environment (workflows/layouts untouched) |
| 3 | stripe | Deletes ONLY customers referenced by the local DB — the test account is shared with staging, so there is no sweep mode by design; products/prices are never touched |
| 4 | facturapi | Cancels test CFDIs (motive 02), then deletes customers referenced by the local DB |
| 5 | storage | Deletes the R2 app-upload prefixes (properties/, avatars/, agent-avatars/, teams/, floor_plans/) — never search-seeds/, the Meili restore artifacts |
| 6 | db | Runs bun run db:reset in packages/database as a subprocess (drop schema public + migrate) |
| 7 | valkey | FLUSHDB on db0 (cache + psearch:*), then obliterates the six suffixed BullMQ queues on db1 (never FLUSHDB there) |
| 8 | meili | Deletes the locations / service_areas / agents indexes; the worker’s ensureSearchIndexes() restores them at boot |
Flags: --execute (required to act), --only <csv>, --skip-<step>, --allow-remote-db (needed on the Dokploy dev stack — Postgres/Valkey/Meili hosts aren’t localhost), --allow-unsuffixed, --keep-going.
Guards: refuses under NODE_ENV=production, with a non-sk_test_ Stripe or Knock key, an sk_live_ FacturAPI key, or a data-store host that is not provably local without the flag. Note the FacturAPI check is a denylist (sk_live_), not the allowlist Stripe and Knock get — a key in neither format passes. A DATABASE_URL / VALKEY_URL / MEILISEARCH_HOST that is unset is skipped, but one that is set and unparsable (or scheme-less, which parses to an empty host) is treated as non-local and blocked. Because Stream keys and bucket names carry no dev/prod marker, --execute additionally requires the sweep-target pins in .env.local: DEV_REFRESH_STREAM_KEY must match PUBLIC_GETSTREAM_API_KEY and DEV_REFRESH_BUCKET must match AWS_S3_BUCKET_NAME — a production env copied into .env.local fails the match instead of being swept. External ids are collected before the schema drop, and recorded external failures abort before the DB is touched — unless --keep-going, and excepting CFDI cancellation failures, which are warnings by design (a stamped CFDI that refuses cancellation can’t be fixed by re-running) and so do not block the drop. An orphan-risk guard refuses to drop the DB while Stripe/FacturAPI are unconfigured but the DB still references their objects — skipping those steps silently would strand the external records forever.
After an execute run: restart the worker (obliterate removed the repeatable schedulers; boot re-registers them and reseeds Meilisearch). There is no seed script — everything starts empty.
| Command | Purpose |
|---|
bun run db:reset | Drop schema public, rerun the ordered Kysely migration chain (scripts/db-reset.ts). Postgres only — pair with dev:refresh for the full environment |
bun run db:migrate | Run pending Kysely migrations to latest (scripts/migrate.ts) |
bun run db:codegen | Regenerate src/generated/db.ts from the live DB (kysely-codegen) |
bun run scripts/check-enum-array-parser.ts | Verify the "UserRole"[] enum-array node-pg parser fix (roles must decode as string[]) |
| Command | Purpose |
|---|
bun --env-file=../../.env.local run queue:admin <cmd> | Operational queue CLI — counts, inspect, retry-failed, promote, DLQ replay. See Incident guides |
bun run backfill:search | Enqueue sync-agent / sync-team for all public agents + teams → rebuilds the Meili agents index |
bun run backfill:properties | Enqueue sync-property for every ACTIVE property → rebuilds psearch:* |
bun run backfill:main-images | One-off repair: promote a cover image for properties with photos but no isMain row |
bun run search:schema | Property-search schema migration command (--dry-run / --enqueue / --verify) |
Backfills only enqueue — a running worker does the work.
| Command | Purpose |
|---|
bun run sync:locations | Wipe + rebuild the Meili locations index from the SEPOMEX XML (not in git — slow path; prefer the S3 seed restore) |
bun run sync:service-areas | Same wipe-and-rebuild for service_areas |
bun run export:search-seed | Export locations + service_areas to gzipped NDJSON seeds in R2 (search-seeds/) — what ensureSearchIndexes() restores from |
bun --env-file=../../.env.local run scripts/enrich-viewports.ts | Re-geocode _geo + viewport bounds via Google Geocoding (--execute gated) |
| Command | Purpose |
|---|
bun run scripts/sync-native-i18n.ts | Fan the shared i18n/{user,agent}/{en,es}.json catalogs out to the four native apps |
bun design-tokens/export-native-tokens.mjs | Regenerate Tokens.swift / Tokens.kt from design-tokens/tokens.cjs — commit the output |
bun run packages/contract/scripts/codegen-models.ts | Regenerate the native Swift/Kotlin DTOs from the @repo/contract Zod schemas |
bun run scripts/contract-fixtures/generate-fixtures.ts | Capture real tRPC response envelopes into committed golden fixtures for the native apps |
cd packages/contract && bun run fixtures:validate | Parse each committed fixture through its authoritative Zod schema — fails on contract drift |
| Command | Purpose |
|---|
bun run scripts/check-job-data-casts.ts | Enforce that BullMQ processors Zod-parse job.data instead of type-asserting (FSR-019/086) |
bun run scripts/native-core-drift/check.ts | Detect unported Core/ fixes across the four copy-and-adapt native apps |
bun run docs:check | Build the docs site, then run docs-stale-check.ts (stale references) + docs-link-check.ts (broken relative links) |
bun run scripts/generate-email-previews.ts | Regenerate the static email-templates/ previews from the real email builders — commit the output |
| Command | Purpose |
|---|
packages/api/scripts/parity-*.ts (14 scripts) | Behavior + atomicity tests for the Kysely-migrated services — run against a scratch DB (they write and clean up): DATABASE_URL=<scratch> bun run scripts/parity-leads.ts |
bun run packages/api/scripts/backfill-cfdi-encryption.ts | Encrypt pre-FSR-014 plaintext fiscal PII rows in place (--execute gated) |
bun run packages/server-services/scripts/backfill-inquiry-chat-channels.ts | One-off backfill of Inquiry.chatChannelId from derived GetStream channel ids (migration 0012) |
bun run scripts/firebase-distribute.ts | Build + upload a signed native app artifact to Firebase App Distribution. See Release playbooks |