@rebasepro/cli 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/bin/rebase.js +143 -4
- package/dist/{agent-setup-B5AKHOgt.js → agent-setup-D9mHRZ5R.js} +125 -3
- package/dist/agent-setup-D9mHRZ5R.js.map +1 -0
- package/dist/{api-keys-Tm7zvAPe.js → api-keys-CyJ26KEJ.js} +41 -10
- package/dist/api-keys-CyJ26KEJ.js.map +1 -0
- package/dist/{apps-BsU_ILbP.js → apps-CnLDV5b6.js} +6 -7
- package/dist/{apps-BsU_ILbP.js.map → apps-CnLDV5b6.js.map} +1 -1
- package/dist/{atlas-dev-database-OWtqnG7W.js → atlas-dev-database-BODqdCWt.js} +4 -4
- package/dist/{atlas-dev-database-OWtqnG7W.js.map → atlas-dev-database-BODqdCWt.js.map} +1 -1
- package/dist/{auth-Vl2hFKXP.js → auth-dII2jCO1.js} +10 -8
- package/dist/auth-dII2jCO1.js.map +1 -0
- package/dist/{branch-pointer-9DnwkFJJ.js → branch-pointer-BQUF6lYa.js} +1 -1
- package/dist/{build-BBgT8EJZ.js → build-Cza7ZQrp.js} +6 -7
- package/dist/{build-BBgT8EJZ.js.map → build-Cza7ZQrp.js.map} +1 -1
- package/dist/{bundle-DtCCCr4w.js → bundle-C6yEtMJ-.js} +53 -5
- package/dist/bundle-C6yEtMJ-.js.map +1 -0
- package/dist/bundle.d.ts +14 -0
- package/dist/{cloud-BpH0_bmV.js → cloud-BdnmqQaZ.js} +8528 -4342
- package/dist/cloud-BdnmqQaZ.js.map +1 -0
- package/dist/collections-dir-DlvDhime.js +69 -0
- package/dist/collections-dir-DlvDhime.js.map +1 -0
- package/dist/commands/agent-setup.d.ts +58 -0
- package/dist/commands/api-keys.d.ts +13 -0
- package/dist/commands/cloud/activity.d.ts +25 -0
- package/dist/commands/cloud/app-key.d.ts +18 -0
- package/dist/commands/cloud/auth.d.ts +1 -0
- package/dist/commands/cloud/billing-invoices.d.ts +39 -0
- package/dist/commands/cloud/claim.d.ts +7 -0
- package/dist/commands/cloud/context.d.ts +115 -6
- package/dist/commands/cloud/deploy-summary.d.ts +162 -0
- package/dist/commands/cloud/deploy.d.ts +64 -0
- package/dist/commands/cloud/env.d.ts +24 -0
- package/dist/commands/cloud/index.d.ts +13 -1
- package/dist/commands/cloud/init.d.ts +143 -0
- package/dist/commands/cloud/login-flow.d.ts +51 -0
- package/dist/commands/cloud/notifications.d.ts +28 -0
- package/dist/commands/cloud/orgs.d.ts +13 -0
- package/dist/commands/cloud/previews.d.ts +123 -0
- package/dist/commands/cloud/resources.d.ts +68 -14
- package/dist/commands/cloud/runtime-logs.d.ts +176 -0
- package/dist/commands/cloud/service-key.d.ts +15 -0
- package/dist/commands/cloud/token-capabilities.d.ts +61 -8
- package/dist/commands/cloud/tokens.d.ts +18 -3
- package/dist/commands/cloud/usage.d.ts +63 -0
- package/dist/commands/dev.d.ts +22 -0
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/generate-view.d.ts +94 -0
- package/dist/commands/generate_sdk.d.ts +19 -2
- package/dist/commands/init.d.ts +20 -0
- package/dist/commands/skills.d.ts +4 -3
- package/dist/commands/start.d.ts +10 -0
- package/dist/commands/storage.d.ts +4 -0
- package/dist/commands/upgrade.d.ts +12 -0
- package/dist/commands/verify.d.ts +96 -0
- package/dist/{consent-ClRHmcxf.js → consent-BD7F29T3.js} +2 -2
- package/dist/{consent-ClRHmcxf.js.map → consent-BD7F29T3.js.map} +1 -1
- package/dist/{console-CEGm0XNc.js → console-BXB6-8GT.js} +12 -6
- package/dist/console-BXB6-8GT.js.map +1 -0
- package/dist/{context-CyflUrKg.js → context-GUxkZm-S.js} +482 -26
- package/dist/context-GUxkZm-S.js.map +1 -0
- package/dist/{daemon-CknkDjd6.js → daemon-Cp7oNXth.js} +160 -5
- package/dist/daemon-Cp7oNXth.js.map +1 -0
- package/dist/{daemon-entry-CbLRrgzy.js → daemon-entry-zOC1revO.js} +6 -3
- package/dist/daemon-entry-zOC1revO.js.map +1 -0
- package/dist/{db-CRNpA5mH.js → db-KzlqddGD.js} +101 -54
- package/dist/db-KzlqddGD.js.map +1 -0
- package/dist/{derive-CsoFGAnK.js → derive-ae9UOoOB.js} +2 -2
- package/dist/{derive-CsoFGAnK.js.map → derive-ae9UOoOB.js.map} +1 -1
- package/dist/{dev-C0pbMmhS.js → dev-DTqVMgB3.js} +231 -44
- package/dist/dev-DTqVMgB3.js.map +1 -0
- package/dist/dev-db/daemon.d.ts +41 -0
- package/dist/dev-db/pull.d.ts +33 -5
- package/dist/dev-db/state.d.ts +9 -0
- package/dist/{dev-preflight-unHpiYO1.js → dev-preflight-Cxv3TiD1.js} +73 -27
- package/dist/dev-preflight-Cxv3TiD1.js.map +1 -0
- package/dist/{dev-server-B1UYifZJ.js → dev-server-OJsUGZEB.js} +14 -3
- package/dist/dev-server-OJsUGZEB.js.map +1 -0
- package/dist/{doctor-CATRdWm6.js → doctor-BRWOlqeG.js} +14 -12
- package/dist/doctor-BRWOlqeG.js.map +1 -0
- package/dist/{eject-DuasTr-F.js → eject-DABcDnPN.js} +3 -4
- package/dist/{eject-DuasTr-F.js.map → eject-DABcDnPN.js.map} +1 -1
- package/dist/{fold-static-hDHtDfTq.js → fold-static-BnAK-anQ.js} +3 -3
- package/dist/{fold-static-hDHtDfTq.js.map → fold-static-BnAK-anQ.js.map} +1 -1
- package/dist/generate-view-IFwCADvb.js +662 -0
- package/dist/generate-view-IFwCADvb.js.map +1 -0
- package/dist/{generate_sdk-Cvg5jjJF.js → generate_sdk-BvcWvoUh.js} +49 -13
- package/dist/generate_sdk-BvcWvoUh.js.map +1 -0
- package/dist/index.es.js +58 -40
- package/dist/index.es.js.map +1 -1
- package/dist/{init-BWAwHPtz.js → init-CaU6GhwI.js} +50 -62
- package/dist/init-CaU6GhwI.js.map +1 -0
- package/dist/invoked-command-CG_rIa_o.js +55 -0
- package/dist/invoked-command-CG_rIa_o.js.map +1 -0
- package/dist/last-dev-database-DpW5dfoX.js +2 -0
- package/dist/{manifest-DRp0UVTj.js → manifest-BtzDy7KQ.js} +36 -3
- package/dist/{manifest-DRp0UVTj.js.map → manifest-BtzDy7KQ.js.map} +1 -1
- package/dist/{normalize-imports-BoBVh5tU.js → normalize-imports-D0zXlM6g.js} +3 -3
- package/dist/{normalize-imports-BoBVh5tU.js.map → normalize-imports-D0zXlM6g.js.map} +1 -1
- package/dist/{path-flags-BFgG9V6y.js → path-flags-DNvtEi2-.js} +5 -4
- package/dist/path-flags-DNvtEi2-.js.map +1 -0
- package/dist/{prepare-DdnU70z8.js → prepare-CTw9NEkh.js} +5 -4
- package/dist/{prepare-DdnU70z8.js.map → prepare-CTw9NEkh.js.map} +1 -1
- package/dist/project-CasK56vN.js +701 -0
- package/dist/project-CasK56vN.js.map +1 -0
- package/dist/{pull-C3PXduzl.js → pull-DDMpsZ5r.js} +131 -24
- package/dist/pull-DDMpsZ5r.js.map +1 -0
- package/dist/refusal-ReeeGP8a.js +26 -0
- package/dist/refusal-ReeeGP8a.js.map +1 -0
- package/dist/resolve-D647F1LA.js +2 -0
- package/dist/{resources-BdNRDEAy.js → resources-CPSC5Kcu.js} +4 -5
- package/dist/{resources-BdNRDEAy.js.map → resources-CPSC5Kcu.js.map} +1 -1
- package/dist/{schema-B0-p6T1O.js → schema-BSXwZSiI.js} +9 -8
- package/dist/schema-BSXwZSiI.js.map +1 -0
- package/dist/{skills-DfnTeyRz.js → skills-BU2flMOy.js} +52 -8
- package/dist/skills-BU2flMOy.js.map +1 -0
- package/dist/{start-BmEZkS0n.js → start-mVb6upwY.js} +20 -7
- package/dist/start-mVb6upwY.js.map +1 -0
- package/dist/{state-CCB-FAQ9.js → state-CYdBvnwA.js} +3 -2
- package/dist/state-CYdBvnwA.js.map +1 -0
- package/dist/static-syntax-check.d.ts +64 -0
- package/dist/{status-BJ26yZOo.js → status-BREvtzJm.js} +8 -9
- package/dist/{status-BJ26yZOo.js.map → status-BREvtzJm.js.map} +1 -1
- package/dist/{storage-DKPUBTWG.js → storage-DtOXGflp.js} +159 -16
- package/dist/storage-DtOXGflp.js.map +1 -0
- package/dist/{telemetry-EMFRVRQi.js → telemetry-CgwJPVMO.js} +2 -2
- package/dist/{telemetry-EMFRVRQi.js.map → telemetry-CgwJPVMO.js.map} +1 -1
- package/dist/{telemetry-m9czTNGR.js → telemetry-DwK5RQJl.js} +4 -4
- package/dist/{telemetry-m9czTNGR.js.map → telemetry-DwK5RQJl.js.map} +1 -1
- package/dist/{upgrade-DjihWuZL.js → upgrade-B7sVwL8S.js} +26 -4
- package/dist/{upgrade-DjihWuZL.js.map → upgrade-B7sVwL8S.js.map} +1 -1
- package/dist/utils/args.d.ts +32 -3
- package/dist/utils/boot-retry.d.ts +41 -0
- package/dist/utils/collections-dir.d.ts +14 -0
- package/dist/utils/dev-preflight.d.ts +34 -1
- package/dist/utils/dev-server.d.ts +11 -0
- package/dist/utils/path-flags.d.ts +2 -1
- package/dist/utils/project.d.ts +5 -1
- package/dist/utils/tool-stdio.d.ts +1 -1
- package/dist/utils/unknown-command.d.ts +4 -0
- package/dist/verify-D7UsV_qe.js +730 -0
- package/dist/verify-D7UsV_qe.js.map +1 -0
- package/package.json +9 -7
- package/templates/eject/backend/src/index.ts +9 -1
- package/templates/generated-schemas/ecommerce.ts +3 -0
- package/templates/overlays/baas/ai-instructions.md +13 -0
- package/templates/resource-graphs/blank.json +25 -0
- package/templates/resource-graphs/blog.json +28 -0
- package/templates/resource-graphs/ecommerce.json +28 -0
- package/templates/resource-graphs/headless.json +22 -0
- package/templates/template/.claude/hooks/post-edit-collection.sh +99 -0
- package/templates/template/.claude/settings.json +17 -0
- package/templates/template/.env.example +15 -5
- package/templates/template/ai-instructions.md +21 -1
- package/templates/template/config/collections/presets/ecommerce/index.ts +23 -0
- package/templates/template/docker-compose.yml +4 -1
- package/templates/template/frontend/package.json +1 -0
- package/templates/template/frontend/src/App.tsx +10 -9
- package/templates/views/AppView.tsx.tmpl +138 -0
- package/templates/views/EntityView.tsx.tmpl +64 -0
- package/templates/views/index.ts.tmpl +8 -0
- package/dist/agent-setup-B5AKHOgt.js.map +0 -1
- package/dist/api-keys-Tm7zvAPe.js.map +0 -1
- package/dist/args-BX0YD14a.js +0 -266
- package/dist/args-BX0YD14a.js.map +0 -1
- package/dist/auth-Vl2hFKXP.js.map +0 -1
- package/dist/bundle-DtCCCr4w.js.map +0 -1
- package/dist/cloud-BpH0_bmV.js.map +0 -1
- package/dist/console-CEGm0XNc.js.map +0 -1
- package/dist/context-CyflUrKg.js.map +0 -1
- package/dist/daemon-CknkDjd6.js.map +0 -1
- package/dist/daemon-entry-CbLRrgzy.js.map +0 -1
- package/dist/db-CRNpA5mH.js.map +0 -1
- package/dist/dev-C0pbMmhS.js.map +0 -1
- package/dist/dev-preflight-unHpiYO1.js.map +0 -1
- package/dist/dev-server-B1UYifZJ.js.map +0 -1
- package/dist/doctor-CATRdWm6.js.map +0 -1
- package/dist/generate_sdk-Cvg5jjJF.js.map +0 -1
- package/dist/init-BWAwHPtz.js.map +0 -1
- package/dist/last-dev-database-c_dKz1S3.js +0 -2
- package/dist/path-flags-BFgG9V6y.js.map +0 -1
- package/dist/project-Wp2zoyeN.js +0 -349
- package/dist/project-Wp2zoyeN.js.map +0 -1
- package/dist/pull-C3PXduzl.js.map +0 -1
- package/dist/resolve-CNQjsatH.js +0 -2
- package/dist/schema-B0-p6T1O.js.map +0 -1
- package/dist/skills-DfnTeyRz.js.map +0 -1
- package/dist/start-BmEZkS0n.js.map +0 -1
- package/dist/state-CCB-FAQ9.js.map +0 -1
- package/dist/storage-DKPUBTWG.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-KzlqddGD.js","names":[],"sources":["../src/utils/command-words.ts","../src/utils/spawn-error.ts","../src/commands/db.ts"],"sourcesContent":["/**\n * The words that name a command, with the flags taken back out.\n *\n * `rawArgs` is the whole of `process.argv`, and reading the command's words at\n * fixed indices off it assumes nothing precedes them. Something routinely does:\n * `--debug` is what `bin/rebase.js` prints after every failure as the thing to\n * re-run with, so it is the single most likely token to appear before a command\n * word, and it shifted every index by one.\n *\n * What that cost is not a bad error message. `rebase db branch switch feature`\n * writes a per-checkout pointer that the CLI owns; the driver, running as a\n * child process, cannot persist it and does not try. The dispatch found\n * `switch` by position, so `rebase --debug db branch switch feature` missed the\n * CLI's branch and handed the line to the driver — which reported success and\n * left the checkout on the main database. Every subsequent `dev`, `push` and\n * `backup` then ran against the wrong database, believing it was the branch,\n * which is the exact failure branching exists to prevent.\n *\n * The words are anchored on the command name rather than taken from position\n * zero, because a flag written in the space form (`--database-url <url>`) leaves\n * its value behind as a bare token and no parser at this level knows which\n * flags take values. Anchoring survives that: the value would have to be the\n * literal command name to fool it.\n *\n * Returns `[]` when the command name is not on the line at all.\n */\nexport function commandWords(rawArgs: readonly string[], command: string): string[] {\n const words = rawArgs.slice(2).filter(token => !token.startsWith(\"-\"));\n const start = words.indexOf(command);\n return start === -1 ? [] : words.slice(start);\n}\n\n/**\n * The line to hand a spawned driver, starting at the command word.\n *\n * The same defect one layer down, and it does not degrade gracefully: the\n * driver reads its domain from `args[0]`, so `rebase --debug db push` spawned\n * it with `[\"--debug\", \"db\", \"push\"]` and it answered \"Unknown domain command:\n * --debug\" — for a flag the CLI itself tells you to add after any failure.\n *\n * Tokens written *before* the command word are moved after it rather than\n * dropped. Dropping them would be the same class of bug in the other direction:\n * `rebase --database-url postgres://… db push` would silently lose the flag that\n * says which database to touch. Appending keeps each flag next to its value, and\n * `arg` reads flags wherever they appear.\n *\n * Falls back to `rawArgs.slice(2)` when the command word is not on the line,\n * which is what an internally synthesised argv looks like.\n */\nexport function argsFromCommand(rawArgs: readonly string[], command: string): string[] {\n const tail = rawArgs.slice(2);\n const start = tail.findIndex(token => token === command);\n if (start <= 0) return [...tail];\n return [...tail.slice(start), ...tail.slice(0, start)];\n}\n","/**\n * What to say when a spawned child process fails.\n *\n * Two failures arrive through the same `catch` and need opposite treatment:\n *\n * - **The child ran and exited non-zero.** It had inherited stdio, so it has\n * already printed its own diagnostics. execa's \"Command failed with exit\n * code 1: …\" is a second, worse copy of the same news with the whole argv\n * appended.\n * - **The child never started.** ENOENT on the tsx binary, EACCES on a script\n * that lost its permissions, ENOMEM on a full machine. Nobody printed\n * anything, because nothing ran. `schema.ts` and `doctor.ts` both swallowed\n * these — `catch { process.exit(1); }`, the error not even bound — so\n * `rebase schema generate` against a broken tsx exited 1 in silence, and so\n * did `rebase doctor`, the command whose entire job is to say what is wrong.\n *\n * The tsx symlink surviving a cleaned pnpm store is the ordinary way to get\n * here: `resolveLocalBin` finds the symlink, so \"dependencies are not\n * installed\" never fires, and the spawn is where it breaks.\n *\n * ## Why the message is not what decides it\n *\n * `db.ts` did make this distinction, by testing the message against\n * `/Command failed|exited with code/i` — and that test is wrong for every case\n * it was written to catch. execa prefixes **all** of them the same way:\n *\n * Command failed with exit code 3: /bin/sh -c 'exit 3'\n * Command failed with ENOENT: /…/tsx x⏎spawn /…/tsx ENOENT\n * Command failed with EACCES: /…/tsx x⏎spawn /…/tsx EACCES\n *\n * So the filter matched the spawn failures too and `db push` was as silent as\n * the other two. The structured fields do distinguish them: a child that ran\n * has a numeric `exitCode` (or a `signal`, if something killed it), and a child\n * that never started has neither — only an errno in `code`.\n */\nimport chalk from \"chalk\";\n\ninterface ChildProcessFailure {\n exitCode?: unknown;\n signal?: unknown;\n /** execa's message without the \"Command failed with …: <argv>\" preamble. */\n originalMessage?: unknown;\n}\n\n/** Did the process start? Only then has anything else already been printed. */\nfunction childRan(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n const failure = error as Error & ChildProcessFailure;\n return typeof failure.exitCode === \"number\" || typeof failure.signal === \"string\";\n}\n\nexport function reportSpawnFailure(error: unknown): void {\n if (childRan(error) || !(error instanceof Error)) return;\n\n // `originalMessage` is \"spawn /…/tsx ENOENT\" — the same fact as `message`\n // without a second copy of the argv the user just typed. Absent on the\n // errors these commands raise themselves, which are already one sentence.\n const failure = error as Error & ChildProcessFailure;\n const message = typeof failure.originalMessage === \"string\"\n ? failure.originalMessage\n : error.message;\n if (message) console.error(chalk.red(`✗ ${message}`));\n}\n","/**\n * CLI command: rebase db <action>\n */\nimport type arg from \"arg\";\nimport chalk from \"chalk\";\nimport fs from \"fs\";\nimport inquirer from \"inquirer\";\nimport os from \"os\";\nimport path from \"path\";\nimport { execa } from \"execa\";\nimport {\n requireProjectRoot,\n requireBackendDir,\n getActiveBackendPlugin,\n resolvePluginCliScript,\n resolveTsx,\n resolveTsxLoader,\n findEnvFile,\n dependenciesNotInstalled\n} from \"../utils/project\";\nimport { reportSpawnFailure } from \"../utils/spawn-error\";\nimport { parseCommandArgs, resolveSwitchValues, switchOn, UsageError } from \"../utils/args\";\nimport { redactConnectionPasswords } from \"../utils/redact\";\nimport { suggestCommand, unknownCommand } from \"../utils/unknown-command\";\nimport { argsFromCommand, commandWords } from \"../utils/command-words\";\nimport { COLLECTIONS_FLAG_HELP, refuseMissingCollectionsPath } from \"../utils/path-flags\";\nimport { collectionsDirArgs } from \"../utils/collections-dir\";\nimport { recordEvent } from \"../telemetry\";\nimport { DEV_DATABASE_KIND_ENV, devDatabaseKind, type PreparedDatabase } from \"../dev-db/prepare\";\nimport { type DevDatabase } from \"../dev-db/resolve\";\nimport type { AtlasDevDatabase } from \"../dev-db/atlas-dev-database\";\n\n/**\n * The connection string a prepared database actually answers on.\n *\n * One function, because `db url` and `db pull` disagreed about it and the\n * disagreement was a dead command: `prepareDatabaseEnv` returns `env: {}` for a\n * plain external database — the connection string is already somewhere the\n * child looks — so `db pull`'s `prepared.env.DATABASE_URL ?? process.env\n * .DATABASE_URL` was empty for the standard `.env` configuration and answered\n * `✗ No local database to pull into.` on a project whose `rebase db url` had\n * just printed one. Exporting the same URL in the shell made it work, which is\n * the shape of a bug nobody reports as one.\n *\n * `readEnvFile` is passed in rather than imported: this module loads\n * `../utils/project` dynamically everywhere else, and the callers already have\n * it open.\n */\nexport function databaseUrlOf(\n prepared: Pick<PreparedDatabase, \"env\" | \"database\">,\n envFile: Record<string, string>\n): string | undefined {\n // An empty string is \"nothing named a database\", not \"the database is the\n // empty string\" — `docker` resolves to `url: null` when the compose file\n // declares no usable db service.\n const named = (value: string | null | undefined) => (value && value.trim() ? value : undefined);\n\n return named(prepared.env.DATABASE_URL)\n ?? named(prepared.database.kind === \"managed\" ? undefined : prepared.database.url)\n ?? named(envFile.DATABASE_URL)\n ?? named(process.env.DATABASE_URL);\n}\n\n/**\n * A destination that names a remote store rather than a local path.\n *\n * Matched as \"scheme://\" generally, not as a list of the schemes we support:\n * an unknown scheme is still not something to join onto a filesystem path, and\n * a Windows drive letter (\"C:\\backups\") has no \"//\" so it stays a path.\n */\nconst REMOTE_DESTINATION_RE = /^[a-z][a-z0-9+.-]*:\\/\\//i;\n\n/**\n * The flags whose value is a local path, on every line the CLI relays.\n *\n * All three spellings of the destination, because the driver accepts all\n * three: `backup-cli.ts` maps `--output` and `-o` onto `--out`, and `rebase db\n * backup --help` advertises `--output` as an alias. Only `--out` and `-o` were\n * absolutised once, so the one spelling the help page names was the one that\n * landed in `backend/` — and the success line still echoed the path as typed.\n *\n * And `--collections`, which was not absolutised at all: from the project root,\n * `rebase schema generate --collections ./config/collections` was \"not found\",\n * while the same spelling worked for `generate-sdk`. See `utils/path-flags.ts`.\n */\nexport const LOCAL_PATH_FLAGS: readonly string[] = [\"--out\", \"--output\", \"-o\", \"--collections\", \"-c\"];\n\n/**\n * Rewrite local path arguments so they mean what the user typed.\n *\n * The plugin CLI is spawned with `cwd: backendDir` — it has to be, because that\n * is where the plugin and its dependencies resolve from. But the developer runs\n * `rebase db` from the project root, so a relative `--out ./backups` was being\n * resolved against `backend/` and landed in `backend/backups`, while the\n * success line echoed the path as typed. The file was real and the reported\n * location was wrong, which is the worst way for a backup command to behave.\n * `rebase db --help` documents exactly this invocation.\n *\n * Absolutising here rather than inside the plugin keeps the fix where the cwd\n * is actually changed, and leaves the plugin usable on its own terms.\n */\nexport function absolutizeLocalPathArgs(\n args: string[],\n cwd: string,\n pathFlags: readonly string[] = LOCAL_PATH_FLAGS\n): string[] {\n const takesPath = (flag: string) => pathFlags.includes(flag);\n /**\n * Flags whose next token is a value, not a positional.\n *\n * Mirrors the `arg` specs in `@rebasepro/server-postgres`'s backup CLI.\n * Needed only to find the `db restore` dump argument: without it,\n * `restore --target-db app_restored ./x.dump` treats `app_restored` as the\n * positional — it is the first token not starting with `-` — and turns a\n * database name into a path while leaving the real dump path unresolved.\n */\n const VALUE_FLAGS = new Set([\n \"--out\", \"--output\", \"-o\", \"--target-db\", \"--exclude-schema\", \"--row-security-role\"\n ]);\n const out = [...args];\n\n for (let i = 0; i < out.length; i++) {\n const arg = out[i];\n\n // `--out=<value>`\n const eq = arg.indexOf(\"=\");\n if (eq > 0 && takesPath(arg.slice(0, eq))) {\n const value = arg.slice(eq + 1);\n if (value && !REMOTE_DESTINATION_RE.test(value)) {\n out[i] = `${arg.slice(0, eq)}=${path.resolve(cwd, value)}`;\n }\n continue;\n }\n\n // `--out <value>`\n if (takesPath(arg)) {\n const value = out[i + 1];\n if (value && !value.startsWith(\"-\") && !REMOTE_DESTINATION_RE.test(value)) {\n out[i + 1] = path.resolve(cwd, value);\n i++;\n }\n }\n }\n\n // `db restore <file>` — the dump to read is a positional, and a relative one\n // was resolved against `backend/` too, so the path printed by a preceding\n // `db backup` could not be pasted into `db restore`.\n const restoreAt = out.indexOf(\"restore\");\n if (restoreAt !== -1) {\n for (let i = restoreAt + 1; i < out.length; i++) {\n const arg = out[i];\n if (arg.startsWith(\"-\")) {\n // `--flag=value` carries its value inline; `--flag value` eats\n // the next token only when the flag actually takes one.\n if (!arg.includes(\"=\") && VALUE_FLAGS.has(arg)) i++;\n continue;\n }\n // First true positional: the dump to restore.\n if (!REMOTE_DESTINATION_RE.test(arg)) out[i] = path.resolve(cwd, arg);\n break;\n }\n }\n\n return out;\n}\n\n/**\n/**\n * Read `--flag value` or `--flag=value` out of the raw argv.\n *\n * This command forwards its arguments to the driver plugin rather than parsing\n * them, so the two flags the CLI itself acts on are picked out by hand instead\n * of adding a spec that would then have to strip them back out again.\n */\nfunction readFlagValue(rawArgs: readonly string[], flag: string): string | null {\n for (let index = 0; index < rawArgs.length; index += 1) {\n const arg = rawArgs[index];\n if (arg === flag) return rawArgs[index + 1] ?? null;\n if (arg.startsWith(`${flag}=`)) return arg.slice(flag.length + 1);\n }\n\n return null;\n}\n\n/** `rebase db pull`'s flags. */\nconst PULL_FLAGS = {\n \"--from\": String,\n \"--schema\": [String],\n \"--anonymize\": Boolean,\n \"--yes\": Boolean,\n \"-y\": \"--yes\",\n \"--docker\": Boolean,\n // Declared so it is refused with its own reason rather than as a typo:\n // the target of a pull is never selectable. See `pullIntoLocal`.\n \"--database-url\": String\n} satisfies arg.Spec;\n\n/** `rebase db url`'s flags. */\nconst URL_FLAGS = { \"--database-url\": String, \"--docker\": Boolean } satisfies arg.Spec;\n\n/** `rebase db reset`'s flags. */\nconst RESET_FLAGS = { \"--yes\": Boolean, \"-y\": \"--yes\" } satisfies arg.Spec;\n\n/** `rebase db stop` takes none. */\nconst STOP_FLAGS = {} satisfies arg.Spec;\n\n/** `rebase db pull`'s line, parsed strictly. */\nfunction parsePullFlags(rawArgs: readonly string[]): arg.Result<typeof PULL_FLAGS> {\n return parseCommandArgs({ spec: PULL_FLAGS, rawArgs: [...rawArgs], commandWords: 2, command: \"db pull\", maxPositionals: 0 }).flags;\n}\n\n/** `rebase db url`'s line, parsed strictly. */\nfunction parseUrlFlags(rawArgs: readonly string[]): arg.Result<typeof URL_FLAGS> {\n return parseCommandArgs({ spec: URL_FLAGS, rawArgs: [...rawArgs], commandWords: 2, command: \"db url\", maxPositionals: 0 }).flags;\n}\n\n/** `rebase db reset`'s line, parsed strictly. */\nfunction parseResetFlags(rawArgs: readonly string[]): arg.Result<typeof RESET_FLAGS> {\n return parseCommandArgs({ spec: RESET_FLAGS, rawArgs: [...rawArgs], commandWords: 2, command: \"db reset\", maxPositionals: 0 }).flags;\n}\n\n/** `rebase db stop`'s line, parsed strictly. */\nfunction parseStopFlags(rawArgs: readonly string[]): arg.Result<typeof STOP_FLAGS> {\n return parseCommandArgs({ spec: STOP_FLAGS, rawArgs: [...rawArgs], commandWords: 2, command: \"db stop\", maxPositionals: 0 }).flags;\n}\n\n/**\n * The `db` subcommands this CLI answers itself, each with its strict parse.\n *\n * The driver's `db` commands refuse a flag they do not take; these four never\n * reach the driver, and read their flags out of argv by hand, so they accepted\n * anything. On `pull` that cost personal data: `--anonymise` (or `--anonymiz`,\n * or `--anon`) was dropped and the copy arrived as it was. An undeclared flag,\n * or a word where none is taken, is now a `UsageError` naming the flag it was\n * probably meant to be, raised before anything has run.\n */\nconst OWN_SUBCOMMAND_PARSERS: Readonly<Record<string, ((rawArgs: readonly string[]) => unknown) | undefined>> = {\n pull: parsePullFlags,\n url: parseUrlFlags,\n reset: parseResetFlags,\n stop: parseStopFlags\n};\n\n/**\n * Run a database subcommand through the active driver's CLI, throwing on\n * failure instead of exiting.\n *\n * `dbCommand` below turns every failure into `process.exit(1)`, which is right\n * for a command the user invoked directly and wrong for a caller that has more\n * to do afterwards — `rebase dev` runs a schema push during start-up and must\n * survive it failing. Exiting is therefore the wrapper's job, not this\n * function's.\n *\n * The database is resolved *here* rather than in the wrapper, so the schema\n * push `rebase dev` performs during start-up reaches the managed database too.\n */\n/**\n * The `db` subcommands that plan their work with Atlas.\n *\n * Atlas computes a diff by replaying the desired schema into a **second, empty\n * database** and comparing. That is why these three are listed and `backup` and\n * `restore` are not — those shell out to `pg_dump`, which fails loudly on its\n * own. `branch` is refused too, but for its own reason and with its own\n * message: see {@link refuseBranchOnManagedDatabase}.\n */\nconst ATLAS_BACKED_SUBCOMMANDS = new Set([\"push\", \"generate\", \"migrate\"]);\n\n/**\n * A managed-database refusal, raised so the caller decides what it costs.\n *\n * These two guards used to `process.exit(1)` where they stood. That is right\n * for `rebase db push`, typed by a person, and catastrophic for the same code\n * reached from inside `rebase dev`: the dev server's first-boot schema push\n * runs in-process, so an exit there took the whole dev server down — with a\n * `try/catch` around it written expressly to prevent that, which could never\n * run. The first `rebase dev --docker` of every scaffold died this way.\n *\n * So the refusal is a value now. It carries the block it wants printed rather\n * than printing it, because who prints (and whether printing is even right) is\n * the caller's question: `dbCommand` prints it and exits 1, and `rebase dev`\n * keeps serving.\n */\nexport class ManagedDatabaseRefusal extends Error {\n /** The lines to print, in order, already coloured. */\n readonly lines: readonly string[];\n\n constructor(summary: string, lines: readonly string[]) {\n super(summary);\n this.name = \"ManagedDatabaseRefusal\";\n this.lines = lines;\n }\n}\n\n/** Print a refusal exactly as the guards used to print it themselves. */\nexport function printManagedDatabaseRefusal(refusal: ManagedDatabaseRefusal): void {\n for (const line of refusal.lines) console.error(line);\n}\n\n/**\n * The variable the driver reads for Atlas's dev database\n * (`ATLAS_DEV_URL_ENV` in `@rebasepro/server-postgres`, which this CLI does not\n * depend on).\n */\nexport const ATLAS_DEV_URL_ENV = \"REBASE_ATLAS_DEV_URL\";\n\n/**\n * The variable that tells the driver where `rebase` was run\n * (`INVOCATION_CWD_ENV` in `@rebasepro/server-postgres`). The driver runs in\n * `backend/`, so a path it resolves itself, such as `db backup`'s default\n * `./backups`, needs to know the directory the developer meant.\n */\nconst INVOCATION_CWD_ENV = \"REBASE_INVOCATION_CWD\";\n\n/** Loaded only when a command needs one: PGlite is 25 MB of WASM. */\nconst startAtlasDevDatabase = async (): Promise<AtlasDevDatabase> =>\n (await import(\"../dev-db/atlas-dev-database\")).startAtlasDevDatabase();\n\n/**\n * The empty database an Atlas-backed subcommand plans against, on the managed\n * development database.\n *\n * **PGlite serves exactly one database.** Atlas computes a change by replaying\n * the desired schema into a second, empty one; on a real server the driver\n * makes it as `<db>_dev_diff`, but `CREATE DATABASE` against PGlite reports\n * success and creates nothing, so Atlas connected straight back to the\n * database it was comparing and stopped with `connected database is not\n * clean`. These commands were refused outright on the managed database — and\n * with them every way to drop, rename or narrow a column there, since boot\n * only adds.\n *\n * So a throwaway second PGlite is started for the command instead — in\n * memory, in this process, gone when the command ends — and handed to the\n * driver as `REBASE_ATLAS_DEV_URL`. Nothing else about the push changes: the\n * destructive gate is the driver's, and holds as it does everywhere.\n *\n * `undefined` when none is needed: another database kind (the driver makes its\n * own), a subcommand that is not Atlas's, or a `REBASE_ATLAS_DEV_URL` the\n * developer set. When one cannot be started — PGlite is an optional\n * dependency — the old refusal stands, with the reason.\n */\nexport async function atlasDevDatabaseFor(\n rawArgs: string[],\n kind: string,\n env: Readonly<Record<string, string | undefined>>,\n start: () => Promise<AtlasDevDatabase> = startAtlasDevDatabase\n): Promise<AtlasDevDatabase | undefined> {\n if (kind !== \"managed\") return undefined;\n const [domain, subcommand] = commandWords(rawArgs, \"db\");\n if (domain !== \"db\" || !ATLAS_BACKED_SUBCOMMANDS.has(subcommand ?? \"\")) return undefined;\n if (env[ATLAS_DEV_URL_ENV]?.trim()) return undefined;\n\n try {\n return await start();\n } catch (err) {\n const summary = `rebase db ${subcommand} could not start the database Atlas plans against.`;\n throw new ManagedDatabaseRefusal(summary, [\n \"\",\n chalk.red(` ✗ ${summary}`),\n chalk.gray(` ${err instanceof Error ? err.message : String(err)}`),\n \"\",\n chalk.gray(\" Atlas plans every change against a second, empty database. On the managed\"),\n chalk.gray(\" database that is a throwaway PGlite, started for the command.\"),\n \"\",\n chalk.gray(` ${chalk.cyan(\"rebase dev\")} still applies your collections at boot, additively. Otherwise,`),\n chalk.gray(` set ${ATLAS_DEV_URL_ENV} to an empty Postgres database Atlas may fill and clear,`),\n chalk.gray(\" or point the project at a real Postgres (uncomment DATABASE_URL in .env).\"),\n \"\"\n ]);\n }\n}\n\n/**\n * Stop `rebase db branch` before it reports a branch that is not one.\n *\n * **PGlite serves exactly one database**, and this is the second thing that\n * follows from it. `CREATE DATABASE \"rb_feature_x\" TEMPLATE \"postgres\"` there\n * writes a `pg_database` catalog row and nothing else. Nothing errors, so the\n * whole feature reports success end to end:\n *\n * $ rebase db branch create feature_x\n * ✓ Branch \"feature_x\" created successfully.\n * $ rebase db branch list\n * ● feature_x (7.1 MB) — created just now\n *\n * `listBranches` joins `pg_database`, the catalog row is there, and\n * `pg_database_size` answers with the one database's size — so the listing\n * corroborates the lie. Then connecting to `rb_feature_x` reports\n * `current_database()` = `postgres`, and a table created \"in the branch\" is\n * visible in the parent immediately. Measured on a fresh `rebase init` scaffold.\n *\n * **The branch is the parent.** Every write made in the belief that it is\n * sandboxed lands in the developer's real development database — which is the\n * exact failure branching exists to prevent, announced as a success.\n *\n * So this refuses the whole `branch` domain rather than `create` alone: `list`\n * on the managed database ends with \"Create one with: rebase db branch create\",\n * an invitation to do the broken thing, and `info` would confirm a size for a\n * database that was never made.\n *\n * Push, generate and migrate had the same problem — Atlas's scratch database\n * is a second database too — and get a throwaway PGlite of their own instead\n * (`atlasDevDatabaseFor` above). A branch cannot: it is meant to keep the\n * project's data, and a throwaway instance has none.\n */\nexport function refuseBranchOnManagedDatabase(rawArgs: string[], kind: string): void {\n if (kind !== \"managed\") return;\n const [domain, subcommand] = commandWords(rawArgs, \"db\");\n if (domain !== \"db\" || subcommand !== \"branch\") return;\n\n const summary = \"rebase db branch does not work on the managed development database.\";\n\n throw new ManagedDatabaseRefusal(summary, [\n \"\",\n chalk.red(` ✗ ${summary}`),\n \"\",\n chalk.gray(\" Branching copies a database with CREATE DATABASE ... TEMPLATE. The managed\"),\n chalk.gray(\" database is PGlite, which serves exactly one — the copy would be the\"),\n chalk.gray(\" original, and every write you meant to sandbox would land in it.\"),\n \"\",\n chalk.gray(\" Branching needs a real Postgres. Either:\"),\n chalk.gray(` ${chalk.cyan(\"rebase dev --docker\")} starts one, and branches work against it`),\n chalk.gray(\" or uncomment DATABASE_URL in .env to point at your own.\"),\n \"\"\n ]);\n}\n\n/**\n * The variable a driver child reads what named its database from: the\n * resolution's `source` (`flag`, `environment`, `env-file`, `branch`,\n * `docker`, `last-dev` or `managed`). Read as a literal by\n * `@rebasepro/server-postgres`'s `cli-errors.ts`.\n */\nexport const DATABASE_SOURCE_ENV = \"REBASE_DATABASE_SOURCE\";\n\n/**\n * Refuse `rebase db restore` into the managed development database.\n *\n * A backup carries its rows as COPY data, and the managed database's socket\n * server cannot take `COPY … FROM stdin`: `rebase db restore --clean` into it\n * hung the daemon, and every command after it, until the daemon was killed.\n * `pg_restore` has no way to write the rows as INSERTs. `rebase db pull`\n * dumps them that way itself (see `dev-db/pull.ts`), so the route is to\n * restore into a real Postgres and pull the copy across.\n */\nexport function refuseRestoreOnManagedDatabase(rawArgs: string[], kind: string): void {\n if (kind !== \"managed\") return;\n const words = argsFromCommand(rawArgs, \"db\");\n if (words[0] !== \"db\" || words[1] !== \"restore\") return;\n\n let dump = \"<dump>\";\n for (let i = 2; i < words.length; i++) {\n if (words[i] === \"--target-db\") i++;\n else if (!words[i].startsWith(\"-\")) {\n dump = words[i];\n break;\n }\n }\n const summary = \"rebase db restore does not work on the managed development database.\";\n\n throw new ManagedDatabaseRefusal(summary, [\n \"\",\n chalk.red(` ✗ ${summary} Nothing was restored.`),\n \"\",\n chalk.gray(\" A backup carries its rows as COPY data, which the managed database (PGlite)\"),\n chalk.gray(\" cannot receive: the restore would leave it hung until it is stopped.\"),\n \"\",\n chalk.gray(\" Restore into Postgres in Docker instead:\"),\n chalk.gray(` ${chalk.cyan(`rebase db restore ${dump} --docker --clean --yes`)}`),\n chalk.gray(\" and, to have the copy in the managed database too, pull it across:\"),\n chalk.gray(` ${chalk.cyan(\"rebase db pull --from \\\"$(rebase db url --docker)\\\" --yes\")}`),\n \"\"\n ]);\n}\n\n/**\n * Resolve the active driver's CLI and the environment it should run in.\n *\n * Shared by the two runners below. Deliberately does NOT resolve a database:\n * `schema generate` reads collection files and writes one file, and starting a\n * database to run it would be a side effect nobody asked for.\n */\nexport async function resolveDriverCli(): Promise<{\n projectRoot: string;\n backendDir: string;\n pluginCli: string;\n env: Record<string, string>;\n}> {\n const projectRoot = requireProjectRoot();\n const backendDir = requireBackendDir(projectRoot);\n\n const activePlugin = getActiveBackendPlugin(backendDir);\n if (!activePlugin) {\n throw new Error(\n \"Could not detect an active database plugin. Make sure a package like \"\n + \"@rebasepro/server-postgres is installed in backend/package.json.\"\n );\n }\n const pluginCli = resolvePluginCliScript(backendDir, activePlugin);\n if (!pluginCli) {\n throw new Error(dependenciesNotInstalled(projectRoot));\n }\n\n const envFile = findEnvFile(projectRoot);\n const env: Record<string, string> = { ...process.env as Record<string, string> };\n if (envFile) {\n env.DOTENV_CONFIG_PATH = envFile;\n }\n env[DEV_DATABASE_KIND_ENV] = devDatabaseKind(projectRoot) ?? \"\";\n\n return { projectRoot, backendDir, pluginCli, env };\n}\n\n/**\n * The one place this CLI spawns the driver's own CLI.\n *\n * There were three, each with its own copy of \"tsx for a `.ts` entry, `node`\n * otherwise\". That rule was right only while the driver shipped `src/`; once it\n * shipped `dist/cli.js` all three quietly switched to plain `node`, and the\n * driver loads the PROJECT's TypeScript in process:\n *\n * - `schema stale` reads the collections, could not resolve `./authors` onto\n * `authors.ts`, and answered \"⏭ Not checked\" while exiting 0;\n * - `db push` does the same in `ensureAuthTables`, so `rebase.users` was never\n * created and the push died at \"Applying RLS policies\" with\n * `relation \"rebase.users\" does not exist` — on a virgin database, which is\n * the documented bring-your-own-Postgres first run.\n *\n * Two were fixed and the third was missed, because three copies of a decision\n * are three chances to miss one. There is one now, and a gate holds it at one.\n *\n * tsx whenever it is installed — a devDependency of every scaffold, for exactly\n * this reason. Without it a `.ts` entry cannot run at all; a built entry still\n * runs, and the subcommands that never touch the project's TypeScript still work.\n *\n * tsx's loader in one Node (`node --import`) rather than the `tsx` binary,\n * which is a shell shim and a relay Node in front of the same loader: one Node\n * start instead of two, ~50–100 ms on each of the driver runs every `rebase\n * dev` start and every collection save makes. The binary stays the fallback for\n * a tsx whose loader does not resolve as a package — a global install.\n */\nexport async function spawnDriverCli(\n resolved: { projectRoot: string; backendDir: string; pluginCli: string; env: Record<string, string> },\n childArgs: string[],\n options: { quiet?: boolean } = {}\n): Promise<void> {\n const { projectRoot, backendDir, pluginCli, env } = resolved;\n const stdio = options.quiet ? \"pipe\" : \"inherit\";\n\n const tsxBin = resolveTsx(projectRoot);\n const tsxLoader = resolveTsxLoader(projectRoot);\n if (!tsxBin && !tsxLoader && pluginCli.endsWith(\".ts\")) {\n throw new Error(dependenciesNotInstalled(projectRoot));\n }\n\n const [command, ...prefix] = tsxLoader ? [process.execPath, \"--import\", tsxLoader] : [tsxBin ?? \"node\"];\n await execa(command, [...prefix, pluginCli, ...childArgs], { cwd: backendDir, stdio, env });\n}\n\n/** @deprecated Use {@link spawnDriverCli}. Kept as the internal name callers use. */\nconst execDriverCli = spawnDriverCli;\n\n/**\n * Run a `schema` subcommand through the active driver's CLI.\n *\n * Separate from {@link runDriverDbCommand} because it must NOT resolve a\n * database. `rebase dev` calls this before the database exists, which is the\n * whole point: the generated schema has to be right before anything reads it.\n * Throws rather than exiting — the caller has more to do.\n */\nexport async function runDriverSchemaCommand(\n rawArgs: string[],\n options: { quiet?: boolean } = {}\n): Promise<void> {\n const resolved = await resolveDriverCli();\n const driverArgs = rawArgs.slice(2);\n await execDriverCli(resolved, [...driverArgs, ...collectionsDirArgs(resolved.projectRoot, driverArgs)], options);\n}\n\nexport async function runDriverDbCommand(\n rawArgs: string[],\n options: { quiet?: boolean } = {}\n): Promise<void> {\n const projectRoot = requireProjectRoot();\n const backendDir = requireBackendDir(projectRoot);\n\n const activePlugin = getActiveBackendPlugin(backendDir);\n if (!activePlugin) {\n throw new Error(\n \"Could not detect an active database plugin. Make sure a package like \"\n + \"@rebasepro/server-postgres is installed in backend/package.json.\"\n );\n }\n\n const pluginCli = resolvePluginCliScript(backendDir, activePlugin);\n if (!pluginCli) {\n throw new Error(dependenciesNotInstalled(projectRoot));\n }\n\n // Set up environment with DOTENV_CONFIG_PATH\n const envFile = findEnvFile(projectRoot);\n const env: Record<string, string> = { ...process.env as Record<string, string> };\n if (envFile) {\n env.DOTENV_CONFIG_PATH = envFile;\n }\n env[DEV_DATABASE_KIND_ENV] = devDatabaseKind(projectRoot) ?? \"\";\n\n // Every `db` subcommand reaches Postgres through the driver plugin, which\n // reads DATABASE_URL from this environment — so resolving the database once\n // here covers push, generate, migrate, backup and restore alike, and any\n // subcommand a driver adds later. When the developer has named their own\n // database this adds nothing at all.\n // Refused before anything is started. `prepareDatabaseEnv` below *boots*\n // the managed database, and booting PGlite in order to say that PGlite\n // cannot branch is latency spent on an answer already known — the resolver\n // is a pure function and settles it. It also keeps the refusal working when\n // the managed database would have failed to start, where the developer\n // would otherwise be told about a daemon rather than about branching.\n const { resolveProjectDatabase } = await import(\"../dev-db/prepare\");\n const resolvedKind = resolveProjectDatabase(projectRoot, {\n flagUrl: readFlagValue(rawArgs, \"--database-url\"),\n flagDocker: switchOn(rawArgs, \"--docker\")\n }).kind;\n refuseBranchOnManagedDatabase(rawArgs, resolvedKind);\n refuseRestoreOnManagedDatabase(rawArgs, resolvedKind);\n\n const { prepareDatabaseEnv, managedNotices } = await import(\"../dev-db/prepare\");\n const prepared = await prepareDatabaseEnv(projectRoot, {\n flagUrl: readFlagValue(rawArgs, \"--database-url\"),\n flagDocker: switchOn(rawArgs, \"--docker\"),\n onProgress: (message) => { if (!options.quiet) console.log(chalk.gray(` ${message}`)); }\n });\n Object.assign(env, prepared.env);\n // What named the database, for the driver's \"Cannot connect\" banner to\n // send the reader to it rather than to a `.env` that played no part.\n env[DATABASE_SOURCE_ENV] = prepared.database.source;\n // And which kind it is, for the managed database's Atlas handling\n // (`--skip-lock`, the revisions table made first): the project default set\n // above is wrong for a command whose --database-url or --docker named\n // another database.\n env[DEV_DATABASE_KIND_ENV] = prepared.database.kind;\n // Suppressed for an internal caller. `rebase dev` runs a schema push during\n // start-up and has already said all of this in its own banner; repeating it\n // mid-boot reads as something having gone wrong.\n if (!options.quiet) {\n for (const line of managedNotices(prepared)) console.log(chalk.gray(` ${line}`));\n }\n\n // Started after the managed database, stopped when the driver exits —\n // whatever it exits with.\n const atlasDev = await atlasDevDatabaseFor(rawArgs, prepared.database.kind, env);\n if (atlasDev) env[ATLAS_DEV_URL_ENV] = atlasDev.url;\n env[INVOCATION_CWD_ENV] = process.cwd();\n\n // Resolved against the directory the developer is standing in, before the\n // child is handed a different one. See absolutizeLocalPathArgs.\n //\n // From the command word, not from index 2: the driver reads its domain out\n // of `args[0]`, so `rebase --debug db push` spawned it with [\"--debug\",\n // \"db\", \"push\"] and it answered \"Unknown domain command: --debug\" — for a\n // flag this CLI prints after every failure as the thing to re-run with.\n const typedArgs = absolutizeLocalPathArgs(argsFromCommand(rawArgs, \"db\"), process.cwd());\n // The collections `rebase.json` declares (`apps.<backend>.config`), when the\n // line names none — the driver alone would read `../config/collections`.\n const childArgs = [...typedArgs, ...collectionsDirArgs(projectRoot, typedArgs)];\n\n try {\n await spawnDriverCli({ projectRoot, backendDir, pluginCli, env }, childArgs, options);\n } finally {\n await atlasDev?.stop();\n }\n}\n\nexport async function dbCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void> {\n // `--help` is answered here, before anything else, and never by a\n // subcommand.\n //\n // `cli.ts` rewrites the subcommand to `\"--help\"` only when the user named\n // none, so `rebase db --help` was covered and `rebase db push --help` was\n // not: the flag went through to `runDriverDbCommand`, which resolves (and\n // will START) the development database and then hands the whole line to the\n // driver, whose own dispatch has no `--help` case for `push`. **It ran the\n // push.** A flag whose entire job is to print text applied a schema to a\n // database — the single most dangerous shape a help flag can have, and the\n // same class as `skills install --help` writing files and `auth\n // reset-password --help` running an UPDATE.\n //\n // Answering before `requireProjectRoot` and before the driver spawn makes\n // it structurally impossible rather than conditionally safe: no subcommand\n // can act on `--help`, because no subcommand is reached.\n if (!subcommand || subcommand === \"--help\" || rawArgs.includes(\"--help\") || rawArgs.includes(\"-h\")) {\n printDbHelp(subcommand === \"--help\" ? undefined : subcommand);\n return;\n }\n\n // A typo is answered here, not by the driver, and not after a database has\n // been started for it.\n //\n // `rebase db psh` used to reach `runDriverDbCommand`, which resolves the\n // database first — so it **booted the managed PGlite**, a 180 MB daemon,\n // in order to hand the driver an argv it then rejected with `Unknown db\n // command. Valid: push, generate, migrate, branch, backup, restore,\n // backups`: a list missing `url`, `pull`, `stop` and `reset`, all four of\n // which `rebase db --help` lists and this CLI implements itself. The driver\n // cannot know about them, which is exactly why the answer does not belong\n // to the driver.\n //\n // The set comes from the help table, so the list a typo is measured against\n // and the list `--help` prints cannot drift apart.\n if (!DB_ACTION_HELP[subcommand]) unknownCommand(subcommand, Object.keys(DB_ACTION_HELP), \"db\");\n // `db backups` has one action, and its typo is answered here for the same reason.\n if (subcommand === \"backups\") {\n const action = argsFromCommand(rawArgs, \"backups\")[1];\n if (action !== undefined && action !== \"list\") unknownCommand(action, [\"list\"], \"db backups\");\n }\n\n // The subcommands answered here rather than by the driver are strict about\n // their flags the way the driver's are, and refuse before anything is read,\n // started or copied. See `OWN_SUBCOMMAND_PARSERS`.\n OWN_SUBCOMMAND_PARSERS[subcommand]?.(rawArgs);\n\n const projectRoot = requireProjectRoot();\n\n // Fire-and-forget, and a no-op unless the developer opted in. Never awaited:\n // the command is what the user is waiting for, and a slow collector must not\n // sit in front of it.\n void recordEvent(\"cli.db\", { subcommand: subcommand ?? \"none\" }, { projectRoot });\n\n // Handled here rather than by the driver plugin: these are about the\n // *managed* database's process and data directory, which is a CLI concern.\n // A project pointed at its own Postgres has nothing here to stop or reset,\n // and is told so rather than silently doing nothing.\n if (subcommand === \"stop\" || subcommand === \"reset\") {\n await manageLocalDatabase(subcommand, projectRoot, rawArgs);\n\n return;\n }\n\n // Each starts the remembered compose database itself, once what it was\n // asked for has been checked: a pull refused for a missing --from has no\n // reason to start a container first.\n if (subcommand === \"pull\") {\n await pullIntoLocal(projectRoot, rawArgs);\n\n return;\n }\n\n if (subcommand === \"url\") {\n await printDatabaseUrl(projectRoot, rawArgs);\n\n return;\n }\n\n // Every other subcommand is the driver's, and the driver is spawned only\n // once a database is running for it. Its flags are judged here, by its own\n // check, before the line below starts the remembered container and\n // `runDriverDbCommand` boots the managed one. `branch switch` is this\n // CLI's, and is judged by its own check, before the same start.\n if (subcommand === \"branch\" && commandWords(rawArgs, \"db\")[2] === \"switch\") {\n assertSwitchFlags(rawArgs);\n } else {\n await refuseDriverLineBeforeStarting(projectRoot, rawArgs);\n // And a `--collections` path that does not exist, for the same\n // reason, in the reader's terms. See utils/path-flags.ts.\n refuseMissingCollectionsPath(rawArgs, process.cwd(), requireBackendDir(projectRoot));\n }\n\n // `--docker` on a `db` subcommand meant \"resolve the compose URL\", and\n // nothing started it: `rebase db branch list --docker` on a stock scaffold\n // answered `✗ Failed query: CREATE SCHEMA IF NOT EXISTS rebase` and\n // `connect ECONNREFUSED 127.0.0.1:5436` — two lines, no diagnosis — while\n // the managed-database refusal that sends the reader here says \"`rebase dev\n // --docker` starts one, and branches work against it\".\n //\n // The same ensure step `rebase dev --docker` runs, not a second one.\n await ensureDockerDatabase(projectRoot, rawArgs);\n\n // Handled here rather than by the driver, for the same reason `stop` and\n // `reset` are: it writes per-checkout CLI state. The driver creates and\n // drops the databases; which one this checkout talks to is not its\n // business, and it runs as a child process that could not persist the\n // answer anyway.\n if (subcommand === \"branch\" && commandWords(rawArgs, \"db\")[2] === \"switch\") {\n await switchBranch(projectRoot, rawArgs);\n\n return;\n }\n\n // The other two branch actions that need to know where this checkout is\n // standing, for reasons the driver — a child process with no idea a pointer\n // exists — cannot answer.\n let driverArgs = rawArgs;\n if (subcommand === \"branch\") {\n const action = commandWords(rawArgs, \"db\")[2];\n if (action === \"delete\") await refuseDeletingActiveBranch(projectRoot, rawArgs);\n if (action === \"prune\" || action === \"list\") await tellDriverTheActiveBranch(projectRoot);\n if (action && REGISTRY_ACTIONS.has(action)) driverArgs = await onParentDatabase(projectRoot, rawArgs, action);\n }\n\n try {\n await runDriverDbCommand(driverArgs);\n } catch (error) {\n // The CLI entry point is where a managed-database refusal becomes an\n // exit code. The guards raise it instead of exiting so that `rebase dev`,\n // which runs `db push` in-process, survives one; here, where a person\n // typed the command, it is exactly as fatal as it always was.\n if (error instanceof ManagedDatabaseRefusal) {\n printManagedDatabaseRefusal(error);\n process.exit(1);\n }\n // A child that exited non-zero already printed its diagnostics through\n // inherited stdio; only the errors raised above have a message worth\n // adding here.\n reportSpawnFailure(error);\n process.exit(1);\n }\n}\n\n/**\n * Refuse a driver `db` line the driver would refuse on its flags, before\n * anything is started for it.\n *\n * The driver is spawned with a database already running, because getting one\n * means starting one: the remembered compose container, or the managed PGlite\n * daemon. So `rebase db push --dry-rn` started the database and only then\n * heard that `--dry-rn` does not exist. The check is the driver's own\n * (`assertDbCommandLine`, the same parse each of its commands runs), so this\n * answer and the one the driver gives cannot drift apart.\n *\n * It is this CLI's copy of `@rebasepro/server-postgres`, which ships at the\n * CLI's version, as the project's does. For any other driver the line goes\n * through as it is, and that driver judges it.\n *\n * @throws UsageError with the driver's sentence: nothing ran.\n */\nasync function refuseDriverLineBeforeStarting(projectRoot: string, rawArgs: readonly string[]): Promise<void> {\n if (getActiveBackendPlugin(requireBackendDir(projectRoot)) !== \"@rebasepro/server-postgres\") return;\n const { assertDbCommandLine } = await import(\"@rebasepro/server-postgres\");\n try {\n assertDbCommandLine(argsFromCommand(rawArgs, \"db\"));\n } catch (error) {\n if (error instanceof Error) throw new UsageError(error.message);\n throw error;\n }\n}\n\n/**\n * `rebase db url` — the connection string this project is using, on stdout.\n *\n * The managed database is the one case where nothing on disk names it: `.env`\n * ships `DATABASE_URL` commented out on purpose, the port is derived from the\n * project path, and the data lives under `.rebase/`. A headless project has no\n * admin panel to open either, so before this there was no way to point `psql`,\n * a GUI client or a seeding script at the database `rebase dev` had just\n * created — short of reading the CLI's source.\n *\n * Resolution is the same ordered rule every other command uses, so this prints\n * your own `DATABASE_URL` when you have set one rather than inventing a second\n * answer. Nothing but the URL goes to stdout, so it pipes:\n *\n * psql \"$(rebase db url)\"\n *\n * It starts the managed database if it is not running, because a connection\n * string for a database nobody is serving is not an answer.\n */\nasync function printDatabaseUrl(projectRoot: string, rawArgs: readonly string[]): Promise<void> {\n // Strict about its flags, like every other command.\n //\n // The `db` family's strictness lives in the driver, and `url` is answered\n // here, before the driver is ever spawned — so it accepted anything.\n // `rebase db url --bogus` printed the URL and exited 0, and so did\n // `rebase db url --json`, which is worse: a flag that looks like it asks\n // for a different output format, silently ignored, hands a script a bare\n // string it will try to parse as JSON.\n const flags = parseUrlFlags(rawArgs);\n\n // After the flags, and before the URL is resolved: a remembered container\n // that is stopped is started, with what that says on stderr.\n await ensureDockerDatabase(projectRoot, rawArgs);\n\n const { prepareDatabaseEnv } = await import(\"../dev-db/prepare\");\n const { readEnvFile } = await import(\"../utils/project\");\n\n const prepared = await prepareDatabaseEnv(projectRoot, {\n flagUrl: flags[\"--database-url\"] ?? null,\n flagDocker: flags[\"--docker\"] === true,\n quiet: true,\n // Progress goes to stderr: stdout is the URL and nothing else.\n onProgress: message => console.error(chalk.gray(` ${message}`))\n });\n\n const url = databaseUrlOf(prepared, readEnvFile(projectRoot));\n if (!url) {\n console.error(chalk.red(\" ✗ This project has no database URL to print.\"));\n console.error(chalk.gray(\" Set DATABASE_URL in .env, or run `rebase dev` to start the managed one.\"));\n process.exit(1);\n }\n\n console.log(url);\n}\n\n/**\n * `rebase db stop` and `rebase db reset`, for the managed database only.\n *\n * `reset` deletes the data directory rather than dropping schemas: \"give me an\n * empty database\" is the only thing anyone means by it, and doing it by\n * removing the directory cannot leave a half-dropped schema behind.\n */\nasync function manageLocalDatabase(\n subcommand: \"stop\" | \"reset\",\n projectRoot: string,\n rawArgs: readonly string[]\n): Promise<void> {\n const { resetManagedDatabase, stopManagedDatabase, stoppedManagedDatabaseLines, findRunningDaemon } = await import(\"../dev-db/daemon\");\n const { dataDir } = await import(\"../dev-db/state\");\n\n // What \"nothing here\" means, in the words of the project it is said to.\n //\n // \"No development database was running\" / \"No development database to\n // reset\" is true of the managed one and reads as being about the reader's\n // own Postgres — which these commands do not touch and could not stop.\n // On a project that named its own database, the sentence they need is that\n // it was not what this command was about.\n const nothingHere = (): string => {\n const kind = devDatabaseKind(projectRoot);\n const verb = subcommand === \"stop\" ? \"was not running\" : \"does not exist\";\n if (kind === \"docker\") {\n // The compose `db` service: the project names no DATABASE_URL for it.\n return ` The managed development database (PGlite) ${verb}; `\n + \"this project is on Postgres in Docker, which is untouched.\"\n + (subcommand === \"stop\" ? \" `docker compose stop db` stops it.\" : \"\");\n }\n if (kind === \"external\") {\n return ` The managed development database (PGlite) ${verb}; `\n + \"this project uses its own DATABASE_URL, which is untouched.\";\n }\n\n return subcommand === \"stop\"\n ? \" The managed development database (PGlite) was not running.\"\n : \" There is no managed development database (PGlite) to reset.\";\n };\n\n if (subcommand === \"stop\") {\n if (!(await stopManagedDatabase(projectRoot))) {\n console.log(chalk.gray(nothingHere()));\n\n return;\n }\n // \"`rebase dev` will start it again\" was not true of a dev that is\n // running: see `stoppedManagedDatabaseLines`.\n const [headline, ...detail] = stoppedManagedDatabaseLines(projectRoot);\n console.log(chalk.green(headline));\n for (const line of detail) console.log(chalk.gray(line));\n\n return;\n }\n\n const running = await findRunningDaemon(projectRoot);\n const hasData = fs.existsSync(dataDir(projectRoot));\n if (!running && !hasData) {\n console.log(chalk.gray(nothingHere()));\n\n return;\n }\n\n // Destructive, and the data is not backed up anywhere: it only ever existed\n // on this machine. So it asks, unless the caller has said not to — and\n // without a TTY there is nobody to ask, which is a refusal rather than an\n // assumption.\n if (parseResetFlags(rawArgs)[\"--yes\"] !== true) {\n if (!process.stdin.isTTY) {\n console.error(chalk.red(\"✗ `rebase db reset` deletes the local database and cannot prompt here.\"));\n console.error(chalk.yellow(\" Re-run with --yes if that is what you intend.\"));\n process.exit(1);\n }\n\n const { confirmed } = await inquirer.prompt([{\n type: \"confirm\",\n name: \"confirmed\",\n message: `Delete the development database at ${dataDir(projectRoot)}? This cannot be undone.`,\n default: false\n }]);\n if (!confirmed) {\n console.log(chalk.gray(\" Left alone.\"));\n\n return;\n }\n }\n\n await resetManagedDatabase(projectRoot);\n console.log(chalk.green(\"✓ Development database deleted. `rebase dev` will create a fresh one.\"));\n}\n\n/**\n * `rebase db pull --from <url>` — copy another database's contents into local\n * development.\n *\n * Deliberately one-directional. There is no flag that makes this push, because\n * a tool that can copy in both directions eventually copies in the wrong one,\n * and the wrong one here means overwriting production with a laptop.\n */\nasync function pullIntoLocal(projectRoot: string, rawArgs: readonly string[]): Promise<void> {\n const { describeRestoreFailure, describeTarget, dumpSource, findPgTool, restoreCopy, sourceProblem } =\n await import(\"../dev-db/pull\");\n const { prepareDatabaseEnv } = await import(\"../dev-db/prepare\");\n const { readEnvFile } = await import(\"../utils/project\");\n\n // Parsed strictly, not read by hand. `rawArgs.includes(\"--anonymize\")` made\n // the privacy flag the one a typo turned off: `--anonymise`, `--anonymiz`\n // and `--anon` were dropped on the floor and the copy went ahead with real\n // personal data, and `--schem public` pulled every schema.\n const flags = parsePullFlags(rawArgs);\n const schemas = flags[\"--schema\"] ?? [];\n const anonymize = flags[\"--anonymize\"] === true;\n\n // `--database-url` is refused rather than honoured, and refused rather than\n // ignored — which is what it was.\n //\n // Ignored is the dangerous one. The flag was accepted, dropped on the floor,\n // and the pull went ahead against the `.env` database: someone writing\n // `rebase db pull --from prod --database-url scratch --yes` destroyed their\n // working database while naming a different one. A flag that changes nothing\n // is a bug; a destructive flag that changes nothing is a data-loss bug.\n //\n // Honoured is the other tempting answer, and it breaks the one guarantee\n // this command makes. The target is always the local development database\n // *by construction* — there is no `--to`, and no flag that reverses the\n // direction — because a tool that can copy both ways eventually copies the\n // wrong way, and the wrong way here is a laptop over production.\n // `--database-url prod` is exactly that instruction written as a target.\n //\n // `--docker` is a different question and is passed through: it says how to\n // get a local database, not which database to overwrite.\n const targetFlag = flags[\"--database-url\"];\n if (targetFlag !== undefined) {\n console.error(chalk.red(\"✗ `rebase db pull` does not take --database-url.\"));\n console.error(\"\");\n console.error(chalk.gray(\" The target is always this project's local development database. There is\"));\n console.error(chalk.gray(\" no flag that makes this write somewhere else, deliberately: a command that\"));\n console.error(chalk.gray(\" can copy in both directions eventually copies in the wrong one.\"));\n console.error(\"\");\n // Never the password, and quoted: a connection string's `&` and `?`\n // are shell syntax, so the line as it was printed did not paste.\n const shown = redactConnectionPasswords(targetFlag);\n console.error(chalk.gray(` To read from another database, that is ${chalk.cyan(\"--from\")}:`));\n console.error(chalk.gray(` rebase db pull --from \"${shown}\"`));\n if (shown !== targetFlag) console.error(chalk.gray(\" with the password in place of ***.\"));\n process.exit(1);\n }\n\n const source = flags[\"--from\"];\n if (!source) {\n console.error(chalk.red(\"✗ `rebase db pull` needs a source: --from <connection-string>\"));\n console.error(chalk.gray(\" Example: rebase db pull --from \\\"$PRODUCTION_DATABASE_URL\\\"\"));\n process.exit(1);\n }\n // Known from the string alone, so answered before anything is checked,\n // started or announced. The value is not repeated: see `sourceProblem`.\n const notASource = sourceProblem(source);\n if (notASource !== null) {\n console.error(chalk.red(`✗ The --from value is not a connection string: ${notASource}. Nothing was read or changed.`));\n console.error(chalk.gray(\" Pass a URL, postgresql://user:password@host:5432/database, or libpq's\"));\n console.error(chalk.gray(\" host=… port=… dbname=… user=… pairs, quoted as one argument.\"));\n process.exit(1);\n }\n\n // Both checked before anything destructive: discovering either is missing\n // after dropping the local database would be the worst possible ordering.\n for (const tool of [\"pg_dump\", \"pg_restore\"] as const) {\n if (!await findPgTool(tool)) {\n console.error(chalk.red(`✗ \\`${tool}\\` is not on PATH, and this command cannot run without it.`));\n console.error(chalk.gray(\" Install the PostgreSQL client tools, e.g. `brew install libpq`.\"));\n process.exit(1);\n }\n }\n\n // The local database the copy goes into, started if it is the remembered\n // compose one and stopped — once nothing above has refused.\n await ensureDockerDatabase(projectRoot, rawArgs);\n\n const prepared = await prepareDatabaseEnv(projectRoot, {\n flagDocker: flags[\"--docker\"] === true,\n onProgress: (message) => console.log(chalk.gray(` ${message}`))\n });\n // The database `rebase db url` prints, resolved the one way — see\n // `databaseUrlOf`. Reading only `prepared.env` missed the commonest\n // configuration there is: `DATABASE_URL` in `.env` and nowhere else.\n const target = databaseUrlOf(prepared, readEnvFile(projectRoot)) ?? \"\";\n if (!target) {\n console.error(chalk.red(\"✗ No local database to pull into.\"));\n console.error(chalk.gray(\" Set DATABASE_URL in .env, or run `rebase dev` to start the managed one.\"));\n process.exit(1);\n }\n\n const plan = { source, target, anonymize, schemas };\n\n // Said in full, before anything happens. The target is destroyed, and where\n // the data comes to rest on disk is the part people forget.\n console.log(\"\");\n console.log(chalk.bold(\" This will replace your local database.\"));\n console.log(` ${chalk.gray(\"From\")} ${describeTarget(source)}`);\n console.log(` ${chalk.gray(\"Into\")} ${describeTarget(target)}${prepared.dataDir ? chalk.gray(` (${prepared.dataDir})`) : \"\"}`);\n console.log(` ${chalk.gray(\"Data\")} ${anonymize\n ? \"personal-looking columns will be overwritten after the copy\"\n : chalk.yellow(\"copied as-is, including any personal data\")}`);\n if (schemas.length > 0) console.log(` ${chalk.gray(\"Schemas\")} ${schemas.join(\", \")}`);\n console.log(\"\");\n\n if (flags[\"--yes\"] !== true) {\n if (!process.stdin.isTTY) {\n console.error(chalk.red(\"✗ This replaces the local database and cannot prompt here.\"));\n console.error(chalk.yellow(\" Re-run with --yes if that is what you intend.\"));\n process.exit(1);\n }\n const { confirmed } = await inquirer.prompt([{\n type: \"confirm\", name: \"confirmed\", message: \"Continue?\", default: false\n }]);\n if (!confirmed) {\n console.log(chalk.gray(\" Nothing was changed.\"));\n\n return;\n }\n }\n\n const dumpFile = path.join(os.tmpdir(), `rebase-pull-${process.pid}.dump`);\n console.log(chalk.gray(\" Dumping…\"));\n // In the form the local database can restore: the managed one takes the\n // rows as INSERTs, because PGlite cannot receive COPY data. A source it\n // cannot reach is said here, in the banner, before anything is emptied.\n const dumped = await dumpSource(plan, dumpFile);\n if (!dumped.ok) {\n for (const line of dumped.lines) console.error(line.startsWith(\"✗\") ? chalk.red(line) : line);\n process.exit(1);\n }\n\n /** What failed, the state the local database is left in, and what to do — see `describeRestoreFailure`. */\n let failure: string[] | null = null;\n let anonymizeFailure: string[] | null = null;\n try {\n console.log(chalk.gray(\" Restoring…\"));\n // Empties the local schemas the copy replaces, then restores into\n // them; `pg_restore --clean` alone left anything local the source\n // lacks in the way. A failure is reported only after whatever did\n // arrive has been anonymized.\n const restored = await restoreCopy(dumped.plan, dumpFile);\n failure = restored.failure ? describeRestoreFailure(restored, describeTarget(source)) : null;\n\n // Before the verdict: a partial copy of personal data on a laptop is\n // still personal data on a laptop.\n if (anonymize && restored.started) {\n anonymizeFailure = await anonymizeTarget(target, describeTarget(source), prepared.database.kind);\n }\n\n // Whatever arrived, whole or partial, anonymized or emptied: pg_dump\n // stripped its grants either way, and a pull that failed is pulled\n // again into a database that should still be readable meanwhile.\n if (restored.started) await restoreAppRole(target);\n } finally {\n fs.rmSync(dumpFile, { force: true });\n }\n\n if (anonymizeFailure) {\n for (const line of anonymizeFailure) console.error(line);\n if (failure) console.error(chalk.red(`✗ ${failure[0]}`));\n process.exit(1);\n }\n\n if (failure) {\n const [what, state, ...advice] = failure;\n console.error(chalk.red(`✗ ${what}`));\n console.error(chalk.yellow(` ${state}`));\n for (const line of advice) console.error(chalk.gray(` ${line}`));\n process.exit(1);\n }\n\n console.log(chalk.green(\"✓ Local database now holds a copy of \" + describeTarget(source)));\n}\n\n/**\n * Give the restored copy back the privileges `pg_dump` stripped.\n *\n * A pull without this ends on a green tick and hands over a database the\n * application cannot read: `--no-privileges` removes every GRANT, so the copy\n * has the source's RLS policies and none of the grants behind them, and the\n * first query as `rebase_user` fails with `permission denied`. Measured — 68\n * policies and 60 grants in, 68 policies and 0 grants out.\n *\n * `ensureAppRole` is the routine boot runs and `rebase db push` runs, so the\n * pulled database ends up in the state a booted one is in, and there is no\n * second description of these grants to drift. `detectConnectionPosture` gates\n * it the same way `db push` does: a connection that cannot create roles reports\n * that rather than failing the pull, which has already succeeded by this point.\n *\n * Failure here is a warning, not an error. The data is restored and correct; a\n * missing grant is repaired by the next `rebase dev` or `rebase db push`, and\n * saying so beats unwinding a copy that is fine.\n */\nasync function restoreAppRole(target: string): Promise<void> {\n const { Client } = await import(\"pg\");\n const { provisionableSchemas } = await import(\"../dev-db/pull\");\n\n const client = new Client({ connectionString: target });\n client.on(\"error\", () => undefined);\n try {\n // Inside the `try`: a refused connection is the same warning as a\n // refused grant, not an exception past the pull's own verdict. And\n // before the driver is loaded, which there is no point in doing for a\n // database that cannot be reached.\n await client.connect();\n const { detectConnectionPosture, ensureAppRole, REBASE_USER_ROLE } =\n await import(\"@rebasepro/server-postgres\");\n const runSql = async (text: string) => (await client.query(text)).rows as Record<string, unknown>[];\n const posture = await detectConnectionPosture(runSql);\n if (!posture.privileged) {\n console.log(chalk.yellow(\n ` ⚠ Could not re-grant the \"${REBASE_USER_ROLE}\" role — this connection may not create roles.`\n ));\n console.log(chalk.gray(\" The copy has its RLS policies but no privileges behind them;\"));\n console.log(chalk.gray(\" `rebase dev` or `rebase db push` will provision them.\"));\n\n return;\n }\n\n const { rows } = await client.query<{ schema: string }>(\n \"SELECT nspname AS \\\"schema\\\" FROM pg_namespace\"\n );\n const schemas = provisionableSchemas(rows);\n await ensureAppRole(runSql, schemas);\n console.log(chalk.gray(\n ` ✓ Re-granted \"${REBASE_USER_ROLE}\" on ${schemas.length} schema(s) — pg_dump strips privileges.`\n ));\n } catch (error) {\n console.log(chalk.yellow(\n ` ⚠ Restored, but could not re-grant \"rebase_user\": ${error instanceof Error ? error.message : String(error)}`\n ));\n console.log(chalk.gray(\" Run `rebase db push` (or start `rebase dev`) to provision it.\"));\n } finally {\n await client.end().catch(() => undefined);\n }\n}\n\n/**\n * Anonymize the restored copy, and say what happened if that did not work.\n *\n * Returns null when the copy is anonymized, and otherwise the lines that say —\n * in red, because `--anonymize` was the developer saying this data must not\n * arrive as it is — what state the local database is in now: emptied of what\n * could not be anonymized, or, when even that failed, holding real data that\n * has to be deleted before anything else.\n */\nasync function anonymizeTarget(\n target: string,\n source: string,\n kind: DevDatabase[\"kind\"]\n): Promise<string[] | null> {\n const { Client } = await import(\"pg\");\n const { anonymizeCopy } = await import(\"../dev-db/pull\");\n\n const deleteIt = kind === \"managed\"\n ? `Delete it now: ${chalk.cyan(\"rebase db reset --yes\")}`\n : kind === \"docker\"\n ? `Delete it now: ${chalk.cyan(\"docker compose down -v\")}`\n : \"Delete it now: drop the restored tables, or the database, before anything else reads it.\";\n const holdsRealData = (reason: string): string[] => [\n chalk.red(`✗ The copy could not be anonymized: ${reason}`),\n chalk.red.bold(` The local database holds an UN-ANONYMIZED copy of ${source}, personal data included.`),\n chalk.yellow(` ${deleteIt}`)\n ];\n\n const client = new Client({ connectionString: target });\n client.on(\"error\", () => undefined);\n try {\n await client.connect();\n } catch (error) {\n return holdsRealData(error instanceof Error ? error.message : String(error));\n }\n\n try {\n const outcome = await anonymizeCopy(client);\n if (outcome.ok) {\n console.log(chalk.green(` ✓ Anonymized ${outcome.tables.length} table(s).`));\n console.log(chalk.gray(\" Name-based and best-effort: a free-text column may still hold personal data.\"));\n\n return null;\n }\n if (outcome.emptyError !== null) {\n return [...holdsRealData(outcome.error), chalk.red(` Emptying the affected tables failed too: ${outcome.emptyError}`)];\n }\n\n return [\n chalk.red(`✗ The copy could not be anonymized: ${outcome.error}`),\n chalk.yellow(\" Nothing was anonymized — the pass is one transaction — so the tables it would have\"),\n chalk.yellow(\" rewritten were emptied instead, with every row that references them:\"),\n ...(outcome.emptied.length > 0 ? [chalk.gray(` ${outcome.emptied.join(\", \")}`)] : []),\n chalk.gray(` The rest of the copy of ${source} is in place. Fix that error and pull again.`)\n ];\n } finally {\n await client.end().catch(() => undefined);\n }\n}\n\n/**\n * Start the compose database a `db … --docker` was asked to talk to.\n *\n * `--docker` resolved the compose URL and stopped there, so every `db`\n * subcommand carrying it reached a container nobody had started —\n * `ECONNREFUSED 127.0.0.1:5436`, two lines, no diagnosis — including the\n * commands the managed-database refusals send readers to `--docker` for.\n *\n * `rebase dev --docker`'s preflight, called with the compose URL and nothing\n * to push: this is not a schema step, and `rebase db push --docker` is the\n * push. A failure here is reported and not fatal — the command runs and its\n * own connection error follows, which is the same contract `dev` keeps.\n */\nexport async function ensureDockerDatabase(projectRoot: string, rawArgs: readonly string[]): Promise<void> {\n const { ensureDevDatabase } = await import(\"../utils/dev-preflight\");\n const { resolveProjectDatabase } = await import(\"../dev-db/prepare\");\n\n // `--docker` on this command line, or on the last `rebase dev`: a command\n // that follows the remembered choice reaches the same container, so it has\n // to be up for the same reason. Only when that choice is the one that wins —\n // a DATABASE_URL anywhere outranks it, typed flag or not, and then there is\n // nothing to start: `prepareDatabaseEnv` says the flag was ignored.\n const resolved = resolveProjectDatabase(projectRoot, {\n flagUrl: readFlagValue(rawArgs, \"--database-url\"),\n flagDocker: switchOn(rawArgs, \"--docker\")\n });\n if (resolved.kind !== \"docker\") return;\n\n const composeUrl = resolved.url;\n if (!composeUrl) {\n // `prepareDatabaseEnv` raises the full message a moment later; saying\n // it twice would be worse than saying it once, in its own words.\n return;\n }\n\n await ensureDevDatabase({\n projectRoot,\n databaseUrl: composeUrl,\n disabled: false,\n // Never a push. `rebase db push --docker` is one, and it runs next.\n hasCollections: false,\n pushSchema: async () => undefined,\n // stderr, like every notice about which database a command resolved:\n // stdout belongs to the command. On stdout these lines were the first\n // thing `psql \"$(rebase db url)\"`, `DATABASE_URL=\"$(rebase db url)\"`\n // and `rebase console -e … | jq` read, whenever the container had to\n // be started first.\n log: message => console.error(message)\n });\n}\n\n/**\n * You cannot delete the branch you are standing on.\n *\n * Not a new rule — it was always refused, by a message about a database the\n * developer had never named. After `switch feature_x` the resolved DSN *is*\n * `rb_feature_x`, so `BranchService.deleteBranch` compared it against the\n * connected database, found them equal, and answered `✗ Cannot delete the main\n * database.` The main database is precisely what the developer was not on.\n *\n * Answered here because the pointer is the CLI's: the driver runs as a child\n * process that has never heard of it, and cannot tell \"the branch you are on\"\n * from \"the main database\" — those are the same connection string to it.\n */\nasync function refuseDeletingActiveBranch(projectRoot: string, rawArgs: readonly string[]): Promise<void> {\n const [, , , name] = commandWords(rawArgs, \"db\");\n if (!name) return;\n\n const { readActiveBranch } = await import(\"../dev-db/branch-pointer\");\n const active = readActiveBranch(projectRoot);\n if (active?.name !== name) return;\n\n const off = \"rebase db branch switch --off\";\n const del = `rebase db branch delete ${name}`;\n const column = Math.max(off.length, del.length) + 4;\n\n console.error(\"\");\n console.error(chalk.red(`✗ You are on branch \"${name}\" — leave it before deleting it.`));\n console.error(\"\");\n console.error(chalk.gray(` Every database command in this checkout is pointed at ${active.database},`));\n console.error(chalk.gray(\" including this one, so the delete would be asking that database to drop\"));\n console.error(chalk.gray(\" itself.\"));\n console.error(\"\");\n console.error(` ${chalk.cyan(off)}${\" \".repeat(column - off.length)}back to the main database`);\n console.error(` ${chalk.cyan(del)}${\" \".repeat(column - del.length)}then this works`);\n console.error(\"\");\n process.exit(1);\n}\n\n/**\n * The variable that tells the driver which database this checkout is switched\n * to. Spelled the same in `packages/server-postgres/src/branch-prune.ts`.\n */\nconst ACTIVE_BRANCH_DATABASE_ENV = \"REBASE_ACTIVE_BRANCH_DATABASE\";\n\n/**\n * Tell the driver which branch this checkout is on, for the two actions that\n * answer differently for it.\n *\n * - `prune` keeps it, as `delete` refuses it. `prune --older-than 0d --yes`\n * dropped it, and the checkout then pointed every command at a database\n * that no longer existed, with no word of `switch --off`.\n * - `list` marks it, where every branch had the same bullet.\n *\n * The pointer is the CLI's and the driver is a child process, so it is told\n * the database in its environment.\n */\nasync function tellDriverTheActiveBranch(projectRoot: string): Promise<void> {\n const { readActiveBranch } = await import(\"../dev-db/branch-pointer\");\n const active = readActiveBranch(projectRoot);\n if (active) process.env[ACTIVE_BRANCH_DATABASE_ENV] = active.database;\n else delete process.env[ACTIVE_BRANCH_DATABASE_ENV];\n}\n\n/** The `branch` actions that read or write the registry — every one but `switch`. */\nconst REGISTRY_ACTIONS = new Set([\"list\", \"create\", \"delete\", \"info\", \"diff\", \"prune\"]);\n\n/**\n * Every `branch` action runs against the parent, where the registry lives.\n *\n * `rebase.branches` is written in the database branches are made *from*, and a\n * branch is a `CREATE DATABASE … TEMPLATE` copy — so the copy carries a\n * snapshot of that table taken at branch time, and every branch made since is\n * missing from it. After `switch feature_x`, `branch list` connected to\n * `rb_feature_x` and answered `No branches found. Create one with: rebase db\n * branch create <name>` while `branch switch` with no argument, one command\n * earlier, had said `● On branch feature_x`.\n *\n * The same snapshot answered `create`, `delete`, `info` and `prune`: a branch\n * created from a switched checkout was recorded only in the copy, so `list`\n * never showed it and `delete` from the main checkout said \"not found\" while\n * its database stayed; `info` and `delete` could not find a branch made after\n * the one this checkout is on. The Branches pane in Studio was fixed the same\n * way (`REBASE_BRANCH_PARENT_DATABASE`).\n *\n * `create` still copies what this checkout is on — a branch of the branch, a\n * thing the developer asked for by switching first — so it is given\n * `--from <the branch's database>` unless it names a source itself.\n *\n * Resolved with `branch: null` — the same rule `switch` uses to find what to\n * branch *from* — and relayed as `--database-url`, which the driver honours\n * over anything it would resolve for itself.\n */\nasync function onParentDatabase(projectRoot: string, rawArgs: string[], action: string): Promise<string[]> {\n // An explicit `--database-url` is the developer naming the database; this\n // never overrides one.\n if (readFlagValue(rawArgs, \"--database-url\") !== null) return rawArgs;\n\n const { readActiveBranch } = await import(\"../dev-db/branch-pointer\");\n const active = readActiveBranch(projectRoot);\n if (!active) return rawArgs;\n\n const { readEnvFile } = await import(\"../utils/project\");\n const { resolveDevDatabase } = await import(\"../dev-db/resolve\");\n const { resolveComposeUrl } = await import(\"../dev-db/prepare\");\n\n const { readLastDevDocker } = await import(\"../dev-db/last-dev-database\");\n\n const envFile = readEnvFile(projectRoot);\n const parent = resolveDevDatabase({\n flagDocker: switchOn(rawArgs, \"--docker\"),\n lastDevDocker: readLastDevDocker(projectRoot),\n env: process.env,\n envFile,\n branch: null,\n composeUrl: resolveComposeUrl(projectRoot, envFile)\n });\n const url = (parent.kind === \"managed\" ? null : parent.url)?.trim();\n if (!url) return rawArgs;\n\n // stderr, like every notice about which database a command resolved:\n // stdout belongs to the command. `branch list --json` printed this on\n // stdout, ahead of whatever followed.\n console.error(chalk.gray(` ● On branch ${active.name} — using the main database, where the branch registry lives.`));\n\n const from = action === \"create\" && readFlagValue(rawArgs, \"--from\") === null\n ? [\"--from\", active.database]\n : [];\n\n return [...rawArgs, ...from, \"--database-url\", url];\n}\n\n/**\n * The flags `rebase db branch switch` takes, the relayed database flags and\n * `--debug` included.\n */\nconst SWITCH_FLAGS = {\n \"--off\": Boolean,\n \"--main\": Boolean,\n \"--database-url\": String,\n \"--docker\": Boolean,\n \"--debug\": Boolean\n} satisfies arg.Spec;\n\n/** The long flags in {@link SWITCH_FLAGS}, for the did-you-mean. */\nconst SWITCH_FLAG_NAMES: readonly string[] = Object.keys(SWITCH_FLAGS);\n\n/**\n * Refuse a flag `switch` does not take, naming the one it was probably meant to be.\n *\n * `switch` is answered here, never by the driver, so the driver's strict\n * branch flags do not reach it: `rebase db branch switch --of` reported the\n * current branch, exit 0, instead of leaving it.\n */\nexport function assertSwitchFlags(rawArgs: readonly string[]): void {\n for (const word of rawArgs.slice(2)) {\n if (!word.startsWith(\"-\")) continue;\n // The flag part only: a value given inline, or quoted into the same\n // argument after a space, may be a connection string.\n const flag = word.split(/[=\\s]/)[0];\n if (SWITCH_FLAG_NAMES.includes(flag)) continue;\n const near = suggestCommand(flag.replace(/^-+/, \"\"), SWITCH_FLAG_NAMES.map(known => known.slice(2)));\n throw new UsageError(\n `\\`rebase db branch switch\\` has no option ${flag}` +\n (near ? ` — did you mean --${near}?` : \" — it takes --off (or --main) and a branch name.\") +\n \" Nothing was run. See `rebase db branch --help`.\"\n );\n }\n // Every flag `switch` takes but `--database-url` is a switch: `=true` or\n // `=1` turns one on, `=false` or `=0` leaves it off, and nothing else is\n // read. `--off=false` is not \"leave\".\n resolveSwitchValues(SWITCH_FLAGS, rawArgs.slice(2), \"db branch switch\");\n}\n\n/**\n * What `switch` cannot change: a `rebase dev` started before it connected to\n * the database it was given then, and serves it until it is restarted.\n */\nconst RUNNING_DEV_KEEPS_ITS_DATABASE =\n \"A `rebase dev` that is already running keeps the database it started on until you restart it.\";\n\n/**\n * `rebase db branch switch <name>` — point this checkout at a branch.\n *\n * The step branching was missing. `create` copied the database in about a\n * second and then printed `Database: rb_feature_auth` and stopped: there was no\n * `switch`, no `--branch` on `rebase dev`, and no `REBASE_BRANCH`. The only way\n * to use a branch was to hand-edit `DATABASE_URL`, while the documentation said\n * \"the CLI updates your local development configuration\" — it did not, and the\n * `.env` was byte-identical afterwards.\n *\n * Three things it insists on:\n *\n * - **The branch has to exist.** Writing a pointer to a database that was never\n * created turns a typo into a connection error on the *next* command, which\n * is the one place it cannot be explained. Checked against\n * `rebase.branches` and `pg_database` both, because a branch dropped outside\n * Rebase leaves the metadata row behind.\n *\n * - **It says what changed, and how to undo it.** A command that silently\n * redirects every subsequent database operation owes the reader the database\n * name it moved to and the words that move it back.\n *\n * - **It never edits `.env`.** See `branch-pointer.ts`.\n */\nasync function switchBranch(projectRoot: string, rawArgs: readonly string[]): Promise<void> {\n assertSwitchFlags(rawArgs);\n\n const { branchDatabaseName, clearActiveBranch, databaseNameOf, readActiveBranch, writeActiveBranch } =\n await import(\"../dev-db/branch-pointer\");\n const { readEnvFile } = await import(\"../utils/project\");\n const { resolveDevDatabase } = await import(\"../dev-db/resolve\");\n const { resolveComposeUrl } = await import(\"../dev-db/prepare\");\n const { readLastDevDocker } = await import(\"../dev-db/last-dev-database\");\n\n // Read as a flag rather than as the name in the third position, so\n // `switch --off` and `switch feature --off` mean the same thing. The doc\n // verifier reads a command's flags out of exactly this shape, which is the\n // other reason: a flag it cannot see is one it reports as unrunnable in\n // every page that documents it.\n const goingOff = switchOn(rawArgs, \"--off\") || switchOn(rawArgs, \"--main\");\n\n // [\"db\", \"branch\", \"switch\", <name>], with any flags taken back out —\n // `rebase --debug db branch switch feature` read the name as \"switch\".\n const name = commandWords(rawArgs, \"db\")[3];\n\n // The database to branch from, resolved by the ordered rule every sibling\n // uses. This read `readEnvFile(projectRoot).DATABASE_URL` and nothing else,\n // so `export DATABASE_URL=…; rebase db branch create x` created a branch and\n // `rebase db branch switch x` then answered \"This project has no\n // DATABASE_URL, so there is no database to branch from\" — about a project\n // that had one, in the shell, where `branching.md` documents it as rule (2)\n // and `create` had just read it.\n //\n // `branch: null`, deliberately: this command is what sets the pointer, and\n // the pointer that exists must never become the base of the next switch —\n // that is how a branch of a branch of a branch happens by accident.\n const envFile = readEnvFile(projectRoot);\n const resolved = resolveDevDatabase({\n flagUrl: readFlagValue(rawArgs, \"--database-url\"),\n flagDocker: switchOn(rawArgs, \"--docker\"),\n lastDevDocker: readLastDevDocker(projectRoot),\n env: process.env,\n envFile,\n branch: null,\n composeUrl: resolveComposeUrl(projectRoot, envFile)\n });\n const base = (resolved.kind === \"managed\" ? null : resolved.url)?.trim() || undefined;\n\n // `switch` with no argument reports rather than changes. \"Which branch am I\n // on\" is asked far more often than \"move me\", and answering it should not\n // require guessing a flag.\n if (!name && !goingOff) {\n const active = readActiveBranch(projectRoot);\n console.log(\"\");\n if (active) {\n console.log(` ${chalk.green(\"●\")} On branch ${chalk.bold(active.name)} ${chalk.gray(`(${active.database})`)}`);\n console.log(chalk.gray(` Back to the main database: ${chalk.cyan(\"rebase db branch switch --off\")}`));\n } else {\n const main = base ? databaseNameOf(base) : null;\n console.log(` ${chalk.gray(\"●\")} On the main database${main ? chalk.gray(` (${main})`) : \"\"}`);\n console.log(chalk.gray(` Switch to a branch: ${chalk.cyan(\"rebase db branch switch <name>\")}`));\n }\n console.log(\"\");\n\n return;\n }\n\n if (goingOff) {\n const active = readActiveBranch(projectRoot);\n clearActiveBranch(projectRoot);\n console.log(\"\");\n console.log(active\n ? chalk.green(` ✓ Back on the main database${base ? ` (${databaseNameOf(base)})` : \"\"}.`)\n : chalk.gray(\" Already on the main database.\"));\n if (active) console.log(chalk.gray(` ${RUNNING_DEV_KEEPS_ITS_DATABASE}`));\n console.log(\"\");\n\n return;\n }\n\n if (!base) {\n console.error(chalk.red(\"✗ There is no database to branch from.\"));\n console.error(chalk.gray(resolved.kind === \"managed\"\n ? \" This project is on the managed development database, which serves exactly one —\"\n : \" --docker needs a docker-compose.yml with a db service, and this project has none —\"));\n console.error(chalk.gray(\" and branching copies a database. Set DATABASE_URL in .env or in your\"));\n console.error(chalk.gray(\" shell, or run `rebase dev --docker`.\"));\n process.exit(1);\n }\n\n const database = branchDatabaseName(name);\n\n // First, as every `branch` action the driver runs does: a database a\n // killed `create --force` left closed is opened again, and said.\n const { reopenClosedDatabases, reopenedDatabasesNotice } = await import(\"@rebasepro/server-postgres\");\n const healed = await reopenClosedDatabases(base, [readActiveBranch(projectRoot)?.database]);\n for (const line of reopenedDatabasesNotice(healed)) console.error(chalk.yellow(` ${line}`));\n\n // Verified before the pointer is written, not after.\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString: base });\n try {\n await client.connect();\n } catch (error) {\n console.error(chalk.red(`✗ Could not reach the database to check that branch \"${name}\" exists.`));\n console.error(chalk.gray(` ${error instanceof Error ? error.message : String(error)}`));\n process.exit(1);\n }\n try {\n const { rows } = await client.query<{ registered: boolean; present: boolean }>(\n `SELECT EXISTS (SELECT 1 FROM rebase.branches WHERE name = $1) AS registered,\n EXISTS (SELECT 1 FROM pg_database WHERE datname = $2) AS present`,\n [name, database]\n ).catch(() => ({ rows: [{ registered: false, present: false }] }));\n\n const { registered, present } = rows[0] ?? { registered: false, present: false };\n if (!present) {\n console.error(chalk.red(`✗ Branch \"${name}\" does not exist.`));\n console.error(registered\n // The metadata row outlives a database dropped with plain SQL.\n ? chalk.gray(` It is registered but its database (${database}) is gone — someone dropped it outside Rebase.`)\n : chalk.gray(` ${chalk.cyan(\"rebase db branch list\")} shows the ones that do.`));\n process.exit(1);\n }\n } finally {\n await client.end();\n }\n\n writeActiveBranch(projectRoot, { name, database });\n\n console.log(\"\");\n console.log(chalk.green(` ✓ Switched to branch \"${name}\".`));\n console.log(chalk.gray(` Database: ${database}`));\n console.log(chalk.gray(\" Every rebase command in this checkout now uses it — dev, db push, migrate.\"));\n console.log(chalk.gray(` ${RUNNING_DEV_KEEPS_ITS_DATABASE}`));\n console.log(chalk.gray(` Back to the main database: ${chalk.cyan(\"rebase db branch switch --off\")}`));\n console.log(\"\");\n}\n\n/**\n * What each `rebase db <action>` does and takes.\n *\n * Kept here rather than delegated to the driver because the driver is reached\n * by *running* it — which is exactly what `--help` must not do. The page is\n * deliberately short: the authority on a flag is the command's own spec, and\n * `check-doc-commands.mjs` holds every command written in this repository's\n * markdown to it. What a reader needs from here is which subcommands exist and\n * what the destructive ones want before they will run.\n */\n/** The database flags every driver command takes; `db url --help` has its own wording. */\nconst DATABASE_FLAGS_HELP =\n \"--database-url <url> names the database; --docker uses the compose one, starting it if needed.\";\n\nconst DB_ACTION_HELP: Record<string, { usage: string; summary: string; notes?: string[] }> = {\n push: {\n usage: \"rebase db push [--collections <dir>] [--dry-run] [--allow-destructive] [--yes]\",\n summary: \"Apply the schema straight to the database. Development only — it does not write a migration.\",\n notes: [\n \"--dry-run prints the SQL and applies nothing. Read it before you approve it.\",\n \"A change that drops data, or re-keys a table (every row gets a new id), needs --allow-destructive.\",\n \"--yes approves non-destructive pushes. It still approves destructive ones too in this release, with a \"\n + \"deprecation warning; from the next release it will not — pass --allow-destructive for those.\",\n `--collections <dir>: ${COLLECTIONS_FLAG_HELP}.`,\n DATABASE_FLAGS_HELP\n ]\n },\n generate: {\n usage: \"rebase db generate [--collections <dir>]\",\n summary: \"Generate the Drizzle schema, the Postgres DDL and a SQL migration file from the collections.\",\n notes: [\n \"A migration that drops or narrows data is named when it is written. It is still written.\",\n `--collections <dir>: ${COLLECTIONS_FLAG_HELP}.`,\n DATABASE_FLAGS_HELP\n ]\n },\n migrate: {\n usage: \"rebase db migrate [--baseline <version>] [--refuse-destructive] [amount]\",\n summary: \"Run the pending migration files against the database.\",\n notes: [\n \"--refuse-destructive dry-runs the pending files and applies nothing when one drops or narrows data.\",\n \"--baseline <version> records that version as already applied and starts from the next one.\",\n \"It is what a database Rebase has already booted against needs: boot ensures the schema, so the first migration would try to create tables that are already there.\",\n \"The version is the numeric prefix of a file in drizzle/migrations.\",\n DATABASE_FLAGS_HELP\n ]\n },\n branch: {\n // Every action the dispatch answers and every flag its specs declare.\n // The page is kept here rather than delegated because reaching the\n // driver's own `printBranchHelp` means *running* the driver, which is\n // what `--help` must not do — so `help-coverage.test.ts` holds the two\n // to each other instead. It went two releases without learning `prune`,\n // `--from`, `--force` or `--older-than`, all four of which the dispatch\n // answers and the docs teach.\n usage: \"rebase db branch <create|list|switch|delete|info|diff|prune> [name]\",\n summary: \"Database branching.\",\n notes: [\n \"create <name> [--from <source>] copies an existing database, files and all.\",\n \"switch <name> points this checkout at a branch; every later command uses it.\",\n \"switch with no name reports where you are; switch --off returns to the main database.\",\n \"diff <name> shows what the branch changed against the database it was made from: tables, columns, RLS policies, and the rows it inserted, updated and deleted. --json prints all of it, with up to --sample-limit <n> rows a table (default 100).\",\n \"prune [--older-than <14|14d|2w>] removes branches nothing is using, and the entries that drifted.\",\n \"prune --include-dev-diff also removes the Atlas scratch databases `db push` leaves behind.\",\n \"prune asks before it drops anything; --yes, -y answers for you, which is the form a CI job wants.\",\n \"--force disconnects other sessions first — Postgres refuses to copy or drop a database anything is connected to, usually your own `rebase dev`.\"\n ]\n },\n backup: {\n usage: \"rebase db backup [--out <path|s3://…>]\",\n summary: \"Create a pg_dump backup. --out is resolved against the directory you are standing in.\",\n notes: [\n \"--output is accepted as an alias, as it is on `rebase build`.\",\n \"`rebase db backup list` lists them, the way `rebase cloud db backup list` does.\"\n ]\n },\n backups: {\n usage: \"rebase db backups list [--out <path|s3://…>]\",\n summary: \"List stored backups.\",\n notes: [\"`rebase db backup list` is the same command, spelled the cloud family's way.\"]\n },\n url: {\n usage: \"rebase db url [--database-url <url>] [--docker]\",\n summary: \"Print the connection string this project uses. Nothing else goes to stdout, so it pipes.\",\n notes: [\n \"psql \\\"$(rebase db url)\\\"\",\n \"Starts the managed database if it is not already running.\",\n \"--database-url and --docker answer for the database `rebase dev` would pick with the same flag.\",\n \"After `rebase dev --docker` it prints the Docker database without the flag, as every db command does.\"\n ]\n },\n restore: {\n usage: \"rebase db restore <dump> [--target-db <name>] [--create-db] [--clean] [--yes]\",\n summary: \"Restore a backup with pg_restore.\",\n notes: [\n \"Destructive: on a terminal it asks first, and with no terminal it refuses without --yes.\",\n \"Into a database that already holds what the backup creates, it needs --clean, and changes nothing without it.\"\n ]\n },\n pull: {\n usage: \"rebase db pull --from <url> [--schema <name>] [--anonymize] [--yes]\",\n summary: \"Copy another database into local development. One-directional by design — it can never push.\",\n notes: [\n \"--anonymize overwrites personal-looking columns after the copy. A misspelt flag is refused, never ignored.\",\n \"--schema <name> copies only that schema; repeat it for more.\",\n \"--docker pulls into Postgres in Docker rather than the database `rebase dev` would pick.\",\n \"The target is not selectable: --database-url is refused, so this can never write to a remote database.\"\n ]\n },\n stop: { usage: \"rebase db stop\", summary: \"Stop the managed development database. Data is kept.\" },\n reset: {\n usage: \"rebase db reset [--yes]\",\n summary: \"Delete the managed development database and start over.\",\n notes: [\"Destructive, and the data exists only on this machine.\"]\n }\n};\n\n/** Every subcommand the family dispatches — the help table's keys. */\nexport const DB_SUBCOMMANDS = Object.keys(DB_ACTION_HELP);\n\nfunction printDbHelp(action?: string) {\n const entry = action ? DB_ACTION_HELP[action] : undefined;\n if (entry) {\n console.log(`\n${chalk.bold(`rebase db ${action}`)}\n\n ${entry.summary}\n\n${chalk.green.bold(\"Usage\")}\n ${chalk.blue(entry.usage)}\n${entry.notes?.length ? `\\n${chalk.green.bold(\"Notes\")}\\n${entry.notes.map(n => ` ${chalk.gray(`• ${n}`)}`).join(\"\\n\")}\\n` : \"\"}\n${chalk.gray(\"Run `rebase db --help` for every subcommand.\")}\n`);\n return;\n }\n\n console.log(`\n${chalk.bold(\"rebase db\")} — Database management commands\n\n${chalk.green.bold(\"Usage\")}\n rebase db ${chalk.blue(\"<command>\")} [options]\n\n${chalk.green.bold(\"Commands\")}\n ${chalk.gray(\"(Commands are provided by your active database driver plugin)\")}\n ${chalk.blue.bold(\"url\")} Print the connection string this project uses (pipes into psql)\n ${chalk.blue.bold(\"pull\")} Copy another database into local dev (--from <url>, --anonymize)\n ${chalk.blue.bold(\"stop\")} Stop the managed development database (data is kept)\n ${chalk.blue.bold(\"reset\")} Delete the managed development database and start over\n ${chalk.blue.bold(\"push\")} Apply schema directly to database (development)\n ${chalk.blue.bold(\"generate\")} Generate migration files\n ${chalk.blue.bold(\"migrate\")} Run pending migrations\n ${chalk.blue.bold(\"branch\")} Database branching (create, list, switch, delete, info, prune)\n ${chalk.blue.bold(\"backup\")} Create a backup with pg_dump (--out <path|s3://…>)\n ${chalk.blue.bold(\"restore\")} Restore a backup with pg_restore (destructive; asks, or --yes)\n ${chalk.blue.bold(\"backups\")} List stored backups (db backup list is the same)\n\n${chalk.green.bold(\"Examples\")}\n${dbExamples(devDatabaseKind())}\n ${chalk.gray(\"# Back up to a local directory, then to object storage\")}\n rebase db backup --out ./backups\n rebase db backup --out s3://my-private-bucket/backups\n\n ${chalk.gray(\"# Restore into a fresh database (safe: does not touch the live one)\")}\n rebase db restore ./backups/rebase-app-20260714T030000Z.dump --create-db --target-db app_restored\n`);\n}\n\n/**\n * The examples, which are not the same on the managed development database.\n *\n * Every one of the four the help used to lead with — `db push`, `db generate`,\n * `db migrate`, `db branch` — is refused there, by guards in this same file\n * that explain exactly why. So on the project the CLI had just scaffolded, the\n * first thing `rebase db --help` offered was a command that answers with a\n * refusal, and the reader's most likely conclusion is that their install is\n * broken.\n *\n * Exported for its test.\n */\nexport function dbExamples(kind: DevDatabase[\"kind\"] | null): string {\n if (kind === \"managed\") {\n return ` ${chalk.gray(\"# Quick development workflow (this project is on the managed database)\")}\n rebase schema generate\n rebase dev ${chalk.gray(\"# boot applies your collections to it, additively\")}\n rebase db push ${chalk.gray(\"# also drops, renames and narrows columns\")}\n\n ${chalk.gray(\"# branch needs a Postgres of your own\")}\n rebase dev --docker ${chalk.gray(\"# starts one; or uncomment DATABASE_URL in .env\")}\n\n`;\n }\n\n return ` ${chalk.gray(\"# Quick development workflow\")}\n rebase schema generate && rebase db push\n\n ${chalk.gray(\"# Production migration workflow\")}\n rebase db generate\n rebase db migrate\n\n ${chalk.gray(\"# Create a database branch and work on it\")}\n rebase db branch create feature_auth\n rebase db branch switch feature_auth\n rebase db branch switch --off\n\n`;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { n as cliVersion } from "./version-Buyyo1ut.js";
|
|
3
|
-
import { g as resolveBackendPaths } from "./manifest-
|
|
3
|
+
import { g as resolveBackendPaths } from "./manifest-BtzDy7KQ.js";
|
|
4
4
|
import { t as analyseFunctionsDirectory } from "./function-portability-BIvlvR9q.js";
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
import path from "path";
|
|
@@ -496,4 +496,4 @@ function projectResourceGraph(graph) {
|
|
|
496
496
|
//#endregion
|
|
497
497
|
export { projectResourceGraph as a, withImplicitDefaults as c, derive_exports as i, writeResourceGraphFile as l, deriveOptionsFor as n, readResourceGraphFile as o, deriveResourceGraph as r, serializeResourceGraph as s, RESOURCE_GRAPH_FILENAME as t };
|
|
498
498
|
|
|
499
|
-
//# sourceMappingURL=derive-
|
|
499
|
+
//# sourceMappingURL=derive-ae9UOoOB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive-CsoFGAnK.js","names":[],"sources":["../src/resources/derive.ts"],"sourcesContent":["/**\n * Deriving the resource graph from a project's config, and recording it.\n *\n * ## Why the graph is derived rather than authored\n *\n * A resource is declared once, in code. But a host has to know what a project\n * needs *before* it runs anything — that is how a console can say \"this project\n * wants a `media` bucket and has none\" on a first deploy, and it is the reason\n * storage topology used to be hand-written into `rebase.json` in the first\n * place. A `custom` runtime makes it sharper still: it emits no bundle\n * manifest, so without a committed artifact there is nothing to read at all.\n *\n * So the graph is derived by evaluating the config and written to\n * `rebase.resources.json`, which is committed. Same arrangement as\n * `schema.generated.ts`: authored in one place, generated into another, and a\n * gate fails if the two disagree. The file is an *output*, never an input —\n * which is what removes the old silent merge, because there is no longer a\n * second declaration for anything to be merged with.\n *\n * ## Why evaluation rather than static analysis\n *\n * Encore parses the source and builds the graph from the AST. Rebase already\n * evaluates config to load collections, and evaluation is exact where a parser\n * is approximate — a resource declared in a loop, or behind a helper, is\n * invisible to a parser and obvious to an interpreter. The cost is running user\n * code at build time, which this project already accepts everywhere else.\n */\nimport fs from \"fs\";\nimport path from \"path\";\nimport { createRequire } from \"module\";\nimport { pathToFileURL } from \"url\";\nimport {\n DEFAULT_RESOURCE_KEY,\n buildResourceGraph,\n declareFunction,\n declaredQueueConsumers,\n declaredResources,\n declaredSubscriptions,\n findEnvSuffixCollision,\n isResourceHandle,\n resetDeclaredQueueConsumers,\n resetDeclaredResources,\n resetDeclaredSubscriptions,\n resolveResourceRefs,\n resourceId,\n resourceKeyOf,\n type CollectionConfig,\n type RebaseBackendAppConfig,\n type ResourceDeclaration,\n type ResourceGraph\n} from \"@rebasepro/types\";\nimport { analyseFunctionsDirectory } from \"../function-portability\";\nimport { resolveBackendPaths } from \"../manifest\";\nimport { cliVersion } from \"../utils/version\";\n\n/** The committed, generated record of what a project needs. */\nexport const RESOURCE_GRAPH_FILENAME = \"rebase.resources.json\";\n\n/** A problem found while deriving, reported with the rest rather than thrown one at a time. */\nexport interface ResourceIssue {\n path: string;\n message: string;\n}\n\n/**\n * Which `@rebasepro/*` package a module error is about, when it is about one.\n *\n * Both directions of a version skew arrive as an error about the *user's* file:\n * `resources.ts The requested module '@rebasepro/types' does not provide an\n * export named 'queue'`. Nothing in that says which copy of `@rebasepro/types`\n * is installed, what version this CLI is, or that the two disagree — so the\n * reader goes looking for a typo in code they just copied out of the docs.\n *\n * Two shapes, because the two directions produce different errors: a named\n * export the installed copy is too old for, and a package the installed tree\n * does not have at all.\n */\nconst NAMED_EXPORT_MISSING = /does not provide an export named ['\"]([^'\"]+)['\"]/;\nconst SPECIFIER = /(@rebasepro\\/[a-z0-9-]+)/;\n\n/** The version of a package as resolved from the project, or null. */\nfunction installedVersion(specifier: string, from: string): string | null {\n try {\n const require = createRequire(path.join(from, \"noop.js\"));\n const manifest = require.resolve(`${specifier}/package.json`);\n const version: unknown = JSON.parse(fs.readFileSync(manifest, \"utf-8\")).version;\n return typeof version === \"string\" ? version : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The message an issue carries, with the skew named when there is one.\n *\n * `main` runs 497 commits ahead of npm `latest` and the docs site publishes from\n * `main`, so the shape this exists for is routine: a scaffold pinned to the\n * published version, config code written against today's documentation, and an\n * error about the config file. The fix is one command and the error named none\n * of it — the same failure as `404 No PUT route on collection 'projects'`, one\n * layer down.\n *\n * Says nothing extra when the two versions agree, or when either is unreadable:\n * a sentence naming one version twice is worse than no sentence.\n */\nexport function describeIssue(err: unknown, projectRoot: string, ownVersion = cliVersion()): string {\n const message = err instanceof Error ? err.message : String(err);\n const code = (err as { code?: unknown } | null)?.code;\n const missingExport = NAMED_EXPORT_MISSING.exec(message);\n const isSkewShaped = missingExport !== null || code === \"ERR_MODULE_NOT_FOUND\";\n if (!isSkewShaped) return message;\n\n const specifier = SPECIFIER.exec(message)?.[1];\n if (!specifier) return message;\n\n // One line, not two: every caller renders an issue as `path message` on a\n // single row, so a newline here would land the useful half unindented under\n // the file name.\n const lead = /[.!?]$/.test(message.trim()) ? message.trim() : `${message.trim()}.`;\n\n const installed = installedVersion(specifier, projectRoot);\n if (installed === null) {\n return `${lead} ${specifier} is not installed in this project; this CLI is ${ownVersion}. `\n + `Run pnpm add ${specifier}@${ownVersion}`;\n }\n if (ownVersion === \"unknown\" || installed === ownVersion) return message;\n\n // Which side to move is the question the reader actually has, and it has a\n // right answer: the CLI is what evaluates this file, so the packages follow\n // it — unless the CLI is the older one, in which case following it would\n // move the project backwards.\n const cliIsNewer = compareVersions(ownVersion, installed) > 0;\n return cliIsNewer\n ? `${lead} ${specifier} ${installed} is installed; this CLI is ${ownVersion}. `\n + `Run pnpm add ${specifier}@${ownVersion}`\n : `${lead} ${specifier} ${installed} is installed; this CLI is ${ownVersion}, which is older. `\n + `Update the CLI to ${installed}, or pin ${specifier} back to ${ownVersion}`;\n}\n\n/** -1 / 0 / 1 on the numeric parts, ignoring any prerelease tail. */\nfunction compareVersions(a: string, b: string): number {\n const parts = (v: string): number[] => v.split(\"-\")[0].split(\".\").map(n => Number(n) || 0);\n const [x, y] = [parts(a), parts(b)];\n for (let i = 0; i < 3; i++) {\n if ((x[i] ?? 0) !== (y[i] ?? 0)) return (x[i] ?? 0) < (y[i] ?? 0) ? -1 : 1;\n }\n return 0;\n}\n\n/** Files that declare resources, in the order they are loaded. */\nconst RESOURCE_ENTRY_NAMES = [\"resources.ts\", \"resources.js\", \"resources/index.ts\", \"resources/index.js\"];\n\n/** Collection files, which may also declare a resource beside the collection using it. */\nfunction collectionFiles(configDir: string): string[] {\n const dir = path.join(configDir, \"collections\");\n if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return [];\n return fs.readdirSync(dir)\n .filter(f => /\\.(ts|js|mts|mjs)$/.test(f) && !/\\.(test|spec|d)\\./.test(f))\n .sort()\n .map(f => path.join(dir, f));\n}\n\n/**\n * Make the project's own TypeScript loader active in this process.\n *\n * `import()` below relies on tsx's resolver, and nothing had registered it:\n * `rebase build` runs from `node_modules/.bin/rebase`, which is plain Node.\n * On a modern Node that *looks* like it works — native type stripping imports\n * `collections/index.ts` happily — right up to the first relative specifier\n * inside it. The scaffolded template writes `import posts from \"./posts.js\"`,\n * which is the correct thing for TypeScript to emit and which native stripping\n * does not remap back to `posts.ts`. So `rebase build` failed on every\n * scaffolded project with `Cannot find module …/posts.js`, and the failure\n * looked like a broken template rather than a missing loader.\n *\n * Resolved from the project, not from this package, exactly as `resolveTsx`\n * does for the schema-generation subprocess: tsx is the project's dependency\n * (the scaffold declares it), and taking it from anywhere else would mean\n * evaluating a project's config with a loader it did not choose.\n *\n * Best-effort by design. A project with no tsx keeps the previous behaviour —\n * which is correct for one whose config is plain JavaScript, or whose imports\n * carry no extension — rather than failing on a dependency it never needed.\n * Registered once per process; `register` is cheap but not free, and deriving\n * twice in one process is an ordinary thing for a watch mode to do.\n */\nlet tsxRegistered = false;\n\nasync function registerProjectTsx(configDir: string): Promise<void> {\n if (tsxRegistered) return;\n tsxRegistered = true;\n\n // Where tsx might be, in the order most likely to find it. The scaffold\n // declares tsx in the *backend* workspace, and pnpm's isolated layout puts\n // it in `backend/node_modules` rather than hoisting it — so resolving only\n // from the config directory finds nothing on exactly the project shape this\n // exists to serve.\n const projectRoot = path.dirname(configDir);\n const bases = [configDir, projectRoot, path.join(projectRoot, \"backend\")];\n\n for (const base of bases) {\n try {\n // Resolved against a file *inside* the project so Node walks that\n // project's `node_modules`, not this package's.\n const requireFromProject = createRequire(path.join(base, \"noop.js\"));\n const api = await import(pathToFileURL(requireFromProject.resolve(\"tsx/esm/api\")).href) as\n { register?: () => unknown; default?: { register?: () => unknown } };\n\n // tsx's own `register`, not `module.register`. `tsx/esm/api` is the\n // API module; the loader is `tsx/esm`. Handing the API module to\n // `module.register` registers something with no hooks and reports\n // success — a worse failure than throwing, because the import below\n // then resolves exactly as it did before and the bug looks unfixed.\n const register = api.register ?? api.default?.register;\n if (typeof register === \"function\") {\n register();\n return;\n }\n } catch {\n // Not here. Try the next base.\n }\n }\n\n // No tsx anywhere in the project. The import below still runs; it simply\n // resolves the way Node alone would, which is correct for a config written\n // in plain JavaScript or whose imports carry no extension.\n}\n\n/**\n * Import a module for its side effects.\n *\n * Plain `import()` so tsx's loader hooks resolve `.ts` and workspace\n * specifiers, exactly as the collection loader does.\n */\nasync function evaluate(filePath: string): Promise<Record<string, unknown>> {\n return await import(pathToFileURL(filePath).href) as Record<string, unknown>;\n}\n\nexport interface DeriveOptions {\n /** Absolute path to the project's config directory. */\n configDir: string;\n /**\n * Whether to evaluate collection files too. Resources are conventionally\n * declared in `resources.ts`, but nothing stops a bucket being declared\n * beside the collection that stores into it, and a graph that missed it\n * would under-report what the project needs.\n */\n includeCollections?: boolean;\n /**\n * Absolute path to the crons directory. Each file is evaluated through the\n * same loader the runtime uses, which declares the cron under the same id\n * the scheduler runs it as. Absent, or missing on disk: no crons.\n */\n cronsDir?: string;\n /**\n * Absolute path to the functions directory. Read statically — the\n * bundler's portability analysis — never evaluated: a function's module\n * scope belongs to a running backend, not a build.\n */\n functionsDir?: string;\n /** The project root, for the paths a graph records. Defaults to the config directory's parent. */\n projectRoot?: string;\n}\n\n/** The derive options for a backend app, from its manifest entry. */\nexport function deriveOptionsFor(projectRoot: string, app: RebaseBackendAppConfig): DeriveOptions {\n const paths = resolveBackendPaths(app, projectRoot);\n return {\n projectRoot,\n configDir: path.join(projectRoot, paths.config),\n cronsDir: path.join(projectRoot, paths.crons),\n functionsDir: path.join(projectRoot, paths.functions)\n };\n}\n\n/** Whether a module's default export looks like a collection. */\nfunction isCollectionLike(value: unknown): value is CollectionConfig {\n return typeof value === \"object\" && value !== null\n && typeof (value as { slug?: unknown }).slug === \"string\"\n && typeof (value as { properties?: unknown }).properties === \"object\";\n}\n\n/**\n * The resources a collection reaches: its database, and every bucket a\n * storage property stores into. Only resources the graph declares get an\n * edge — the implicit default database is not in the graph, so a collection\n * that names no `dataSource` records nothing.\n */\nfunction collectionEdges(collection: CollectionConfig, declared: Set<string>): Array<[string, string]> {\n const edges: Array<[string, string]> = [];\n const from = `collection:${collection.slug}`;\n const databaseId = resourceId(\"database\", collection.dataSource ?? DEFAULT_RESOURCE_KEY);\n if (declared.has(databaseId)) edges.push([databaseId, from]);\n\n const walk = (properties: Record<string, unknown> | undefined, prefix: string): void => {\n for (const [name, raw] of Object.entries(properties ?? {})) {\n const property = raw as {\n storage?: { storageSource?: unknown };\n of?: unknown;\n properties?: Record<string, unknown>;\n };\n if (!property || typeof property !== \"object\") continue;\n if (property.storage) {\n const key = property.storage.storageSource === undefined\n ? DEFAULT_RESOURCE_KEY\n : resourceKeyOf(property.storage.storageSource as string);\n const bucketId = resourceId(\"bucket\", key);\n if (declared.has(bucketId)) edges.push([bucketId, `property:${collection.slug}.${prefix}${name}`]);\n }\n if (property.properties) walk(property.properties, `${prefix}${name}.`);\n if (property.of && typeof property.of === \"object\") walk({ [name]: property.of }, `${prefix}`);\n }\n };\n walk(collection.properties as Record<string, unknown>, \"\");\n return edges;\n}\n\n/**\n * A cron file that would not import, said in terms somebody can act on.\n *\n * The graph is derived by evaluating each cron module, in a process with none\n * of the deployment's environment — so a cron that reaches a module validating\n * `DATABASE_URL` at import fails here and works perfectly at boot. The raw\n * message is a Zod error about a variable, which reads as \"the environment is\n * wrong\" when the environment is not the point.\n */\nfunction cronLoadIssue(problem: string): string {\n return `${summariseCronProblem(problem)}\\n` +\n \" `rebase resources` evaluates each cron file to read its schedule, and it is a build \" +\n \"step: no .env, no secrets. Move work that needs the deployment's environment inside the \" +\n \"handler — `const { x } = await import(\\\"…\\\")` — so the module scope imports nothing that \" +\n \"reads configuration.\";\n}\n\n/**\n * The cron loader's `<file> (threw: <message>)`, as one line.\n *\n * The message is whatever the module threw, and the common case is a schema\n * validator: `loadEnv()` at module scope with no `.env` present throws a\n * `ZodError`, whose `.message` is the JSON array of its issues. That arrived\n * here verbatim — ten lines of `{ \"expected\": \"string\", \"code\":\n * \"invalid_type\", … }` — with the sentence that says what to do underneath it,\n * so the reader met \"Invalid input: expected string, received undefined\" twice\n * before reaching the explanation.\n *\n * A validator's own sentences are the useful half of that object, so they are\n * kept and the serialisation is dropped.\n */\nfunction summariseCronProblem(problem: string): string {\n const match = problem.match(/^(.*) \\(threw: ([\\s\\S]*)\\)$/);\n if (!match) return problem.split(\"\\n\")[0];\n const [, file, thrown] = match;\n return `${file}: ${summariseThrownMessage(thrown)}`;\n}\n\n/** One sentence out of a thrown message, whatever shape it arrived in. */\nfunction summariseThrownMessage(message: string): string {\n const trimmed = message.trim();\n if (trimmed.startsWith(\"[\") || trimmed.startsWith(\"{\")) {\n try {\n const parsed: unknown = JSON.parse(trimmed);\n const issues = Array.isArray(parsed) ? parsed : [parsed];\n const sentences = [...new Set(\n issues\n .map(issue => (issue as { message?: unknown } | null)?.message)\n .filter((m): m is string => typeof m === \"string\" && m.length > 0)\n )];\n if (sentences.length > 0) return sentences.join(\"; \");\n } catch {\n // Not JSON after all — fall through to the first line.\n }\n }\n return trimmed.split(\"\\n\")[0];\n}\n\n/**\n * Evaluate a project's config and return the graph it declares.\n *\n * Clears any previously registered declarations first, so deriving twice in one\n * process — a watch mode, a test — describes the project rather than the union\n * of every project seen so far.\n */\nexport async function deriveResourceGraph(options: DeriveOptions): Promise<{ graph: ResourceGraph; issues: ResourceIssue[] }> {\n const { configDir, includeCollections = true, cronsDir, functionsDir } = options;\n const projectRoot = options.projectRoot ?? path.dirname(configDir);\n // Before anything is evaluated: the loader has to be in place for the very\n // first import, and the first import is a collection file.\n await registerProjectTsx(configDir);\n\n // All three registries, and the side lists are the easy ones to forget:\n // resetting only the resources leaves one project's handlers attached\n // while its topics are gone — every one of them then reads as orphaned\n // against the *next* project. Caught by deriving twice in one process,\n // which is what a watch mode does.\n resetDeclaredResources();\n resetDeclaredSubscriptions();\n resetDeclaredQueueConsumers();\n\n const issues: ResourceIssue[] = [];\n const usedBy = new Map<string, string[]>();\n // `recordUse`, not `use`: React 19 made `use` a hook name, and\n // `react-hooks/rules-of-hooks` refuses a call to one from a plain function\n // — a lint error on a file with no React in it.\n const recordUse = (resource: string, by: string): void => {\n const list = usedBy.get(resource) ?? [];\n if (!list.includes(by)) list.push(by);\n usedBy.set(resource, list);\n };\n\n // ── Declarations ─────────────────────────────────────────────────────\n // Which export name is which resource, so a function's `import { media }`\n // can be recorded as reaching `bucket:media` without evaluating the\n // function. The export name and the key are usually the same word and\n // nothing requires them to be.\n const handleExports = new Map<string, string>();\n for (const name of RESOURCE_ENTRY_NAMES) {\n const candidate = path.join(configDir, name);\n if (!fs.existsSync(candidate)) continue;\n try {\n const mod = await evaluate(candidate);\n for (const [exportName, value] of Object.entries(mod)) {\n if (isResourceHandle(value)) handleExports.set(exportName, resourceId(value.kind, value.key));\n }\n } catch (err) {\n issues.push({ path: path.relative(configDir, candidate), message: describeIssue(err, projectRoot) });\n }\n break;\n }\n\n // ── Collections ──────────────────────────────────────────────────────\n const collections: CollectionConfig[] = [];\n if (includeCollections) {\n for (const entry of collectionFiles(configDir)) {\n try {\n const mod = await evaluate(entry);\n // As the loader sees it at boot: a handle written where a key\n // belongs is its key here too.\n if (isCollectionLike(mod.default)) collections.push(resolveResourceRefs(mod.default));\n } catch (err) {\n // Named individually: \"could not derive the graph\" with no file in\n // it sends somebody reading every module in the directory.\n issues.push({\n path: path.relative(configDir, entry),\n message: describeIssue(err, projectRoot)\n });\n }\n }\n }\n\n // ── Crons ────────────────────────────────────────────────────────────\n // Through the runtime's own loader, so the id the graph records is the id\n // the scheduler runs — the filename — and a cron that would not load at\n // boot does not load here either.\n //\n // Which means the module is EVALUATED, in a build step that has none of the\n // deployment's environment. A cron whose module scope reaches something that\n // validates `DATABASE_URL` at import therefore derives on a laptop with a\n // `.env` and nowhere else. That is worth an explicit sentence rather than a\n // stack, so `cronLoadIssue` adds one.\n if (cronsDir && fs.existsSync(cronsDir)) {\n // The runtime's loader logs each job as it loads, to stdout, as JSON\n // — which is also where `rebase resources --json` writes the graph.\n // Its problems come back as a list, so the log adds nothing here.\n const previousLevel = process.env.LOG_LEVEL;\n process.env.LOG_LEVEL = \"error\";\n try {\n const { loadCrons, declareCrons } = await import(\"@rebasepro/server\");\n const reportProblem = (problem: string) => {\n issues.push({ path: path.relative(projectRoot, cronsDir), message: cronLoadIssue(problem) });\n };\n // This process's own `import()`, not the loader's `new Function`\n // one: tsx's hooks are registered here, and a test runner's module\n // sandbox answers the other with \"a dynamic import callback was\n // not specified\" — the same trap the split-roles e2e records.\n const crons = await loadCrons(cronsDir, { importModule: url => import(url), onSkip: reportProblem });\n // One at a time, so a cron the graph refuses is reported by name\n // and the others are still declared.\n for (const [id, definition] of Object.entries(crons)) {\n try {\n declareCrons({ [id]: definition });\n } catch (err) {\n reportProblem(`${id} (threw: ${err instanceof Error ? err.message : String(err)})`);\n }\n }\n } catch (err) {\n issues.push({\n path: path.relative(projectRoot, cronsDir),\n message: cronLoadIssue(err instanceof Error ? err.message : String(err))\n });\n } finally {\n if (previousLevel === undefined) delete process.env.LOG_LEVEL;\n else process.env.LOG_LEVEL = previousLevel;\n }\n }\n\n // ── Functions ────────────────────────────────────────────────────────\n if (functionsDir && fs.existsSync(functionsDir)) {\n for (const report of analyseFunctionsDirectory(functionsDir, projectRoot)) {\n const requires = [...new Set(\n report.issues\n .filter(issue => issue.kind === \"node-builtin\" || issue.kind === \"node-only-package\")\n .map(issue => issue.message)\n )];\n try {\n declareFunction(report.name, {\n file: report.file,\n portable: report.portable,\n ...(requires.length > 0 ? { requires } : {})\n });\n } catch (err) {\n issues.push({ path: report.file, message: describeIssue(err, projectRoot) });\n }\n for (const imported of report.resourceImports) {\n const resource = handleExports.get(imported);\n if (resource) recordUse(resource, `function:${report.name}`);\n }\n }\n }\n\n // ── Edges ────────────────────────────────────────────────────────────\n const declared = new Set(declaredResources().map(r => resourceId(r.kind, r.key)));\n for (const collection of collections) {\n for (const [resource, by] of collectionEdges(collection, declared)) recordUse(resource, by);\n }\n for (const sub of declaredSubscriptions()) {\n const id = resourceId(\"topic\", sub.topic);\n if (declared.has(id)) recordUse(id, `subscription:${sub.topic}.${sub.name}`);\n }\n for (const consumer of declaredQueueConsumers()) {\n const id = resourceId(\"queue\", consumer.queue);\n if (declared.has(id)) recordUse(id, `handler:${consumer.queue}`);\n }\n\n const graph = buildResourceGraph(usedBy);\n\n // Two resources of a kind whose env suffixes collide would silently read\n // each other's configuration, which is indistinguishable from a\n // misconfigured deployment.\n const byKind = new Map<string, string[]>();\n for (const r of graph.resources) {\n byKind.set(r.kind, [...(byKind.get(r.kind) ?? []), r.key]);\n }\n for (const [kind, keys] of byKind) {\n const collision = findEnvSuffixCollision(keys);\n if (collision) {\n issues.push({\n path: `${kind}.${collision.b}`,\n message:\n `\"${collision.a}\" and \"${collision.b}\" both bind from ${collision.suffix}, so one would ` +\n \"read the other's configuration. Rename one.\"\n });\n }\n }\n\n // A subscription on a topic nobody declared is a handler that will never\n // run, and nothing else would ever say so.\n const topics = new Set(graph.resources.filter(r => r.kind === \"topic\").map(r => r.key));\n for (const sub of declaredSubscriptions()) {\n if (!topics.has(sub.topic)) {\n issues.push({\n path: `topic.${sub.topic}`,\n message:\n `Subscription \"${sub.name}\" is declared on topic \"${sub.topic}\", which nothing declares. ` +\n \"The handler would never run.\"\n });\n }\n }\n\n // A collection routed to a database nothing declares boots into a\n // refusal, and a property stored in a bucket nothing declares answers 501\n // on its first upload. Both are cheaper to hear about here.\n const databases = new Set(graph.resources.filter(r => r.kind === \"database\").map(r => r.key));\n const buckets = new Set(graph.resources.filter(r => r.kind === \"bucket\").map(r => r.key));\n for (const collection of collections) {\n if (collection.dataSource && collection.dataSource !== DEFAULT_RESOURCE_KEY && !databases.has(collection.dataSource)) {\n issues.push({\n path: `collection.${collection.slug}`,\n message:\n `routes to dataSource \"${collection.dataSource}\", which nothing declares. ` +\n `Add database(\"${collection.dataSource}\") to resources.ts, or import the handle and use it here.`\n });\n }\n for (const [name, raw] of Object.entries(collection.properties ?? {})) {\n const key = (raw as { storage?: { storageSource?: unknown } })?.storage?.storageSource;\n if (typeof key === \"string\" && key !== DEFAULT_RESOURCE_KEY && buckets.size > 0 && !buckets.has(key)) {\n issues.push({\n path: `collection.${collection.slug}.${name}`,\n message: `stores into bucket \"${key}\", which nothing declares. Add bucket(\"${key}\") to resources.ts.`\n });\n }\n }\n }\n\n return { graph, issues };\n}\n\n/** The graph as it is written to disk: stable key order, trailing newline. */\nexport function serializeResourceGraph(graph: ResourceGraph): string {\n const resources = graph.resources.map(r => ({\n kind: r.kind,\n key: r.key,\n engine: r.engine,\n transport: r.transport,\n ...(r.label !== undefined ? { label: r.label } : {}),\n ...(Object.keys(r.options).length > 0 ? { options: r.options } : {}),\n ...(r.usedBy && r.usedBy.length > 0 ? { usedBy: r.usedBy } : {})\n }));\n return JSON.stringify({\n $generated: `Generated from config by \\`rebase resources\\`. Edit the declarations, not this file.`,\n version: graph.version,\n resources\n }, null, 4) + \"\\n\";\n}\n\n/** Read the committed graph, or null when a project has none yet. */\nexport function readResourceGraphFile(projectRoot: string): string | null {\n const file = path.join(projectRoot, RESOURCE_GRAPH_FILENAME);\n return fs.existsSync(file) ? fs.readFileSync(file, \"utf-8\") : null;\n}\n\n/**\n * Write the graph, and say whether the file changed.\n *\n * The boolean is what a `--check` mode reports on: a project whose committed\n * graph disagrees with its config has a host reading one thing and a runtime\n * doing another.\n */\nexport function writeResourceGraphFile(projectRoot: string, graph: ResourceGraph): { changed: boolean; file: string } {\n const file = path.join(projectRoot, RESOURCE_GRAPH_FILENAME);\n const next = serializeResourceGraph(graph);\n const previous = fs.existsSync(file) ? fs.readFileSync(file, \"utf-8\") : null;\n if (previous === next) return { changed: false, file };\n fs.writeFileSync(file, next);\n return { changed: true, file };\n}\n\n/** Parse a committed graph file, tolerating the `$generated` banner. */\nexport function parseResourceGraph(contents: string): ResourceGraph {\n const raw = JSON.parse(contents) as { version?: number; resources?: unknown };\n if (raw.version !== 1) {\n throw new Error(\n `Unsupported resource graph version ${String(raw.version)}. ` +\n \"This project was generated by a newer Rebase; upgrade rather than provisioning half of it.\"\n );\n }\n const entries = Array.isArray(raw.resources) ? raw.resources as Record<string, unknown>[] : [];\n // `options` is omitted from the file when empty; a reader of the graph\n // expects it present, so it is restored here.\n return { version: 1, resources: entries.map(r => ({ options: {}, ...r })) as ResourceGraph[\"resources\"] };\n}\n\n/**\n * Add the resources a project has without declaring them.\n *\n * A backend has a database whether or not anyone said so, and a project that\n * declares no buckets still gets one default storage source from the plain\n * unsuffixed variables. Both are load-bearing defaults and both are invisible\n * in the graph, so a status view built only from declarations would show an\n * empty screen to the majority of projects — the ones that most need to be told\n * which variable their one database reads.\n *\n * Declaring a *named* one leaves the default where it is, as the server does\n * (`resolveDataSources` and `resolveStorageSources` in `@rebasepro/server`):\n * `bucket(\"media\")` adds a source and never moves the files the default holds,\n * so both are listed. The default is displaced only by declaring it — or, for a\n * bucket, by marking exactly one server-side bucket `default: true`, which then\n * serves the uploads that name no `storageSource`. This used to drop the\n * default once a bucket was declared, and `rebase status` and `rebase\n * resources` stopped listing a source the server was still serving.\n */\nexport function withImplicitDefaults(graph: ResourceGraph): {\n declaration: ResourceDeclaration;\n implicit: boolean;\n}[] {\n const entries = graph.resources.map(declaration => ({ declaration, implicit: false }));\n const declaresDefault = (kind: string) => graph.resources.some(r => r.kind === kind && r.key === DEFAULT_RESOURCE_KEY);\n const markedDefault = graph.resources.filter(r =>\n r.kind === \"bucket\" && (r.transport ?? \"server\") === \"server\" && r.options.default === true);\n\n if (!declaresDefault(\"database\")) {\n entries.unshift({\n declaration: {\n kind: \"database\",\n key: DEFAULT_RESOURCE_KEY,\n engine: \"postgres\",\n transport: \"server\",\n options: {}\n },\n implicit: true\n });\n }\n if (!declaresDefault(\"bucket\") && markedDefault.length !== 1) {\n const implicitBucket = {\n declaration: {\n kind: \"bucket\",\n key: DEFAULT_RESOURCE_KEY,\n // `local` is what an unconfigured default source resolves to,\n // and naming it here keeps the row honest about what a project\n // with no S3 variables actually gets: a directory that a\n // container erases on restart, which production then drops.\n engine: \"local\",\n transport: \"server\" as const,\n options: {}\n },\n implicit: true\n };\n // First among the buckets, as the database default is first among the\n // databases; last when there are none.\n const firstBucket = entries.findIndex(entry => entry.declaration.kind === \"bucket\");\n if (firstBucket === -1) entries.push(implicitBucket);\n else entries.splice(firstBucket, 0, implicitBucket);\n }\n return entries;\n}\n\n/**\n * The resources a project *has*, which is what a person is asking about.\n *\n * One projection, because two of them disagreed. `rebase status` built its\n * rows from {@link withImplicitDefaults} and listed `buckets ✓ (default) local\n * · implicit`; `rebase resources` and `rebase resources --json` built theirs\n * from the raw graph and listed only the database and the function. Two\n * commands whose whole job is to answer \"what does this project need\", on the\n * same stock scaffold, giving different answers.\n *\n * The implicit entries are marked rather than hidden, because the distinction\n * is real and load-bearing: a declared resource is recorded in\n * `rebase.resources.json` for a host to provision, and an implicit one is a\n * default the runtime supplies whether or not anyone wrote it down. That file\n * still holds declarations only — it is a wire contract a host reads, and\n * putting defaults in it would ask for something to be provisioned that\n * nobody declared.\n */\nexport type ProjectedResource = ResourceDeclaration & { implicit?: true };\n\nexport function projectResourceGraph(graph: ResourceGraph): ProjectedResource[] {\n return withImplicitDefaults(graph).map(({ declaration, implicit }) =>\n (implicit ? { ...declaration, implicit: true as const } : declaration));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"derive-ae9UOoOB.js","names":[],"sources":["../src/resources/derive.ts"],"sourcesContent":["/**\n * Deriving the resource graph from a project's config, and recording it.\n *\n * ## Why the graph is derived rather than authored\n *\n * A resource is declared once, in code. But a host has to know what a project\n * needs *before* it runs anything — that is how a console can say \"this project\n * wants a `media` bucket and has none\" on a first deploy, and it is the reason\n * storage topology used to be hand-written into `rebase.json` in the first\n * place. A `custom` runtime makes it sharper still: it emits no bundle\n * manifest, so without a committed artifact there is nothing to read at all.\n *\n * So the graph is derived by evaluating the config and written to\n * `rebase.resources.json`, which is committed. Same arrangement as\n * `schema.generated.ts`: authored in one place, generated into another, and a\n * gate fails if the two disagree. The file is an *output*, never an input —\n * which is what removes the old silent merge, because there is no longer a\n * second declaration for anything to be merged with.\n *\n * ## Why evaluation rather than static analysis\n *\n * Encore parses the source and builds the graph from the AST. Rebase already\n * evaluates config to load collections, and evaluation is exact where a parser\n * is approximate — a resource declared in a loop, or behind a helper, is\n * invisible to a parser and obvious to an interpreter. The cost is running user\n * code at build time, which this project already accepts everywhere else.\n */\nimport fs from \"fs\";\nimport path from \"path\";\nimport { createRequire } from \"module\";\nimport { pathToFileURL } from \"url\";\nimport {\n DEFAULT_RESOURCE_KEY,\n buildResourceGraph,\n declareFunction,\n declaredQueueConsumers,\n declaredResources,\n declaredSubscriptions,\n findEnvSuffixCollision,\n isResourceHandle,\n resetDeclaredQueueConsumers,\n resetDeclaredResources,\n resetDeclaredSubscriptions,\n resolveResourceRefs,\n resourceId,\n resourceKeyOf,\n type CollectionConfig,\n type RebaseBackendAppConfig,\n type ResourceDeclaration,\n type ResourceGraph\n} from \"@rebasepro/types\";\nimport { analyseFunctionsDirectory } from \"../function-portability\";\nimport { resolveBackendPaths } from \"../manifest\";\nimport { cliVersion } from \"../utils/version\";\n\n/** The committed, generated record of what a project needs. */\nexport const RESOURCE_GRAPH_FILENAME = \"rebase.resources.json\";\n\n/** A problem found while deriving, reported with the rest rather than thrown one at a time. */\nexport interface ResourceIssue {\n path: string;\n message: string;\n}\n\n/**\n * Which `@rebasepro/*` package a module error is about, when it is about one.\n *\n * Both directions of a version skew arrive as an error about the *user's* file:\n * `resources.ts The requested module '@rebasepro/types' does not provide an\n * export named 'queue'`. Nothing in that says which copy of `@rebasepro/types`\n * is installed, what version this CLI is, or that the two disagree — so the\n * reader goes looking for a typo in code they just copied out of the docs.\n *\n * Two shapes, because the two directions produce different errors: a named\n * export the installed copy is too old for, and a package the installed tree\n * does not have at all.\n */\nconst NAMED_EXPORT_MISSING = /does not provide an export named ['\"]([^'\"]+)['\"]/;\nconst SPECIFIER = /(@rebasepro\\/[a-z0-9-]+)/;\n\n/** The version of a package as resolved from the project, or null. */\nfunction installedVersion(specifier: string, from: string): string | null {\n try {\n const require = createRequire(path.join(from, \"noop.js\"));\n const manifest = require.resolve(`${specifier}/package.json`);\n const version: unknown = JSON.parse(fs.readFileSync(manifest, \"utf-8\")).version;\n return typeof version === \"string\" ? version : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The message an issue carries, with the skew named when there is one.\n *\n * `main` runs 497 commits ahead of npm `latest` and the docs site publishes from\n * `main`, so the shape this exists for is routine: a scaffold pinned to the\n * published version, config code written against today's documentation, and an\n * error about the config file. The fix is one command and the error named none\n * of it — the same failure as `404 No PUT route on collection 'projects'`, one\n * layer down.\n *\n * Says nothing extra when the two versions agree, or when either is unreadable:\n * a sentence naming one version twice is worse than no sentence.\n */\nexport function describeIssue(err: unknown, projectRoot: string, ownVersion = cliVersion()): string {\n const message = err instanceof Error ? err.message : String(err);\n const code = (err as { code?: unknown } | null)?.code;\n const missingExport = NAMED_EXPORT_MISSING.exec(message);\n const isSkewShaped = missingExport !== null || code === \"ERR_MODULE_NOT_FOUND\";\n if (!isSkewShaped) return message;\n\n const specifier = SPECIFIER.exec(message)?.[1];\n if (!specifier) return message;\n\n // One line, not two: every caller renders an issue as `path message` on a\n // single row, so a newline here would land the useful half unindented under\n // the file name.\n const lead = /[.!?]$/.test(message.trim()) ? message.trim() : `${message.trim()}.`;\n\n const installed = installedVersion(specifier, projectRoot);\n if (installed === null) {\n return `${lead} ${specifier} is not installed in this project; this CLI is ${ownVersion}. `\n + `Run pnpm add ${specifier}@${ownVersion}`;\n }\n if (ownVersion === \"unknown\" || installed === ownVersion) return message;\n\n // Which side to move is the question the reader actually has, and it has a\n // right answer: the CLI is what evaluates this file, so the packages follow\n // it — unless the CLI is the older one, in which case following it would\n // move the project backwards.\n const cliIsNewer = compareVersions(ownVersion, installed) > 0;\n return cliIsNewer\n ? `${lead} ${specifier} ${installed} is installed; this CLI is ${ownVersion}. `\n + `Run pnpm add ${specifier}@${ownVersion}`\n : `${lead} ${specifier} ${installed} is installed; this CLI is ${ownVersion}, which is older. `\n + `Update the CLI to ${installed}, or pin ${specifier} back to ${ownVersion}`;\n}\n\n/** -1 / 0 / 1 on the numeric parts, ignoring any prerelease tail. */\nfunction compareVersions(a: string, b: string): number {\n const parts = (v: string): number[] => v.split(\"-\")[0].split(\".\").map(n => Number(n) || 0);\n const [x, y] = [parts(a), parts(b)];\n for (let i = 0; i < 3; i++) {\n if ((x[i] ?? 0) !== (y[i] ?? 0)) return (x[i] ?? 0) < (y[i] ?? 0) ? -1 : 1;\n }\n return 0;\n}\n\n/** Files that declare resources, in the order they are loaded. */\nconst RESOURCE_ENTRY_NAMES = [\"resources.ts\", \"resources.js\", \"resources/index.ts\", \"resources/index.js\"];\n\n/** Collection files, which may also declare a resource beside the collection using it. */\nfunction collectionFiles(configDir: string): string[] {\n const dir = path.join(configDir, \"collections\");\n if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return [];\n return fs.readdirSync(dir)\n .filter(f => /\\.(ts|js|mts|mjs)$/.test(f) && !/\\.(test|spec|d)\\./.test(f))\n .sort()\n .map(f => path.join(dir, f));\n}\n\n/**\n * Make the project's own TypeScript loader active in this process.\n *\n * `import()` below relies on tsx's resolver, and nothing had registered it:\n * `rebase build` runs from `node_modules/.bin/rebase`, which is plain Node.\n * On a modern Node that *looks* like it works — native type stripping imports\n * `collections/index.ts` happily — right up to the first relative specifier\n * inside it. The scaffolded template writes `import posts from \"./posts.js\"`,\n * which is the correct thing for TypeScript to emit and which native stripping\n * does not remap back to `posts.ts`. So `rebase build` failed on every\n * scaffolded project with `Cannot find module …/posts.js`, and the failure\n * looked like a broken template rather than a missing loader.\n *\n * Resolved from the project, not from this package, exactly as `resolveTsx`\n * does for the schema-generation subprocess: tsx is the project's dependency\n * (the scaffold declares it), and taking it from anywhere else would mean\n * evaluating a project's config with a loader it did not choose.\n *\n * Best-effort by design. A project with no tsx keeps the previous behaviour —\n * which is correct for one whose config is plain JavaScript, or whose imports\n * carry no extension — rather than failing on a dependency it never needed.\n * Registered once per process; `register` is cheap but not free, and deriving\n * twice in one process is an ordinary thing for a watch mode to do.\n */\nlet tsxRegistered = false;\n\nasync function registerProjectTsx(configDir: string): Promise<void> {\n if (tsxRegistered) return;\n tsxRegistered = true;\n\n // Where tsx might be, in the order most likely to find it. The scaffold\n // declares tsx in the *backend* workspace, and pnpm's isolated layout puts\n // it in `backend/node_modules` rather than hoisting it — so resolving only\n // from the config directory finds nothing on exactly the project shape this\n // exists to serve.\n const projectRoot = path.dirname(configDir);\n const bases = [configDir, projectRoot, path.join(projectRoot, \"backend\")];\n\n for (const base of bases) {\n try {\n // Resolved against a file *inside* the project so Node walks that\n // project's `node_modules`, not this package's.\n const requireFromProject = createRequire(path.join(base, \"noop.js\"));\n const api = await import(pathToFileURL(requireFromProject.resolve(\"tsx/esm/api\")).href) as\n { register?: () => unknown; default?: { register?: () => unknown } };\n\n // tsx's own `register`, not `module.register`. `tsx/esm/api` is the\n // API module; the loader is `tsx/esm`. Handing the API module to\n // `module.register` registers something with no hooks and reports\n // success — a worse failure than throwing, because the import below\n // then resolves exactly as it did before and the bug looks unfixed.\n const register = api.register ?? api.default?.register;\n if (typeof register === \"function\") {\n register();\n return;\n }\n } catch {\n // Not here. Try the next base.\n }\n }\n\n // No tsx anywhere in the project. The import below still runs; it simply\n // resolves the way Node alone would, which is correct for a config written\n // in plain JavaScript or whose imports carry no extension.\n}\n\n/**\n * Import a module for its side effects.\n *\n * Plain `import()` so tsx's loader hooks resolve `.ts` and workspace\n * specifiers, exactly as the collection loader does.\n */\nasync function evaluate(filePath: string): Promise<Record<string, unknown>> {\n return await import(pathToFileURL(filePath).href) as Record<string, unknown>;\n}\n\nexport interface DeriveOptions {\n /** Absolute path to the project's config directory. */\n configDir: string;\n /**\n * Whether to evaluate collection files too. Resources are conventionally\n * declared in `resources.ts`, but nothing stops a bucket being declared\n * beside the collection that stores into it, and a graph that missed it\n * would under-report what the project needs.\n */\n includeCollections?: boolean;\n /**\n * Absolute path to the crons directory. Each file is evaluated through the\n * same loader the runtime uses, which declares the cron under the same id\n * the scheduler runs it as. Absent, or missing on disk: no crons.\n */\n cronsDir?: string;\n /**\n * Absolute path to the functions directory. Read statically — the\n * bundler's portability analysis — never evaluated: a function's module\n * scope belongs to a running backend, not a build.\n */\n functionsDir?: string;\n /** The project root, for the paths a graph records. Defaults to the config directory's parent. */\n projectRoot?: string;\n}\n\n/** The derive options for a backend app, from its manifest entry. */\nexport function deriveOptionsFor(projectRoot: string, app: RebaseBackendAppConfig): DeriveOptions {\n const paths = resolveBackendPaths(app, projectRoot);\n return {\n projectRoot,\n configDir: path.join(projectRoot, paths.config),\n cronsDir: path.join(projectRoot, paths.crons),\n functionsDir: path.join(projectRoot, paths.functions)\n };\n}\n\n/** Whether a module's default export looks like a collection. */\nfunction isCollectionLike(value: unknown): value is CollectionConfig {\n return typeof value === \"object\" && value !== null\n && typeof (value as { slug?: unknown }).slug === \"string\"\n && typeof (value as { properties?: unknown }).properties === \"object\";\n}\n\n/**\n * The resources a collection reaches: its database, and every bucket a\n * storage property stores into. Only resources the graph declares get an\n * edge — the implicit default database is not in the graph, so a collection\n * that names no `dataSource` records nothing.\n */\nfunction collectionEdges(collection: CollectionConfig, declared: Set<string>): Array<[string, string]> {\n const edges: Array<[string, string]> = [];\n const from = `collection:${collection.slug}`;\n const databaseId = resourceId(\"database\", collection.dataSource ?? DEFAULT_RESOURCE_KEY);\n if (declared.has(databaseId)) edges.push([databaseId, from]);\n\n const walk = (properties: Record<string, unknown> | undefined, prefix: string): void => {\n for (const [name, raw] of Object.entries(properties ?? {})) {\n const property = raw as {\n storage?: { storageSource?: unknown };\n of?: unknown;\n properties?: Record<string, unknown>;\n };\n if (!property || typeof property !== \"object\") continue;\n if (property.storage) {\n const key = property.storage.storageSource === undefined\n ? DEFAULT_RESOURCE_KEY\n : resourceKeyOf(property.storage.storageSource as string);\n const bucketId = resourceId(\"bucket\", key);\n if (declared.has(bucketId)) edges.push([bucketId, `property:${collection.slug}.${prefix}${name}`]);\n }\n if (property.properties) walk(property.properties, `${prefix}${name}.`);\n if (property.of && typeof property.of === \"object\") walk({ [name]: property.of }, `${prefix}`);\n }\n };\n walk(collection.properties as Record<string, unknown>, \"\");\n return edges;\n}\n\n/**\n * A cron file that would not import, said in terms somebody can act on.\n *\n * The graph is derived by evaluating each cron module, in a process with none\n * of the deployment's environment — so a cron that reaches a module validating\n * `DATABASE_URL` at import fails here and works perfectly at boot. The raw\n * message is a Zod error about a variable, which reads as \"the environment is\n * wrong\" when the environment is not the point.\n */\nfunction cronLoadIssue(problem: string): string {\n return `${summariseCronProblem(problem)}\\n` +\n \" `rebase resources` evaluates each cron file to read its schedule, and it is a build \" +\n \"step: no .env, no secrets. Move work that needs the deployment's environment inside the \" +\n \"handler — `const { x } = await import(\\\"…\\\")` — so the module scope imports nothing that \" +\n \"reads configuration.\";\n}\n\n/**\n * The cron loader's `<file> (threw: <message>)`, as one line.\n *\n * The message is whatever the module threw, and the common case is a schema\n * validator: `loadEnv()` at module scope with no `.env` present throws a\n * `ZodError`, whose `.message` is the JSON array of its issues. That arrived\n * here verbatim — ten lines of `{ \"expected\": \"string\", \"code\":\n * \"invalid_type\", … }` — with the sentence that says what to do underneath it,\n * so the reader met \"Invalid input: expected string, received undefined\" twice\n * before reaching the explanation.\n *\n * A validator's own sentences are the useful half of that object, so they are\n * kept and the serialisation is dropped.\n */\nfunction summariseCronProblem(problem: string): string {\n const match = problem.match(/^(.*) \\(threw: ([\\s\\S]*)\\)$/);\n if (!match) return problem.split(\"\\n\")[0];\n const [, file, thrown] = match;\n return `${file}: ${summariseThrownMessage(thrown)}`;\n}\n\n/** One sentence out of a thrown message, whatever shape it arrived in. */\nfunction summariseThrownMessage(message: string): string {\n const trimmed = message.trim();\n if (trimmed.startsWith(\"[\") || trimmed.startsWith(\"{\")) {\n try {\n const parsed: unknown = JSON.parse(trimmed);\n const issues = Array.isArray(parsed) ? parsed : [parsed];\n const sentences = [...new Set(\n issues\n .map(issue => (issue as { message?: unknown } | null)?.message)\n .filter((m): m is string => typeof m === \"string\" && m.length > 0)\n )];\n if (sentences.length > 0) return sentences.join(\"; \");\n } catch {\n // Not JSON after all — fall through to the first line.\n }\n }\n return trimmed.split(\"\\n\")[0];\n}\n\n/**\n * Evaluate a project's config and return the graph it declares.\n *\n * Clears any previously registered declarations first, so deriving twice in one\n * process — a watch mode, a test — describes the project rather than the union\n * of every project seen so far.\n */\nexport async function deriveResourceGraph(options: DeriveOptions): Promise<{ graph: ResourceGraph; issues: ResourceIssue[] }> {\n const { configDir, includeCollections = true, cronsDir, functionsDir } = options;\n const projectRoot = options.projectRoot ?? path.dirname(configDir);\n // Before anything is evaluated: the loader has to be in place for the very\n // first import, and the first import is a collection file.\n await registerProjectTsx(configDir);\n\n // All three registries, and the side lists are the easy ones to forget:\n // resetting only the resources leaves one project's handlers attached\n // while its topics are gone — every one of them then reads as orphaned\n // against the *next* project. Caught by deriving twice in one process,\n // which is what a watch mode does.\n resetDeclaredResources();\n resetDeclaredSubscriptions();\n resetDeclaredQueueConsumers();\n\n const issues: ResourceIssue[] = [];\n const usedBy = new Map<string, string[]>();\n // `recordUse`, not `use`: React 19 made `use` a hook name, and\n // `react-hooks/rules-of-hooks` refuses a call to one from a plain function\n // — a lint error on a file with no React in it.\n const recordUse = (resource: string, by: string): void => {\n const list = usedBy.get(resource) ?? [];\n if (!list.includes(by)) list.push(by);\n usedBy.set(resource, list);\n };\n\n // ── Declarations ─────────────────────────────────────────────────────\n // Which export name is which resource, so a function's `import { media }`\n // can be recorded as reaching `bucket:media` without evaluating the\n // function. The export name and the key are usually the same word and\n // nothing requires them to be.\n const handleExports = new Map<string, string>();\n for (const name of RESOURCE_ENTRY_NAMES) {\n const candidate = path.join(configDir, name);\n if (!fs.existsSync(candidate)) continue;\n try {\n const mod = await evaluate(candidate);\n for (const [exportName, value] of Object.entries(mod)) {\n if (isResourceHandle(value)) handleExports.set(exportName, resourceId(value.kind, value.key));\n }\n } catch (err) {\n issues.push({ path: path.relative(configDir, candidate), message: describeIssue(err, projectRoot) });\n }\n break;\n }\n\n // ── Collections ──────────────────────────────────────────────────────\n const collections: CollectionConfig[] = [];\n if (includeCollections) {\n for (const entry of collectionFiles(configDir)) {\n try {\n const mod = await evaluate(entry);\n // As the loader sees it at boot: a handle written where a key\n // belongs is its key here too.\n if (isCollectionLike(mod.default)) collections.push(resolveResourceRefs(mod.default));\n } catch (err) {\n // Named individually: \"could not derive the graph\" with no file in\n // it sends somebody reading every module in the directory.\n issues.push({\n path: path.relative(configDir, entry),\n message: describeIssue(err, projectRoot)\n });\n }\n }\n }\n\n // ── Crons ────────────────────────────────────────────────────────────\n // Through the runtime's own loader, so the id the graph records is the id\n // the scheduler runs — the filename — and a cron that would not load at\n // boot does not load here either.\n //\n // Which means the module is EVALUATED, in a build step that has none of the\n // deployment's environment. A cron whose module scope reaches something that\n // validates `DATABASE_URL` at import therefore derives on a laptop with a\n // `.env` and nowhere else. That is worth an explicit sentence rather than a\n // stack, so `cronLoadIssue` adds one.\n if (cronsDir && fs.existsSync(cronsDir)) {\n // The runtime's loader logs each job as it loads, to stdout, as JSON\n // — which is also where `rebase resources --json` writes the graph.\n // Its problems come back as a list, so the log adds nothing here.\n const previousLevel = process.env.LOG_LEVEL;\n process.env.LOG_LEVEL = \"error\";\n try {\n const { loadCrons, declareCrons } = await import(\"@rebasepro/server\");\n const reportProblem = (problem: string) => {\n issues.push({ path: path.relative(projectRoot, cronsDir), message: cronLoadIssue(problem) });\n };\n // This process's own `import()`, not the loader's `new Function`\n // one: tsx's hooks are registered here, and a test runner's module\n // sandbox answers the other with \"a dynamic import callback was\n // not specified\" — the same trap the split-roles e2e records.\n const crons = await loadCrons(cronsDir, { importModule: url => import(url), onSkip: reportProblem });\n // One at a time, so a cron the graph refuses is reported by name\n // and the others are still declared.\n for (const [id, definition] of Object.entries(crons)) {\n try {\n declareCrons({ [id]: definition });\n } catch (err) {\n reportProblem(`${id} (threw: ${err instanceof Error ? err.message : String(err)})`);\n }\n }\n } catch (err) {\n issues.push({\n path: path.relative(projectRoot, cronsDir),\n message: cronLoadIssue(err instanceof Error ? err.message : String(err))\n });\n } finally {\n if (previousLevel === undefined) delete process.env.LOG_LEVEL;\n else process.env.LOG_LEVEL = previousLevel;\n }\n }\n\n // ── Functions ────────────────────────────────────────────────────────\n if (functionsDir && fs.existsSync(functionsDir)) {\n for (const report of analyseFunctionsDirectory(functionsDir, projectRoot)) {\n const requires = [...new Set(\n report.issues\n .filter(issue => issue.kind === \"node-builtin\" || issue.kind === \"node-only-package\")\n .map(issue => issue.message)\n )];\n try {\n declareFunction(report.name, {\n file: report.file,\n portable: report.portable,\n ...(requires.length > 0 ? { requires } : {})\n });\n } catch (err) {\n issues.push({ path: report.file, message: describeIssue(err, projectRoot) });\n }\n for (const imported of report.resourceImports) {\n const resource = handleExports.get(imported);\n if (resource) recordUse(resource, `function:${report.name}`);\n }\n }\n }\n\n // ── Edges ────────────────────────────────────────────────────────────\n const declared = new Set(declaredResources().map(r => resourceId(r.kind, r.key)));\n for (const collection of collections) {\n for (const [resource, by] of collectionEdges(collection, declared)) recordUse(resource, by);\n }\n for (const sub of declaredSubscriptions()) {\n const id = resourceId(\"topic\", sub.topic);\n if (declared.has(id)) recordUse(id, `subscription:${sub.topic}.${sub.name}`);\n }\n for (const consumer of declaredQueueConsumers()) {\n const id = resourceId(\"queue\", consumer.queue);\n if (declared.has(id)) recordUse(id, `handler:${consumer.queue}`);\n }\n\n const graph = buildResourceGraph(usedBy);\n\n // Two resources of a kind whose env suffixes collide would silently read\n // each other's configuration, which is indistinguishable from a\n // misconfigured deployment.\n const byKind = new Map<string, string[]>();\n for (const r of graph.resources) {\n byKind.set(r.kind, [...(byKind.get(r.kind) ?? []), r.key]);\n }\n for (const [kind, keys] of byKind) {\n const collision = findEnvSuffixCollision(keys);\n if (collision) {\n issues.push({\n path: `${kind}.${collision.b}`,\n message:\n `\"${collision.a}\" and \"${collision.b}\" both bind from ${collision.suffix}, so one would ` +\n \"read the other's configuration. Rename one.\"\n });\n }\n }\n\n // A subscription on a topic nobody declared is a handler that will never\n // run, and nothing else would ever say so.\n const topics = new Set(graph.resources.filter(r => r.kind === \"topic\").map(r => r.key));\n for (const sub of declaredSubscriptions()) {\n if (!topics.has(sub.topic)) {\n issues.push({\n path: `topic.${sub.topic}`,\n message:\n `Subscription \"${sub.name}\" is declared on topic \"${sub.topic}\", which nothing declares. ` +\n \"The handler would never run.\"\n });\n }\n }\n\n // A collection routed to a database nothing declares boots into a\n // refusal, and a property stored in a bucket nothing declares answers 501\n // on its first upload. Both are cheaper to hear about here.\n const databases = new Set(graph.resources.filter(r => r.kind === \"database\").map(r => r.key));\n const buckets = new Set(graph.resources.filter(r => r.kind === \"bucket\").map(r => r.key));\n for (const collection of collections) {\n if (collection.dataSource && collection.dataSource !== DEFAULT_RESOURCE_KEY && !databases.has(collection.dataSource)) {\n issues.push({\n path: `collection.${collection.slug}`,\n message:\n `routes to dataSource \"${collection.dataSource}\", which nothing declares. ` +\n `Add database(\"${collection.dataSource}\") to resources.ts, or import the handle and use it here.`\n });\n }\n for (const [name, raw] of Object.entries(collection.properties ?? {})) {\n const key = (raw as { storage?: { storageSource?: unknown } })?.storage?.storageSource;\n if (typeof key === \"string\" && key !== DEFAULT_RESOURCE_KEY && buckets.size > 0 && !buckets.has(key)) {\n issues.push({\n path: `collection.${collection.slug}.${name}`,\n message: `stores into bucket \"${key}\", which nothing declares. Add bucket(\"${key}\") to resources.ts.`\n });\n }\n }\n }\n\n return { graph, issues };\n}\n\n/** The graph as it is written to disk: stable key order, trailing newline. */\nexport function serializeResourceGraph(graph: ResourceGraph): string {\n const resources = graph.resources.map(r => ({\n kind: r.kind,\n key: r.key,\n engine: r.engine,\n transport: r.transport,\n ...(r.label !== undefined ? { label: r.label } : {}),\n ...(Object.keys(r.options).length > 0 ? { options: r.options } : {}),\n ...(r.usedBy && r.usedBy.length > 0 ? { usedBy: r.usedBy } : {})\n }));\n return JSON.stringify({\n $generated: `Generated from config by \\`rebase resources\\`. Edit the declarations, not this file.`,\n version: graph.version,\n resources\n }, null, 4) + \"\\n\";\n}\n\n/** Read the committed graph, or null when a project has none yet. */\nexport function readResourceGraphFile(projectRoot: string): string | null {\n const file = path.join(projectRoot, RESOURCE_GRAPH_FILENAME);\n return fs.existsSync(file) ? fs.readFileSync(file, \"utf-8\") : null;\n}\n\n/**\n * Write the graph, and say whether the file changed.\n *\n * The boolean is what a `--check` mode reports on: a project whose committed\n * graph disagrees with its config has a host reading one thing and a runtime\n * doing another.\n */\nexport function writeResourceGraphFile(projectRoot: string, graph: ResourceGraph): { changed: boolean; file: string } {\n const file = path.join(projectRoot, RESOURCE_GRAPH_FILENAME);\n const next = serializeResourceGraph(graph);\n const previous = fs.existsSync(file) ? fs.readFileSync(file, \"utf-8\") : null;\n if (previous === next) return { changed: false, file };\n fs.writeFileSync(file, next);\n return { changed: true, file };\n}\n\n/** Parse a committed graph file, tolerating the `$generated` banner. */\nexport function parseResourceGraph(contents: string): ResourceGraph {\n const raw = JSON.parse(contents) as { version?: number; resources?: unknown };\n if (raw.version !== 1) {\n throw new Error(\n `Unsupported resource graph version ${String(raw.version)}. ` +\n \"This project was generated by a newer Rebase; upgrade rather than provisioning half of it.\"\n );\n }\n const entries = Array.isArray(raw.resources) ? raw.resources as Record<string, unknown>[] : [];\n // `options` is omitted from the file when empty; a reader of the graph\n // expects it present, so it is restored here.\n return { version: 1, resources: entries.map(r => ({ options: {}, ...r })) as ResourceGraph[\"resources\"] };\n}\n\n/**\n * Add the resources a project has without declaring them.\n *\n * A backend has a database whether or not anyone said so, and a project that\n * declares no buckets still gets one default storage source from the plain\n * unsuffixed variables. Both are load-bearing defaults and both are invisible\n * in the graph, so a status view built only from declarations would show an\n * empty screen to the majority of projects — the ones that most need to be told\n * which variable their one database reads.\n *\n * Declaring a *named* one leaves the default where it is, as the server does\n * (`resolveDataSources` and `resolveStorageSources` in `@rebasepro/server`):\n * `bucket(\"media\")` adds a source and never moves the files the default holds,\n * so both are listed. The default is displaced only by declaring it — or, for a\n * bucket, by marking exactly one server-side bucket `default: true`, which then\n * serves the uploads that name no `storageSource`. This used to drop the\n * default once a bucket was declared, and `rebase status` and `rebase\n * resources` stopped listing a source the server was still serving.\n */\nexport function withImplicitDefaults(graph: ResourceGraph): {\n declaration: ResourceDeclaration;\n implicit: boolean;\n}[] {\n const entries = graph.resources.map(declaration => ({ declaration, implicit: false }));\n const declaresDefault = (kind: string) => graph.resources.some(r => r.kind === kind && r.key === DEFAULT_RESOURCE_KEY);\n const markedDefault = graph.resources.filter(r =>\n r.kind === \"bucket\" && (r.transport ?? \"server\") === \"server\" && r.options.default === true);\n\n if (!declaresDefault(\"database\")) {\n entries.unshift({\n declaration: {\n kind: \"database\",\n key: DEFAULT_RESOURCE_KEY,\n engine: \"postgres\",\n transport: \"server\",\n options: {}\n },\n implicit: true\n });\n }\n if (!declaresDefault(\"bucket\") && markedDefault.length !== 1) {\n const implicitBucket = {\n declaration: {\n kind: \"bucket\",\n key: DEFAULT_RESOURCE_KEY,\n // `local` is what an unconfigured default source resolves to,\n // and naming it here keeps the row honest about what a project\n // with no S3 variables actually gets: a directory that a\n // container erases on restart, which production then drops.\n engine: \"local\",\n transport: \"server\" as const,\n options: {}\n },\n implicit: true\n };\n // First among the buckets, as the database default is first among the\n // databases; last when there are none.\n const firstBucket = entries.findIndex(entry => entry.declaration.kind === \"bucket\");\n if (firstBucket === -1) entries.push(implicitBucket);\n else entries.splice(firstBucket, 0, implicitBucket);\n }\n return entries;\n}\n\n/**\n * The resources a project *has*, which is what a person is asking about.\n *\n * One projection, because two of them disagreed. `rebase status` built its\n * rows from {@link withImplicitDefaults} and listed `buckets ✓ (default) local\n * · implicit`; `rebase resources` and `rebase resources --json` built theirs\n * from the raw graph and listed only the database and the function. Two\n * commands whose whole job is to answer \"what does this project need\", on the\n * same stock scaffold, giving different answers.\n *\n * The implicit entries are marked rather than hidden, because the distinction\n * is real and load-bearing: a declared resource is recorded in\n * `rebase.resources.json` for a host to provision, and an implicit one is a\n * default the runtime supplies whether or not anyone wrote it down. That file\n * still holds declarations only — it is a wire contract a host reads, and\n * putting defaults in it would ask for something to be provisioned that\n * nobody declared.\n */\nexport type ProjectedResource = ResourceDeclaration & { implicit?: true };\n\nexport function projectResourceGraph(graph: ResourceGraph): ProjectedResource[] {\n return withImplicitDefaults(graph).map(({ declaration, implicit }) =>\n (implicit ? { ...declaration, implicit: true as const } : declaration));\n}\n"],"mappings":"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"}
|