@rebasepro/server-postgres 0.25.0-canary.g70dad01 → 0.25.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/dist/{BranchService-DO_3_CXt.js → BranchService-iXXZeVYD.js} +133 -20
- package/dist/BranchService-iXXZeVYD.js.map +1 -0
- package/dist/backup/backup-service.d.ts +8 -3
- package/dist/backup/pg-tools.d.ts +13 -1
- package/dist/{backup-cli-Ap2WX2uW.js → backup-cli-B2CAn9fR.js} +3 -3
- package/dist/{backup-cli-Ap2WX2uW.js.map → backup-cli-B2CAn9fR.js.map} +1 -1
- package/dist/{backup-service-DytMimsL.js → backup-service-Bb39plam.js} +67 -10
- package/dist/backup-service-Bb39plam.js.map +1 -0
- package/dist/{boot-ddl-D1lMCZLd.js → boot-ddl-s_3Hkz94.js} +2 -2
- package/dist/{boot-ddl-D1lMCZLd.js.map → boot-ddl-s_3Hkz94.js.map} +1 -1
- package/dist/{cli-console-CQGsBzSY.js → cli-console-DQ_K-sdM.js} +4 -4
- package/dist/{cli-console-CQGsBzSY.js.map → cli-console-DQ_K-sdM.js.map} +1 -1
- package/dist/{cli-errors-CJTKAKax.js → cli-errors-DwQnNATs.js} +52 -2
- package/dist/{cli-errors-CJTKAKax.js.map → cli-errors-DwQnNATs.js.map} +1 -1
- package/dist/cli-errors.d.ts +10 -0
- package/dist/cli.js +29 -24
- package/dist/cli.js.map +1 -1
- package/dist/{deferred-index-build-BiTqxqdl.js → deferred-index-build-Cis_HoGn.js} +3 -3
- package/dist/{deferred-index-build-BiTqxqdl.js.map → deferred-index-build-Cis_HoGn.js.map} +1 -1
- package/dist/{doctor-N3J88BN_.js → doctor-DvJH-IQG.js} +2 -2
- package/dist/{doctor-N3J88BN_.js.map → doctor-DvJH-IQG.js.map} +1 -1
- package/dist/{ensure-collection-policies-CVP6kIPT.js → ensure-collection-policies-CCqayoUz.js} +4 -4
- package/dist/{ensure-collection-policies-CVP6kIPT.js.map → ensure-collection-policies-CCqayoUz.js.map} +1 -1
- package/dist/{ensure-collection-tables-1WVNALOW.js → ensure-collection-tables-DvvImXDe.js} +5 -5
- package/dist/{ensure-collection-tables-1WVNALOW.js.map → ensure-collection-tables-DvvImXDe.js.map} +1 -1
- package/dist/{ensure-tables-D-Nm2k3S.js → ensure-tables-Dpgwx5sy.js} +2 -2
- package/dist/{ensure-tables-D-Nm2k3S.js.map → ensure-tables-Dpgwx5sy.js.map} +1 -1
- package/dist/{enum-labels-kept-e_93v2Ic.js → enum-labels-kept-CUlUlRkO.js} +2 -2
- package/dist/{enum-labels-kept-e_93v2Ic.js.map → enum-labels-kept-CUlUlRkO.js.map} +1 -1
- package/dist/index.es.js +6 -6
- package/dist/{open-rebase-Cwnqo5LU.js → open-rebase-BKA7Nyoe.js} +21 -16
- package/dist/{open-rebase-Cwnqo5LU.js.map → open-rebase-BKA7Nyoe.js.map} +1 -1
- package/dist/{pg-error-utils-Cmo8wZPy.js → pg-error-utils-Bv5n2PLn.js} +2 -2
- package/dist/{pg-error-utils-Cmo8wZPy.js.map → pg-error-utils-Bv5n2PLn.js.map} +1 -1
- package/dist/{push-index-prebuild-SXRlWBjc.js → push-index-prebuild-DIequ4bV.js} +3 -3
- package/dist/{push-index-prebuild-SXRlWBjc.js.map → push-index-prebuild-DIequ4bV.js.map} +1 -1
- package/dist/{push-legacy-renames-B37yKDac.js → push-legacy-renames-BqJut4F3.js} +3 -3
- package/dist/{push-legacy-renames-B37yKDac.js.map → push-legacy-renames-BqJut4F3.js.map} +1 -1
- package/dist/{push-policy-plan-jXzHAWSu.js → push-policy-plan-CvX8hiPb.js} +2 -2
- package/dist/{push-policy-plan-jXzHAWSu.js.map → push-policy-plan-CvX8hiPb.js.map} +1 -1
- package/dist/schema/doctor-cli.js +2 -2
- package/dist/schema/introspect-db.js +1 -1
- package/dist/services/BranchService.d.ts +40 -4
- package/dist/{websocket-BT85Yv_G.js → websocket-MC0zTBar.js} +3 -3
- package/dist/{websocket-BT85Yv_G.js.map → websocket-MC0zTBar.js.map} +1 -1
- package/package.json +7 -7
- package/dist/BranchService-DO_3_CXt.js.map +0 -1
- package/dist/backup-service-DytMimsL.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-errors-CJTKAKax.js","names":[],"sources":["../src/utils/error-cause.ts","../src/utils/connection-string.ts","../src/schema/destructive-sql.ts","../src/schema/undeclarable-constraints.ts","../src/module-dir.ts","../src/generated-sql.ts","../src/cli-helpers.ts","../src/cli-errors.ts"],"sourcesContent":["/**\n * Walk the error cause chain and return the deepest meaningful message.\n *\n * A leaf module, imported by the driver CLI: `pg-error-utils.ts`, where it used\n * to live, imports `@rebasepro/server`, and the CLI must start without it.\n */\nexport function extractCauseMessage(error: unknown): string | null {\n if (!error || typeof error !== \"object\") return null;\n if (!(error instanceof Error)) return null;\n\n if (error.cause && typeof error.cause === \"object\") {\n const deeper = extractCauseMessage(error.cause);\n if (deeper) return deeper;\n // The cause itself has a message\n if (error.cause instanceof Error && error.cause.message) {\n return error.cause.message;\n }\n }\n return null;\n}\n","/**\n * Connection-string rewrites for the tools Rebase shells out to.\n *\n * Pure and dependency-free: `pg-tools` builds argument vectors without a live\n * server, and these have to be usable from there.\n */\n\n/**\n * The `sslmode` values libpq accepts. `no-verify` is conspicuously not one.\n */\nconst LIBPQ_SSLMODES = new Set([\"disable\", \"allow\", \"prefer\", \"require\", \"verify-ca\", \"verify-full\"]);\n\n/**\n * Make a connection string safe to hand to a libpq program.\n *\n * `sslmode=no-verify` is a node-postgres convention — encrypt, but do not check\n * the certificate — and libpq does not have it. It does not degrade or warn:\n *\n * psql: error: invalid sslmode value: \"no-verify\"\n *\n * which means a `DATABASE_URL` that works perfectly for the app makes `psql`,\n * `pg_dump`, `pg_restore` and Atlas all refuse to start, with an error that\n * points at the value rather than at the convention it comes from. It has cost\n * this project time more than once.\n *\n * `require` is the honest translation: libpq's `require` encrypts and does not\n * verify the certificate either, which is exactly what `no-verify` asks for.\n * Nothing is relaxed by the rewrite — `verify-ca` and `verify-full` are left\n * alone, so a connection string that asked for verification still gets it.\n *\n * Only `sslmode` is touched, and only when it is a value libpq would reject.\n * Anything unparseable is returned as given: a connection that works\n * unverified beats one this corrupted.\n */\nexport function forLibpq(connectionString: string): string {\n let url: URL;\n try {\n url = new URL(connectionString);\n } catch {\n // Key/value DSNs and anything else we cannot parse. A `sslmode` in one\n // of those is space-separated rather than a query parameter, and\n // rewriting it with a regex is how a password containing \"sslmode=\"\n // gets mangled.\n return connectionString;\n }\n if (url.protocol !== \"postgres:\" && url.protocol !== \"postgresql:\") return connectionString;\n\n const sslmode = url.searchParams.get(\"sslmode\");\n if (sslmode === null || LIBPQ_SSLMODES.has(sslmode)) return connectionString;\n\n url.searchParams.set(\"sslmode\", \"require\");\n // Re-serialize by hand for the reason `pinSearchPath` does: URLSearchParams\n // writes a space as `+`, which node-postgres decodes and libpq does not.\n url.search = Array.from(url.searchParams.entries())\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join(\"&\");\n return url.toString();\n}\n\n/** Hosts that are this machine: `localhost`, `127.0.0.0/8`, `::1` and `0.0.0.0`. */\nconst isLoopbackHost = (hostname: string): boolean =>\n hostname === \"localhost\" || hostname === \"[::1]\" || hostname === \"0.0.0.0\" || /^127(\\.\\d{1,3}){3}$/.test(hostname);\n\n/**\n * A connection string for Atlas: {@link forLibpq}, and `sslmode=disable` for a\n * database on this machine whose URL says nothing about SSL.\n *\n * Atlas connects through lib/pq, whose default `sslmode` is `require`.\n * node-postgres — the app, and every step of these commands — asks for no SSL\n * when the URL names none. So a URL that served the app from a local Postgres\n * without SSL (the Docker image, a Homebrew install) failed every schema\n * command with `pq: SSL is not enabled on the server`. Given here, Atlas\n * connects the way the app already does.\n *\n * Nothing that asked for something is touched: a URL naming `sslmode`, `ssl`,\n * `host` or `hostaddr`, any other host, and anything that is not a postgres\n * URL are returned as {@link forLibpq} returns them.\n */\nexport function forAtlas(connectionString: string): string {\n const libpq = forLibpq(connectionString);\n let url: URL;\n try {\n url = new URL(libpq);\n } catch {\n return libpq;\n }\n if (url.protocol !== \"postgres:\" && url.protocol !== \"postgresql:\") return libpq;\n if (!isLoopbackHost(url.hostname)) return libpq;\n if ([\"sslmode\", \"ssl\", \"host\", \"hostaddr\"].some(key => url.searchParams.has(key))) return libpq;\n\n url.searchParams.set(\"sslmode\", \"disable\");\n // By hand, for the reason `forLibpq` re-serializes by hand.\n url.search = Array.from(url.searchParams.entries())\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join(\"&\");\n return url.toString();\n}\n\n/**\n * A connection string with its password taken out, and the password.\n *\n * For a libpq program: the URL goes on its command line, and the password goes\n * in `PGPASSWORD`. On the command line it was readable by anyone who can list\n * processes, and it came back verbatim in every failure, because execa's error\n * message repeats the command: `Command failed with exit code 1: pg_restore\n * '--dbname=postgresql://app:s3cret@…'`, in a terminal, a CI log and the\n * scheduled backup's run history.\n *\n * libpq takes the password from the URL's user info or a `password` parameter,\n * so both are removed. The value is percent-decoded, because `PGPASSWORD` is\n * read literally. Anything that is not a URL is returned as given, with no\n * password: a key/value DSN keeps its own.\n */\nexport function splitPassword(connectionString: string): { connectionString: string; password: string | null } {\n let url: URL;\n try {\n url = new URL(connectionString);\n } catch {\n return { connectionString, password: null };\n }\n if (url.protocol !== \"postgres:\" && url.protocol !== \"postgresql:\") return { connectionString, password: null };\n\n const fromUserInfo = url.password ? decodePercent(url.password) : null;\n const fromParameter = url.searchParams.get(\"password\");\n if (fromUserInfo === null && fromParameter === null) return { connectionString, password: null };\n\n url.password = \"\";\n if (fromParameter !== null) {\n url.searchParams.delete(\"password\");\n // By hand, as `forLibpq` does: URLSearchParams writes a space as `+`.\n url.search = Array.from(url.searchParams.entries())\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join(\"&\");\n }\n // libpq reads the user info first, as the URL's own password.\n return { connectionString: url.toString(), password: fromUserInfo ?? fromParameter };\n}\n\nfunction decodePercent(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\n/**\n * Every password a line of text carries in a connection string, replaced with\n * `***`.\n *\n * For whatever is about to be printed or logged and may have a connection\n * string in it: an error a client tool or a driver raised, a message that\n * quotes a command line. Covers the URL user-info form (`scheme://user:pass@`,\n * any scheme, a password with a raw `@` in it included) and `password=` in a\n * query string or a key/value DSN. The user, host and database stay, because\n * they are what a reader needs to tell which database it was.\n */\nexport function redactConnectionPasswords(text: string): string {\n return text\n .replace(/(\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s/?#@'\"]*):[^\\s/?#'\"]*@/gi, \"$1:***@\")\n .replace(/(\\bpassword\\s*=\\s*)('[^']*'|[^\\s&'\"]+)/gi, \"$1***\");\n}\n","/**\n * Pure helpers that classify an Atlas declarative-apply plan as destructive\n * or not, and decide what `rebase db push` should do about it.\n *\n * `db push` runs `atlas schema apply` to make the live database match the\n * generated `schema.sql`. Removing a collection field compiles to\n * `DROP COLUMN`; renaming compiles to drop-then-add — either destroys data.\n * We first run the apply with `--dry-run` to obtain the planned SQL, scan it\n * here, and refuse to auto-approve anything destructive without an explicit\n * opt-in.\n *\n * A type change is a destructive plan that names no DROP. `ALTER COLUMN\n * \"price\" TYPE integer` rounds every 19.99 to 20, and `TYPE date` throws away\n * every timestamp's time of day, in a statement that reads like a routine\n * alter. So a type change is gated too, unless it is a widening every value\n * survives unchanged — which needs the column's current type, read from the\n * catalogue by the caller.\n *\n * A primary key change names no DROP of data either: `DROP CONSTRAINT\n * \"<table>_pkey\", ADD PRIMARY KEY (\"id\", \"locale\")` keeps every value and\n * gives every row a new address. It is gated like a drop.\n *\n * So is the drop of a CHECK or EXCLUDE constraint. It keeps every row and\n * stops refusing the ones the rule was there to refuse. `db push` keeps Atlas\n * away from those constraints (`schema/undeclarable-constraints.ts`), so a plan\n * that still drops one is a pattern that missed, and the gate is the second\n * line. Which constraints those are is read from the catalogue by the caller.\n *\n * Everything in this file is side-effect free so it can be unit-tested\n * without Atlas or a database.\n */\n\nimport type { ExistingConstraint } from \"./undeclarable-constraints\";\n\n/**\n * SQL fragments that destroy data or data-bearing objects. Matched\n * case-insensitively against each statement of the plan. `IF EXISTS` /\n * whitespace variations are tolerated by the regexes below.\n */\nconst DESTRUCTIVE_PATTERNS: { label: string; re: RegExp }[] = [\n { label: \"DROP TABLE\", re: /\\bDROP\\s+TABLE\\b/i },\n { label: \"DROP COLUMN\", re: /\\bDROP\\s+COLUMN\\b/i },\n { label: \"DROP SCHEMA\", re: /\\bDROP\\s+SCHEMA\\b/i },\n { label: \"DROP VIEW\", re: /\\bDROP\\s+(MATERIALIZED\\s+)?VIEW\\b/i },\n { label: \"DROP TYPE\", re: /\\bDROP\\s+TYPE\\b/i },\n { label: \"TRUNCATE\", re: /\\bTRUNCATE\\b/i }\n];\n\n/**\n * Split a SQL script into individual statements, dropping blank lines and\n * `--` comment lines. Deliberately simple: Atlas emits one plain statement\n * per `;`, without string literals that contain semicolons in a schema DDL\n * plan, so a naive split is safe and keeps this dependency-free.\n */\nexport function splitSqlStatements(sql: string): string[] {\n // Strip full-line SQL comments so a commented-out DROP never trips the\n // detector, then split on semicolons.\n const withoutComments = sql\n .split(\"\\n\")\n .filter((line) => !line.trim().startsWith(\"--\"))\n .join(\"\\n\");\n return withoutComments\n .split(\";\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n}\n\n/** `->`, the marker Atlas prints in front of the first line of every statement. */\nconst PLAN_MARKER_RE = /^\\s*->\\s?/;\n/** `-------`, both the rule between sections and the one above a summary. */\nconst PLAN_RULE_RE = /^\\s*-{5,}\\s*$/;\n/** `Planning migration statements (3 in total):` — and the count it announces. */\nconst PLAN_HEADING_RE = /^Planning migration statements \\((\\d+) in total\\):/m;\n/**\n * What Atlas prints, and nothing else, when there is nothing to plan — from\n * `schema apply`, or from `migrate apply` with no file pending.\n */\nconst PLAN_SYNCED_RE = /^\\s*(?:Schema is synced, no changes to be made|No migration files to execute)\\s*$/;\n\n/** What {@link readPlan} could make of a plan. */\ninterface ReadPlan {\n statements: string[];\n /** The `(N in total)` Atlas announced, when its report said one. */\n announced?: number;\n}\n\n/**\n * Read the statements out of whatever Atlas printed.\n *\n * `atlas schema apply --dry-run` does not print SQL. It prints a report, and\n * the same statements appear in it twice:\n *\n * Planning migration statements (2 in total):\n *\n * -- modify \"people\" table:\n * -> ALTER TABLE \"public\".\"people\" ALTER COLUMN \"rating\" TYPE numeric(4,1);\n * -- create \"notes\" table:\n * -> CREATE TABLE \"public\".\"notes\" (\n * \"body\" text NULL DEFAULT 'a;b',\n * …\n * );\n *\n * -------------------------------------------\n *\n * Analyzing planned statements (2 in total):\n * …\n * Running dry-run migration (2 statements in total):\n * -- modify \"people\" table\n * -> ALTER TABLE …; ← the same statement, again\n * -- ok (19µs)\n *\n * Splitting that on `;` is how the push gate went blind: the heading has no\n * `;` and does not start with `--`, so it became the first line of the first\n * statement, and an `ALTER TABLE` parse anchored at the start of a statement\n * read nothing — every narrowing type change applied without a prompt. Reading\n * both sections was a second bug, counting every destructive statement twice.\n *\n * So the report is read as a report: a statement is a `->` line and the lines\n * indented under it, up to the next `--` heading, `->`, blank line or rule;\n * only the first section that holds a `->` is read; and `;` plays no part,\n * because a default like `'a;b'` contains one.\n *\n * Plain SQL — a migration file, or an Atlas that printed SQL — has no `->`\n * and is split the old way.\n */\nfunction readPlan(plan: string): ReadPlan {\n if (PLAN_SYNCED_RE.test(plan)) return { statements: [] };\n const lines = plan.split(/\\r?\\n/);\n if (!lines.some(line => PLAN_MARKER_RE.test(line))) return { statements: splitSqlStatements(plan) };\n\n const heading = PLAN_HEADING_RE.exec(plan);\n const statements: string[] = [];\n let current: string[] | null = null;\n const flush = () => {\n if (current) statements.push(current.join(\"\\n\").trim().replace(/;\\s*$/, \"\").trimEnd());\n current = null;\n };\n\n for (const line of lines) {\n if (PLAN_MARKER_RE.test(line)) {\n flush();\n current = [line.replace(PLAN_MARKER_RE, \"\")];\n continue;\n }\n // A section heading sits in column 0 and is neither a marker nor a\n // `--` line; the rule between sections is dashes. Either one after a\n // statement has been read means the plan is over and what follows is\n // Atlas re-printing it.\n const sectionEnds = PLAN_RULE_RE.test(line) || (/^\\S/.test(line) && !line.startsWith(\"--\"));\n if (sectionEnds) {\n flush();\n if (statements.length > 0) break;\n continue;\n }\n if (!current) continue;\n if (line.trim() === \"\" || line.trim().startsWith(\"--\")) {\n flush();\n continue;\n }\n current.push(line);\n }\n flush();\n\n return { statements, announced: heading ? Number(heading[1]) : undefined };\n}\n\n/**\n * The statements of an Atlas plan, each exactly once, without Atlas's\n * rendering and without the trailing `;`. See {@link readPlan} for the shape\n * it reads; `test/fixtures/atlas-dry-run/` holds real captures of it.\n */\nexport function extractPlanStatements(plan: string): string[] {\n return readPlan(plan).statements;\n}\n\n/**\n * Whether Atlas answered the dry-run with \"Schema is synced\" and nothing else.\n *\n * Positive evidence only: an empty or unreadable report also yields no\n * statements, and is not the same answer.\n */\nexport function isSyncedPlan(plan: string): boolean {\n return PLAN_SYNCED_RE.test(plan);\n}\n\nexport interface DestructiveStatement {\n /** The offending statement (trimmed, without the trailing `;`). */\n statement: string;\n /** Which destructive operation it was flagged for, e.g. \"DROP COLUMN\". */\n kind: string;\n /**\n * What in the statement loses data, when the kind alone does not say:\n * for `ALTER COLUMN TYPE`, each lossy column with its old and new type.\n */\n detail?: string;\n}\n\n/** A column as the database has it now, with its type as `format_type` spells it. */\nexport interface ExistingColumnType {\n schema: string;\n table: string;\n column: string;\n /** e.g. `character varying(255)`, `numeric(10,2)`, `timestamp with time zone`. */\n type: string;\n}\n\n/**\n * Every column of every ordinary table outside the system schemas, with its\n * type. What {@link detectDestructiveStatements} compares a planned type\n * change against; `format_type` because it carries the modifier (`(255)`,\n * `(10,2)`) that decides whether a change widens or narrows.\n */\nexport const COLUMN_TYPES_SQL = `\n SELECT n.nspname AS \"schema\",\n c.relname AS \"table\",\n a.attname AS \"column\",\n format_type(a.atttypid, a.atttypmod) AS \"type\"\n FROM pg_attribute a\n JOIN pg_class c ON c.oid = a.attrelid\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind IN ('r', 'p')\n AND a.attnum > 0\n AND NOT a.attisdropped\n AND n.nspname NOT IN ('pg_catalog', 'information_schema')\n AND n.nspname NOT LIKE 'pg\\\\_%'\n`;\n\n/**\n * Scan an Atlas plan (the SQL printed by `schema apply --dry-run`) and return\n * the statements that would destroy data. An empty array means the plan is\n * safe to auto-approve.\n *\n * `columnTypes` is what the database holds now. A type change to a column it\n * does not list is flagged: not knowing what a column is means not knowing\n * whether the change keeps its values.\n *\n * `constraints` are the CHECK and EXCLUDE constraints the database holds now.\n * A `DROP CONSTRAINT` of one of them is flagged; of any other constraint (a\n * foreign key, a `UNIQUE`) it is not, as before.\n */\nexport function detectDestructiveStatements(\n planSql: string,\n columnTypes: readonly ExistingColumnType[] = [],\n constraints: readonly ExistingConstraint[] = []\n): DestructiveStatement[] {\n const found: DestructiveStatement[] = [];\n const { statements, announced } = readPlan(planSql);\n // Fail closed on a plan this could not read. Atlas says how many statements\n // it planned; reading a different number means its rendering changed under\n // us, and a gate that has not read the plan cannot call it safe. Reported\n // as one more destructive finding, so it takes the same road: a prompt on\n // a terminal, a refusal elsewhere, `--allow-destructive` to go ahead.\n if (announced !== undefined && announced !== statements.length) {\n found.push({\n statement: `Planning migration statements (${announced} in total)`,\n kind: \"UNREADABLE PLAN\",\n detail: `Atlas announced ${announced} statement(s) and ${statements.length} could be read — `\n + \"read the plan yourself before allowing it\"\n });\n }\n for (const statement of statements) {\n // One finding per statement, under the label that says the most, with\n // everything else it does in the detail: Atlas folds a key change, a\n // retype and a drop into one `ALTER TABLE`, and a reader shown only\n // the first would approve the rest unread.\n const dropped = DESTRUCTIVE_PATTERNS.find(({ re }) => re.test(statement));\n const rekeyed = describePrimaryKeyChange(statement);\n const lossy = parseColumnTypeChanges(statement)\n .map(change => ({ change, from: currentTypeOf(change, columnTypes) }))\n .filter(({ change, from }) => from === undefined || !isLosslessTypeChange(from, change.to))\n .map(({ change, from }) => `\"${change.column}\" ${from ?? \"(current type unknown)\"} → ${change.to}`)\n .join(\"; \");\n const unenforced = describeConstraintDrops(statement, constraints);\n const kind = dropped?.label\n ?? (rekeyed ? \"PRIMARY KEY CHANGE\" : lossy ? \"ALTER COLUMN TYPE\" : unenforced ? \"DROP CONSTRAINT\" : undefined);\n if (!kind) continue;\n const detail = [rekeyed, lossy, unenforced].filter(Boolean).join(\"; \");\n found.push(detail ? { statement, kind, detail } : { statement, kind });\n }\n return found;\n}\n\n/** A column an `ALTER TABLE` retypes, with the type the plan gives it. */\ninterface ColumnTypeChange {\n /** Absent when the plan did not qualify the table. */\n schema?: string;\n table: string;\n column: string;\n /** As the plan writes it, without any `USING` or `COLLATE`. */\n to: string;\n}\n\nconst IDENT = String.raw`(?:\"(?:[^\"]|\"\")+\"|[A-Za-z_][A-Za-z0-9_$]*)`;\nconst ALTER_TABLE_RE = new RegExp(\n String.raw`^ALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(?:ONLY\\s+)?(${IDENT})(?:\\.(${IDENT}))?\\s+([\\s\\S]*)$`, \"i\"\n);\nconst TYPE_CLAUSE_RE = new RegExp(\n String.raw`^ALTER\\s+(?:COLUMN\\s+)?(${IDENT})\\s+(?:SET\\s+DATA\\s+)?TYPE\\s+([\\s\\S]+)$`, \"i\"\n);\n\nconst unquote = (ident: string): string =>\n ident.startsWith(\"\\\"\") ? ident.slice(1, -1).replace(/\"\"/g, \"\\\"\") : ident;\n\n/**\n * Split an `ALTER TABLE`'s clause list on its top-level commas — the ones\n * outside parentheses and quotes, so `TYPE numeric(10, 2)` stays one clause.\n */\nfunction splitClauses(body: string): string[] {\n const clauses: string[] = [];\n let depth = 0;\n let quote: string | undefined;\n let start = 0;\n for (let i = 0; i < body.length; i++) {\n const ch = body[i];\n if (quote) {\n if (ch === quote) quote = undefined;\n } else if (ch === \"\\\"\" || ch === \"'\") {\n quote = ch;\n } else if (ch === \"(\") {\n depth++;\n } else if (ch === \")\") {\n depth--;\n } else if (ch === \",\" && depth === 0) {\n clauses.push(body.slice(start, i).trim());\n start = i + 1;\n }\n }\n clauses.push(body.slice(start).trim());\n return clauses.filter(clause => clause.length > 0);\n}\n\n/**\n * The statement from its `ALTER TABLE` on, wherever on a line that starts.\n *\n * {@link readPlan} hands over clean statements. This is the second line of\n * defence for text it could not clean — a rendering it does not know yet —\n * where a line of prose above the statement must not hide the statement, which\n * is exactly how an anchored match once read Atlas's whole plan as safe.\n */\nexport function fromAlterTable(statement: string): string {\n const start = /(?:^|\\n)[ \\t]*(?:->[ \\t]*)?(ALTER\\s+TABLE\\b)/i.exec(statement);\n return start ? statement.slice(start.index + start[0].length - start[1].length).trim() : statement.trim();\n}\n\n/**\n * Every `ALTER COLUMN … [SET DATA] TYPE` in one statement. Atlas prints its\n * dry-run statements under a `-> ` marker and puts several clauses in one\n * `ALTER TABLE`, so both are handled.\n */\nfunction parseColumnTypeChanges(statement: string): ColumnTypeChange[] {\n const table = ALTER_TABLE_RE.exec(fromAlterTable(statement));\n if (!table) return [];\n const [schema, name] = table[2]\n ? [unquote(table[1]), unquote(table[2])]\n : [undefined, unquote(table[1])];\n const changes: ColumnTypeChange[] = [];\n for (const clause of splitClauses(table[3])) {\n const match = TYPE_CLAUSE_RE.exec(clause);\n if (!match) continue;\n const to = match[2].split(/\\s+(?:USING|COLLATE)\\s/i)[0].trim();\n changes.push({ schema, table: name, column: unquote(match[1]), to });\n }\n return changes;\n}\n\n/** `ADD [CONSTRAINT \"x\"] PRIMARY KEY (\"a\", \"b\")`, one clause of an `ALTER TABLE`. */\nconst ADD_PRIMARY_KEY_RE = new RegExp(\n String.raw`^ADD\\s+(?:CONSTRAINT\\s+${IDENT}\\s+)?PRIMARY\\s+KEY\\b\\s*(?:\\(([^)]*)\\))?`, \"i\"\n);\n/** `DROP CONSTRAINT [IF EXISTS] \"x\"`, one clause of an `ALTER TABLE`. */\nconst DROP_CONSTRAINT_RE = new RegExp(\n String.raw`^DROP\\s+CONSTRAINT\\s+(?:IF\\s+EXISTS\\s+)?(${IDENT})`, \"i\"\n);\n\n/** What a row loses when the columns that address it change. */\nconst ROW_ADDRESS_LOST = \"every row's id changes, so anything that refers to a row by its id — a URL, \" +\n \"a foreign key, an id stored elsewhere — stops finding it\";\n\n/**\n * What an `ALTER TABLE` does to the table's primary key, in a sentence — or\n * `undefined` when it leaves the key alone.\n *\n * The key is a row's address: Rebase reads, links and routes every row by it.\n * Atlas plans a key change as `DROP CONSTRAINT \"<table>_pkey\", ADD PRIMARY KEY\n * (…)` — one routine-looking statement that names no DROP of data — so moving\n * a collection from `(id)` to `(id, locale)` re-keyed a populated table with\n * no prompt. A `CREATE TABLE`'s own `PRIMARY KEY` is not a change and is never\n * read here: only an `ALTER TABLE`'s clauses are. A dropped constraint counts\n * as the key when it carries Postgres's default name for one, `<table>_pkey`;\n * a key under another name is caught by the `ADD PRIMARY KEY` that replaces it.\n */\nfunction describePrimaryKeyChange(statement: string): string | undefined {\n const alter = ALTER_TABLE_RE.exec(fromAlterTable(statement));\n if (!alter) return undefined;\n const table = alter[2] ? `${alter[1]}.${alter[2]}` : alter[1];\n\n let added: string | undefined;\n let dropped: string | undefined;\n for (const clause of splitClauses(alter[3])) {\n const add = ADD_PRIMARY_KEY_RE.exec(clause);\n if (add) {\n added = add[1] === undefined ? \"a new key\" : `(${add[1].split(\",\").map(c => c.trim()).join(\", \")})`;\n continue;\n }\n const drop = DROP_CONSTRAINT_RE.exec(clause);\n if (drop && unquote(drop[1]).endsWith(\"_pkey\")) dropped = drop[1];\n }\n\n if (added && dropped) return `${table} is re-keyed on ${added}: ${ROW_ADDRESS_LOST}`;\n if (added) {\n return `${table} gets the primary key ${added}: rows are addressed by it from now on, ` +\n \"and the push fails if two rows share a value\";\n }\n if (dropped) return `${table} loses its primary key ${dropped}: ${ROW_ADDRESS_LOST}`;\n return undefined;\n}\n\n/**\n * The CHECK and EXCLUDE constraints an `ALTER TABLE` drops, in a sentence — or\n * `undefined` when it drops none.\n *\n * Matched by name on the statement's table, in its schema when the plan names\n * one (Atlas always does). A constraint the catalogue does not list as a CHECK\n * or an EXCLUDE — a foreign key, a `UNIQUE`, the primary key — is not this.\n */\nfunction describeConstraintDrops(statement: string, constraints: readonly ExistingConstraint[]): string | undefined {\n if (constraints.length === 0) return undefined;\n const alter = ALTER_TABLE_RE.exec(fromAlterTable(statement));\n if (!alter) return undefined;\n const [schema, table] = alter[2] ? [unquote(alter[1]), unquote(alter[2])] : [undefined, unquote(alter[1])];\n\n const lost: string[] = [];\n for (const clause of splitClauses(alter[3])) {\n const drop = DROP_CONSTRAINT_RE.exec(clause);\n if (!drop) continue;\n const name = unquote(drop[1]);\n const constraint = constraints.find(c => c.table === table && c.name === name\n && (schema === undefined || c.schema === schema));\n if (constraint) lost.push(`${constraint.kind === \"check\" ? \"CHECK\" : \"EXCLUDE\"} \"${name}\"`);\n }\n if (lost.length === 0) return undefined;\n const subject = schema ? `${schema}.${table}` : table;\n return `${subject} loses ${lost.join(\", \")}: no row is lost, and the database accepts every row ` +\n `${lost.length === 1 ? \"it refuses\" : \"they refuse\"} today`;\n}\n\n/**\n * The column's current type. A table named without its schema matches in any\n * schema, but only when every match agrees — two same-named tables with\n * different types leave the answer unknown.\n */\nfunction currentTypeOf(change: ColumnTypeChange, columnTypes: readonly ExistingColumnType[]): string | undefined {\n const types = new Set(columnTypes\n .filter(col => col.table === change.table && col.column === change.column\n && (change.schema === undefined || col.schema === change.schema))\n .map(col => col.type));\n return types.size === 1 ? [...types][0] : undefined;\n}\n\n/** Spellings of one type, mapped to one name. Anything absent compares verbatim. */\nconst TYPE_ALIASES: Record<string, string> = {\n \"int\": \"integer\",\n \"int4\": \"integer\",\n \"int2\": \"smallint\",\n \"int8\": \"bigint\",\n \"character varying\": \"varchar\",\n \"decimal\": \"numeric\",\n \"timestamp without time zone\": \"timestamp\",\n \"timestamp with time zone\": \"timestamptz\",\n \"time without time zone\": \"time\",\n \"time with time zone\": \"timetz\",\n \"bool\": \"boolean\",\n \"float4\": \"real\",\n \"float8\": \"double precision\",\n \"character\": \"bpchar\",\n \"char\": \"bpchar\"\n};\n\n/** Decimal digits each integer type holds, which is what a `numeric` must hold too. */\nconst INTEGER_DIGITS: Record<string, number> = { smallint: 5, integer: 10, bigint: 19 };\n\n/** Types whose modifier is a fractional-seconds precision, 6 when absent. */\nconst SECONDS_PRECISION = new Set([\"timestamp\", \"timestamptz\", \"time\", \"timetz\"]);\n\ninterface ParsedType {\n base: string;\n /** The modifier, e.g. `[10, 2]` for `numeric(10,2)`. */\n args?: number[];\n /** Array dimensions. */\n dims: number;\n}\n\nfunction parseType(raw: string): ParsedType {\n let text = raw.trim().replace(/\\s+/g, \" \");\n let dims = 0;\n for (let suffix = /\\s*\\[\\d*\\]$/.exec(text); suffix; suffix = /\\s*\\[\\d*\\]$/.exec(text)) {\n dims++;\n text = text.slice(0, suffix.index);\n }\n // A quoted name is a user-defined type, compared exactly as written.\n if (text.includes(\"\\\"\")) return { base: text, dims };\n text = text.toLowerCase().replace(/^pg_catalog\\./, \"\");\n let args: number[] | undefined;\n const modifier = /\\(([^)]*)\\)/.exec(text);\n if (modifier) {\n args = modifier[1].split(\",\").map(part => Number(part.trim()));\n text = `${text.slice(0, modifier.index)} ${text.slice(modifier.index + modifier[0].length)}`\n .replace(/\\s+/g, \" \")\n .trim();\n }\n return { base: TYPE_ALIASES[text] ?? text, args, dims };\n}\n\n/** Does moving from one modifier to another, on the same type, keep every value? */\nfunction modifierWidens(base: string, from: number[] | undefined, to: number[] | undefined): boolean {\n if (JSON.stringify(from) === JSON.stringify(to)) return true;\n if (base === \"varchar\") {\n return to === undefined || (from !== undefined && to[0] >= from[0]);\n }\n if (base === \"numeric\") {\n if (to === undefined) return true;\n if (from === undefined) return false;\n const [p1, s1 = 0] = from;\n const [p2, s2 = 0] = to;\n return s2 >= s1 && p2 - s2 >= p1 - s1;\n }\n if (SECONDS_PRECISION.has(base)) {\n return (to?.[0] ?? 6) >= (from?.[0] ?? 6);\n }\n return false;\n}\n\n/**\n * Is `ALTER COLUMN … TYPE to` a widening, one that keeps every value exactly\n * as it was?\n *\n * Deliberately short. Everything not listed needs a confirmation, including\n * changes that are usually harmless: `timestamp` → `timestamptz` reads each\n * value in the session's time zone, `json` → `jsonb` drops duplicate keys,\n * `real` → `double precision` prints 0.1 as 0.100000001490116, and `integer` →\n * `text` keeps the digits but changes what the column is.\n *\n * - the same type, with a modifier at least as wide (`varchar(100)` →\n * `varchar(255)`, `numeric(10,2)` → `numeric(12,2)` or unbounded,\n * `timestamptz(3)` → `timestamptz`);\n * - `smallint` → `integer` → `bigint`, and any of them to a `numeric` with\n * room for all their digits;\n * - `varchar(n)` → `text`, and `text` → an unbounded `varchar`.\n *\n * Arrays follow their element type, and never gain or lose a dimension.\n */\nexport function isLosslessTypeChange(from: string, to: string): boolean {\n const a = parseType(from);\n const b = parseType(to);\n if (a.dims !== b.dims) return false;\n if (a.args?.some(Number.isNaN) || b.args?.some(Number.isNaN)) return false;\n if (a.base === b.base) return modifierWidens(a.base, a.args, b.args);\n\n const fromDigits = INTEGER_DIGITS[a.base];\n if (fromDigits !== undefined) {\n const toDigits = INTEGER_DIGITS[b.base];\n if (toDigits !== undefined) return toDigits >= fromDigits;\n if (b.base === \"numeric\") {\n return b.args === undefined || b.args[0] - (b.args[1] ?? 0) >= fromDigits;\n }\n return false;\n }\n if (a.base === \"varchar\" && b.base === \"text\") return true;\n if (a.base === \"text\" && b.base === \"varchar\") return b.args === undefined;\n return false;\n}\n\nexport type PushDecision = \"apply\" | \"confirm\" | \"refuse\";\n\n/**\n * The flag that opted a push into destructive changes, as it was given —\n * `--allow-destructive`, or its alias `--yes` / `-y` — or `undefined` for\n * none. The line that says why a destructive push went ahead names the one\n * the user typed: \"because --allow-destructive was passed\" to someone who\n * typed `--yes` reads like a flag they never set.\n */\nexport function destructiveOptInFlag(args: { \"--allow-destructive\"?: boolean; \"--yes\"?: boolean }): \"--allow-destructive\" | \"--yes\" | undefined {\n if (args[\"--allow-destructive\"] === true) return \"--allow-destructive\";\n if (args[\"--yes\"] === true) return \"--yes\";\n return undefined;\n}\n\n/**\n * Decide how `db push` should proceed given the plan's destructiveness and\n * the invocation context.\n *\n * - No destructive statements → `apply` (safe to auto-approve).\n * - Destructive + `--allow-destructive` → `apply` (operator opted in).\n * - Destructive + interactive TTY → `confirm` (prompt before applying).\n * - Destructive + non-interactive → `refuse` (never silently drop data in\n * CI / scripts / agents).\n */\nexport function decidePushSafety(opts: {\n destructiveCount: number;\n allowDestructive: boolean;\n interactive: boolean;\n}): PushDecision {\n if (opts.destructiveCount === 0) return \"apply\";\n if (opts.allowDestructive) return \"apply\";\n return opts.interactive ? \"confirm\" : \"refuse\";\n}\n","/**\n * The constraints a collection cannot declare, and how `db push` keeps Atlas\n * from dropping them.\n *\n * `db push` is declarative: Atlas makes each managed table match `schema.sql`,\n * and anything on the table that `schema.sql` does not mention is planned as a\n * drop. The collection model declares a table's primary key, its columns'\n * `NOT NULL`, `UNIQUE` and foreign keys, and nothing else. So a hand-written\n * `CHECK (total >= 0)` — or one an adopted database arrived with, which\n * `schema introspect` reads as `validation.min: 0` — was dropped by the first\n * push, in the same `ALTER TABLE` as whatever else the push changed, with no\n * prompt. From then on the database accepted every row the constraint used to\n * refuse, while the form still enforced the rule and so hid that it had gone.\n *\n * EXCLUDE constraints are the same case: Atlas inspects them and plans their\n * drop the same way. Triggers and functions are not: Atlas's free tier does\n * not inspect either, so it never plans a drop for one. Measured on the pinned\n * atlas 1.2.3 against PG 18; `introspect-check-constraints-e2e` pins all four.\n *\n * Ownership, as with indexes (`getForeignIndexExcludes` in `cli-helpers.ts`)\n * and policies: a constraint the desired state declares is\n * Atlas's to manage, and every other CHECK or EXCLUDE is somebody else's and is\n * kept out of the plan with `--exclude`. Today the desired state declares\n * neither kind, so every one is kept. A rule nobody wants any more is dropped\n * by hand (`ALTER TABLE … DROP CONSTRAINT …`), which is the only statement that\n * can say so.\n *\n * Side-effect free, so it is unit-tested without Atlas or a database.\n */\n\nimport { atlasExcludePattern } from \"./atlas-exclude\";\n\n/** A constraint kind the collection model has no way to declare. */\nexport type UndeclarableConstraintKind = \"check\" | \"exclude\";\n\n/** A CHECK or EXCLUDE constraint, as the database holds it now. */\nexport interface ExistingConstraint {\n schema: string;\n table: string;\n name: string;\n kind: UndeclarableConstraintKind;\n}\n\n/**\n * Every CHECK and EXCLUDE constraint on an ordinary or partitioned table\n * outside the system schemas.\n *\n * `contype` `c` and `x` only. A primary key (`p`), `UNIQUE` (`u`), foreign key\n * (`f`) and, from PG 18, a catalogued `NOT NULL` (`n`) are what a collection\n * declares, so Atlas keeps managing them.\n */\nexport const UNDECLARABLE_CONSTRAINTS_SQL = `\n SELECT n.nspname AS \"schema\",\n c.relname AS \"table\",\n con.conname AS \"name\",\n CASE con.contype WHEN 'c' THEN 'check' ELSE 'exclude' END AS \"kind\"\n FROM pg_constraint con\n JOIN pg_class c ON c.oid = con.conrelid\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE con.contype IN ('c', 'x')\n AND c.relkind IN ('r', 'p')\n AND n.nspname NOT IN ('pg_catalog', 'information_schema')\n AND n.nspname NOT LIKE 'pg\\\\_%'\n`;\n\nconst DECLARED_CONSTRAINT_RE = /\\bCONSTRAINT\\s+\"((?:[^\"]|\"\")+)\"\\s+(?:CHECK|EXCLUDE)\\b/gi;\n\n/**\n * The CHECK and EXCLUDE constraint names a desired state (`schema.sql`)\n * declares — Atlas's to manage, and so never excluded.\n */\nexport function declaredConstraintNames(desiredSql: string): Set<string> {\n const names = new Set<string>();\n for (const match of desiredSql.matchAll(DECLARED_CONSTRAINT_RE)) {\n names.add(match[1].replace(/\"\"/g, \"\\\"\"));\n }\n return names;\n}\n\n/**\n * The `--exclude` patterns that keep Atlas from dropping the CHECK and EXCLUDE\n * constraints the desired state does not declare. Three-part\n * `schema.table.constraint`, which is what Atlas matches a constraint by: a\n * two-part pattern reads as a table, matches nothing, and reports no error.\n */\nexport function undeclaredConstraintExcludes(\n existing: readonly ExistingConstraint[],\n desiredSql: string\n): string[] {\n const declared = declaredConstraintNames(desiredSql);\n return existing\n .filter(constraint => !declared.has(constraint.name))\n .map(constraint => atlasExcludePattern(constraint.schema, constraint.table, constraint.name));\n}\n","import path from \"path\";\nimport { fileURLToPath } from \"url\";\n\n// Isolated in its own module so the jest CommonJS transform can swap it for a\n// __dirname shim via moduleNameMapper — `import.meta` is a syntax error once\n// ts-jest transpiles to CJS.\nexport const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n","/**\n * Where `db push` and `db generate` put the SQL they render from the collections.\n *\n * Five files — `schema.sql` (Atlas's desired state), `policies.sql`,\n * `search.sql`, `vector.sql` and `triggers.sql` — and every one of them is\n * scratch. Both commands regenerate all five before reading any of them, and\n * nothing else reads them at all. They used to be written into `drizzle/`, beside\n * the migrations, and committed: a copy nobody read, which every collection\n * change rewrote in the diff and every two branches touching collections\n * conflicted on.\n *\n * Relative to the directory the command runs in (the backend), like the\n * migrations are.\n */\nimport fs from \"fs\";\nimport path from \"path\";\n\n/** The directory, relative to the working directory. POSIX, because it is also a URL path. */\nexport const GENERATED_SQL_DIR = \".rebase/sql\";\n\n/**\n * `schema.sql` as Atlas takes it. Relative on purpose, like the\n * `file://drizzle/migrations` beside it: Atlas joins the URL's host and path, so\n * `.rebase` is read as the first path segment, measured against the pinned 1.2.3.\n */\nexport const DESIRED_STATE_URL = `file://${GENERATED_SQL_DIR}/schema.sql`;\n\n/** The files the DDL generator writes, all of them derived from the collections. */\nexport const GENERATED_SQL_FILES = [\"schema.sql\", \"policies.sql\", \"search.sql\", \"vector.sql\", \"triggers.sql\"] as const;\n\n/** The absolute directory, for the working directory the command runs in. */\nexport function generatedSqlDir(cwd: string = process.cwd()): string {\n return path.resolve(cwd, GENERATED_SQL_DIR);\n}\n\n/**\n * The first line of every file the generator writes, in every release that\n * wrote them into `drizzle/`. Matching it is what makes removing the old copy\n * safe: a file there under one of these names that does not open this way was\n * written by a person, and is left alone.\n */\nconst GENERATED_HEADER = /^-- This file (is auto-generated by the Rebase DDL generator|contains RLS policies generated by Rebase)\\./;\n\n/**\n * Get the directory ready to be written, and retire the copies an older release\n * committed.\n *\n * The directory ignores itself, rather than relying on the project's\n * `.gitignore`: a scaffold ignores `.rebase/`, but a project created before it\n * did would otherwise start committing these files from their new home.\n *\n * The old copies in `drizzle/` are deleted, not left behind. Nothing writes them\n * any more, so from the first push after an upgrade they only fall further\n * behind — and a stale `policies.sql` in the repository is exactly what someone\n * reviewing the project's RLS would read.\n *\n * With `retire: false` (`db push --dry-run`, which writes nothing of the\n * project's) the old copies are only found, not removed.\n *\n * @returns the old files it removed — or, with `retire: false`, would remove —\n * relative to the working directory.\n */\nexport function prepareGeneratedSqlDir(\n cwd: string = process.cwd(),\n options: { retire?: boolean } = {}\n): string[] {\n const dir = generatedSqlDir(cwd);\n fs.mkdirSync(dir, { recursive: true });\n const ignore = path.join(dir, \".gitignore\");\n if (!fs.existsSync(ignore)) {\n fs.writeFileSync(ignore, \"# Generated by `rebase db push` / `rebase db generate` from the collections.\\n*\\n\");\n }\n\n const removed: string[] = [];\n for (const name of GENERATED_SQL_FILES) {\n const legacy = path.join(cwd, \"drizzle\", name);\n if (!fs.existsSync(legacy)) continue;\n const firstLine = fs.readFileSync(legacy, \"utf-8\").split(\"\\n\", 1)[0];\n if (!GENERATED_HEADER.test(firstLine)) continue;\n if (options.retire !== false) fs.rmSync(legacy);\n removed.push(path.posix.join(\"drizzle\", name));\n }\n return removed;\n}\n","import path from \"path\";\nimport fs from \"fs\";\nimport { execSync } from \"child_process\";\nimport { createRequire } from \"module\";\nimport { pathToFileURL } from \"url\";\nimport chalk from \"chalk\";\nimport { isRebaseIndexName } from \"./schema/collection-index\";\nimport type { GeneratedColumnDependency } from \"./schema/generated-column-conflicts\";\nimport { COLUMN_TYPES_SQL, type ExistingColumnType } from \"./schema/destructive-sql\";\nimport { UNDECLARABLE_CONSTRAINTS_SQL, undeclaredConstraintExcludes, type ExistingConstraint } from \"./schema/undeclarable-constraints\";\nimport { atlasExcludePattern, excludePatternParts } from \"./schema/atlas-exclude\";\nimport { out, outWarn } from \"./cli-output\";\nimport type { CollectionConfig } from \"@rebasepro/types\";\nimport { moduleDir as __helpersDirname } from \"./module-dir\";\nimport { generatedSqlDir } from \"./generated-sql\";\n\n\n\n/**\n * Why is a dependency's binary missing — never installed, or installed with its\n * build script blocked?\n *\n * These need opposite advice, and getting it wrong is not a cosmetic miss. pnpm\n * 10+ refuses to run a dependency's lifecycle scripts unless it is allowlisted\n * (`pnpm.onlyBuiltDependencies`, or `allowBuilds` in `pnpm-workspace.yaml`).\n * `@ariga/atlas` downloads its platform binary in `preinstall`, so a blocked\n * script leaves a state that looks like a successful install: the package is on\n * disk with its `install.js` and `package.json`, `node_modules/.bin` is empty,\n * the install exits 0, and the only signal is `ERR_PNPM_IGNORED_BUILDS` several\n * screens up.\n *\n * Telling somebody in that state to install the package again sends them round\n * the same loop forever — the add succeeds, the script is blocked again,\n * nothing changes. Verified by doing it: `pnpm add @ariga/atlas` into a bare\n * project yields exactly this, three \"Failed to create bin … ENOENT\" warnings\n * and no binary.\n *\n * Resolution is attempted from the user's project first and this package\n * second, matching the order {@link resolveLocalBin} searches — the driver may\n * be installed a level up from where the command runs.\n */\nexport type ProjectPackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\" | \"unknown\";\n\n/**\n * Which package manager installed this project, read off its lockfile.\n *\n * Every remedy for a blocked build script is package-manager specific, and\n * naming the wrong one is the same failure as naming none: `pnpm approve-builds`\n * is not a thing an npm user can run, and `pnpm.onlyBuiltDependencies` is a key\n * npm does not read. This existed as a pnpm-only message for long enough that\n * npm 12 — which blocks dependency lifecycle scripts by default, the way pnpm 10\n * does — could ship and leave every npm reader three unfollowable commands deep.\n *\n * The lockfile, not `npm_config_user_agent`: the question is how the project's\n * `node_modules` was built, not which binary happens to be invoking us. Walks up\n * because commands run from `backend/` in a scaffolded project, and the lockfile\n * is at the workspace root.\n */\nexport function detectProjectPackageManager(startDir: string = process.cwd()): ProjectPackageManager {\n const lockfiles: Array<[string, ProjectPackageManager]> = [\n [\"pnpm-lock.yaml\", \"pnpm\"],\n [\"bun.lock\", \"bun\"],\n [\"bun.lockb\", \"bun\"],\n [\"yarn.lock\", \"yarn\"],\n [\"package-lock.json\", \"npm\"]\n ];\n\n let dir = path.resolve(startDir);\n while (true) {\n for (const [file, manager] of lockfiles) {\n if (fs.existsSync(path.join(dir, file))) return manager;\n }\n const parent = path.dirname(dir);\n if (parent === dir) return \"unknown\";\n dir = parent;\n }\n}\n\n/**\n * How to let `@ariga/atlas` run its `preinstall`, in the reader's own package\n * manager.\n *\n * Returns the lines to print after \"the binary is missing but the package is\n * installed\". Kept next to {@link detectProjectPackageManager} so the two move\n * together, and returned rather than printed so it can be asserted on.\n */\nexport function describeBuildScriptRemedy(\n packageName: string,\n manager: ProjectPackageManager = detectProjectPackageManager()\n): string[] {\n switch (manager) {\n case \"npm\":\n // npm 12 blocks dependency install scripts unless the root\n // package.json allowlists them. `rebase init` scaffolds the entry;\n // a project that predates it, or one that vendored its own\n // package.json, needs to add it.\n return [\n ` npm install-scripts approve ${packageName}`,\n \"\",\n \" or, to record it in the project (what `rebase init` scaffolds):\",\n \"\",\n \" // package.json\",\n ` \"allowScripts\": { \"${packageName}\": true }`,\n \"\",\n \" Then re-run `npm install`.\"\n ];\n case \"yarn\":\n return [\n \" // .yarnrc.yml\",\n \" enableScripts: true\",\n \"\",\n \" Then re-run `yarn install`.\"\n ];\n case \"bun\":\n return [\n \" // package.json\",\n ` \"trustedDependencies\": [\"${packageName}\"]`,\n \"\",\n \" Then re-run `bun install`.\"\n ];\n case \"pnpm\":\n default:\n return [\n \" pnpm approve-builds\",\n \"\",\n \" or, to record it in the project (what `rebase init` scaffolds):\",\n \"\",\n \" // package.json\",\n ` \"pnpm\": { \"onlyBuiltDependencies\": [\"${packageName}\"] }`,\n \"\",\n \" Then re-run `pnpm install`.\"\n ];\n }\n}\n\n/**\n * \"Install again, from scratch\", in the reader's own package manager.\n *\n * For the `bin-link-missing` state: the tree is half-written, and re-resolving\n * it is what recreates the `.bin` shim. `--force` on pnpm is what makes it\n * relink rather than decide the tree is already up to date.\n */\nexport function describeReinstallCommand(\n manager: ProjectPackageManager = detectProjectPackageManager()\n): string {\n switch (manager) {\n case \"npm\": return \"rm -rf node_modules && npm install\";\n case \"yarn\": return \"yarn install --force\";\n case \"bun\": return \"rm -rf node_modules && bun install\";\n case \"pnpm\":\n default: return \"pnpm install --force\";\n }\n}\n\n/**\n * The workspace member a command runs in, relative to the workspace's root —\n * `backend` in a scaffolded project — or null at that root and outside any\n * workspace.\n *\n * The member is the nearest directory at or above `startDir` holding a\n * `package.json`; the root is the nearest one above it holding a\n * `pnpm-workspace.yaml`, or a `package.json` that declares `workspaces`.\n */\nexport function workspaceMemberDir(startDir: string = process.cwd()): string | null {\n let member = path.resolve(startDir);\n while (!fs.existsSync(path.join(member, \"package.json\"))) {\n const parent = path.dirname(member);\n if (parent === member) return null;\n member = parent;\n }\n for (let dir = path.dirname(member); ; dir = path.dirname(dir)) {\n if (isWorkspaceRoot(dir)) return path.relative(dir, member).split(path.sep).join(\"/\");\n if (path.dirname(dir) === dir) return null;\n }\n}\n\nfunction isWorkspaceRoot(dir: string): boolean {\n if (fs.existsSync(path.join(dir, \"pnpm-workspace.yaml\"))) return true;\n try {\n const manifest: unknown = JSON.parse(fs.readFileSync(path.join(dir, \"package.json\"), \"utf8\"));\n return typeof manifest === \"object\" && manifest !== null && \"workspaces\" in manifest;\n } catch {\n return false;\n }\n}\n\n/**\n * `add <pkg>` as a dev dependency, in the reader's own package manager, typed\n * from the project root: into `workspaceDir` when the command runs in a\n * workspace member, where a bare `pnpm add -D` at the root refuses with\n * `ERR_PNPM_ADDING_TO_ROOT`.\n */\nexport function describeDevAddCommand(\n packageName: string,\n manager: ProjectPackageManager = detectProjectPackageManager(),\n workspaceDir: string | null = workspaceMemberDir()\n): string {\n const dir = workspaceDir === null ? null : shellWord(workspaceDir);\n switch (manager) {\n case \"npm\": return dir ? `npm install -D ${packageName} -w ${dir}` : `npm install -D ${packageName}`;\n case \"yarn\": return dir ? `yarn --cwd ${dir} add -D ${packageName}` : `yarn add -D ${packageName}`;\n case \"bun\": return dir ? `bun add -d ${packageName} --cwd ${dir}` : `bun add -d ${packageName}`;\n case \"pnpm\":\n default: return dir ? `pnpm --dir ${dir} add -D ${packageName}` : `pnpm add -D ${packageName}`;\n }\n}\n\n/** A path as one shell word: as it is when it needs no quoting, single-quoted otherwise. */\nconst shellWord = (value: string): string =>\n /^[\\w./-]+$/.test(value) ? value : `'${value.replace(/'/g, \"'\\\\''\")}'`;\n\n/**\n * Why a dependency's binary is not on `PATH`.\n *\n * `bin-link-missing` is the third state, and the reason this is not a boolean.\n * The `preinstall` did run and the binary IS on disk — only the\n * `node_modules/.bin` shim that points at it is absent. pnpm produces exactly\n * that when it writes the link before the script that creates the target\n * (\"Failed to create bin … ENOENT\"), and so does a tree copied without its\n * symlinks. Telling that reader their build scripts are blocked sends them to\n * `approve-builds`, which does nothing, because nothing is blocked.\n */\nexport type MissingBinDiagnosis = \"not-installed\" | \"build-script-blocked\" | \"bin-link-missing\";\n\n/**\n * Where a package's own manifest lives, or null if it is not installed here.\n *\n * `package.json` rather than the package root: a package with an `exports` map\n * that omits `.` is unresolvable by name even when it is perfectly installed,\n * which would misreport it as absent.\n */\nfunction resolvePackageManifest(packageName: string): string | null {\n const bases = [\n pathToFileURL(path.join(process.cwd(), \"package.json\")),\n pathToFileURL(path.join(__helpersDirname, \"package.json\"))\n ];\n for (const base of bases) {\n try {\n return createRequire(base).resolve(`${packageName}/package.json`);\n } catch {\n // Try the next base.\n }\n }\n return null;\n}\n\n/**\n * The binaries a package's own manifest declares, as absolute paths.\n *\n * `bin` is a string for a single binary named after the package, or a map.\n * Both forms appear in the wild and `@ariga/atlas` uses the map.\n */\nexport function declaredBinaries(manifestPath: string): string[] {\n let manifest: { name?: string; bin?: string | Record<string, string> };\n try {\n manifest = JSON.parse(fs.readFileSync(manifestPath, \"utf8\")) as typeof manifest;\n } catch {\n return [];\n }\n const dir = path.dirname(manifestPath);\n if (typeof manifest.bin === \"string\") return [path.resolve(dir, manifest.bin)];\n if (manifest.bin && typeof manifest.bin === \"object\") {\n return Object.values(manifest.bin)\n .filter((target): target is string => typeof target === \"string\")\n .map(target => path.resolve(dir, target));\n }\n return [];\n}\n\nexport function diagnoseMissingBin(packageName: string): MissingBinDiagnosis {\n const manifestPath = resolvePackageManifest(packageName);\n if (!manifestPath) return \"not-installed\";\n\n // Installed — but \"installed\" was the whole verdict, and it is not enough.\n // `@ariga/atlas` downloads its platform binary in `preinstall`, so a blocked\n // script leaves the package on disk with its `install.js` and manifest and\n // no binary: the state this branch was written for. It is NOT the state of\n // a tree whose binary exists and whose `.bin` shim does not, and those two\n // need opposite advice. Ask the manifest which file the binary is, and look.\n const binaries = declaredBinaries(manifestPath);\n if (binaries.length === 0) return \"build-script-blocked\";\n return binaries.some(file => fs.existsSync(file)) ? \"bin-link-missing\" : \"build-script-blocked\";\n}\n\nexport function resolveLocalBin(binName: string): string | null {\n // Try to find node_modules/.bin upwards from __helpersDirname first (package-relative)\n let dir = __helpersDirname;\n while (true) {\n const candidate = path.join(dir, \"node_modules\", \".bin\", binName);\n if (fs.existsSync(candidate)) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n let cwd = process.cwd();\n // Try to find node_modules/.bin upwards from process.cwd()\n while (true) {\n const candidate = path.join(cwd, \"node_modules\", \".bin\", binName);\n if (fs.existsSync(candidate)) return candidate;\n const parent = path.dirname(cwd);\n if (parent === cwd) break;\n cwd = parent;\n }\n // Fall back to globally installed binary via which/where\n try {\n const cmd = process.platform === \"win32\" ? `where ${binName}` : `which ${binName}`;\n const globalPath = execSync(cmd, { encoding: \"utf-8\" }).trim().split(\"\\n\")[0].trim();\n if (globalPath && fs.existsSync(globalPath)) return globalPath;\n } catch {\n // not found globally\n }\n return null;\n}\n\nexport async function getTableIncludesFromCollections(allCollections: CollectionConfig[]): Promise<string[]> {\n const { getTableName, relationalCollections, resolveJunctionSpecs } = await import(\"@rebasepro/common\");\n const { isPostgresCollectionConfig } = await import(\"@rebasepro/types\");\n\n // The include list is the inverse of {@link getTableExcludes}: a name on it\n // is a name Atlas is allowed to drop. A Firestore collection called\n // `exercises` must not put `public.exercises` here — a real, unrelated table\n // of that name would lose its protection and be dropped by the next\n // auto-approved `db push`.\n const collections = relationalCollections(allCollections);\n const includes: string[] = [];\n for (const col of collections) {\n const tableName = getTableName(col);\n const schema = isPostgresCollectionConfig(col) && col.schema ? col.schema : \"public\";\n if (tableName) {\n includes.push(`${schema}.${tableName}`);\n }\n }\n // Junctions where they are created — `public`, whatever schema either\n // endpoint is in. Qualifying one with an endpoint's schema left the real\n // junction excluded, invisible to Atlas, and planned for creation again.\n for (const junction of resolveJunctionSpecs(collections).values()) {\n includes.push(`${junction.schema}.${junction.table}`);\n }\n\n return Array.from(new Set(includes));\n}\n\nexport async function getTableIncludes(collectionsPath: string): Promise<string[]> {\n return getTableIncludesFromCollections(await loadCollectionsForCli(collectionsPath));\n}\n\n/**\n * Load a project's collections the way the Atlas-facing commands need them.\n *\n * Deliberately forgiving — a file that fails to import is skipped rather than\n * fatal — because the callers use this to *narrow* what Atlas may touch, and a\n * hard failure here would block a push over an unrelated broken file. Callers\n * that cannot tolerate a partial answer (the table excludes, which fail closed)\n * check the result themselves.\n */\n/** Paths this process has already complained about. See {@link loadCollectionsForCli}. */\nconst warnedMissingCollectionsPaths = new Set<string>();\n\nexport async function loadCollectionsForCli(collectionsPath: string): Promise<CollectionConfig[]> {\n // Note: a relative --collections path resolves against the *current working\n // directory*, not the backend package. When invoked via the generated npm\n // script (cwd = backend/), the script uses \"../config/collections\" to\n // compensate — so running the CLI directly from the repo root with a\n // different relative path can silently point at the wrong place.\n const resolvedPath = path.resolve(collectionsPath);\n const collections: CollectionConfig[] = [];\n if (!fs.existsSync(resolvedPath) && !warnedMissingCollectionsPaths.has(resolvedPath)) {\n // Once per path per process. `db push` re-enters this from\n // `schemaCommand`, `generatePostgresDdlCommand` and the Atlas argv\n // assembly, so the same four lines were printed four times — sixteen\n // lines of identical text ahead of the real output.\n //\n // An explicitly typed `--collections` never reaches here at all: it is\n // refused at the entry point, before anything is generated. See\n // `cli-collections-path.ts`. What is left is the default, which a\n // headless project legitimately does not have.\n warnedMissingCollectionsPaths.add(resolvedPath);\n outWarn(chalk.yellow(\n ` ⚠ Collections path not found: \"${collectionsPath}\"\\n` +\n ` Resolved to: ${resolvedPath}\\n` +\n ` (relative to cwd: ${process.cwd()})\\n` +\n ` Pass an absolute path, or a path relative to where you run the command.`\n ));\n }\n if (fs.existsSync(resolvedPath)) {\n const stats = fs.statSync(resolvedPath);\n if (stats.isDirectory()) {\n const files = fs.readdirSync(resolvedPath);\n for (const file of files) {\n if ((file.endsWith(\".ts\") || file.endsWith(\".js\")) &&\n !file.includes(\".test.\") &&\n !file.endsWith(\".d.ts\") &&\n file !== \"index.ts\" && file !== \"index.js\") {\n try {\n const filePath = path.join(resolvedPath, file);\n const fileUrl = pathToFileURL(filePath).href;\n const module = await import(fileUrl);\n if (module && module.default) {\n collections.push(module.default);\n }\n } catch {\n // ignore\n }\n }\n }\n }\n }\n\n return collections;\n}\n\n/**\n * The app database connections this process has open, by URL — one each.\n *\n * `rebase db push` used to open a connection per step: the auth bootstrap, the\n * connectivity probe, each catalogue read behind the Atlas excludes, the\n * column types, the generated-column edges, and then every post-step — RLS\n * lock, auth tables, vector, search, triggers, policies, reconcile, role,\n * legacy schema. 18 of them on a push that changed nothing. Locally each costs\n * a few milliseconds; against a hosted Postgres over TLS a connect is four or\n * five round trips (TCP, TLS 1.3, startup, SCRAM twice) — about 4 s of every\n * push at 40 ms, and more on a serverless database that wakes per connection.\n *\n * Opened on first use and closed by {@link closeAppClients} when the command\n * ends. The scratch database Atlas plans in is created and dropped through the\n * `postgres` database, on a connection of its own: it is not this one.\n */\nconst appClients = new Map<string, Promise<PgClient>>();\n\ntype PgClient = import(\"pg\").Client;\n\nasync function openAppClient(databaseUrl: string, connectionTimeoutMillis?: number): Promise<PgClient> {\n const open = appClients.get(databaseUrl);\n if (open) return open;\n const opening = (async () => {\n const { Client } = await import(\"pg\");\n const client = new Client({\n connectionString: databaseUrl,\n ...(connectionTimeoutMillis ? { connectionTimeoutMillis } : {})\n });\n // A connection can die while it sits idle between steps — Atlas runs\n // for seconds with this one open. Unheard, that `error` is an uncaught\n // exception and kills the process; heard, the next step opens another,\n // and a step running on it fails with its own error, as before.\n const forget = (): void => {\n if (appClients.get(databaseUrl) === opening) appClients.delete(databaseUrl);\n };\n client.on(\"error\", forget);\n client.on(\"end\", forget);\n await client.connect();\n return client;\n })();\n appClients.set(databaseUrl, opening);\n try {\n return await opening;\n } catch (err) {\n // Not kept: the next step tries again, and reports its own failure the\n // way it did when every step connected for itself.\n appClients.delete(databaseUrl);\n throw err;\n }\n}\n\n/**\n * Run one step on this process's connection to the app database.\n *\n * Every step leaves the session as it found it, which is what a connection of\n * its own used to guarantee by being closed. None of them sets anything for\n * the session — `SET LOCAL` inside their own transaction, or `RESET` — and one\n * that fails part-way through a transaction it opened is rolled back here, so\n * the next step does not meet `current transaction is aborted`.\n *\n * @param options.connectionTimeoutMillis applies only when this call is the one\n * that connects.\n */\nexport async function withAppClient<T>(\n databaseUrl: string,\n step: (client: PgClient) => Promise<T>,\n options: { connectionTimeoutMillis?: number } = {}\n): Promise<T> {\n const client = await openAppClient(databaseUrl, options.connectionTimeoutMillis);\n try {\n return await step(client);\n } catch (err) {\n // Outside a transaction this is a warning Postgres sends and nobody\n // reads; on a dead connection it fails too, which changes nothing.\n await client.query(\"ROLLBACK\").catch(() => undefined);\n throw err;\n }\n}\n\n/** Close every connection {@link withAppClient} opened. Called once, as the command ends. */\nexport async function closeAppClients(): Promise<void> {\n const open = [...appClients.values()];\n appClients.clear();\n await Promise.all(open.map(async opening => {\n try {\n await (await opening).end();\n } catch {\n // Already gone, or never connected: nothing to close.\n }\n }));\n}\n\n/**\n * Names the empty database Atlas plans against, instead of `<db>_dev_diff`.\n *\n * The derived one is made with `CREATE DATABASE` beside the real database,\n * which assumes a server that holds more than one. PGlite — the managed\n * development database — serves exactly one: the CREATE succeeds and creates\n * nothing, so Atlas connected straight back to the database it was comparing\n * and refused it as \"not clean\". `rebase db push` on the managed database now\n * starts a second, throwaway PGlite and names it here. Anyone can name their\n * own the same way; a named database is neither created nor dropped.\n */\nexport const ATLAS_DEV_URL_ENV = \"REBASE_ATLAS_DEV_URL\";\n\n/**\n * The database Atlas replays the desired schema into: the one\n * {@link ATLAS_DEV_URL_ENV} names (`named: true` — not ours to create or\n * drop), else `<db>_dev_diff`.\n */\nexport function atlasDevDatabaseUrl(\n databaseUrl: string,\n env: Readonly<Record<string, string | undefined>>\n): { url: string; named: boolean } {\n const named = env[ATLAS_DEV_URL_ENV]?.trim();\n if (!named) return { url: getDevDatabaseUrl(databaseUrl), named: false };\n if (named === databaseUrl.trim()) {\n // Atlas would replay the schema into the database it is about to change.\n throw new Error(\n `${ATLAS_DEV_URL_ENV} names the same database as DATABASE_URL. It must be a separate, empty ` +\n \"database that Atlas may fill and clear.\"\n );\n }\n return { url: named, named: true };\n}\n\nexport function getDevDatabaseUrl(databaseUrl: string): string {\n try {\n const parsed = new URL(databaseUrl);\n const dbName = parsed.pathname.slice(1);\n parsed.pathname = `/${dbName}_dev_diff`;\n return parsed.toString();\n } catch {\n return databaseUrl + \"_dev_diff\";\n }\n}\n\n/**\n * Create the scratch database Atlas plans against, and say so when we cannot.\n *\n * Atlas needs a `--dev-url` — an empty database it can build the desired state\n * in — and this makes `<db>_dev_diff` next to the real one. The whole body used\n * to be wrapped in an empty catch commented \"Ignore, let Atlas handle\n * connection failures\", and Atlas's version of handling it is `postgres:\n * querying system variables: pq: database \"app_dev_diff\" does not exist\n * (3D000)` — the symptom, four frames downstream of the cause, with the cause\n * thrown away.\n *\n * The cause is almost always `42501`: `CREATE DATABASE` is a privilege managed\n * providers do not grant, so this is the first thing a hosted user hits and the\n * one they can do least with.\n *\n * @returns whether the scratch database was created by *this* call — the only\n * case in which dropping it afterwards is ours to do.\n */\nexport async function ensureDevDatabaseExists(databaseUrl: string, devDatabaseUrl: string): Promise<boolean> {\n let devDbName = \"\";\n try {\n const { Client } = await import(\"pg\");\n const parsed = new URL(databaseUrl);\n devDbName = new URL(devDatabaseUrl).pathname.slice(1);\n\n parsed.pathname = \"/postgres\";\n const client = new Client({ connectionString: parsed.toString() });\n await client.connect();\n try {\n const res = await client.query(\"SELECT 1 FROM pg_database WHERE datname = $1\", [devDbName]);\n if (res.rowCount === 0) {\n await client.query(`CREATE DATABASE \"${devDbName}\"`);\n out(chalk.gray(` ✓ Created validation database \"${devDbName}\"`));\n\n return true;\n }\n } finally {\n await client.end();\n }\n } catch (err) {\n outWarn(describeDevDatabaseFailure(err, devDbName || \"<db>_dev_diff\"));\n }\n\n return false;\n}\n\n/**\n * What went wrong creating the scratch database, and what to do about it.\n *\n * Exported for its test: the `42501` branch is the one that matters and it\n * cannot be provoked without a non-superuser role.\n */\nexport function describeDevDatabaseFailure(err: unknown, devDbName: string): string {\n const code = (err as { code?: string } | null)?.code;\n const message = err instanceof Error ? err.message : String(err);\n\n const lines = [\n chalk.yellow(` ⚠ Could not create the Atlas scratch database \"${devDbName}\": ${message}`)\n ];\n if (code === \"42501\") {\n lines.push(chalk.gray(\n \" Atlas plans every change in a throwaway copy of the schema, and CREATE DATABASE\\n\" +\n \" is a privilege most managed providers withhold. Two ways forward:\\n\" +\n ` • GRANT the role CREATEDB: ALTER ROLE \"<role>\" CREATEDB;\\n` +\n ` • or create it once by hand: CREATE DATABASE \"${devDbName}\";`\n ));\n } else {\n lines.push(chalk.gray(\" Atlas will fail next with `database \\\"\" + devDbName + \"\\\" does not exist (3D000)`, which is this.\"));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Drop the scratch database again, after the command that needed it succeeded.\n *\n * It is a full copy of the schema — no rows, but every table, type and index —\n * and it was left behind per target forever. The only notice anyone got was\n * `rebase db branch prune` reporting \"3 Atlas scratch database(s) left over\n * from db push\", and on a provider that bills per database it is a silent cost.\n *\n * Deliberately NOT called on the failure path: after a failed push the scratch\n * database holds the state Atlas was planning against, which is the one thing\n * worth inspecting.\n */\nexport async function dropDevDatabase(databaseUrl: string, devDatabaseUrl: string): Promise<void> {\n try {\n const { Client } = await import(\"pg\");\n const parsed = new URL(databaseUrl);\n const devDbName = new URL(devDatabaseUrl).pathname.slice(1);\n\n parsed.pathname = \"/postgres\";\n const client = new Client({ connectionString: parsed.toString() });\n await client.connect();\n try {\n await client.query(`DROP DATABASE IF EXISTS \"${devDbName}\"`);\n out(chalk.gray(` ✓ Removed validation database \"${devDbName}\"`));\n } finally {\n await client.end();\n }\n } catch (err) {\n // Not fatal, and not silent: the push has already succeeded, so the\n // worst case is one database left where `prune` can find it.\n outWarn(chalk.gray(\n ` ⚠ Could not remove the Atlas scratch database: ${err instanceof Error ? err.message : String(err)}\\n` +\n \" `rebase db branch prune --include-dev-diff` clears them.\"\n ));\n }\n}\n\n/**\n * The generated SQL for the project's `search` blocks, if it has any.\n *\n * @param sqlDir directory holding the generated SQL. Defaults to\n * `.rebase/sql` under the working directory.\n */\nexport function readSearchDdl(sqlDir: string = generatedSqlDir()): string {\n const searchFile = path.join(sqlDir, \"search.sql\");\n if (!fs.existsSync(searchFile)) return \"\";\n return fs.readFileSync(searchFile, \"utf-8\").trim();\n}\n\n/**\n * Bring the search column, its index and their helpers up to date.\n *\n * Runs *after* Atlas, not before: the statements are `ALTER TABLE ... ADD\n * COLUMN`, so the table has to exist. Atlas is told to ignore these objects\n * entirely (`getSearchExcludes`) — it cannot manage them, and left to itself it\n * would plan a `DROP COLUMN` for every one, since they are absent from the\n * desired state it was given.\n *\n * A no-op when no collection declared `search`. A failure is *not* swallowed:\n * silently pushing a schema whose search column never appeared is how a\n * collection ends up with search configured, no error anywhere, and no results.\n */\nexport async function applySearchDdl(\n databaseUrl: string,\n sqlDir: string = generatedSqlDir()\n): Promise<void> {\n const sql = readSearchDdl(sqlDir);\n if (!sql) return;\n\n await withAppClient(databaseUrl, client => client.query(sql));\n out(chalk.gray(\" ✓ Applied full-text search columns and indexes\"));\n}\n\n/**\n * Glob patterns keeping Atlas away from the search objects.\n *\n * Returns an empty list — and so changes nothing — for a project with no\n * `search` block, which is every project that has not opted in.\n */\nexport async function getSearchExcludes(collectionsPath: string): Promise<string[]> {\n const { searchExcludePatterns } = await import(\"./schema/generate-postgres-ddl-logic\");\n return searchExcludePatterns(await loadCollectionsForCli(collectionsPath));\n}\n\n/**\n * Every `generated column → column it reads` edge in the database.\n *\n * Read from `pg_depend` rather than by matching names in the expression text:\n * a generated column's `pg_attrdef` row carries one `deptype = 'n'` dependency\n * per column its expression references, which is the catalogue's own answer and\n * cannot be fooled by a column name that also appears inside a string literal\n * or as part of a longer identifier.\n *\n * Used by `db push` to find what has to come out of the way before Atlas runs —\n * see `generated-column-conflicts.ts` for why that is necessary at all.\n */\nexport async function queryGeneratedColumnDependencies(\n databaseUrl: string\n): Promise<GeneratedColumnDependency[]> {\n return withAppClient(databaseUrl, async client => {\n const res = await client.query(`\n SELECT n.nspname AS \"schema\",\n c.relname AS \"table\",\n gen.attname AS \"column\",\n dep.attname AS \"dependsOn\"\n FROM pg_attrdef ad\n JOIN pg_class c ON c.oid = ad.adrelid\n JOIN pg_namespace n ON n.oid = c.relnamespace\n JOIN pg_attribute gen ON gen.attrelid = ad.adrelid AND gen.attnum = ad.adnum\n JOIN pg_depend d ON d.classid = 'pg_attrdef'::regclass\n AND d.objid = ad.oid\n AND d.refclassid = 'pg_class'::regclass\n AND d.refobjid = ad.adrelid\n AND d.deptype = 'n'\n JOIN pg_attribute dep ON dep.attrelid = ad.adrelid AND dep.attnum = d.refobjsubid\n WHERE gen.attgenerated <> ''\n AND NOT gen.attisdropped\n AND NOT dep.attisdropped;\n `);\n return res.rows as GeneratedColumnDependency[];\n });\n}\n\n/**\n * Every column in the database with its current type, for the destructive\n * gate to tell a widening `ALTER COLUMN … TYPE` from one that loses data.\n */\nexport async function queryColumnTypes(databaseUrl: string): Promise<ExistingColumnType[]> {\n return withAppClient(databaseUrl, async client => (await client.query<ExistingColumnType>(COLUMN_TYPES_SQL)).rows);\n}\n\n/**\n * Drop the generated columns standing in the way of an apply.\n *\n * Their indexes and their `COMMENT ON COLUMN` stamp go with them, which is\n * exactly right: `applySearchDdl` rebuilds all three from `search.sql` and a\n * surviving stamp would make the rebuilt column look like drift.\n */\nexport async function dropGeneratedColumns(\n databaseUrl: string,\n statements: string[]\n): Promise<void> {\n if (statements.length === 0) return;\n await withAppClient(databaseUrl, async client => {\n for (const statement of statements) {\n await client.query(statement);\n }\n });\n}\n\n/**\n * The generated SQL for the project's `vector` properties, if it has any.\n *\n * @param sqlDir directory holding the generated SQL. Defaults to\n * `.rebase/sql` under the working directory.\n */\nexport function readVectorDdl(sqlDir: string = generatedSqlDir()): string {\n const vectorFile = path.join(sqlDir, \"vector.sql\");\n if (!fs.existsSync(vectorFile)) return \"\";\n return fs.readFileSync(vectorFile, \"utf-8\").trim();\n}\n\n/**\n * Install pgvector and bring the vector columns and their ANN indexes up to\n * date.\n *\n * Runs *after* Atlas, like {@link applySearchDdl} and for the same reason: the\n * statements are `ALTER TABLE … ADD COLUMN`, so the table has to exist. Atlas\n * is told to ignore these objects entirely ({@link getVectorExcludes}) — it\n * cannot manage a type its dev database is structurally incapable of having,\n * and left to itself it would plan a `DROP COLUMN` for every one.\n *\n * A no-op when no collection declares a `vector` property. A failure is *not*\n * swallowed: a push that reported success while the embedding column never\n * appeared is how a project ends up with vector search configured, no error\n * anywhere, and no results.\n */\nexport async function applyVectorDdl(\n databaseUrl: string,\n sqlDir: string = generatedSqlDir()\n): Promise<void> {\n const sql = readVectorDdl(sqlDir);\n if (!sql) return;\n\n const { vectorExtensionHint } = await import(\"./schema/vector-index\");\n try {\n await withAppClient(databaseUrl, client => client.query(sql));\n } catch (err) {\n // The boot ensure explains this failure and the push has to as well —\n // it is the same missing extension, and a push is the *more* likely\n // place to meet it. Without this the developer gets a bare\n // `type \"vector\" does not exist`, which names nothing they can act on\n // and reads like a broken database rather than a line of config.\n const message = err instanceof Error ? err.message : String(err);\n const hint = vectorExtensionHint(message);\n if (!hint) throw err;\n throw new Error(`${message}${hint}`, { cause: err });\n }\n out(chalk.gray(\" ✓ Applied vector columns and ANN indexes\"));\n}\n\n/**\n * Glob patterns keeping Atlas away from the vector objects.\n *\n * Returns an empty list — and so changes nothing — for a project with no\n * `vector` property, which is every project that has not opted in.\n */\nexport async function getVectorExcludes(collectionsPath: string): Promise<string[]> {\n const { vectorExcludePatterns } = await import(\"./schema/generate-postgres-ddl-logic\");\n return vectorExcludePatterns(await loadCollectionsForCli(collectionsPath));\n}\n\n/**\n * The generated SQL for the project's `autoValue: \"on_update\"` properties, if\n * it has any.\n *\n * @param sqlDir directory holding the generated SQL. Defaults to\n * `.rebase/sql` under the working directory.\n */\nexport function readTriggersDdl(sqlDir: string = generatedSqlDir()): string {\n const triggersFile = path.join(sqlDir, \"triggers.sql\");\n if (!fs.existsSync(triggersFile)) return \"\";\n return fs.readFileSync(triggersFile, \"utf-8\").trim();\n}\n\n/**\n * Install the `updated_at` trigger function and bind it to every column that\n * asked for one.\n *\n * Runs *after* Atlas, like {@link applySearchDdl} and {@link applyVectorDdl}:\n * a trigger needs its table and its column to exist. Atlas is told to ignore\n * the triggers ({@link getTriggerExcludes}) because it cannot manage a function\n * at all — its free tier refuses to parse a desired state containing one.\n *\n * A no-op when no property declares `autoValue: \"on_update\"`. A failure is\n * *not* swallowed: a push that reported success while the trigger never\n * appeared is how `updated_at` ends up silently stale for every writer that is\n * not the driver, which is the whole reason the trigger exists.\n */\nexport async function applyTriggersDdl(\n databaseUrl: string,\n sqlDir: string = generatedSqlDir()\n): Promise<void> {\n const sql = readTriggersDdl(sqlDir);\n if (!sql) return;\n\n await withAppClient(databaseUrl, client => client.query(sql));\n out(chalk.gray(\" ✓ Applied `updated_at` triggers\"));\n}\n\n/** @see getSearchExcludes */\nexport async function getTriggerExcludes(collectionsPath: string): Promise<string[]> {\n const { triggerExcludePatterns } = await import(\"./schema/generate-postgres-ddl-logic\");\n return triggerExcludePatterns(await loadCollectionsForCli(collectionsPath));\n}\n\n/**\n * Query the live database for every user table/view outside the system\n * catalogs. Separated from {@link getTableExcludes} so its failure mode can\n * be handled explicitly (fail closed) and so tests can inject a stub.\n */\n/** An index as the catalogue names it: its schema, its table and its own name. */\nexport interface ExistingIndex {\n schema: string;\n table: string;\n index: string;\n}\n\n/**\n * Every index Postgres holds on a table, by its parts — never joined with `.`,\n * which a name may itself contain.\n *\n * Constraint-backed indexes are left out: a `PRIMARY KEY` or a `UNIQUE` is a\n * *constraint* to Atlas, diffed from the constraint declaration, and naming its\n * index in an exclude would shield the constraint itself.\n */\nexport async function queryExistingIndexes(databaseUrl: string): Promise<ExistingIndex[]> {\n return withAppClient(databaseUrl, async client => {\n const res = await client.query<ExistingIndex>(`\n SELECT n.nspname AS \"schema\", t.relname AS \"table\", i.relname AS \"index\"\n FROM pg_index x\n JOIN pg_class i ON i.oid = x.indexrelid\n JOIN pg_class t ON t.oid = x.indrelid\n JOIN pg_namespace n ON n.oid = t.relnamespace\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n AND t.relkind IN ('r', 'p')\n AND NOT EXISTS (\n SELECT 1 FROM pg_constraint c WHERE c.conindid = i.oid\n );\n `);\n return res.rows;\n });\n}\n\n/**\n * Glob patterns keeping Atlas away from indexes that are not Rebase's.\n *\n * The problem this solves is live and predates the `indexes:` block. `db push`\n * is declarative, so an index on a managed table that is absent from\n * `schema.sql` is drift — and Atlas plans `DROP INDEX` for it. Verified\n * against atlas v1.2.3: create one by hand, re-run an unchanged push, and the\n * plan is a bare drop. `DROP INDEX` is not in `DESTRUCTIVE_PATTERNS`, so the\n * auto-approved apply took it without asking. Until now the only way to have\n * an index at all was to write it by hand, which made that the *only* outcome.\n *\n * Ownership rather than a prompt, because once indexes are declarable a drop\n * is usually correct: removing one from the config should remove it from the\n * database, quietly. What must never be dropped is an index Rebase never\n * created — a hand-written one, or one an introspected database arrived with.\n * {@link isRebaseIndexName} is the test, the same arrangement\n * `isGeneratedPolicyName` uses to let policy reconciliation drop only what it\n * generated.\n *\n * The named-in-the-desired-state check comes first and is what makes removal\n * work: a declared index that the author has just deleted still matches the\n * name pattern, is no longer in the plan, and so is *not* excluded — Atlas\n * drops it, as intended.\n *\n * Three-part `schema.table.index`, never two: a two-part pattern reads as a\n * *table* named `<index>` in a *schema* named `<table>`, matches nothing, and\n * reports no error — the trap already recorded for the search excludes. And\n * spelled by `atlasExcludePattern`: a raw `orders_idx[v1]` is a glob that\n * matches `orders_idxv`, so that index was dropped with no prompt.\n */\nexport async function getForeignIndexExcludes(\n databaseUrl: string,\n collectionsPath: string,\n deps: {\n queryExistingIndexes?: (databaseUrl: string) => Promise<ExistingIndex[]>;\n getManagedIndexNames?: (collectionsPath: string) => Promise<Set<string>>;\n } = {}\n): Promise<string[]> {\n const queryIndexes = deps.queryExistingIndexes ?? queryExistingIndexes;\n const getManaged = deps.getManagedIndexNames ?? managedIndexNames;\n\n const managed = await getManaged(collectionsPath);\n\n let existing: ExistingIndex[];\n try {\n existing = await queryIndexes(databaseUrl);\n } catch (err) {\n // Fails CLOSED, like getTableExcludes: without the catalogue we cannot\n // tell a foreign index from one of ours, and guessing wrong destroys\n // an index somebody is relying on.\n throw new ExcludeIntrospectionError(\n `Failed to introspect the database for unmanaged indexes: ${err instanceof Error ? err.message : String(err)}`,\n err\n );\n }\n\n return existing\n .filter(({ index }) => {\n if (managed.has(index)) return false; // ours, and still declared\n return !isRebaseIndexName(index); // ours, but no longer declared -> let it drop\n })\n .map(({ schema, table, index }) => atlasExcludePattern(schema, table, index));\n}\n\n/**\n * The index names the current collections would create — search, vector and\n * declared alike. Anything Atlas would emit is by definition not foreign.\n */\nasync function managedIndexNames(collectionsPath: string): Promise<Set<string>> {\n const collections = await loadCollectionsForCli(collectionsPath);\n const { searchExcludePatterns, vectorExcludePatterns } =\n await import(\"./schema/generate-postgres-ddl-logic\");\n const { resolveColumnName } = await import(\"./schema/column-plan-helpers\");\n const { buildCollectionIndexPlan } = await import(\"./schema/collection-index\");\n\n const names = new Set<string>(\n buildCollectionIndexPlan(collections, resolveColumnName).map(spec => spec.indexName)\n );\n // Search and vector objects are excluded from Atlas wholesale by their own\n // patterns; listing them here too is harmless and keeps this the single\n // answer to \"is this index one of ours\". The doc comment above claimed\n // vector was here long before it was — so an ANN index answered that\n // question with \"somebody else's\".\n for (const pattern of [...searchExcludePatterns(collections), ...vectorExcludePatterns(collections)]) {\n const object = excludePatternParts(pattern)?.[2];\n if (object !== undefined) names.add(object);\n }\n return names;\n}\n\n/** Every CHECK and EXCLUDE constraint the database holds. @see UNDECLARABLE_CONSTRAINTS_SQL */\nexport async function queryUndeclarableConstraints(databaseUrl: string): Promise<ExistingConstraint[]> {\n return withAppClient(databaseUrl, async client =>\n (await client.query<ExistingConstraint>(UNDECLARABLE_CONSTRAINTS_SQL)).rows);\n}\n\n/**\n * Patterns keeping Atlas away from the CHECK and EXCLUDE constraints the\n * collections do not declare — which, since a collection cannot declare\n * either, is all of them. See `schema/undeclarable-constraints.ts`.\n *\n * Fails CLOSED, like {@link getTableExcludes}: without the catalogue nothing\n * says which constraints are there, and a partial list lets the push drop a\n * rule the database enforces. `desiredSql` is the `schema.sql` Atlas is about\n * to diff against; a constraint it declares is Atlas's to manage.\n */\nexport async function getUndeclaredConstraintExcludes(\n databaseUrl: string,\n desiredSql: string,\n deps: { queryConstraints?: (databaseUrl: string) => Promise<ExistingConstraint[]> } = {}\n): Promise<string[]> {\n const queryConstraints = deps.queryConstraints ?? queryUndeclarableConstraints;\n let existing: ExistingConstraint[];\n try {\n existing = await queryConstraints(databaseUrl);\n } catch (err) {\n throw new ExcludeIntrospectionError(\n `Failed to introspect the database for CHECK and EXCLUDE constraints: ${err instanceof Error ? err.message : String(err)}`,\n err\n );\n }\n return undeclaredConstraintExcludes(existing, desiredSql);\n}\n\n/** A table or view as the catalogue names it. */\nexport interface ExistingTable {\n schema: string;\n table: string;\n}\n\nexport async function queryExistingTables(databaseUrl: string): Promise<ExistingTable[]> {\n return withAppClient(databaseUrl, async client => {\n const res = await client.query<ExistingTable>(`\n SELECT table_schema AS \"schema\", table_name AS \"table\"\n FROM information_schema.tables\n WHERE table_schema NOT IN ('pg_catalog', 'information_schema')\n AND table_type IN ('BASE TABLE', 'VIEW');\n `);\n return res.rows;\n });\n}\n\n/**\n * Raised when the exclude list could not be built. `db push` MUST abort on\n * this rather than continue: the exclude list is the only thing shielding\n * non-collection (user/system) tables from the auto-approved declarative\n * apply. A partial list — the old fail-open behaviour — meant a transient\n * introspection hiccup dropped every table not present in `schema.sql`.\n */\nexport class ExcludeIntrospectionError extends Error {\n constructor(message: string, readonly cause?: unknown) {\n super(message);\n this.name = \"ExcludeIntrospectionError\";\n }\n}\n\n/**\n * Build the `--exclude` list that protects tables Rebase doesn't manage from\n * the declarative apply. Anything not backing a collection (or its M2M\n * junctions) is excluded so Atlas never drops it.\n *\n * Fails **closed**: if the database can't be introspected we cannot know\n * which tables to protect, so we throw {@link ExcludeIntrospectionError}\n * instead of returning a near-empty list and letting the caller drop\n * everything.\n *\n * `deps` is injectable for tests; production uses the real pg-backed queries.\n */\nexport async function getTableExcludes(\n databaseUrl: string,\n collectionsPath: string,\n deps: {\n queryExistingTables?: (databaseUrl: string) => Promise<ExistingTable[]>;\n getIncludes?: (collectionsPath: string) => Promise<string[]>;\n } = {}\n): Promise<string[]> {\n const getIncludes = deps.getIncludes ?? getTableIncludes;\n const queryTables = deps.queryExistingTables ?? queryExistingTables;\n\n const includes = await getIncludes(collectionsPath);\n // Schemas the declarative apply must never touch. `rebase` holds the auth\n // tables (users, refresh_tokens, …), the RLS helper functions and Atlas's\n // own revision table. Excluding both the schema object AND its contents\n // keeps Atlas from planning a `DROP SCHEMA … CASCADE` — which on a live\n // database would take the user/auth tables with it.\n // `atlas_schema_revisions.*` covers a revision table living outside the\n // `rebase` schema — an externally managed Atlas setup — which the apply\n // must not plan changes against either.\n //\n // `auth` stays on this list even though Rebase no longer creates it, and\n // that is the point: on a database still holding the pre-1.0 schema it must\n // survive until the guarded cleanup retires it, and on a Supabase database\n // it is somebody else's and must never be in scope at all. Removing it here\n // would let a `db push` plan `DROP SCHEMA auth CASCADE` against a live\n // Supabase installation.\n const excludes: string[] = [\"atlas_schema_revisions.*\", \"auth\", \"auth.*\", \"rebase\", \"rebase.*\"];\n\n let existingTables: ExistingTable[];\n try {\n existingTables = await queryTables(databaseUrl);\n } catch (err) {\n throw new ExcludeIntrospectionError(\n `Failed to introspect the database for unmapped tables: ${err instanceof Error ? err.message : String(err)}`,\n err\n );\n }\n\n for (const { schema, table } of existingTables) {\n if (!includes.includes(`${schema}.${table}`)) {\n excludes.push(atlasExcludePattern(schema, table));\n }\n }\n\n return excludes;\n}\n","import chalk from \"chalk\";\nimport { outWarn, outError } from \"./cli-output\";\nimport { extractCauseMessage } from \"./utils/error-cause\";\nimport { redactConnectionPasswords } from \"./utils/connection-string\";\nimport { detectProjectPackageManager, withAppClient, type ProjectPackageManager } from \"./cli-helpers\";\n\n/**\n * Detect whether an error (or AggregateError wrapping multiple attempts)\n * represents an ECONNREFUSED — i.e. the database is simply not running.\n *\n * Handles:\n * - Direct `{ code: \"ECONNREFUSED\" }` errors from Node `net`\n * - `AggregateError` from dual-stack IPv4+IPv6 connection attempts\n * - Drizzle's `cause`-wrapped pg errors\n */\nexport function isEconnrefused(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { code?: string; cause?: unknown; errors?: unknown[] };\n if (e.code === \"ECONNREFUSED\") return true;\n // AggregateError from Node net (dual-stack IPv4 + IPv6)\n if (Array.isArray(e.errors)) {\n return e.errors.some(inner =>\n inner && typeof inner === \"object\" && (inner as { code?: string }).code === \"ECONNREFUSED\"\n );\n }\n // Drizzle wraps the pg error in `cause`\n if (e.cause && typeof e.cause === \"object\") {\n return isEconnrefused(e.cause);\n }\n return false;\n}\n\n/**\n * Detect PostgreSQL authentication failures.\n * PG error codes: 28P01 (invalid_password), 28000 (invalid_authorization_specification)\n */\nexport function isAuthFailure(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { code?: string; cause?: unknown };\n if (e.code === \"28P01\" || e.code === \"28000\") return true;\n if (e.cause && typeof e.cause === \"object\") {\n return isAuthFailure(e.cause);\n }\n // Also check the message for common pg auth failure text\n if (\"message\" in e && typeof (e as { message?: string }).message === \"string\") {\n const msg = (e as { message: string }).message.toLowerCase();\n if (msg.includes(\"password authentication failed\") || msg.includes(\"no pg_hba.conf entry\")) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Detect the \"SSL is not enabled on the server\" failure — the client attempted\n * an SSL handshake against a Postgres server that doesn't support it (common\n * with a plain local dev database). The fix is `?sslmode=disable` on the URL.\n */\nexport function isSslNotEnabled(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { message?: string; cause?: unknown };\n if (typeof e.message === \"string\" && e.message.toLowerCase().includes(\"ssl is not enabled on the server\")) {\n return true;\n }\n if (e.cause && typeof e.cause === \"object\") {\n return isSslNotEnabled(e.cause);\n }\n return false;\n}\n\n/**\n * Detect PostgreSQL \"cannot drop ... because other objects depend on it\"\n * (error code 2BP01, dependent_objects_still_exist). This is the failure that\n * strands a declarative `db push` half-applied when a collection is removed but\n * an enum type it defined is still referenced by another object.\n */\nexport function isDependencyDropError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { code?: string; message?: string; cause?: unknown };\n if (e.code === \"2BP01\") return true;\n if (typeof e.message === \"string\") {\n const msg = e.message.toLowerCase();\n if (msg.includes(\"other objects depend on it\") || msg.includes(\"cannot drop type\")) {\n return true;\n }\n }\n if (e.cause && typeof e.cause === \"object\") {\n return isDependencyDropError(e.cause);\n }\n return false;\n}\n\n/**\n * Parse host:port from a DATABASE_URL for display purposes.\n *\n * Exported because every message about a connection has to name the thing it\n * could not reach, and the boot path needs the same rendering the CLI banners\n * use — including the same refusal to print the URL itself, which carries the\n * password.\n */\nexport function parseHostInfo(databaseUrl: string): string {\n try {\n const parsed = new URL(databaseUrl);\n return `${parsed.hostname}:${parsed.port || 5432}`;\n } catch {\n return \"unknown\";\n }\n}\n\n/**\n * The sentence the operating system actually produced, dug out of the wrappers.\n *\n * `connect ECONNREFUSED 127.0.0.1:5432` is written by `net`, then wrapped by\n * `pg`, then wrapped again by Drizzle as `Failed query: …` — and on a\n * dual-stack host it is not in `.cause` at all but inside the\n * `AggregateError.errors` array of one attempt per resolved address. Printing\n * the banner without it loses the one token every search engine, runbook and\n * colleague recognises.\n */\nexport function deepestErrorMessage(err: unknown): string | null {\n if (!err || typeof err !== \"object\") return null;\n const e = err as { message?: string; code?: string; cause?: unknown; errors?: unknown[] };\n\n if (Array.isArray(e.errors)) {\n for (const inner of e.errors) {\n const deeper = deepestErrorMessage(inner);\n if (deeper) return deeper;\n }\n }\n if (e.cause) {\n const deeper = deepestErrorMessage(e.cause);\n if (deeper) return deeper;\n }\n if (typeof e.message === \"string\" && e.message && !e.message.startsWith(\"Failed query:\")) {\n return e.code ? `${e.message} (${e.code})` : e.message;\n }\n return null;\n}\n\n/** One indented line naming the driver's own reason, or nothing. */\nfunction driverReasonLine(err: unknown): string {\n const reason = err === undefined ? null : deepestErrorMessage(err);\n return reason ? ` The driver said: ${reason}\\n\\n` : \"\";\n}\n\n/**\n * What named the database a db command connects to, as the CLI resolved it\n * and says in `REBASE_DATABASE_SOURCE`: `flag` (`--database-url`),\n * `environment` (the shell's DATABASE_URL), `env-file` (`.env`), `branch` (a\n * `db branch switch`), `docker` / `last-dev` (the compose `db` service) or\n * `managed` (the managed development database). `undefined` when nothing\n * says — a boot, or the driver run by hand.\n */\nfunction databaseSource(): string | undefined {\n const source = process.env.REBASE_DATABASE_SOURCE?.trim();\n return source ? source : undefined;\n}\n\n/** Where the connection string is, in the words a reader looks for it by. */\nfunction connectionStringPlace(source: string | undefined): string {\n switch (source) {\n case \"flag\": return \"the URL passed with --database-url\";\n case \"environment\": return \"DATABASE_URL in this shell's environment\";\n default: return \"DATABASE_URL\";\n }\n}\n\n/** The fixes for a database that does not answer, for what named it. */\nfunction connectionRefusedFixes(source: string | undefined): string[] {\n switch (source) {\n case \"flag\": return [\"Check the URL passed with --database-url, and that its server is running\"];\n case \"environment\": return [\"Check DATABASE_URL in this shell's environment, and that its server is running\"];\n case \"env-file\": return [\"Verify DATABASE_URL in your .env file, and that its server is running\"];\n case \"branch\": return [\n \"The branch's database is on the server DATABASE_URL in .env names: start that server\",\n \"Or leave the branch: rebase db branch switch --off\"\n ];\n case \"docker\":\n case \"last-dev\": return [\"docker compose up -d db (the project's Postgres in Docker)\"];\n case \"managed\": return [\"Restart the managed development database: rebase db stop, then run the command again\"];\n default: return [\n \"docker compose up -d db (the service a Rebase scaffold ships)\",\n \"brew services start postgresql@18\",\n \"Verify DATABASE_URL in your .env file\"\n ];\n }\n}\n\n/**\n * Format a diagnostic banner for ECONNREFUSED errors.\n */\nfunction formatConnectionRefusedBanner(databaseUrl: string, err?: unknown): string {\n const hostInfo = parseHostInfo(databaseUrl);\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Cannot connect to PostgreSQL at ${hostInfo}\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n driverReasonLine(err) +\n ` The database server is not running or is not accepting\\n` +\n ` connections. Common fixes:\\n` +\n `\\n` +\n connectionRefusedFixes(databaseSource()).map((fix) => ` • ${fix}\\n`).join(\"\") +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for authentication failures.\n */\nfunction formatAuthFailureBanner(databaseUrl: string, err?: unknown): string {\n const hostInfo = parseHostInfo(databaseUrl);\n let username = \"unknown\";\n try {\n username = new URL(databaseUrl).username || \"unknown\";\n } catch { /* ignore */ }\n\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Authentication failed for user \"${username}\" at ${hostInfo}\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n driverReasonLine(err) +\n ` PostgreSQL rejected the credentials. Common fixes:\\n` +\n `\\n` +\n ` • Check the username and password in ${connectionStringPlace(databaseSource())}\\n` +\n ` • Verify the user exists: psql -c \"\\\\du\"\\n` +\n ` • Reset the password: ALTER USER ${username} PASSWORD 'new_password';\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for \"SSL is not enabled on the server\".\n */\nfunction formatSslNotEnabledBanner(databaseUrl: string): string {\n const hostInfo = parseHostInfo(databaseUrl);\n const suggestion = databaseUrl.includes(\"?\") ? \"&sslmode=disable\" : \"?sslmode=disable\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ SSL is not enabled on the PostgreSQL server at ${hostInfo}\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` The client tried to connect over SSL, but the server does not\\n` +\n ` support it. This is normal for a plain local dev database.\\n` +\n `\\n` +\n ` Fix: append ${chalk.bold(\"sslmode=disable\")} to DATABASE_URL, e.g.\\n` +\n `\\n` +\n ` DATABASE_URL=...${suggestion}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for a dependency-drop failure during `db push`.\n * Explains that the database may be left partially migrated and how to recover.\n *\n * The DROP reads the connection string from `rebase db url`, as the NOT NULL\n * box's backfill does: on the managed development database `.env` leaves\n * `DATABASE_URL` unset, and the shell has none.\n */\nfunction formatDependencyDropBanner(manager: ProjectPackageManager = detectProjectPackageManager()): string {\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Schema push failed: a type/table could not be dropped\\n` +\n ` because other objects still depend on it.\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` ${chalk.yellow(\"The database may now be partially migrated.\")} Atlas applies\\n` +\n ` statements individually, so earlier changes in this push may\\n` +\n ` already be committed while later ones failed.\\n` +\n `\\n` +\n ` This commonly happens when a collection is removed but an enum\\n` +\n ` type it defined is still referenced. To recover:\\n` +\n `\\n` +\n ` 1. Inspect the leftover object named in the error above.\\n` +\n ` 2. Drop it with CASCADE, e.g.:\\n` +\n ` psql \"$(${projectRebaseCommand(manager)} db url)\" -c 'DROP TYPE \"<name>\" CASCADE;'\\n` +\n ` 3. Re-run: ${chalk.bold.green(\"rebase db push\")}\\n` +\n `\\n` +\n ` Prefer a safe, versioned workflow? Use ${chalk.bold(\"rebase db generate\")}\\n` +\n ` + ${chalk.bold(\"rebase db migrate\")} instead of push for destructive changes.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Pre-flight check: verify that the database is reachable before running\n * a heavy subprocess (Atlas, migrations, etc.).\n *\n * Exits with code 1 and a friendly banner on known failure modes.\n * On unknown errors, logs a warning and allows the caller to proceed.\n */\nexport async function checkDatabaseConnectivity(databaseUrl: string): Promise<void> {\n try {\n // On the command's own connection, which every step after this uses\n // too — so a probe that passes leaves it open rather than closing a\n // connection the next step would reopen. See `withAppClient`.\n await withAppClient(databaseUrl, client => client.query(\"SELECT 1\"), { connectionTimeoutMillis: 5000 });\n } catch (err: unknown) {\n if (isEconnrefused(err)) {\n outError(formatConnectionRefusedBanner(databaseUrl, err));\n process.exit(1);\n }\n if (isAuthFailure(err)) {\n outError(formatAuthFailureBanner(databaseUrl, err));\n process.exit(1);\n }\n if (isSslNotEnabled(err)) {\n outError(formatSslNotEnabledBanner(databaseUrl));\n process.exit(1);\n }\n // Unknown error — warn but don't block; let the downstream tool surface details\n outWarn(chalk.yellow(` ⚠ Could not verify database connectivity: ${err instanceof Error ? err.message : String(err)}`));\n outWarn(chalk.gray(\" Proceeding anyway — the command may fail if the database is unreachable.\"));\n }\n}\n\n/**\n * Post-hoc error diagnosis for direct database operations (e.g. applyPolicies).\n * Returns a formatted diagnostic string if the error matches a known pattern,\n * or null if unrecognized.\n */\nexport function diagnoseDbError(err: unknown, databaseUrl?: string): string | null {\n if (isEconnrefused(err)) {\n return formatConnectionRefusedBanner(databaseUrl || \"\", err);\n }\n if (isAuthFailure(err)) {\n return formatAuthFailureBanner(databaseUrl || \"\", err);\n }\n if (isSslNotEnabled(err)) {\n return formatSslNotEnabledBanner(databaseUrl || \"\");\n }\n if (isDependencyDropError(err)) {\n return formatDependencyDropBanner();\n }\n return null;\n}\n\n/**\n * Say why the command failed, on the way out.\n *\n * The entry point below used to be `.catch(() => process.exit(1))`, which threw\n * the error away. Every message this file and its services raise — \"Branch\n * \\\"x\\\" already exists.\", \"the source database has active connections\", \"Branch\n * name is too long\" — was written, wrapped in the right PG error code, and then\n * discarded one frame before it reached a terminal. What a developer saw was a\n * header line, no error, and exit 1.\n *\n * Two shapes are deliberately kept quiet:\n *\n * - **A child process that already spoke.** Atlas, `pg_dump` and `psql` run\n * with inherited stdio, so their diagnosis is on the terminal already and\n * execa's wrapper adds only `Command failed with exit code 1: atlas …`.\n * `packages/cli` filters exactly these two phrasings one level up\n * (`runDbCommand`), and this is that filter, for the process that is actually\n * throwing.\n *\n * - **A message that is only a query.** Drizzle reports failures as\n * `Failed query: <sql> params:` and hides the real PostgreSQL error in\n * `cause`, so the wrapper alone tells a reader nothing they can act on. The\n * cause is appended when it says something the message does not.\n *\n * - **An error that has already printed its own diagnosis**, marked\n * `alreadyReported`. `CollectionsPathMissing` is one: it prints the path, what\n * it resolved to and the cwd it resolved against, and then throws so the entry\n * point owns the exit code. Repeating its one-line summary underneath would\n * undo the \"printed once\" this whole path exists for.\n *\n * Whatever is printed has its connection-string passwords replaced (see\n * `redactConnectionPasswords`): a driver or tool error can quote the URL it was\n * given, and this line lands in terminals and CI logs.\n */\nexport function reportCommandFailure(error: unknown): void {\n const message = redactConnectionPasswords(error instanceof Error ? error.message : String(error ?? \"\"));\n\n if ((error as { alreadyReported?: boolean } | null)?.alreadyReported) return;\n if (!message || /Command failed|exited with code/i.test(message)) return;\n\n outError(\"\");\n outError(chalk.red(` ✗ ${message}`));\n\n const rawCause = extractCauseMessage(error);\n const cause = rawCause ? redactConnectionPasswords(rawCause) : rawCause;\n if (cause && !message.includes(cause)) {\n outError(chalk.gray(` ${cause}`));\n }\n outError(\"\");\n}\n\n/* ------------------------------------------------------------------------- *\n * Atlas failures that have a remedy\n *\n * Atlas runs with a teed stderr, so what reaches these is its output as text,\n * not a `pg` error object carrying a `code`. They are deliberately NOT part of\n * `diagnoseDbError`: that one is shared with the boot path, and \"record a\n * baseline with `rebase db migrate`\" is advice for somebody standing at a\n * terminal, not for a container that has just failed to start.\n * ------------------------------------------------------------------------- */\n\n/** The table Atlas keeps its migration history in, in the `rebase` schema. */\nconst ATLAS_REVISIONS_TABLE = \"atlas_schema_revisions\";\n\n/**\n * `migrate apply` refusing because the database already has the schema.\n *\n * `42710` (duplicate_object), `42P07` (duplicate_table), `42701`\n * (duplicate_column) and `42P06` (duplicate_schema) are what a migration hits\n * when boot-ensure — or a `db push` — has already provisioned the objects it\n * was going to create. Since boot-ensure provisions *every* production\n * database, this is the normal case rather than the exotic one, and the raw\n * failure (`pq: type \"posts_status\" already exists (42710)`, then `sql/migrate:\n * write revision: … current transaction is aborted`) names no way forward.\n *\n * `42701` is the everyday one in development: `rebase dev` adds a new\n * column at boot, `db generate` writes the migration that adds it, and\n * `db migrate` then finds it there. A column is named `table.column`.\n */\nexport function parseAlreadyProvisioned(text: string): { object: string; code: string } | null {\n const column = /column \"([^\"]+)\" of relation \"([^\"]+)\" already exists/.exec(text);\n if (column && /42701/.test(text)) return { object: `${column[2]}.${column[1]}`, code: \"42701\" };\n\n const withCode = /(?:type|relation|constraint|schema) \"([^\"]+)\" already exists \\((42710|42P07|42P06)\\)/.exec(text);\n const bare = /(?:type|relation|constraint|schema) \"([^\"]+)\" already exists/.exec(text);\n // Atlas's own revisions table is not schema a migration creates: Atlas\n // failed to set up the history `--baseline` writes into, so a baseline\n // fails the same way.\n if ((withCode ?? bare)?.[1] === ATLAS_REVISIONS_TABLE) return null;\n if (withCode) return { object: withCode[1], code: withCode[2] };\n // Some Atlas builds keep the SQLSTATE on a following line instead.\n const code = /42P07|42P06|42710/.exec(text)?.[0];\n if (bare && code) return { object: bare[1], code };\n return null;\n}\n\n/**\n * The migration Atlas was running when it failed: `from version \"<v>\"` in\n * `sql/migrate: executing statement \"…\" from version \"20261013201353\": …`.\n */\nexport function parseFailedMigrationVersion(text: string): string | null {\n return /from version \"([^\"]+)\"/.exec(text)?.[1] ?? null;\n}\n\n/**\n * What to do about it: tell Atlas the database is already at a version.\n *\n * Atlas's own mechanism (`migrate apply --baseline <version>`), not a ledger of\n * ours — it writes the revision row Atlas reads, so every later `rebase db\n * migrate` is an ordinary one.\n */\nexport function formatBaselineRemedy(version: string | null, object?: string, failedVersion?: string | null): string {\n const versionToken = version ?? \"<version>\";\n // Named too when it is not the newest: marking the newest also skips the\n // migrations after the one that failed, which boot may not have covered\n // (a data migration, an index written by hand).\n const onlyTheFailedOne = failedVersion && failedVersion !== version\n ? ` To mark only the migration that failed, and run the ones after it:\\n` +\n `\\n` +\n ` ${chalk.bold.green(`rebase db migrate --baseline ${failedVersion}`)}\\n` +\n `\\n`\n : \"\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ This database already has the schema this migration creates\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n (object ? ` ${chalk.gray(`\"${object}\" is already there, so the migration cannot create it.`)}\\n\\n` : \"\") +\n ` Nothing is wrong with the migration — the database was provisioned\\n` +\n ` another way. Every Rebase boot ensures the schema, and ${chalk.bold(\"rebase db push\")}\\n` +\n ` applies it directly, so a database that has ever run either one is\\n` +\n ` ahead of the migrations recorded on it.\\n` +\n `\\n` +\n ` Record where it already is, then migrate normally:\\n` +\n `\\n` +\n ` ${chalk.bold.green(`rebase db migrate --baseline ${versionToken}`)}\\n` +\n ` ${chalk.bold.green(\"rebase db migrate\")}\\n` +\n `\\n` +\n ` The baseline is the version already applied — the numeric prefix of\\n` +\n ` the migration file describing what is in the database now. Migrations\\n` +\n ` after it run; it and everything before it are marked done.\\n` +\n `\\n` +\n onlyTheFailedOne +\n ` On a database nothing has ever booted against, none of this is needed.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Atlas's whole report when a connection was closed before it answered:\n * `Error: EOF`, sometimes with the step in front (`Error: postgres: querying\n * system variables: EOF`). The line names neither the database nor the\n * connection, so on its own it reads like a crash.\n */\nexport function parseConnectionEof(text: string): boolean {\n return /^Error: (?:.*: )?EOF\\s*$/m.test(text);\n}\n\n/**\n * What a closed connection means, and that running the command again is safe.\n *\n * On the managed development database it is almost always the connection\n * limit. PGlite is one session behind a socket server that accepts only a few\n * connections; a running `rebase dev` holds some, `atlas schema apply` opens\n * two, and the one over the limit is closed unanswered. Measured: with the\n * daemon's limit of four and three held, `db push --dry-run` failed with a\n * bare `Error: EOF`; with two held it passed. The dev server's pool lets an\n * idle connection go after 30 seconds (`idleTimeoutMillis` in connection.ts),\n * which is why a later attempt usually works.\n */\nexport function formatConnectionEofBanner(managed: boolean): string {\n const why = managed\n ? ` The managed development database accepts only a few connections at a\\n` +\n ` time, and a running ${chalk.bold(\"rebase dev\")} holds some of them. Atlas opened one more,\\n` +\n ` and it was closed before the database answered.\\n`\n : ` The server, or a proxy or pooler in front of it, closed the connection:\\n` +\n ` a restart, a connection limit, or a reaper for idle connections.\\n`;\n const next = managed\n ? ` Run the command again in a moment: it is safe to repeat, and the dev\\n` +\n ` server lets an idle connection go after 30 seconds. If it keeps failing,\\n` +\n ` stop ${chalk.bold(\"rebase dev\")} while you run it.\\n`\n : ` Run the command again: it is safe to repeat. If it keeps failing, check\\n` +\n ` the server's connection limit and that it is up: ${chalk.bold(\"rebase doctor\")}.\\n`;\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Atlas lost its connection to the database (EOF)\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n why +\n `\\n` +\n next +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/** `ALTER TABLE … SET NOT NULL` on a table that already holds rows (23502). */\nexport function parseNotNullViolation(text: string): { table: string; column: string } | null {\n const match = /column \"([^\"]+)\" of relation \"([^\"]+)\" contains null values/.exec(text);\n return match ? { table: match[2], column: match[1] } : null;\n}\n\n/**\n * Whether the column that refused NOT NULL was being added by this push, read\n * from the statement Atlas was running: `true` for `ADD COLUMN \"<column>\"`,\n * `false` for any other statement that names it (`ALTER COLUMN … SET NOT\n * NULL`), `null` when the error carried no statement.\n */\nexport function notNullColumnIsNew(stderr: string, column: string): boolean | null {\n const statement = parseFailedStatement(stderr);\n if (!statement) return null;\n const quoted = column.replace(/\"/g, \"\\\"\\\"\").replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(`ADD\\\\s+COLUMN\\\\s+(?:IF\\\\s+NOT\\\\s+EXISTS\\\\s+)?\"${quoted}\"`, \"i\").test(statement);\n}\n\n/**\n * `rebase`, spelled the way a project runs it from its own shell: a scaffold\n * keeps the CLI in `node_modules/.bin`, not on PATH, so it is `pnpm exec\n * rebase`, as `rebase start`'s hint and `rebase init`'s next steps spell it.\n */\nfunction projectRebaseCommand(manager: ProjectPackageManager): string {\n switch (manager) {\n case \"pnpm\": return \"pnpm exec rebase\";\n case \"npm\": return \"npx rebase\";\n case \"yarn\": return \"yarn rebase\";\n case \"bun\": return \"bunx rebase\";\n default: return \"rebase\";\n }\n}\n\n/**\n * The ways out, and only the ones that work for this column.\n *\n * A `defaultValue` backfills only a column the push is adding: PostgreSQL\n * fills `ADD COLUMN … NOT NULL DEFAULT …` for every existing row, but `ALTER\n * COLUMN … SET DEFAULT` touches no row that is already there, so for an\n * existing column that remedy fails exactly as before. When the error does not\n * say which it was (`isNew` null), the default is offered with that caveat.\n *\n * Boot-ensure handles this case — it adds the column nullable and sets NOT NULL\n * only when the table is empty — so a push that dies here is strictly worse\n * than the boot that would have run instead.\n */\nexport function formatNotNullViolationBanner(\n violation: { table: string; column: string },\n rowCount: number | null,\n isNew: boolean | null = null,\n manager: ProjectPackageManager = detectProjectPackageManager()\n): string {\n const { table, column } = violation;\n const rows = rowCount === null\n ? ` \"${table}\" already holds rows, and none of them has a value for \"${column}\".\\n`\n : ` \"${table}\" already holds ${rowCount} row${rowCount === 1 ? \"\" : \"s\"}, and they have no value for \"${column}\".\\n`;\n const backfill =\n `Backfill by hand first, then push again:\\n` +\n // `rebase db url`, not `$DATABASE_URL`: on the managed development\n // database `.env` leaves DATABASE_URL unset, and the shell has none.\n ` ${chalk.gray(`psql \"$(${projectRebaseCommand(manager)} db url)\" -c 'UPDATE \"${table}\" SET \"${column}\" = … WHERE \"${column}\" IS NULL;'`)}\\n`;\n const leaveOptional = `Leave it optional — take \\`validation.required\\` off the property.\\n`;\n const defaultLine = (caveat: string) =>\n `Give the property a default, so the push can backfill${caveat}:\\n` +\n ` ${chalk.gray(`{ type: \"string\", defaultValue: \"…\", validation: { required: true } }`)}\\n`;\n const remedies = isNew === false\n ? [backfill, leaveOptional]\n : isNew === true\n ? [defaultLine(\"\"), backfill, leaveOptional]\n : [backfill, defaultLine(\" (only if this push adds the column — a default never fills rows that already exist)\"), leaveOptional];\n const count = [\"\", \"One way\", \"Two ways\", \"Three ways\"][remedies.length];\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Cannot make \"${table}\".\"${column}\" required: existing rows are null\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n rows +\n ` PostgreSQL will not add the NOT NULL until every one of them does.\\n` +\n `\\n` +\n ` ${count} forward:\\n` +\n `\\n` +\n remedies.map((r, i) => ` ${i + 1}. ${r}`).join(\"\") +\n `\\n` +\n ` ${chalk.gray(\"Nothing in this push was applied: Atlas applies it as one transaction.\")}\\n` +\n ` ${chalk.gray(\"`rebase dev` would have kept the column nullable instead:\")}\\n` +\n ` ${chalk.gray(\"boot sets NOT NULL only when it is safe.\")}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/** Atlas refusing to remove a label from an enum — a renamed `enum` option id. */\nexport function parseEnumLabelDrop(text: string): { label: string; enumType: string } | null {\n const match = /dropping (?:enum )?value \"([^\"]+)\" from enum \"([^\"]+)\" is not supported/.exec(text);\n return match ? { label: match[1], enumType: match[2] } : null;\n}\n\n/**\n * Why the two paths disagree, and what retiring an option id actually costs.\n *\n * Boot-ensure adds enum labels and never removes one, so `rebase dev` accepts\n * the very edit that stops `db push` dead. PostgreSQL has no `ALTER TYPE …\n * DROP VALUE`: a label goes only by rewriting the type, which is a data\n * migration and not a schema push.\n */\nexport function formatEnumLabelDropBanner(drop: { label: string; enumType: string }): string {\n const { label, enumType } = drop;\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ \"${label}\" cannot be removed from the enum \"${enumType}\"\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` PostgreSQL has no ALTER TYPE … DROP VALUE. An option id can be added\\n` +\n ` to an enum but never taken out of one, so renaming an id reads as a\\n` +\n ` removal and a push cannot carry it.\\n` +\n `\\n` +\n ` ${chalk.yellow(\"Nothing was applied.\")} ${chalk.bold(\"rebase dev\")} accepts this same edit — boot adds\\n` +\n ` new labels and never removes one — so the two paths disagree by\\n` +\n ` design, and the old label would simply have stayed behind, unused.\\n` +\n `\\n` +\n ` To keep the id: put \"${label}\" back and change only its label text.\\n` +\n `\\n` +\n ` To retire it for real, it is a data migration:\\n` +\n `\\n` +\n ` 1. Add the new id alongside the old one, and push.\\n` +\n ` 2. Move the rows: ${chalk.gray(`UPDATE … SET … = '<new>' WHERE … = '${label}';`)}\\n` +\n ` 3. Rewrite the type, which is what actually drops the label:\\n` +\n ` ${chalk.gray(\"rebase db generate retire_option && rebase db migrate\")}\\n` +\n ` ${chalk.gray(\"(a migration can CREATE TYPE …, ALTER TABLE … TYPE … USING, DROP TYPE)\")}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * The statement Atlas was running when it failed, unescaped.\n *\n * Two spellings, one per phase: the apply quotes it\n * (`executing statement \"ALTER TABLE \\\"public\\\".\\\"people\\\" …;\": pq: …`), and\n * the dry-run's analysis does not\n * (`analyzing plan: executing statement: ALTER TABLE \"public\".\"people\" …;: pq: …`).\n */\nexport function parseFailedStatement(text: string): string | null {\n const quoted = /executing statement \"((?:[^\"\\\\]|\\\\.)*)\"/.exec(text);\n if (quoted) return quoted[1].replace(/\\\\(.)/g, \"$1\");\n const bare = /executing statement: ([\\s\\S]*?;): pq:/.exec(text);\n return bare ? bare[1] : null;\n}\n\n/** `ALTER TABLE \"public\".\"people\" …` → `{ schema: \"public\", table: \"people\" }`. */\nexport function failedStatementTable(statement: string): { schema?: string; table: string } | null {\n const ident = String.raw`(?:\"((?:[^\"]|\"\")+)\"|([A-Za-z_][A-Za-z0-9_$]*))`;\n const match = new RegExp(String.raw`^\\s*(?:ALTER\\s+TABLE|CREATE\\s+UNIQUE\\s+INDEX\\s+\\S+\\s+ON)\\s+(?:ONLY\\s+)?${ident}(?:\\.${ident})?`, \"i\")\n .exec(statement);\n if (!match) return null;\n const first = (match[1] ?? match[2]).replace(/\"\"/g, \"\\\"\");\n const second = match[3] ?? match[4];\n return second === undefined\n ? { table: first }\n : { schema: first, table: second.replace(/\"\"/g, \"\\\"\") };\n}\n\n/** A generated column standing in the way of a retype, as the catalogue has it. */\nexport interface GeneratedColumnBlocker {\n schema: string;\n table: string;\n /** The generated column. */\n column: string;\n /** The column it reads, which the statement retypes. */\n reads: string;\n}\n\n/**\n * PostgreSQL refusing to retype a column a `GENERATED ALWAYS AS … STORED`\n * expression reads (0A000). `db push` moves Rebase's own search columns out of\n * the way before the apply; this is what is left when it could not.\n */\nexport function parseGeneratedColumnRefusal(text: string): { statement: string | null } | null {\n if (!/cannot alter type of a column used by a generated column/.test(text)) return null;\n return { statement: parseFailedStatement(text) };\n}\n\n/**\n * Which generated columns read a column the failed statement retypes — read\n * from the catalogue, because PostgreSQL's DETAIL naming them does not survive\n * Atlas.\n */\nasync function generatedColumnsBlocking(\n databaseUrl: string | undefined,\n statement: string | null\n): Promise<GeneratedColumnBlocker[]> {\n if (!databaseUrl || !statement) return [];\n const target = failedStatementTable(statement);\n if (!target) return [];\n const retyped = [...statement.matchAll(/ALTER\\s+(?:COLUMN\\s+)?\"((?:[^\"]|\"\")+)\"\\s+(?:SET\\s+DATA\\s+)?TYPE\\b/gi)]\n .map(match => match[1].replace(/\"\"/g, \"\\\"\"));\n if (retyped.length === 0) return [];\n try {\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 5000 });\n await client.connect();\n try {\n const res = await client.query<GeneratedColumnBlocker>(`\n SELECT n.nspname AS \"schema\", c.relname AS \"table\", gen.attname AS \"column\", dep.attname AS \"reads\"\n FROM pg_attrdef ad\n JOIN pg_class c ON c.oid = ad.adrelid\n JOIN pg_namespace n ON n.oid = c.relnamespace\n JOIN pg_attribute gen ON gen.attrelid = ad.adrelid AND gen.attnum = ad.adnum\n JOIN pg_depend d ON d.classid = 'pg_attrdef'::regclass AND d.objid = ad.oid\n AND d.refclassid = 'pg_class'::regclass AND d.refobjid = ad.adrelid\n JOIN pg_attribute dep ON dep.attrelid = ad.adrelid AND dep.attnum = d.refobjsubid\n WHERE gen.attgenerated <> '' AND c.relname = $1\n AND ($2::text IS NULL OR n.nspname = $2) AND dep.attname = ANY($3::text[])\n ORDER BY 1, 2, 3, 4`, [target.table, target.schema ?? null, retyped]);\n return res.rows;\n } finally {\n await client.end();\n }\n } catch {\n return [];\n }\n}\n\n/**\n * What to do when a generated column stops a retype.\n *\n * A collection's own search column should never reach this — `db push` drops\n * it before the apply and rebuilds it after — so seeing one named here means\n * that machinery missed it, which is a bug worth a report. Any other generated\n * column is somebody's, with an expression Rebase holds no copy of.\n */\nexport function formatGeneratedColumnRefusalBanner(\n refusal: { statement: string | null },\n blockers: GeneratedColumnBlocker[]\n): string {\n const named = blockers.length > 0\n ? blockers.map(b => ` ${chalk.bold(`${b.schema}.${b.table}.${b.column}`)} reads \"${b.reads}\"`).join(\"\\n\") + \"\\n\\n\"\n : refusal.statement\n ? ` The statement:\\n ${chalk.gray(refusal.statement.replace(/\\s+/g, \" \"))}\\n\\n`\n : \"\";\n const first = blockers[0];\n const drop = first\n ? `ALTER TABLE \"${first.schema}\".\"${first.table}\" DROP COLUMN \"${first.column}\";`\n : \"ALTER TABLE … DROP COLUMN <the generated column>;\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ A generated column reads a column this push retypes\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n named +\n ` PostgreSQL will not retype a column while a GENERATED ALWAYS AS …\\n` +\n ` STORED expression reads it, and Atlas applies a push as one\\n` +\n ` transaction — ${chalk.yellow(\"nothing in this push was applied.\")}\\n` +\n `\\n` +\n ` A collection's ${chalk.bold(\"search\")} column is dropped and rebuilt around the apply\\n` +\n ` by the push itself. If that is the column named above, run the push\\n` +\n ` again, and please report it: it should not have got this far.\\n` +\n `\\n` +\n ` Any other generated column is yours. Drop it, push, and put it back\\n` +\n ` with its original expression:\\n` +\n `\\n` +\n ` ${chalk.gray(drop)}\\n` +\n ` ${chalk.gray(\"rebase db push\")}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * PostgreSQL refusing a retype it has no implicit cast for (42804): `string` →\n * `number`, `time` → `date`, an array back to a string. Atlas reports it from\n * the dry-run's analysis as well as from the apply, so without this a\n * `--dry-run` died with the raw error and showed no plan at all.\n */\nexport function parseUncastableRetype(text: string): { column: string; type: string; statement: string | null } | null {\n const match = /column \"((?:[^\"]|\"\")+)\" cannot be cast automatically to type ([^\\n(]+?)\\s*(?:\\(42804\\)|$|\\n)/m.exec(text);\n if (!match) return null;\n return { column: match[1], type: match[2].trim(), statement: parseFailedStatement(text) };\n}\n\n/** The `ALTER … USING` a developer would run, and the other ways past it. Boot's drift report says the same. */\nexport function formatUncastableRetypeBanner(refusal: { column: string; type: string; statement: string | null }): string {\n const { column, type, statement } = refusal;\n const target = statement ? failedStatementTable(statement) : null;\n const table = target ? `${target.schema ? `\"${target.schema}\".` : \"\"}\"${target.table}\"` : \"<table>\";\n const quotedColumn = `\"${column.replace(/\"/g, \"\\\"\\\"\")}\"`;\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ ${table}.${quotedColumn} cannot be converted to ${type} automatically\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` The collection now declares this column as ${type}, and PostgreSQL has\\n` +\n ` no implicit conversion from what the column holds today. Atlas stops\\n` +\n ` at the first such statement — ${chalk.yellow(\"nothing in this push was applied\")}, and a\\n` +\n ` --dry-run cannot show the rest of the plan until this one is settled.\\n` +\n `\\n` +\n ` Three ways forward:\\n` +\n `\\n` +\n ` 1. Convert the values yourself, saying how, then push again:\\n` +\n ` ${chalk.gray(`ALTER TABLE ${table} ALTER COLUMN ${quotedColumn} TYPE ${type} USING ${quotedColumn}::${type};`)}\\n` +\n ` ${chalk.gray(\"(adjust the USING expression to whatever turns each old value into a new one)\")}\\n` +\n ` 2. Add a new property for the new type, copy the values over with an\\n` +\n ` UPDATE, then remove the old property.\\n` +\n ` 3. Keep the column as it is — put the property's type back, or pin the\\n` +\n ` database type with ${chalk.bold(\"columnType\")}.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * A unique constraint or index PostgreSQL could not build because the rows\n * already repeat a value (23505). The DETAIL naming the value does not survive\n * Atlas, so the duplicates are read back from the table.\n */\nexport function parseUniqueBuildFailure(text: string): { index: string; statement: string | null; columns: string[] } | null {\n const match = /could not create unique index \"((?:[^\"]|\"\")+)\"/.exec(text);\n if (!match) return null;\n const statement = parseFailedStatement(text);\n let columns: string[] = [];\n if (statement) {\n const list = /UNIQUE\\s*\\(([^)]*)\\)/i.exec(statement)\n ?? /\\bON\\s+(?:ONLY\\s+)?(?:\"(?:[^\"]|\"\")+\"|[A-Za-z_][\\w$]*)(?:\\.(?:\"(?:[^\"]|\"\")+\"|[A-Za-z_][\\w$]*))?\\s*(?:USING\\s+\\w+\\s*)?\\(([^)]*)\\)/i.exec(statement);\n if (list) {\n columns = [...list[1].matchAll(/\"((?:[^\"]|\"\")+)\"|([A-Za-z_][\\w$]*)/g)]\n .map(m => (m[1] ?? m[2]).replace(/\"\"/g, \"\\\"\"));\n }\n }\n return { index: match[1], statement, columns };\n}\n\n/** Up to five repeated values, read from the database, or `[]` when we cannot say. */\nasync function duplicateValues(\n databaseUrl: string | undefined,\n target: { schema?: string; table: string } | null,\n columns: string[]\n): Promise<{ values: string; count: number }[]> {\n if (!databaseUrl || !target || columns.length === 0) return [];\n const ident = (name: string) => `\"${name.replace(/\"/g, \"\\\"\\\"\")}\"`;\n const table = `${target.schema ? `${ident(target.schema)}.` : \"\"}${ident(target.table)}`;\n const cols = columns.map(ident).join(\", \");\n try {\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 5000 });\n await client.connect();\n try {\n // Identifiers from the failed statement, quoted; no value is interpolated.\n const res = await client.query<{ values: string; count: number }>(\n `SELECT concat_ws(', ', ${columns.map(c => `${ident(c)}::text`).join(\", \")}) AS \"values\", count(*)::int AS \"count\"\n FROM ${table} GROUP BY ${cols} HAVING count(*) > 1 ORDER BY count(*) DESC LIMIT 5`);\n return res.rows;\n } finally {\n await client.end();\n }\n } catch {\n return [];\n }\n}\n\nexport function formatUniqueBuildFailureBanner(\n failure: { index: string; statement: string | null; columns: string[] },\n duplicates: { values: string; count: number }[]\n): string {\n const target = failure.statement ? failedStatementTable(failure.statement) : null;\n const table = target ? `${target.schema ? `\"${target.schema}\".` : \"\"}\"${target.table}\"` : \"<table>\";\n const cols = failure.columns.length > 0 ? failure.columns.map(c => `\"${c}\"`).join(\", \") : \"<column>\";\n const listed = duplicates.length > 0\n ? ` Repeated values:\\n` + duplicates.map(d => ` ${chalk.bold(d.values)} ${chalk.gray(`× ${d.count}`)}`).join(\"\\n\") + `\\n\\n`\n : \"\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Cannot make ${table} (${cols}) unique: rows already repeat a value\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n listed +\n ` PostgreSQL will not build \"${failure.index}\" while two rows share a\\n` +\n ` value, and Atlas applies a push as one transaction — ${chalk.yellow(\"nothing in\")}\\n` +\n ` ${chalk.yellow(\"this push was applied.\")}\\n` +\n `\\n` +\n ` Find them:\\n` +\n ` ${chalk.gray(`SELECT ${cols}, count(*) FROM ${table} GROUP BY ${cols} HAVING count(*) > 1;`)}\\n` +\n `\\n` +\n ` Then change or delete the repeats and push again, or take ${chalk.bold(\"unique\")} off\\n` +\n ` the property.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/** How many rows a table holds, or `null` when we cannot say. */\nasync function countRows(databaseUrl: string | undefined, table: string): Promise<number | null> {\n if (!databaseUrl) return null;\n try {\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 5000 });\n await client.connect();\n try {\n // Identifier interpolation, and it has to be: the name comes from\n // PostgreSQL's own error text, and `count(*)` takes no parameter in\n // that position. Quoted, with any embedded quote doubled.\n const res = await client.query(`SELECT count(*)::int AS n FROM \"${table.replace(/\"/g, \"\\\"\\\"\")}\"`);\n return (res.rows[0] as { n: number } | undefined)?.n ?? null;\n } finally {\n await client.end();\n }\n } catch {\n return null;\n }\n}\n\n/**\n * A hand-written generated column reading a column the plan wants to retype or\n * drop.\n *\n * Rebase's own generated columns are dropped and rebuilt around the apply — it\n * holds their definition in `search.sql`. This one it does not: PostgreSQL\n * stores a generated expression only in the catalogue, so dropping it to let\n * the apply through would destroy the only copy. Refuse instead, and say what\n * the operator can do about it.\n */\nexport function formatForeignGeneratedColumnBanner(\n conflicts: { schema: string; table: string; column: string; blocking: { column: string; kind: string }[] }[]\n): string {\n const lines = conflicts.map(c => {\n const reads = c.blocking.map(b => `\"${b.column}\"`).join(\", \");\n return ` ${chalk.bold(`${c.schema}.${c.table}.${c.column}`)} reads ${reads}`;\n });\n const first = conflicts[0];\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ A generated column depends on a column this push changes\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n lines.join(\"\\n\") + `\\n` +\n `\\n` +\n ` PostgreSQL refuses to retype or drop a column while a GENERATED\\n` +\n ` ALWAYS AS … STORED expression reads it, and an Atlas apply is one\\n` +\n ` transaction — so this refusal would roll back every other statement\\n` +\n ` in the push with it.\\n` +\n `\\n` +\n ` ${chalk.yellow(\"Nothing was applied.\")} Rebase rebuilds the generated columns it made\\n` +\n ` itself (a collection's ${chalk.bold(\"search\")} block) around the apply. It has no\\n` +\n ` definition for this one, and a dropped generated expression is not\\n` +\n ` recoverable from anywhere but your own migration.\\n` +\n `\\n` +\n ` Drop it, push, and put it back:\\n` +\n `\\n` +\n ` ${chalk.gray(`ALTER TABLE \"${first.schema}\".\"${first.table}\" DROP COLUMN \"${first.column}\";`)}\\n` +\n ` ${chalk.gray(\"rebase db push\")}\\n` +\n ` ${chalk.gray(\"-- then re-create the column with its original expression\")}\\n` +\n `\\n` +\n ` Or declare it through a ${chalk.bold(\"search\")} block, and Rebase will own it.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * The remedy for an Atlas invocation that failed, or `null`.\n *\n * Scoped by the invocation, because the same database state means different\n * things to different subcommands: \"already exists\" under `migrate apply` wants\n * a baseline, while under `schema apply` it is a genuine conflict.\n */\nexport async function diagnoseAtlasFailure(context: {\n domain: string;\n args: string[];\n stderr: string;\n databaseUrl?: string;\n /** The newest migration version on disk, named in the baseline remedy. */\n latestMigrationVersion?: string | null;\n /** DATABASE_URL is the managed development database (PGlite). */\n managedDatabase?: boolean;\n}): Promise<string | null> {\n const { domain, args, stderr, databaseUrl } = context;\n\n if (parseConnectionEof(stderr)) return formatConnectionEofBanner(context.managedDatabase === true);\n\n if (domain === \"migrate\" && args.includes(\"apply\")) {\n const provisioned = parseAlreadyProvisioned(stderr);\n if (provisioned) {\n return formatBaselineRemedy(\n context.latestMigrationVersion ?? null,\n provisioned.object,\n parseFailedMigrationVersion(stderr)\n );\n }\n }\n\n if (domain === \"schema\" && args.includes(\"apply\")) {\n const enumDrop = parseEnumLabelDrop(stderr);\n if (enumDrop) return formatEnumLabelDropBanner(enumDrop);\n\n const notNull = parseNotNullViolation(stderr);\n if (notNull) {\n return formatNotNullViolationBanner(notNull, await countRows(databaseUrl, notNull.table), notNullColumnIsNew(stderr, notNull.column));\n }\n\n const uncastable = parseUncastableRetype(stderr);\n if (uncastable) return formatUncastableRetypeBanner(uncastable);\n\n const unique = parseUniqueBuildFailure(stderr);\n if (unique) {\n const target = unique.statement ? failedStatementTable(unique.statement) : null;\n return formatUniqueBuildFailureBanner(unique, await duplicateValues(databaseUrl, target, unique.columns));\n }\n\n const generated = parseGeneratedColumnRefusal(stderr);\n if (generated) {\n return formatGeneratedColumnRefusalBanner(generated, await generatedColumnsBlocking(databaseUrl, generated.statement));\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli-errors-DwQnNATs.js","names":[],"sources":["../src/utils/error-cause.ts","../src/utils/connection-string.ts","../src/schema/destructive-sql.ts","../src/schema/undeclarable-constraints.ts","../src/module-dir.ts","../src/generated-sql.ts","../src/cli-helpers.ts","../src/cli-errors.ts"],"sourcesContent":["/**\n * Walk the error cause chain and return the deepest meaningful message.\n *\n * A leaf module, imported by the driver CLI: `pg-error-utils.ts`, where it used\n * to live, imports `@rebasepro/server`, and the CLI must start without it.\n */\nexport function extractCauseMessage(error: unknown): string | null {\n if (!error || typeof error !== \"object\") return null;\n if (!(error instanceof Error)) return null;\n\n if (error.cause && typeof error.cause === \"object\") {\n const deeper = extractCauseMessage(error.cause);\n if (deeper) return deeper;\n // The cause itself has a message\n if (error.cause instanceof Error && error.cause.message) {\n return error.cause.message;\n }\n }\n return null;\n}\n","/**\n * Connection-string rewrites for the tools Rebase shells out to.\n *\n * Pure and dependency-free: `pg-tools` builds argument vectors without a live\n * server, and these have to be usable from there.\n */\n\n/**\n * The `sslmode` values libpq accepts. `no-verify` is conspicuously not one.\n */\nconst LIBPQ_SSLMODES = new Set([\"disable\", \"allow\", \"prefer\", \"require\", \"verify-ca\", \"verify-full\"]);\n\n/**\n * Make a connection string safe to hand to a libpq program.\n *\n * `sslmode=no-verify` is a node-postgres convention — encrypt, but do not check\n * the certificate — and libpq does not have it. It does not degrade or warn:\n *\n * psql: error: invalid sslmode value: \"no-verify\"\n *\n * which means a `DATABASE_URL` that works perfectly for the app makes `psql`,\n * `pg_dump`, `pg_restore` and Atlas all refuse to start, with an error that\n * points at the value rather than at the convention it comes from. It has cost\n * this project time more than once.\n *\n * `require` is the honest translation: libpq's `require` encrypts and does not\n * verify the certificate either, which is exactly what `no-verify` asks for.\n * Nothing is relaxed by the rewrite — `verify-ca` and `verify-full` are left\n * alone, so a connection string that asked for verification still gets it.\n *\n * Only `sslmode` is touched, and only when it is a value libpq would reject.\n * Anything unparseable is returned as given: a connection that works\n * unverified beats one this corrupted.\n */\nexport function forLibpq(connectionString: string): string {\n let url: URL;\n try {\n url = new URL(connectionString);\n } catch {\n // Key/value DSNs and anything else we cannot parse. A `sslmode` in one\n // of those is space-separated rather than a query parameter, and\n // rewriting it with a regex is how a password containing \"sslmode=\"\n // gets mangled.\n return connectionString;\n }\n if (url.protocol !== \"postgres:\" && url.protocol !== \"postgresql:\") return connectionString;\n\n const sslmode = url.searchParams.get(\"sslmode\");\n if (sslmode === null || LIBPQ_SSLMODES.has(sslmode)) return connectionString;\n\n url.searchParams.set(\"sslmode\", \"require\");\n // Re-serialize by hand for the reason `pinSearchPath` does: URLSearchParams\n // writes a space as `+`, which node-postgres decodes and libpq does not.\n url.search = Array.from(url.searchParams.entries())\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join(\"&\");\n return url.toString();\n}\n\n/** Hosts that are this machine: `localhost`, `127.0.0.0/8`, `::1` and `0.0.0.0`. */\nconst isLoopbackHost = (hostname: string): boolean =>\n hostname === \"localhost\" || hostname === \"[::1]\" || hostname === \"0.0.0.0\" || /^127(\\.\\d{1,3}){3}$/.test(hostname);\n\n/**\n * A connection string for Atlas: {@link forLibpq}, and `sslmode=disable` for a\n * database on this machine whose URL says nothing about SSL.\n *\n * Atlas connects through lib/pq, whose default `sslmode` is `require`.\n * node-postgres — the app, and every step of these commands — asks for no SSL\n * when the URL names none. So a URL that served the app from a local Postgres\n * without SSL (the Docker image, a Homebrew install) failed every schema\n * command with `pq: SSL is not enabled on the server`. Given here, Atlas\n * connects the way the app already does.\n *\n * Nothing that asked for something is touched: a URL naming `sslmode`, `ssl`,\n * `host` or `hostaddr`, any other host, and anything that is not a postgres\n * URL are returned as {@link forLibpq} returns them.\n */\nexport function forAtlas(connectionString: string): string {\n const libpq = forLibpq(connectionString);\n let url: URL;\n try {\n url = new URL(libpq);\n } catch {\n return libpq;\n }\n if (url.protocol !== \"postgres:\" && url.protocol !== \"postgresql:\") return libpq;\n if (!isLoopbackHost(url.hostname)) return libpq;\n if ([\"sslmode\", \"ssl\", \"host\", \"hostaddr\"].some(key => url.searchParams.has(key))) return libpq;\n\n url.searchParams.set(\"sslmode\", \"disable\");\n // By hand, for the reason `forLibpq` re-serializes by hand.\n url.search = Array.from(url.searchParams.entries())\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join(\"&\");\n return url.toString();\n}\n\n/**\n * A connection string with its password taken out, and the password.\n *\n * For a libpq program: the URL goes on its command line, and the password goes\n * in `PGPASSWORD`. On the command line it was readable by anyone who can list\n * processes, and it came back verbatim in every failure, because execa's error\n * message repeats the command: `Command failed with exit code 1: pg_restore\n * '--dbname=postgresql://app:s3cret@…'`, in a terminal, a CI log and the\n * scheduled backup's run history.\n *\n * libpq takes the password from the URL's user info or a `password` parameter,\n * so both are removed. The value is percent-decoded, because `PGPASSWORD` is\n * read literally. Anything that is not a URL is returned as given, with no\n * password: a key/value DSN keeps its own.\n */\nexport function splitPassword(connectionString: string): { connectionString: string; password: string | null } {\n let url: URL;\n try {\n url = new URL(connectionString);\n } catch {\n return { connectionString, password: null };\n }\n if (url.protocol !== \"postgres:\" && url.protocol !== \"postgresql:\") return { connectionString, password: null };\n\n const fromUserInfo = url.password ? decodePercent(url.password) : null;\n const fromParameter = url.searchParams.get(\"password\");\n if (fromUserInfo === null && fromParameter === null) return { connectionString, password: null };\n\n url.password = \"\";\n if (fromParameter !== null) {\n url.searchParams.delete(\"password\");\n // By hand, as `forLibpq` does: URLSearchParams writes a space as `+`.\n url.search = Array.from(url.searchParams.entries())\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join(\"&\");\n }\n // libpq reads the user info first, as the URL's own password.\n return { connectionString: url.toString(), password: fromUserInfo ?? fromParameter };\n}\n\nfunction decodePercent(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\n/**\n * Every password a line of text carries in a connection string, replaced with\n * `***`.\n *\n * For whatever is about to be printed or logged and may have a connection\n * string in it: an error a client tool or a driver raised, a message that\n * quotes a command line. Covers the URL user-info form (`scheme://user:pass@`,\n * any scheme, a password with a raw `@` in it included) and `password=` in a\n * query string or a key/value DSN. The user, host and database stay, because\n * they are what a reader needs to tell which database it was.\n */\nexport function redactConnectionPasswords(text: string): string {\n return text\n .replace(/(\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s/?#@'\"]*):[^\\s/?#'\"]*@/gi, \"$1:***@\")\n .replace(/(\\bpassword\\s*=\\s*)('[^']*'|[^\\s&'\"]+)/gi, \"$1***\");\n}\n","/**\n * Pure helpers that classify an Atlas declarative-apply plan as destructive\n * or not, and decide what `rebase db push` should do about it.\n *\n * `db push` runs `atlas schema apply` to make the live database match the\n * generated `schema.sql`. Removing a collection field compiles to\n * `DROP COLUMN`; renaming compiles to drop-then-add — either destroys data.\n * We first run the apply with `--dry-run` to obtain the planned SQL, scan it\n * here, and refuse to auto-approve anything destructive without an explicit\n * opt-in.\n *\n * A type change is a destructive plan that names no DROP. `ALTER COLUMN\n * \"price\" TYPE integer` rounds every 19.99 to 20, and `TYPE date` throws away\n * every timestamp's time of day, in a statement that reads like a routine\n * alter. So a type change is gated too, unless it is a widening every value\n * survives unchanged — which needs the column's current type, read from the\n * catalogue by the caller.\n *\n * A primary key change names no DROP of data either: `DROP CONSTRAINT\n * \"<table>_pkey\", ADD PRIMARY KEY (\"id\", \"locale\")` keeps every value and\n * gives every row a new address. It is gated like a drop.\n *\n * So is the drop of a CHECK or EXCLUDE constraint. It keeps every row and\n * stops refusing the ones the rule was there to refuse. `db push` keeps Atlas\n * away from those constraints (`schema/undeclarable-constraints.ts`), so a plan\n * that still drops one is a pattern that missed, and the gate is the second\n * line. Which constraints those are is read from the catalogue by the caller.\n *\n * Everything in this file is side-effect free so it can be unit-tested\n * without Atlas or a database.\n */\n\nimport type { ExistingConstraint } from \"./undeclarable-constraints\";\n\n/**\n * SQL fragments that destroy data or data-bearing objects. Matched\n * case-insensitively against each statement of the plan. `IF EXISTS` /\n * whitespace variations are tolerated by the regexes below.\n */\nconst DESTRUCTIVE_PATTERNS: { label: string; re: RegExp }[] = [\n { label: \"DROP TABLE\", re: /\\bDROP\\s+TABLE\\b/i },\n { label: \"DROP COLUMN\", re: /\\bDROP\\s+COLUMN\\b/i },\n { label: \"DROP SCHEMA\", re: /\\bDROP\\s+SCHEMA\\b/i },\n { label: \"DROP VIEW\", re: /\\bDROP\\s+(MATERIALIZED\\s+)?VIEW\\b/i },\n { label: \"DROP TYPE\", re: /\\bDROP\\s+TYPE\\b/i },\n { label: \"TRUNCATE\", re: /\\bTRUNCATE\\b/i }\n];\n\n/**\n * Split a SQL script into individual statements, dropping blank lines and\n * `--` comment lines. Deliberately simple: Atlas emits one plain statement\n * per `;`, without string literals that contain semicolons in a schema DDL\n * plan, so a naive split is safe and keeps this dependency-free.\n */\nexport function splitSqlStatements(sql: string): string[] {\n // Strip full-line SQL comments so a commented-out DROP never trips the\n // detector, then split on semicolons.\n const withoutComments = sql\n .split(\"\\n\")\n .filter((line) => !line.trim().startsWith(\"--\"))\n .join(\"\\n\");\n return withoutComments\n .split(\";\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n}\n\n/** `->`, the marker Atlas prints in front of the first line of every statement. */\nconst PLAN_MARKER_RE = /^\\s*->\\s?/;\n/** `-------`, both the rule between sections and the one above a summary. */\nconst PLAN_RULE_RE = /^\\s*-{5,}\\s*$/;\n/** `Planning migration statements (3 in total):` — and the count it announces. */\nconst PLAN_HEADING_RE = /^Planning migration statements \\((\\d+) in total\\):/m;\n/**\n * What Atlas prints, and nothing else, when there is nothing to plan — from\n * `schema apply`, or from `migrate apply` with no file pending.\n */\nconst PLAN_SYNCED_RE = /^\\s*(?:Schema is synced, no changes to be made|No migration files to execute)\\s*$/;\n\n/** What {@link readPlan} could make of a plan. */\ninterface ReadPlan {\n statements: string[];\n /** The `(N in total)` Atlas announced, when its report said one. */\n announced?: number;\n}\n\n/**\n * Read the statements out of whatever Atlas printed.\n *\n * `atlas schema apply --dry-run` does not print SQL. It prints a report, and\n * the same statements appear in it twice:\n *\n * Planning migration statements (2 in total):\n *\n * -- modify \"people\" table:\n * -> ALTER TABLE \"public\".\"people\" ALTER COLUMN \"rating\" TYPE numeric(4,1);\n * -- create \"notes\" table:\n * -> CREATE TABLE \"public\".\"notes\" (\n * \"body\" text NULL DEFAULT 'a;b',\n * …\n * );\n *\n * -------------------------------------------\n *\n * Analyzing planned statements (2 in total):\n * …\n * Running dry-run migration (2 statements in total):\n * -- modify \"people\" table\n * -> ALTER TABLE …; ← the same statement, again\n * -- ok (19µs)\n *\n * Splitting that on `;` is how the push gate went blind: the heading has no\n * `;` and does not start with `--`, so it became the first line of the first\n * statement, and an `ALTER TABLE` parse anchored at the start of a statement\n * read nothing — every narrowing type change applied without a prompt. Reading\n * both sections was a second bug, counting every destructive statement twice.\n *\n * So the report is read as a report: a statement is a `->` line and the lines\n * indented under it, up to the next `--` heading, `->`, blank line or rule;\n * only the first section that holds a `->` is read; and `;` plays no part,\n * because a default like `'a;b'` contains one.\n *\n * Plain SQL — a migration file, or an Atlas that printed SQL — has no `->`\n * and is split the old way.\n */\nfunction readPlan(plan: string): ReadPlan {\n if (PLAN_SYNCED_RE.test(plan)) return { statements: [] };\n const lines = plan.split(/\\r?\\n/);\n if (!lines.some(line => PLAN_MARKER_RE.test(line))) return { statements: splitSqlStatements(plan) };\n\n const heading = PLAN_HEADING_RE.exec(plan);\n const statements: string[] = [];\n let current: string[] | null = null;\n const flush = () => {\n if (current) statements.push(current.join(\"\\n\").trim().replace(/;\\s*$/, \"\").trimEnd());\n current = null;\n };\n\n for (const line of lines) {\n if (PLAN_MARKER_RE.test(line)) {\n flush();\n current = [line.replace(PLAN_MARKER_RE, \"\")];\n continue;\n }\n // A section heading sits in column 0 and is neither a marker nor a\n // `--` line; the rule between sections is dashes. Either one after a\n // statement has been read means the plan is over and what follows is\n // Atlas re-printing it.\n const sectionEnds = PLAN_RULE_RE.test(line) || (/^\\S/.test(line) && !line.startsWith(\"--\"));\n if (sectionEnds) {\n flush();\n if (statements.length > 0) break;\n continue;\n }\n if (!current) continue;\n if (line.trim() === \"\" || line.trim().startsWith(\"--\")) {\n flush();\n continue;\n }\n current.push(line);\n }\n flush();\n\n return { statements, announced: heading ? Number(heading[1]) : undefined };\n}\n\n/**\n * The statements of an Atlas plan, each exactly once, without Atlas's\n * rendering and without the trailing `;`. See {@link readPlan} for the shape\n * it reads; `test/fixtures/atlas-dry-run/` holds real captures of it.\n */\nexport function extractPlanStatements(plan: string): string[] {\n return readPlan(plan).statements;\n}\n\n/**\n * Whether Atlas answered the dry-run with \"Schema is synced\" and nothing else.\n *\n * Positive evidence only: an empty or unreadable report also yields no\n * statements, and is not the same answer.\n */\nexport function isSyncedPlan(plan: string): boolean {\n return PLAN_SYNCED_RE.test(plan);\n}\n\nexport interface DestructiveStatement {\n /** The offending statement (trimmed, without the trailing `;`). */\n statement: string;\n /** Which destructive operation it was flagged for, e.g. \"DROP COLUMN\". */\n kind: string;\n /**\n * What in the statement loses data, when the kind alone does not say:\n * for `ALTER COLUMN TYPE`, each lossy column with its old and new type.\n */\n detail?: string;\n}\n\n/** A column as the database has it now, with its type as `format_type` spells it. */\nexport interface ExistingColumnType {\n schema: string;\n table: string;\n column: string;\n /** e.g. `character varying(255)`, `numeric(10,2)`, `timestamp with time zone`. */\n type: string;\n}\n\n/**\n * Every column of every ordinary table outside the system schemas, with its\n * type. What {@link detectDestructiveStatements} compares a planned type\n * change against; `format_type` because it carries the modifier (`(255)`,\n * `(10,2)`) that decides whether a change widens or narrows.\n */\nexport const COLUMN_TYPES_SQL = `\n SELECT n.nspname AS \"schema\",\n c.relname AS \"table\",\n a.attname AS \"column\",\n format_type(a.atttypid, a.atttypmod) AS \"type\"\n FROM pg_attribute a\n JOIN pg_class c ON c.oid = a.attrelid\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind IN ('r', 'p')\n AND a.attnum > 0\n AND NOT a.attisdropped\n AND n.nspname NOT IN ('pg_catalog', 'information_schema')\n AND n.nspname NOT LIKE 'pg\\\\_%'\n`;\n\n/**\n * Scan an Atlas plan (the SQL printed by `schema apply --dry-run`) and return\n * the statements that would destroy data. An empty array means the plan is\n * safe to auto-approve.\n *\n * `columnTypes` is what the database holds now. A type change to a column it\n * does not list is flagged: not knowing what a column is means not knowing\n * whether the change keeps its values.\n *\n * `constraints` are the CHECK and EXCLUDE constraints the database holds now.\n * A `DROP CONSTRAINT` of one of them is flagged; of any other constraint (a\n * foreign key, a `UNIQUE`) it is not, as before.\n */\nexport function detectDestructiveStatements(\n planSql: string,\n columnTypes: readonly ExistingColumnType[] = [],\n constraints: readonly ExistingConstraint[] = []\n): DestructiveStatement[] {\n const found: DestructiveStatement[] = [];\n const { statements, announced } = readPlan(planSql);\n // Fail closed on a plan this could not read. Atlas says how many statements\n // it planned; reading a different number means its rendering changed under\n // us, and a gate that has not read the plan cannot call it safe. Reported\n // as one more destructive finding, so it takes the same road: a prompt on\n // a terminal, a refusal elsewhere, `--allow-destructive` to go ahead.\n if (announced !== undefined && announced !== statements.length) {\n found.push({\n statement: `Planning migration statements (${announced} in total)`,\n kind: \"UNREADABLE PLAN\",\n detail: `Atlas announced ${announced} statement(s) and ${statements.length} could be read — `\n + \"read the plan yourself before allowing it\"\n });\n }\n for (const statement of statements) {\n // One finding per statement, under the label that says the most, with\n // everything else it does in the detail: Atlas folds a key change, a\n // retype and a drop into one `ALTER TABLE`, and a reader shown only\n // the first would approve the rest unread.\n const dropped = DESTRUCTIVE_PATTERNS.find(({ re }) => re.test(statement));\n const rekeyed = describePrimaryKeyChange(statement);\n const lossy = parseColumnTypeChanges(statement)\n .map(change => ({ change, from: currentTypeOf(change, columnTypes) }))\n .filter(({ change, from }) => from === undefined || !isLosslessTypeChange(from, change.to))\n .map(({ change, from }) => `\"${change.column}\" ${from ?? \"(current type unknown)\"} → ${change.to}`)\n .join(\"; \");\n const unenforced = describeConstraintDrops(statement, constraints);\n const kind = dropped?.label\n ?? (rekeyed ? \"PRIMARY KEY CHANGE\" : lossy ? \"ALTER COLUMN TYPE\" : unenforced ? \"DROP CONSTRAINT\" : undefined);\n if (!kind) continue;\n const detail = [rekeyed, lossy, unenforced].filter(Boolean).join(\"; \");\n found.push(detail ? { statement, kind, detail } : { statement, kind });\n }\n return found;\n}\n\n/** A column an `ALTER TABLE` retypes, with the type the plan gives it. */\ninterface ColumnTypeChange {\n /** Absent when the plan did not qualify the table. */\n schema?: string;\n table: string;\n column: string;\n /** As the plan writes it, without any `USING` or `COLLATE`. */\n to: string;\n}\n\nconst IDENT = String.raw`(?:\"(?:[^\"]|\"\")+\"|[A-Za-z_][A-Za-z0-9_$]*)`;\nconst ALTER_TABLE_RE = new RegExp(\n String.raw`^ALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?(?:ONLY\\s+)?(${IDENT})(?:\\.(${IDENT}))?\\s+([\\s\\S]*)$`, \"i\"\n);\nconst TYPE_CLAUSE_RE = new RegExp(\n String.raw`^ALTER\\s+(?:COLUMN\\s+)?(${IDENT})\\s+(?:SET\\s+DATA\\s+)?TYPE\\s+([\\s\\S]+)$`, \"i\"\n);\n\nconst unquote = (ident: string): string =>\n ident.startsWith(\"\\\"\") ? ident.slice(1, -1).replace(/\"\"/g, \"\\\"\") : ident;\n\n/**\n * Split an `ALTER TABLE`'s clause list on its top-level commas — the ones\n * outside parentheses and quotes, so `TYPE numeric(10, 2)` stays one clause.\n */\nfunction splitClauses(body: string): string[] {\n const clauses: string[] = [];\n let depth = 0;\n let quote: string | undefined;\n let start = 0;\n for (let i = 0; i < body.length; i++) {\n const ch = body[i];\n if (quote) {\n if (ch === quote) quote = undefined;\n } else if (ch === \"\\\"\" || ch === \"'\") {\n quote = ch;\n } else if (ch === \"(\") {\n depth++;\n } else if (ch === \")\") {\n depth--;\n } else if (ch === \",\" && depth === 0) {\n clauses.push(body.slice(start, i).trim());\n start = i + 1;\n }\n }\n clauses.push(body.slice(start).trim());\n return clauses.filter(clause => clause.length > 0);\n}\n\n/**\n * The statement from its `ALTER TABLE` on, wherever on a line that starts.\n *\n * {@link readPlan} hands over clean statements. This is the second line of\n * defence for text it could not clean — a rendering it does not know yet —\n * where a line of prose above the statement must not hide the statement, which\n * is exactly how an anchored match once read Atlas's whole plan as safe.\n */\nexport function fromAlterTable(statement: string): string {\n const start = /(?:^|\\n)[ \\t]*(?:->[ \\t]*)?(ALTER\\s+TABLE\\b)/i.exec(statement);\n return start ? statement.slice(start.index + start[0].length - start[1].length).trim() : statement.trim();\n}\n\n/**\n * Every `ALTER COLUMN … [SET DATA] TYPE` in one statement. Atlas prints its\n * dry-run statements under a `-> ` marker and puts several clauses in one\n * `ALTER TABLE`, so both are handled.\n */\nfunction parseColumnTypeChanges(statement: string): ColumnTypeChange[] {\n const table = ALTER_TABLE_RE.exec(fromAlterTable(statement));\n if (!table) return [];\n const [schema, name] = table[2]\n ? [unquote(table[1]), unquote(table[2])]\n : [undefined, unquote(table[1])];\n const changes: ColumnTypeChange[] = [];\n for (const clause of splitClauses(table[3])) {\n const match = TYPE_CLAUSE_RE.exec(clause);\n if (!match) continue;\n const to = match[2].split(/\\s+(?:USING|COLLATE)\\s/i)[0].trim();\n changes.push({ schema, table: name, column: unquote(match[1]), to });\n }\n return changes;\n}\n\n/** `ADD [CONSTRAINT \"x\"] PRIMARY KEY (\"a\", \"b\")`, one clause of an `ALTER TABLE`. */\nconst ADD_PRIMARY_KEY_RE = new RegExp(\n String.raw`^ADD\\s+(?:CONSTRAINT\\s+${IDENT}\\s+)?PRIMARY\\s+KEY\\b\\s*(?:\\(([^)]*)\\))?`, \"i\"\n);\n/** `DROP CONSTRAINT [IF EXISTS] \"x\"`, one clause of an `ALTER TABLE`. */\nconst DROP_CONSTRAINT_RE = new RegExp(\n String.raw`^DROP\\s+CONSTRAINT\\s+(?:IF\\s+EXISTS\\s+)?(${IDENT})`, \"i\"\n);\n\n/** What a row loses when the columns that address it change. */\nconst ROW_ADDRESS_LOST = \"every row's id changes, so anything that refers to a row by its id — a URL, \" +\n \"a foreign key, an id stored elsewhere — stops finding it\";\n\n/**\n * What an `ALTER TABLE` does to the table's primary key, in a sentence — or\n * `undefined` when it leaves the key alone.\n *\n * The key is a row's address: Rebase reads, links and routes every row by it.\n * Atlas plans a key change as `DROP CONSTRAINT \"<table>_pkey\", ADD PRIMARY KEY\n * (…)` — one routine-looking statement that names no DROP of data — so moving\n * a collection from `(id)` to `(id, locale)` re-keyed a populated table with\n * no prompt. A `CREATE TABLE`'s own `PRIMARY KEY` is not a change and is never\n * read here: only an `ALTER TABLE`'s clauses are. A dropped constraint counts\n * as the key when it carries Postgres's default name for one, `<table>_pkey`;\n * a key under another name is caught by the `ADD PRIMARY KEY` that replaces it.\n */\nfunction describePrimaryKeyChange(statement: string): string | undefined {\n const alter = ALTER_TABLE_RE.exec(fromAlterTable(statement));\n if (!alter) return undefined;\n const table = alter[2] ? `${alter[1]}.${alter[2]}` : alter[1];\n\n let added: string | undefined;\n let dropped: string | undefined;\n for (const clause of splitClauses(alter[3])) {\n const add = ADD_PRIMARY_KEY_RE.exec(clause);\n if (add) {\n added = add[1] === undefined ? \"a new key\" : `(${add[1].split(\",\").map(c => c.trim()).join(\", \")})`;\n continue;\n }\n const drop = DROP_CONSTRAINT_RE.exec(clause);\n if (drop && unquote(drop[1]).endsWith(\"_pkey\")) dropped = drop[1];\n }\n\n if (added && dropped) return `${table} is re-keyed on ${added}: ${ROW_ADDRESS_LOST}`;\n if (added) {\n return `${table} gets the primary key ${added}: rows are addressed by it from now on, ` +\n \"and the push fails if two rows share a value\";\n }\n if (dropped) return `${table} loses its primary key ${dropped}: ${ROW_ADDRESS_LOST}`;\n return undefined;\n}\n\n/**\n * The CHECK and EXCLUDE constraints an `ALTER TABLE` drops, in a sentence — or\n * `undefined` when it drops none.\n *\n * Matched by name on the statement's table, in its schema when the plan names\n * one (Atlas always does). A constraint the catalogue does not list as a CHECK\n * or an EXCLUDE — a foreign key, a `UNIQUE`, the primary key — is not this.\n */\nfunction describeConstraintDrops(statement: string, constraints: readonly ExistingConstraint[]): string | undefined {\n if (constraints.length === 0) return undefined;\n const alter = ALTER_TABLE_RE.exec(fromAlterTable(statement));\n if (!alter) return undefined;\n const [schema, table] = alter[2] ? [unquote(alter[1]), unquote(alter[2])] : [undefined, unquote(alter[1])];\n\n const lost: string[] = [];\n for (const clause of splitClauses(alter[3])) {\n const drop = DROP_CONSTRAINT_RE.exec(clause);\n if (!drop) continue;\n const name = unquote(drop[1]);\n const constraint = constraints.find(c => c.table === table && c.name === name\n && (schema === undefined || c.schema === schema));\n if (constraint) lost.push(`${constraint.kind === \"check\" ? \"CHECK\" : \"EXCLUDE\"} \"${name}\"`);\n }\n if (lost.length === 0) return undefined;\n const subject = schema ? `${schema}.${table}` : table;\n return `${subject} loses ${lost.join(\", \")}: no row is lost, and the database accepts every row ` +\n `${lost.length === 1 ? \"it refuses\" : \"they refuse\"} today`;\n}\n\n/**\n * The column's current type. A table named without its schema matches in any\n * schema, but only when every match agrees — two same-named tables with\n * different types leave the answer unknown.\n */\nfunction currentTypeOf(change: ColumnTypeChange, columnTypes: readonly ExistingColumnType[]): string | undefined {\n const types = new Set(columnTypes\n .filter(col => col.table === change.table && col.column === change.column\n && (change.schema === undefined || col.schema === change.schema))\n .map(col => col.type));\n return types.size === 1 ? [...types][0] : undefined;\n}\n\n/** Spellings of one type, mapped to one name. Anything absent compares verbatim. */\nconst TYPE_ALIASES: Record<string, string> = {\n \"int\": \"integer\",\n \"int4\": \"integer\",\n \"int2\": \"smallint\",\n \"int8\": \"bigint\",\n \"character varying\": \"varchar\",\n \"decimal\": \"numeric\",\n \"timestamp without time zone\": \"timestamp\",\n \"timestamp with time zone\": \"timestamptz\",\n \"time without time zone\": \"time\",\n \"time with time zone\": \"timetz\",\n \"bool\": \"boolean\",\n \"float4\": \"real\",\n \"float8\": \"double precision\",\n \"character\": \"bpchar\",\n \"char\": \"bpchar\"\n};\n\n/** Decimal digits each integer type holds, which is what a `numeric` must hold too. */\nconst INTEGER_DIGITS: Record<string, number> = { smallint: 5, integer: 10, bigint: 19 };\n\n/** Types whose modifier is a fractional-seconds precision, 6 when absent. */\nconst SECONDS_PRECISION = new Set([\"timestamp\", \"timestamptz\", \"time\", \"timetz\"]);\n\ninterface ParsedType {\n base: string;\n /** The modifier, e.g. `[10, 2]` for `numeric(10,2)`. */\n args?: number[];\n /** Array dimensions. */\n dims: number;\n}\n\nfunction parseType(raw: string): ParsedType {\n let text = raw.trim().replace(/\\s+/g, \" \");\n let dims = 0;\n for (let suffix = /\\s*\\[\\d*\\]$/.exec(text); suffix; suffix = /\\s*\\[\\d*\\]$/.exec(text)) {\n dims++;\n text = text.slice(0, suffix.index);\n }\n // A quoted name is a user-defined type, compared exactly as written.\n if (text.includes(\"\\\"\")) return { base: text, dims };\n text = text.toLowerCase().replace(/^pg_catalog\\./, \"\");\n let args: number[] | undefined;\n const modifier = /\\(([^)]*)\\)/.exec(text);\n if (modifier) {\n args = modifier[1].split(\",\").map(part => Number(part.trim()));\n text = `${text.slice(0, modifier.index)} ${text.slice(modifier.index + modifier[0].length)}`\n .replace(/\\s+/g, \" \")\n .trim();\n }\n return { base: TYPE_ALIASES[text] ?? text, args, dims };\n}\n\n/** Does moving from one modifier to another, on the same type, keep every value? */\nfunction modifierWidens(base: string, from: number[] | undefined, to: number[] | undefined): boolean {\n if (JSON.stringify(from) === JSON.stringify(to)) return true;\n if (base === \"varchar\") {\n return to === undefined || (from !== undefined && to[0] >= from[0]);\n }\n if (base === \"numeric\") {\n if (to === undefined) return true;\n if (from === undefined) return false;\n const [p1, s1 = 0] = from;\n const [p2, s2 = 0] = to;\n return s2 >= s1 && p2 - s2 >= p1 - s1;\n }\n if (SECONDS_PRECISION.has(base)) {\n return (to?.[0] ?? 6) >= (from?.[0] ?? 6);\n }\n return false;\n}\n\n/**\n * Is `ALTER COLUMN … TYPE to` a widening, one that keeps every value exactly\n * as it was?\n *\n * Deliberately short. Everything not listed needs a confirmation, including\n * changes that are usually harmless: `timestamp` → `timestamptz` reads each\n * value in the session's time zone, `json` → `jsonb` drops duplicate keys,\n * `real` → `double precision` prints 0.1 as 0.100000001490116, and `integer` →\n * `text` keeps the digits but changes what the column is.\n *\n * - the same type, with a modifier at least as wide (`varchar(100)` →\n * `varchar(255)`, `numeric(10,2)` → `numeric(12,2)` or unbounded,\n * `timestamptz(3)` → `timestamptz`);\n * - `smallint` → `integer` → `bigint`, and any of them to a `numeric` with\n * room for all their digits;\n * - `varchar(n)` → `text`, and `text` → an unbounded `varchar`.\n *\n * Arrays follow their element type, and never gain or lose a dimension.\n */\nexport function isLosslessTypeChange(from: string, to: string): boolean {\n const a = parseType(from);\n const b = parseType(to);\n if (a.dims !== b.dims) return false;\n if (a.args?.some(Number.isNaN) || b.args?.some(Number.isNaN)) return false;\n if (a.base === b.base) return modifierWidens(a.base, a.args, b.args);\n\n const fromDigits = INTEGER_DIGITS[a.base];\n if (fromDigits !== undefined) {\n const toDigits = INTEGER_DIGITS[b.base];\n if (toDigits !== undefined) return toDigits >= fromDigits;\n if (b.base === \"numeric\") {\n return b.args === undefined || b.args[0] - (b.args[1] ?? 0) >= fromDigits;\n }\n return false;\n }\n if (a.base === \"varchar\" && b.base === \"text\") return true;\n if (a.base === \"text\" && b.base === \"varchar\") return b.args === undefined;\n return false;\n}\n\nexport type PushDecision = \"apply\" | \"confirm\" | \"refuse\";\n\n/**\n * The flag that opted a push into destructive changes, as it was given —\n * `--allow-destructive`, or its alias `--yes` / `-y` — or `undefined` for\n * none. The line that says why a destructive push went ahead names the one\n * the user typed: \"because --allow-destructive was passed\" to someone who\n * typed `--yes` reads like a flag they never set.\n */\nexport function destructiveOptInFlag(args: { \"--allow-destructive\"?: boolean; \"--yes\"?: boolean }): \"--allow-destructive\" | \"--yes\" | undefined {\n if (args[\"--allow-destructive\"] === true) return \"--allow-destructive\";\n if (args[\"--yes\"] === true) return \"--yes\";\n return undefined;\n}\n\n/**\n * Decide how `db push` should proceed given the plan's destructiveness and\n * the invocation context.\n *\n * - No destructive statements → `apply` (safe to auto-approve).\n * - Destructive + `--allow-destructive` → `apply` (operator opted in).\n * - Destructive + interactive TTY → `confirm` (prompt before applying).\n * - Destructive + non-interactive → `refuse` (never silently drop data in\n * CI / scripts / agents).\n */\nexport function decidePushSafety(opts: {\n destructiveCount: number;\n allowDestructive: boolean;\n interactive: boolean;\n}): PushDecision {\n if (opts.destructiveCount === 0) return \"apply\";\n if (opts.allowDestructive) return \"apply\";\n return opts.interactive ? \"confirm\" : \"refuse\";\n}\n","/**\n * The constraints a collection cannot declare, and how `db push` keeps Atlas\n * from dropping them.\n *\n * `db push` is declarative: Atlas makes each managed table match `schema.sql`,\n * and anything on the table that `schema.sql` does not mention is planned as a\n * drop. The collection model declares a table's primary key, its columns'\n * `NOT NULL`, `UNIQUE` and foreign keys, and nothing else. So a hand-written\n * `CHECK (total >= 0)` — or one an adopted database arrived with, which\n * `schema introspect` reads as `validation.min: 0` — was dropped by the first\n * push, in the same `ALTER TABLE` as whatever else the push changed, with no\n * prompt. From then on the database accepted every row the constraint used to\n * refuse, while the form still enforced the rule and so hid that it had gone.\n *\n * EXCLUDE constraints are the same case: Atlas inspects them and plans their\n * drop the same way. Triggers and functions are not: Atlas's free tier does\n * not inspect either, so it never plans a drop for one. Measured on the pinned\n * atlas 1.2.3 against PG 18; `introspect-check-constraints-e2e` pins all four.\n *\n * Ownership, as with indexes (`getForeignIndexExcludes` in `cli-helpers.ts`)\n * and policies: a constraint the desired state declares is\n * Atlas's to manage, and every other CHECK or EXCLUDE is somebody else's and is\n * kept out of the plan with `--exclude`. Today the desired state declares\n * neither kind, so every one is kept. A rule nobody wants any more is dropped\n * by hand (`ALTER TABLE … DROP CONSTRAINT …`), which is the only statement that\n * can say so.\n *\n * Side-effect free, so it is unit-tested without Atlas or a database.\n */\n\nimport { atlasExcludePattern } from \"./atlas-exclude\";\n\n/** A constraint kind the collection model has no way to declare. */\nexport type UndeclarableConstraintKind = \"check\" | \"exclude\";\n\n/** A CHECK or EXCLUDE constraint, as the database holds it now. */\nexport interface ExistingConstraint {\n schema: string;\n table: string;\n name: string;\n kind: UndeclarableConstraintKind;\n}\n\n/**\n * Every CHECK and EXCLUDE constraint on an ordinary or partitioned table\n * outside the system schemas.\n *\n * `contype` `c` and `x` only. A primary key (`p`), `UNIQUE` (`u`), foreign key\n * (`f`) and, from PG 18, a catalogued `NOT NULL` (`n`) are what a collection\n * declares, so Atlas keeps managing them.\n */\nexport const UNDECLARABLE_CONSTRAINTS_SQL = `\n SELECT n.nspname AS \"schema\",\n c.relname AS \"table\",\n con.conname AS \"name\",\n CASE con.contype WHEN 'c' THEN 'check' ELSE 'exclude' END AS \"kind\"\n FROM pg_constraint con\n JOIN pg_class c ON c.oid = con.conrelid\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE con.contype IN ('c', 'x')\n AND c.relkind IN ('r', 'p')\n AND n.nspname NOT IN ('pg_catalog', 'information_schema')\n AND n.nspname NOT LIKE 'pg\\\\_%'\n`;\n\nconst DECLARED_CONSTRAINT_RE = /\\bCONSTRAINT\\s+\"((?:[^\"]|\"\")+)\"\\s+(?:CHECK|EXCLUDE)\\b/gi;\n\n/**\n * The CHECK and EXCLUDE constraint names a desired state (`schema.sql`)\n * declares — Atlas's to manage, and so never excluded.\n */\nexport function declaredConstraintNames(desiredSql: string): Set<string> {\n const names = new Set<string>();\n for (const match of desiredSql.matchAll(DECLARED_CONSTRAINT_RE)) {\n names.add(match[1].replace(/\"\"/g, \"\\\"\"));\n }\n return names;\n}\n\n/**\n * The `--exclude` patterns that keep Atlas from dropping the CHECK and EXCLUDE\n * constraints the desired state does not declare. Three-part\n * `schema.table.constraint`, which is what Atlas matches a constraint by: a\n * two-part pattern reads as a table, matches nothing, and reports no error.\n */\nexport function undeclaredConstraintExcludes(\n existing: readonly ExistingConstraint[],\n desiredSql: string\n): string[] {\n const declared = declaredConstraintNames(desiredSql);\n return existing\n .filter(constraint => !declared.has(constraint.name))\n .map(constraint => atlasExcludePattern(constraint.schema, constraint.table, constraint.name));\n}\n","import path from \"path\";\nimport { fileURLToPath } from \"url\";\n\n// Isolated in its own module so the jest CommonJS transform can swap it for a\n// __dirname shim via moduleNameMapper — `import.meta` is a syntax error once\n// ts-jest transpiles to CJS.\nexport const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n","/**\n * Where `db push` and `db generate` put the SQL they render from the collections.\n *\n * Five files — `schema.sql` (Atlas's desired state), `policies.sql`,\n * `search.sql`, `vector.sql` and `triggers.sql` — and every one of them is\n * scratch. Both commands regenerate all five before reading any of them, and\n * nothing else reads them at all. They used to be written into `drizzle/`, beside\n * the migrations, and committed: a copy nobody read, which every collection\n * change rewrote in the diff and every two branches touching collections\n * conflicted on.\n *\n * Relative to the directory the command runs in (the backend), like the\n * migrations are.\n */\nimport fs from \"fs\";\nimport path from \"path\";\n\n/** The directory, relative to the working directory. POSIX, because it is also a URL path. */\nexport const GENERATED_SQL_DIR = \".rebase/sql\";\n\n/**\n * `schema.sql` as Atlas takes it. Relative on purpose, like the\n * `file://drizzle/migrations` beside it: Atlas joins the URL's host and path, so\n * `.rebase` is read as the first path segment, measured against the pinned 1.2.3.\n */\nexport const DESIRED_STATE_URL = `file://${GENERATED_SQL_DIR}/schema.sql`;\n\n/** The files the DDL generator writes, all of them derived from the collections. */\nexport const GENERATED_SQL_FILES = [\"schema.sql\", \"policies.sql\", \"search.sql\", \"vector.sql\", \"triggers.sql\"] as const;\n\n/** The absolute directory, for the working directory the command runs in. */\nexport function generatedSqlDir(cwd: string = process.cwd()): string {\n return path.resolve(cwd, GENERATED_SQL_DIR);\n}\n\n/**\n * The first line of every file the generator writes, in every release that\n * wrote them into `drizzle/`. Matching it is what makes removing the old copy\n * safe: a file there under one of these names that does not open this way was\n * written by a person, and is left alone.\n */\nconst GENERATED_HEADER = /^-- This file (is auto-generated by the Rebase DDL generator|contains RLS policies generated by Rebase)\\./;\n\n/**\n * Get the directory ready to be written, and retire the copies an older release\n * committed.\n *\n * The directory ignores itself, rather than relying on the project's\n * `.gitignore`: a scaffold ignores `.rebase/`, but a project created before it\n * did would otherwise start committing these files from their new home.\n *\n * The old copies in `drizzle/` are deleted, not left behind. Nothing writes them\n * any more, so from the first push after an upgrade they only fall further\n * behind — and a stale `policies.sql` in the repository is exactly what someone\n * reviewing the project's RLS would read.\n *\n * With `retire: false` (`db push --dry-run`, which writes nothing of the\n * project's) the old copies are only found, not removed.\n *\n * @returns the old files it removed — or, with `retire: false`, would remove —\n * relative to the working directory.\n */\nexport function prepareGeneratedSqlDir(\n cwd: string = process.cwd(),\n options: { retire?: boolean } = {}\n): string[] {\n const dir = generatedSqlDir(cwd);\n fs.mkdirSync(dir, { recursive: true });\n const ignore = path.join(dir, \".gitignore\");\n if (!fs.existsSync(ignore)) {\n fs.writeFileSync(ignore, \"# Generated by `rebase db push` / `rebase db generate` from the collections.\\n*\\n\");\n }\n\n const removed: string[] = [];\n for (const name of GENERATED_SQL_FILES) {\n const legacy = path.join(cwd, \"drizzle\", name);\n if (!fs.existsSync(legacy)) continue;\n const firstLine = fs.readFileSync(legacy, \"utf-8\").split(\"\\n\", 1)[0];\n if (!GENERATED_HEADER.test(firstLine)) continue;\n if (options.retire !== false) fs.rmSync(legacy);\n removed.push(path.posix.join(\"drizzle\", name));\n }\n return removed;\n}\n","import path from \"path\";\nimport fs from \"fs\";\nimport { execSync } from \"child_process\";\nimport { createRequire } from \"module\";\nimport { pathToFileURL } from \"url\";\nimport chalk from \"chalk\";\nimport { isRebaseIndexName } from \"./schema/collection-index\";\nimport type { GeneratedColumnDependency } from \"./schema/generated-column-conflicts\";\nimport { COLUMN_TYPES_SQL, type ExistingColumnType } from \"./schema/destructive-sql\";\nimport { UNDECLARABLE_CONSTRAINTS_SQL, undeclaredConstraintExcludes, type ExistingConstraint } from \"./schema/undeclarable-constraints\";\nimport { atlasExcludePattern, excludePatternParts } from \"./schema/atlas-exclude\";\nimport { out, outWarn } from \"./cli-output\";\nimport type { CollectionConfig } from \"@rebasepro/types\";\nimport { moduleDir as __helpersDirname } from \"./module-dir\";\nimport { generatedSqlDir } from \"./generated-sql\";\n\n\n\n/**\n * Why is a dependency's binary missing — never installed, or installed with its\n * build script blocked?\n *\n * These need opposite advice, and getting it wrong is not a cosmetic miss. pnpm\n * 10+ refuses to run a dependency's lifecycle scripts unless it is allowlisted\n * (`pnpm.onlyBuiltDependencies`, or `allowBuilds` in `pnpm-workspace.yaml`).\n * `@ariga/atlas` downloads its platform binary in `preinstall`, so a blocked\n * script leaves a state that looks like a successful install: the package is on\n * disk with its `install.js` and `package.json`, `node_modules/.bin` is empty,\n * the install exits 0, and the only signal is `ERR_PNPM_IGNORED_BUILDS` several\n * screens up.\n *\n * Telling somebody in that state to install the package again sends them round\n * the same loop forever — the add succeeds, the script is blocked again,\n * nothing changes. Verified by doing it: `pnpm add @ariga/atlas` into a bare\n * project yields exactly this, three \"Failed to create bin … ENOENT\" warnings\n * and no binary.\n *\n * Resolution is attempted from the user's project first and this package\n * second, matching the order {@link resolveLocalBin} searches — the driver may\n * be installed a level up from where the command runs.\n */\nexport type ProjectPackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\" | \"unknown\";\n\n/**\n * Which package manager installed this project, read off its lockfile.\n *\n * Every remedy for a blocked build script is package-manager specific, and\n * naming the wrong one is the same failure as naming none: `pnpm approve-builds`\n * is not a thing an npm user can run, and `pnpm.onlyBuiltDependencies` is a key\n * npm does not read. This existed as a pnpm-only message for long enough that\n * npm 12 — which blocks dependency lifecycle scripts by default, the way pnpm 10\n * does — could ship and leave every npm reader three unfollowable commands deep.\n *\n * The lockfile, not `npm_config_user_agent`: the question is how the project's\n * `node_modules` was built, not which binary happens to be invoking us. Walks up\n * because commands run from `backend/` in a scaffolded project, and the lockfile\n * is at the workspace root.\n */\nexport function detectProjectPackageManager(startDir: string = process.cwd()): ProjectPackageManager {\n const lockfiles: Array<[string, ProjectPackageManager]> = [\n [\"pnpm-lock.yaml\", \"pnpm\"],\n [\"bun.lock\", \"bun\"],\n [\"bun.lockb\", \"bun\"],\n [\"yarn.lock\", \"yarn\"],\n [\"package-lock.json\", \"npm\"]\n ];\n\n let dir = path.resolve(startDir);\n while (true) {\n for (const [file, manager] of lockfiles) {\n if (fs.existsSync(path.join(dir, file))) return manager;\n }\n const parent = path.dirname(dir);\n if (parent === dir) return \"unknown\";\n dir = parent;\n }\n}\n\n/**\n * How to let `@ariga/atlas` run its `preinstall`, in the reader's own package\n * manager.\n *\n * Returns the lines to print after \"the binary is missing but the package is\n * installed\". Kept next to {@link detectProjectPackageManager} so the two move\n * together, and returned rather than printed so it can be asserted on.\n */\nexport function describeBuildScriptRemedy(\n packageName: string,\n manager: ProjectPackageManager = detectProjectPackageManager()\n): string[] {\n switch (manager) {\n case \"npm\":\n // npm 12 blocks dependency install scripts unless the root\n // package.json allowlists them. `rebase init` scaffolds the entry;\n // a project that predates it, or one that vendored its own\n // package.json, needs to add it.\n return [\n ` npm install-scripts approve ${packageName}`,\n \"\",\n \" or, to record it in the project (what `rebase init` scaffolds):\",\n \"\",\n \" // package.json\",\n ` \"allowScripts\": { \"${packageName}\": true }`,\n \"\",\n \" Then re-run `npm install`.\"\n ];\n case \"yarn\":\n return [\n \" // .yarnrc.yml\",\n \" enableScripts: true\",\n \"\",\n \" Then re-run `yarn install`.\"\n ];\n case \"bun\":\n return [\n \" // package.json\",\n ` \"trustedDependencies\": [\"${packageName}\"]`,\n \"\",\n \" Then re-run `bun install`.\"\n ];\n case \"pnpm\":\n default:\n return [\n \" pnpm approve-builds\",\n \"\",\n \" or, to record it in the project (what `rebase init` scaffolds):\",\n \"\",\n \" // package.json\",\n ` \"pnpm\": { \"onlyBuiltDependencies\": [\"${packageName}\"] }`,\n \"\",\n \" Then re-run `pnpm install`.\"\n ];\n }\n}\n\n/**\n * \"Install again, from scratch\", in the reader's own package manager.\n *\n * For the `bin-link-missing` state: the tree is half-written, and re-resolving\n * it is what recreates the `.bin` shim. `--force` on pnpm is what makes it\n * relink rather than decide the tree is already up to date.\n */\nexport function describeReinstallCommand(\n manager: ProjectPackageManager = detectProjectPackageManager()\n): string {\n switch (manager) {\n case \"npm\": return \"rm -rf node_modules && npm install\";\n case \"yarn\": return \"yarn install --force\";\n case \"bun\": return \"rm -rf node_modules && bun install\";\n case \"pnpm\":\n default: return \"pnpm install --force\";\n }\n}\n\n/**\n * The workspace member a command runs in, relative to the workspace's root —\n * `backend` in a scaffolded project — or null at that root and outside any\n * workspace.\n *\n * The member is the nearest directory at or above `startDir` holding a\n * `package.json`; the root is the nearest one above it holding a\n * `pnpm-workspace.yaml`, or a `package.json` that declares `workspaces`.\n */\nexport function workspaceMemberDir(startDir: string = process.cwd()): string | null {\n let member = path.resolve(startDir);\n while (!fs.existsSync(path.join(member, \"package.json\"))) {\n const parent = path.dirname(member);\n if (parent === member) return null;\n member = parent;\n }\n for (let dir = path.dirname(member); ; dir = path.dirname(dir)) {\n if (isWorkspaceRoot(dir)) return path.relative(dir, member).split(path.sep).join(\"/\");\n if (path.dirname(dir) === dir) return null;\n }\n}\n\nfunction isWorkspaceRoot(dir: string): boolean {\n if (fs.existsSync(path.join(dir, \"pnpm-workspace.yaml\"))) return true;\n try {\n const manifest: unknown = JSON.parse(fs.readFileSync(path.join(dir, \"package.json\"), \"utf8\"));\n return typeof manifest === \"object\" && manifest !== null && \"workspaces\" in manifest;\n } catch {\n return false;\n }\n}\n\n/**\n * `add <pkg>` as a dev dependency, in the reader's own package manager, typed\n * from the project root: into `workspaceDir` when the command runs in a\n * workspace member, where a bare `pnpm add -D` at the root refuses with\n * `ERR_PNPM_ADDING_TO_ROOT`.\n */\nexport function describeDevAddCommand(\n packageName: string,\n manager: ProjectPackageManager = detectProjectPackageManager(),\n workspaceDir: string | null = workspaceMemberDir()\n): string {\n const dir = workspaceDir === null ? null : shellWord(workspaceDir);\n switch (manager) {\n case \"npm\": return dir ? `npm install -D ${packageName} -w ${dir}` : `npm install -D ${packageName}`;\n case \"yarn\": return dir ? `yarn --cwd ${dir} add -D ${packageName}` : `yarn add -D ${packageName}`;\n case \"bun\": return dir ? `bun add -d ${packageName} --cwd ${dir}` : `bun add -d ${packageName}`;\n case \"pnpm\":\n default: return dir ? `pnpm --dir ${dir} add -D ${packageName}` : `pnpm add -D ${packageName}`;\n }\n}\n\n/** A path as one shell word: as it is when it needs no quoting, single-quoted otherwise. */\nconst shellWord = (value: string): string =>\n /^[\\w./-]+$/.test(value) ? value : `'${value.replace(/'/g, \"'\\\\''\")}'`;\n\n/**\n * Why a dependency's binary is not on `PATH`.\n *\n * `bin-link-missing` is the third state, and the reason this is not a boolean.\n * The `preinstall` did run and the binary IS on disk — only the\n * `node_modules/.bin` shim that points at it is absent. pnpm produces exactly\n * that when it writes the link before the script that creates the target\n * (\"Failed to create bin … ENOENT\"), and so does a tree copied without its\n * symlinks. Telling that reader their build scripts are blocked sends them to\n * `approve-builds`, which does nothing, because nothing is blocked.\n */\nexport type MissingBinDiagnosis = \"not-installed\" | \"build-script-blocked\" | \"bin-link-missing\";\n\n/**\n * Where a package's own manifest lives, or null if it is not installed here.\n *\n * `package.json` rather than the package root: a package with an `exports` map\n * that omits `.` is unresolvable by name even when it is perfectly installed,\n * which would misreport it as absent.\n */\nfunction resolvePackageManifest(packageName: string): string | null {\n const bases = [\n pathToFileURL(path.join(process.cwd(), \"package.json\")),\n pathToFileURL(path.join(__helpersDirname, \"package.json\"))\n ];\n for (const base of bases) {\n try {\n return createRequire(base).resolve(`${packageName}/package.json`);\n } catch {\n // Try the next base.\n }\n }\n return null;\n}\n\n/**\n * The binaries a package's own manifest declares, as absolute paths.\n *\n * `bin` is a string for a single binary named after the package, or a map.\n * Both forms appear in the wild and `@ariga/atlas` uses the map.\n */\nexport function declaredBinaries(manifestPath: string): string[] {\n let manifest: { name?: string; bin?: string | Record<string, string> };\n try {\n manifest = JSON.parse(fs.readFileSync(manifestPath, \"utf8\")) as typeof manifest;\n } catch {\n return [];\n }\n const dir = path.dirname(manifestPath);\n if (typeof manifest.bin === \"string\") return [path.resolve(dir, manifest.bin)];\n if (manifest.bin && typeof manifest.bin === \"object\") {\n return Object.values(manifest.bin)\n .filter((target): target is string => typeof target === \"string\")\n .map(target => path.resolve(dir, target));\n }\n return [];\n}\n\nexport function diagnoseMissingBin(packageName: string): MissingBinDiagnosis {\n const manifestPath = resolvePackageManifest(packageName);\n if (!manifestPath) return \"not-installed\";\n\n // Installed — but \"installed\" was the whole verdict, and it is not enough.\n // `@ariga/atlas` downloads its platform binary in `preinstall`, so a blocked\n // script leaves the package on disk with its `install.js` and manifest and\n // no binary: the state this branch was written for. It is NOT the state of\n // a tree whose binary exists and whose `.bin` shim does not, and those two\n // need opposite advice. Ask the manifest which file the binary is, and look.\n const binaries = declaredBinaries(manifestPath);\n if (binaries.length === 0) return \"build-script-blocked\";\n return binaries.some(file => fs.existsSync(file)) ? \"bin-link-missing\" : \"build-script-blocked\";\n}\n\nexport function resolveLocalBin(binName: string): string | null {\n // Try to find node_modules/.bin upwards from __helpersDirname first (package-relative)\n let dir = __helpersDirname;\n while (true) {\n const candidate = path.join(dir, \"node_modules\", \".bin\", binName);\n if (fs.existsSync(candidate)) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n let cwd = process.cwd();\n // Try to find node_modules/.bin upwards from process.cwd()\n while (true) {\n const candidate = path.join(cwd, \"node_modules\", \".bin\", binName);\n if (fs.existsSync(candidate)) return candidate;\n const parent = path.dirname(cwd);\n if (parent === cwd) break;\n cwd = parent;\n }\n // Fall back to globally installed binary via which/where\n try {\n const cmd = process.platform === \"win32\" ? `where ${binName}` : `which ${binName}`;\n const globalPath = execSync(cmd, { encoding: \"utf-8\" }).trim().split(\"\\n\")[0].trim();\n if (globalPath && fs.existsSync(globalPath)) return globalPath;\n } catch {\n // not found globally\n }\n return null;\n}\n\nexport async function getTableIncludesFromCollections(allCollections: CollectionConfig[]): Promise<string[]> {\n const { getTableName, relationalCollections, resolveJunctionSpecs } = await import(\"@rebasepro/common\");\n const { isPostgresCollectionConfig } = await import(\"@rebasepro/types\");\n\n // The include list is the inverse of {@link getTableExcludes}: a name on it\n // is a name Atlas is allowed to drop. A Firestore collection called\n // `exercises` must not put `public.exercises` here — a real, unrelated table\n // of that name would lose its protection and be dropped by the next\n // auto-approved `db push`.\n const collections = relationalCollections(allCollections);\n const includes: string[] = [];\n for (const col of collections) {\n const tableName = getTableName(col);\n const schema = isPostgresCollectionConfig(col) && col.schema ? col.schema : \"public\";\n if (tableName) {\n includes.push(`${schema}.${tableName}`);\n }\n }\n // Junctions where they are created — `public`, whatever schema either\n // endpoint is in. Qualifying one with an endpoint's schema left the real\n // junction excluded, invisible to Atlas, and planned for creation again.\n for (const junction of resolveJunctionSpecs(collections).values()) {\n includes.push(`${junction.schema}.${junction.table}`);\n }\n\n return Array.from(new Set(includes));\n}\n\nexport async function getTableIncludes(collectionsPath: string): Promise<string[]> {\n return getTableIncludesFromCollections(await loadCollectionsForCli(collectionsPath));\n}\n\n/**\n * Load a project's collections the way the Atlas-facing commands need them.\n *\n * Deliberately forgiving — a file that fails to import is skipped rather than\n * fatal — because the callers use this to *narrow* what Atlas may touch, and a\n * hard failure here would block a push over an unrelated broken file. Callers\n * that cannot tolerate a partial answer (the table excludes, which fail closed)\n * check the result themselves.\n */\n/** Paths this process has already complained about. See {@link loadCollectionsForCli}. */\nconst warnedMissingCollectionsPaths = new Set<string>();\n\nexport async function loadCollectionsForCli(collectionsPath: string): Promise<CollectionConfig[]> {\n // Note: a relative --collections path resolves against the *current working\n // directory*, not the backend package. When invoked via the generated npm\n // script (cwd = backend/), the script uses \"../config/collections\" to\n // compensate — so running the CLI directly from the repo root with a\n // different relative path can silently point at the wrong place.\n const resolvedPath = path.resolve(collectionsPath);\n const collections: CollectionConfig[] = [];\n if (!fs.existsSync(resolvedPath) && !warnedMissingCollectionsPaths.has(resolvedPath)) {\n // Once per path per process. `db push` re-enters this from\n // `schemaCommand`, `generatePostgresDdlCommand` and the Atlas argv\n // assembly, so the same four lines were printed four times — sixteen\n // lines of identical text ahead of the real output.\n //\n // An explicitly typed `--collections` never reaches here at all: it is\n // refused at the entry point, before anything is generated. See\n // `cli-collections-path.ts`. What is left is the default, which a\n // headless project legitimately does not have.\n warnedMissingCollectionsPaths.add(resolvedPath);\n outWarn(chalk.yellow(\n ` ⚠ Collections path not found: \"${collectionsPath}\"\\n` +\n ` Resolved to: ${resolvedPath}\\n` +\n ` (relative to cwd: ${process.cwd()})\\n` +\n ` Pass an absolute path, or a path relative to where you run the command.`\n ));\n }\n if (fs.existsSync(resolvedPath)) {\n const stats = fs.statSync(resolvedPath);\n if (stats.isDirectory()) {\n const files = fs.readdirSync(resolvedPath);\n for (const file of files) {\n if ((file.endsWith(\".ts\") || file.endsWith(\".js\")) &&\n !file.includes(\".test.\") &&\n !file.endsWith(\".d.ts\") &&\n file !== \"index.ts\" && file !== \"index.js\") {\n try {\n const filePath = path.join(resolvedPath, file);\n const fileUrl = pathToFileURL(filePath).href;\n const module = await import(fileUrl);\n if (module && module.default) {\n collections.push(module.default);\n }\n } catch {\n // ignore\n }\n }\n }\n }\n }\n\n return collections;\n}\n\n/**\n * The app database connections this process has open, by URL — one each.\n *\n * `rebase db push` used to open a connection per step: the auth bootstrap, the\n * connectivity probe, each catalogue read behind the Atlas excludes, the\n * column types, the generated-column edges, and then every post-step — RLS\n * lock, auth tables, vector, search, triggers, policies, reconcile, role,\n * legacy schema. 18 of them on a push that changed nothing. Locally each costs\n * a few milliseconds; against a hosted Postgres over TLS a connect is four or\n * five round trips (TCP, TLS 1.3, startup, SCRAM twice) — about 4 s of every\n * push at 40 ms, and more on a serverless database that wakes per connection.\n *\n * Opened on first use and closed by {@link closeAppClients} when the command\n * ends. The scratch database Atlas plans in is created and dropped through the\n * `postgres` database, on a connection of its own: it is not this one.\n */\nconst appClients = new Map<string, Promise<PgClient>>();\n\ntype PgClient = import(\"pg\").Client;\n\nasync function openAppClient(databaseUrl: string, connectionTimeoutMillis?: number): Promise<PgClient> {\n const open = appClients.get(databaseUrl);\n if (open) return open;\n const opening = (async () => {\n const { Client } = await import(\"pg\");\n const client = new Client({\n connectionString: databaseUrl,\n ...(connectionTimeoutMillis ? { connectionTimeoutMillis } : {})\n });\n // A connection can die while it sits idle between steps — Atlas runs\n // for seconds with this one open. Unheard, that `error` is an uncaught\n // exception and kills the process; heard, the next step opens another,\n // and a step running on it fails with its own error, as before.\n const forget = (): void => {\n if (appClients.get(databaseUrl) === opening) appClients.delete(databaseUrl);\n };\n client.on(\"error\", forget);\n client.on(\"end\", forget);\n await client.connect();\n return client;\n })();\n appClients.set(databaseUrl, opening);\n try {\n return await opening;\n } catch (err) {\n // Not kept: the next step tries again, and reports its own failure the\n // way it did when every step connected for itself.\n appClients.delete(databaseUrl);\n throw err;\n }\n}\n\n/**\n * Run one step on this process's connection to the app database.\n *\n * Every step leaves the session as it found it, which is what a connection of\n * its own used to guarantee by being closed. None of them sets anything for\n * the session — `SET LOCAL` inside their own transaction, or `RESET` — and one\n * that fails part-way through a transaction it opened is rolled back here, so\n * the next step does not meet `current transaction is aborted`.\n *\n * @param options.connectionTimeoutMillis applies only when this call is the one\n * that connects.\n */\nexport async function withAppClient<T>(\n databaseUrl: string,\n step: (client: PgClient) => Promise<T>,\n options: { connectionTimeoutMillis?: number } = {}\n): Promise<T> {\n const client = await openAppClient(databaseUrl, options.connectionTimeoutMillis);\n try {\n return await step(client);\n } catch (err) {\n // Outside a transaction this is a warning Postgres sends and nobody\n // reads; on a dead connection it fails too, which changes nothing.\n await client.query(\"ROLLBACK\").catch(() => undefined);\n throw err;\n }\n}\n\n/** Close every connection {@link withAppClient} opened. Called once, as the command ends. */\nexport async function closeAppClients(): Promise<void> {\n const open = [...appClients.values()];\n appClients.clear();\n await Promise.all(open.map(async opening => {\n try {\n await (await opening).end();\n } catch {\n // Already gone, or never connected: nothing to close.\n }\n }));\n}\n\n/**\n * Names the empty database Atlas plans against, instead of `<db>_dev_diff`.\n *\n * The derived one is made with `CREATE DATABASE` beside the real database,\n * which assumes a server that holds more than one. PGlite — the managed\n * development database — serves exactly one: the CREATE succeeds and creates\n * nothing, so Atlas connected straight back to the database it was comparing\n * and refused it as \"not clean\". `rebase db push` on the managed database now\n * starts a second, throwaway PGlite and names it here. Anyone can name their\n * own the same way; a named database is neither created nor dropped.\n */\nexport const ATLAS_DEV_URL_ENV = \"REBASE_ATLAS_DEV_URL\";\n\n/**\n * The database Atlas replays the desired schema into: the one\n * {@link ATLAS_DEV_URL_ENV} names (`named: true` — not ours to create or\n * drop), else `<db>_dev_diff`.\n */\nexport function atlasDevDatabaseUrl(\n databaseUrl: string,\n env: Readonly<Record<string, string | undefined>>\n): { url: string; named: boolean } {\n const named = env[ATLAS_DEV_URL_ENV]?.trim();\n if (!named) return { url: getDevDatabaseUrl(databaseUrl), named: false };\n if (named === databaseUrl.trim()) {\n // Atlas would replay the schema into the database it is about to change.\n throw new Error(\n `${ATLAS_DEV_URL_ENV} names the same database as DATABASE_URL. It must be a separate, empty ` +\n \"database that Atlas may fill and clear.\"\n );\n }\n return { url: named, named: true };\n}\n\nexport function getDevDatabaseUrl(databaseUrl: string): string {\n try {\n const parsed = new URL(databaseUrl);\n const dbName = parsed.pathname.slice(1);\n parsed.pathname = `/${dbName}_dev_diff`;\n return parsed.toString();\n } catch {\n return databaseUrl + \"_dev_diff\";\n }\n}\n\n/**\n * Create the scratch database Atlas plans against, and say so when we cannot.\n *\n * Atlas needs a `--dev-url` — an empty database it can build the desired state\n * in — and this makes `<db>_dev_diff` next to the real one. The whole body used\n * to be wrapped in an empty catch commented \"Ignore, let Atlas handle\n * connection failures\", and Atlas's version of handling it is `postgres:\n * querying system variables: pq: database \"app_dev_diff\" does not exist\n * (3D000)` — the symptom, four frames downstream of the cause, with the cause\n * thrown away.\n *\n * The cause is almost always `42501`: `CREATE DATABASE` is a privilege managed\n * providers do not grant, so this is the first thing a hosted user hits and the\n * one they can do least with.\n *\n * @returns whether the scratch database was created by *this* call — the only\n * case in which dropping it afterwards is ours to do.\n */\nexport async function ensureDevDatabaseExists(databaseUrl: string, devDatabaseUrl: string): Promise<boolean> {\n let devDbName = \"\";\n try {\n const { Client } = await import(\"pg\");\n const parsed = new URL(databaseUrl);\n devDbName = new URL(devDatabaseUrl).pathname.slice(1);\n\n parsed.pathname = \"/postgres\";\n const client = new Client({ connectionString: parsed.toString() });\n await client.connect();\n try {\n const res = await client.query(\"SELECT 1 FROM pg_database WHERE datname = $1\", [devDbName]);\n if (res.rowCount === 0) {\n await client.query(`CREATE DATABASE \"${devDbName}\"`);\n out(chalk.gray(` ✓ Created validation database \"${devDbName}\"`));\n\n return true;\n }\n } finally {\n await client.end();\n }\n } catch (err) {\n outWarn(describeDevDatabaseFailure(err, devDbName || \"<db>_dev_diff\"));\n }\n\n return false;\n}\n\n/**\n * What went wrong creating the scratch database, and what to do about it.\n *\n * Exported for its test: the `42501` branch is the one that matters and it\n * cannot be provoked without a non-superuser role.\n */\nexport function describeDevDatabaseFailure(err: unknown, devDbName: string): string {\n const code = (err as { code?: string } | null)?.code;\n const message = err instanceof Error ? err.message : String(err);\n\n const lines = [\n chalk.yellow(` ⚠ Could not create the Atlas scratch database \"${devDbName}\": ${message}`)\n ];\n if (code === \"42501\") {\n lines.push(chalk.gray(\n \" Atlas plans every change in a throwaway copy of the schema, and CREATE DATABASE\\n\" +\n \" is a privilege most managed providers withhold. Two ways forward:\\n\" +\n ` • GRANT the role CREATEDB: ALTER ROLE \"<role>\" CREATEDB;\\n` +\n ` • or create it once by hand: CREATE DATABASE \"${devDbName}\";`\n ));\n } else {\n lines.push(chalk.gray(\" Atlas will fail next with `database \\\"\" + devDbName + \"\\\" does not exist (3D000)`, which is this.\"));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Drop the scratch database again, after the command that needed it succeeded.\n *\n * It is a full copy of the schema — no rows, but every table, type and index —\n * and it was left behind per target forever. The only notice anyone got was\n * `rebase db branch prune` reporting \"3 Atlas scratch database(s) left over\n * from db push\", and on a provider that bills per database it is a silent cost.\n *\n * Deliberately NOT called on the failure path: after a failed push the scratch\n * database holds the state Atlas was planning against, which is the one thing\n * worth inspecting.\n */\nexport async function dropDevDatabase(databaseUrl: string, devDatabaseUrl: string): Promise<void> {\n try {\n const { Client } = await import(\"pg\");\n const parsed = new URL(databaseUrl);\n const devDbName = new URL(devDatabaseUrl).pathname.slice(1);\n\n parsed.pathname = \"/postgres\";\n const client = new Client({ connectionString: parsed.toString() });\n await client.connect();\n try {\n await client.query(`DROP DATABASE IF EXISTS \"${devDbName}\"`);\n out(chalk.gray(` ✓ Removed validation database \"${devDbName}\"`));\n } finally {\n await client.end();\n }\n } catch (err) {\n // Not fatal, and not silent: the push has already succeeded, so the\n // worst case is one database left where `prune` can find it.\n outWarn(chalk.gray(\n ` ⚠ Could not remove the Atlas scratch database: ${err instanceof Error ? err.message : String(err)}\\n` +\n \" `rebase db branch prune --include-dev-diff` clears them.\"\n ));\n }\n}\n\n/**\n * The generated SQL for the project's `search` blocks, if it has any.\n *\n * @param sqlDir directory holding the generated SQL. Defaults to\n * `.rebase/sql` under the working directory.\n */\nexport function readSearchDdl(sqlDir: string = generatedSqlDir()): string {\n const searchFile = path.join(sqlDir, \"search.sql\");\n if (!fs.existsSync(searchFile)) return \"\";\n return fs.readFileSync(searchFile, \"utf-8\").trim();\n}\n\n/**\n * Bring the search column, its index and their helpers up to date.\n *\n * Runs *after* Atlas, not before: the statements are `ALTER TABLE ... ADD\n * COLUMN`, so the table has to exist. Atlas is told to ignore these objects\n * entirely (`getSearchExcludes`) — it cannot manage them, and left to itself it\n * would plan a `DROP COLUMN` for every one, since they are absent from the\n * desired state it was given.\n *\n * A no-op when no collection declared `search`. A failure is *not* swallowed:\n * silently pushing a schema whose search column never appeared is how a\n * collection ends up with search configured, no error anywhere, and no results.\n */\nexport async function applySearchDdl(\n databaseUrl: string,\n sqlDir: string = generatedSqlDir()\n): Promise<void> {\n const sql = readSearchDdl(sqlDir);\n if (!sql) return;\n\n await withAppClient(databaseUrl, client => client.query(sql));\n out(chalk.gray(\" ✓ Applied full-text search columns and indexes\"));\n}\n\n/**\n * Glob patterns keeping Atlas away from the search objects.\n *\n * Returns an empty list — and so changes nothing — for a project with no\n * `search` block, which is every project that has not opted in.\n */\nexport async function getSearchExcludes(collectionsPath: string): Promise<string[]> {\n const { searchExcludePatterns } = await import(\"./schema/generate-postgres-ddl-logic\");\n return searchExcludePatterns(await loadCollectionsForCli(collectionsPath));\n}\n\n/**\n * Every `generated column → column it reads` edge in the database.\n *\n * Read from `pg_depend` rather than by matching names in the expression text:\n * a generated column's `pg_attrdef` row carries one `deptype = 'n'` dependency\n * per column its expression references, which is the catalogue's own answer and\n * cannot be fooled by a column name that also appears inside a string literal\n * or as part of a longer identifier.\n *\n * Used by `db push` to find what has to come out of the way before Atlas runs —\n * see `generated-column-conflicts.ts` for why that is necessary at all.\n */\nexport async function queryGeneratedColumnDependencies(\n databaseUrl: string\n): Promise<GeneratedColumnDependency[]> {\n return withAppClient(databaseUrl, async client => {\n const res = await client.query(`\n SELECT n.nspname AS \"schema\",\n c.relname AS \"table\",\n gen.attname AS \"column\",\n dep.attname AS \"dependsOn\"\n FROM pg_attrdef ad\n JOIN pg_class c ON c.oid = ad.adrelid\n JOIN pg_namespace n ON n.oid = c.relnamespace\n JOIN pg_attribute gen ON gen.attrelid = ad.adrelid AND gen.attnum = ad.adnum\n JOIN pg_depend d ON d.classid = 'pg_attrdef'::regclass\n AND d.objid = ad.oid\n AND d.refclassid = 'pg_class'::regclass\n AND d.refobjid = ad.adrelid\n AND d.deptype = 'n'\n JOIN pg_attribute dep ON dep.attrelid = ad.adrelid AND dep.attnum = d.refobjsubid\n WHERE gen.attgenerated <> ''\n AND NOT gen.attisdropped\n AND NOT dep.attisdropped;\n `);\n return res.rows as GeneratedColumnDependency[];\n });\n}\n\n/**\n * Every column in the database with its current type, for the destructive\n * gate to tell a widening `ALTER COLUMN … TYPE` from one that loses data.\n */\nexport async function queryColumnTypes(databaseUrl: string): Promise<ExistingColumnType[]> {\n return withAppClient(databaseUrl, async client => (await client.query<ExistingColumnType>(COLUMN_TYPES_SQL)).rows);\n}\n\n/**\n * Drop the generated columns standing in the way of an apply.\n *\n * Their indexes and their `COMMENT ON COLUMN` stamp go with them, which is\n * exactly right: `applySearchDdl` rebuilds all three from `search.sql` and a\n * surviving stamp would make the rebuilt column look like drift.\n */\nexport async function dropGeneratedColumns(\n databaseUrl: string,\n statements: string[]\n): Promise<void> {\n if (statements.length === 0) return;\n await withAppClient(databaseUrl, async client => {\n for (const statement of statements) {\n await client.query(statement);\n }\n });\n}\n\n/**\n * The generated SQL for the project's `vector` properties, if it has any.\n *\n * @param sqlDir directory holding the generated SQL. Defaults to\n * `.rebase/sql` under the working directory.\n */\nexport function readVectorDdl(sqlDir: string = generatedSqlDir()): string {\n const vectorFile = path.join(sqlDir, \"vector.sql\");\n if (!fs.existsSync(vectorFile)) return \"\";\n return fs.readFileSync(vectorFile, \"utf-8\").trim();\n}\n\n/**\n * Install pgvector and bring the vector columns and their ANN indexes up to\n * date.\n *\n * Runs *after* Atlas, like {@link applySearchDdl} and for the same reason: the\n * statements are `ALTER TABLE … ADD COLUMN`, so the table has to exist. Atlas\n * is told to ignore these objects entirely ({@link getVectorExcludes}) — it\n * cannot manage a type its dev database is structurally incapable of having,\n * and left to itself it would plan a `DROP COLUMN` for every one.\n *\n * A no-op when no collection declares a `vector` property. A failure is *not*\n * swallowed: a push that reported success while the embedding column never\n * appeared is how a project ends up with vector search configured, no error\n * anywhere, and no results.\n */\nexport async function applyVectorDdl(\n databaseUrl: string,\n sqlDir: string = generatedSqlDir()\n): Promise<void> {\n const sql = readVectorDdl(sqlDir);\n if (!sql) return;\n\n const { vectorExtensionHint } = await import(\"./schema/vector-index\");\n try {\n await withAppClient(databaseUrl, client => client.query(sql));\n } catch (err) {\n // The boot ensure explains this failure and the push has to as well —\n // it is the same missing extension, and a push is the *more* likely\n // place to meet it. Without this the developer gets a bare\n // `type \"vector\" does not exist`, which names nothing they can act on\n // and reads like a broken database rather than a line of config.\n const message = err instanceof Error ? err.message : String(err);\n const hint = vectorExtensionHint(message);\n if (!hint) throw err;\n throw new Error(`${message}${hint}`, { cause: err });\n }\n out(chalk.gray(\" ✓ Applied vector columns and ANN indexes\"));\n}\n\n/**\n * Glob patterns keeping Atlas away from the vector objects.\n *\n * Returns an empty list — and so changes nothing — for a project with no\n * `vector` property, which is every project that has not opted in.\n */\nexport async function getVectorExcludes(collectionsPath: string): Promise<string[]> {\n const { vectorExcludePatterns } = await import(\"./schema/generate-postgres-ddl-logic\");\n return vectorExcludePatterns(await loadCollectionsForCli(collectionsPath));\n}\n\n/**\n * The generated SQL for the project's `autoValue: \"on_update\"` properties, if\n * it has any.\n *\n * @param sqlDir directory holding the generated SQL. Defaults to\n * `.rebase/sql` under the working directory.\n */\nexport function readTriggersDdl(sqlDir: string = generatedSqlDir()): string {\n const triggersFile = path.join(sqlDir, \"triggers.sql\");\n if (!fs.existsSync(triggersFile)) return \"\";\n return fs.readFileSync(triggersFile, \"utf-8\").trim();\n}\n\n/**\n * Install the `updated_at` trigger function and bind it to every column that\n * asked for one.\n *\n * Runs *after* Atlas, like {@link applySearchDdl} and {@link applyVectorDdl}:\n * a trigger needs its table and its column to exist. Atlas is told to ignore\n * the triggers ({@link getTriggerExcludes}) because it cannot manage a function\n * at all — its free tier refuses to parse a desired state containing one.\n *\n * A no-op when no property declares `autoValue: \"on_update\"`. A failure is\n * *not* swallowed: a push that reported success while the trigger never\n * appeared is how `updated_at` ends up silently stale for every writer that is\n * not the driver, which is the whole reason the trigger exists.\n */\nexport async function applyTriggersDdl(\n databaseUrl: string,\n sqlDir: string = generatedSqlDir()\n): Promise<void> {\n const sql = readTriggersDdl(sqlDir);\n if (!sql) return;\n\n await withAppClient(databaseUrl, client => client.query(sql));\n out(chalk.gray(\" ✓ Applied `updated_at` triggers\"));\n}\n\n/** @see getSearchExcludes */\nexport async function getTriggerExcludes(collectionsPath: string): Promise<string[]> {\n const { triggerExcludePatterns } = await import(\"./schema/generate-postgres-ddl-logic\");\n return triggerExcludePatterns(await loadCollectionsForCli(collectionsPath));\n}\n\n/**\n * Query the live database for every user table/view outside the system\n * catalogs. Separated from {@link getTableExcludes} so its failure mode can\n * be handled explicitly (fail closed) and so tests can inject a stub.\n */\n/** An index as the catalogue names it: its schema, its table and its own name. */\nexport interface ExistingIndex {\n schema: string;\n table: string;\n index: string;\n}\n\n/**\n * Every index Postgres holds on a table, by its parts — never joined with `.`,\n * which a name may itself contain.\n *\n * Constraint-backed indexes are left out: a `PRIMARY KEY` or a `UNIQUE` is a\n * *constraint* to Atlas, diffed from the constraint declaration, and naming its\n * index in an exclude would shield the constraint itself.\n */\nexport async function queryExistingIndexes(databaseUrl: string): Promise<ExistingIndex[]> {\n return withAppClient(databaseUrl, async client => {\n const res = await client.query<ExistingIndex>(`\n SELECT n.nspname AS \"schema\", t.relname AS \"table\", i.relname AS \"index\"\n FROM pg_index x\n JOIN pg_class i ON i.oid = x.indexrelid\n JOIN pg_class t ON t.oid = x.indrelid\n JOIN pg_namespace n ON n.oid = t.relnamespace\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n AND t.relkind IN ('r', 'p')\n AND NOT EXISTS (\n SELECT 1 FROM pg_constraint c WHERE c.conindid = i.oid\n );\n `);\n return res.rows;\n });\n}\n\n/**\n * Glob patterns keeping Atlas away from indexes that are not Rebase's.\n *\n * The problem this solves is live and predates the `indexes:` block. `db push`\n * is declarative, so an index on a managed table that is absent from\n * `schema.sql` is drift — and Atlas plans `DROP INDEX` for it. Verified\n * against atlas v1.2.3: create one by hand, re-run an unchanged push, and the\n * plan is a bare drop. `DROP INDEX` is not in `DESTRUCTIVE_PATTERNS`, so the\n * auto-approved apply took it without asking. Until now the only way to have\n * an index at all was to write it by hand, which made that the *only* outcome.\n *\n * Ownership rather than a prompt, because once indexes are declarable a drop\n * is usually correct: removing one from the config should remove it from the\n * database, quietly. What must never be dropped is an index Rebase never\n * created — a hand-written one, or one an introspected database arrived with.\n * {@link isRebaseIndexName} is the test, the same arrangement\n * `isGeneratedPolicyName` uses to let policy reconciliation drop only what it\n * generated.\n *\n * The named-in-the-desired-state check comes first and is what makes removal\n * work: a declared index that the author has just deleted still matches the\n * name pattern, is no longer in the plan, and so is *not* excluded — Atlas\n * drops it, as intended.\n *\n * Three-part `schema.table.index`, never two: a two-part pattern reads as a\n * *table* named `<index>` in a *schema* named `<table>`, matches nothing, and\n * reports no error — the trap already recorded for the search excludes. And\n * spelled by `atlasExcludePattern`: a raw `orders_idx[v1]` is a glob that\n * matches `orders_idxv`, so that index was dropped with no prompt.\n */\nexport async function getForeignIndexExcludes(\n databaseUrl: string,\n collectionsPath: string,\n deps: {\n queryExistingIndexes?: (databaseUrl: string) => Promise<ExistingIndex[]>;\n getManagedIndexNames?: (collectionsPath: string) => Promise<Set<string>>;\n } = {}\n): Promise<string[]> {\n const queryIndexes = deps.queryExistingIndexes ?? queryExistingIndexes;\n const getManaged = deps.getManagedIndexNames ?? managedIndexNames;\n\n const managed = await getManaged(collectionsPath);\n\n let existing: ExistingIndex[];\n try {\n existing = await queryIndexes(databaseUrl);\n } catch (err) {\n // Fails CLOSED, like getTableExcludes: without the catalogue we cannot\n // tell a foreign index from one of ours, and guessing wrong destroys\n // an index somebody is relying on.\n throw new ExcludeIntrospectionError(\n `Failed to introspect the database for unmanaged indexes: ${err instanceof Error ? err.message : String(err)}`,\n err\n );\n }\n\n return existing\n .filter(({ index }) => {\n if (managed.has(index)) return false; // ours, and still declared\n return !isRebaseIndexName(index); // ours, but no longer declared -> let it drop\n })\n .map(({ schema, table, index }) => atlasExcludePattern(schema, table, index));\n}\n\n/**\n * The index names the current collections would create — search, vector and\n * declared alike. Anything Atlas would emit is by definition not foreign.\n */\nasync function managedIndexNames(collectionsPath: string): Promise<Set<string>> {\n const collections = await loadCollectionsForCli(collectionsPath);\n const { searchExcludePatterns, vectorExcludePatterns } =\n await import(\"./schema/generate-postgres-ddl-logic\");\n const { resolveColumnName } = await import(\"./schema/column-plan-helpers\");\n const { buildCollectionIndexPlan } = await import(\"./schema/collection-index\");\n\n const names = new Set<string>(\n buildCollectionIndexPlan(collections, resolveColumnName).map(spec => spec.indexName)\n );\n // Search and vector objects are excluded from Atlas wholesale by their own\n // patterns; listing them here too is harmless and keeps this the single\n // answer to \"is this index one of ours\". The doc comment above claimed\n // vector was here long before it was — so an ANN index answered that\n // question with \"somebody else's\".\n for (const pattern of [...searchExcludePatterns(collections), ...vectorExcludePatterns(collections)]) {\n const object = excludePatternParts(pattern)?.[2];\n if (object !== undefined) names.add(object);\n }\n return names;\n}\n\n/** Every CHECK and EXCLUDE constraint the database holds. @see UNDECLARABLE_CONSTRAINTS_SQL */\nexport async function queryUndeclarableConstraints(databaseUrl: string): Promise<ExistingConstraint[]> {\n return withAppClient(databaseUrl, async client =>\n (await client.query<ExistingConstraint>(UNDECLARABLE_CONSTRAINTS_SQL)).rows);\n}\n\n/**\n * Patterns keeping Atlas away from the CHECK and EXCLUDE constraints the\n * collections do not declare — which, since a collection cannot declare\n * either, is all of them. See `schema/undeclarable-constraints.ts`.\n *\n * Fails CLOSED, like {@link getTableExcludes}: without the catalogue nothing\n * says which constraints are there, and a partial list lets the push drop a\n * rule the database enforces. `desiredSql` is the `schema.sql` Atlas is about\n * to diff against; a constraint it declares is Atlas's to manage.\n */\nexport async function getUndeclaredConstraintExcludes(\n databaseUrl: string,\n desiredSql: string,\n deps: { queryConstraints?: (databaseUrl: string) => Promise<ExistingConstraint[]> } = {}\n): Promise<string[]> {\n const queryConstraints = deps.queryConstraints ?? queryUndeclarableConstraints;\n let existing: ExistingConstraint[];\n try {\n existing = await queryConstraints(databaseUrl);\n } catch (err) {\n throw new ExcludeIntrospectionError(\n `Failed to introspect the database for CHECK and EXCLUDE constraints: ${err instanceof Error ? err.message : String(err)}`,\n err\n );\n }\n return undeclaredConstraintExcludes(existing, desiredSql);\n}\n\n/** A table or view as the catalogue names it. */\nexport interface ExistingTable {\n schema: string;\n table: string;\n}\n\nexport async function queryExistingTables(databaseUrl: string): Promise<ExistingTable[]> {\n return withAppClient(databaseUrl, async client => {\n const res = await client.query<ExistingTable>(`\n SELECT table_schema AS \"schema\", table_name AS \"table\"\n FROM information_schema.tables\n WHERE table_schema NOT IN ('pg_catalog', 'information_schema')\n AND table_type IN ('BASE TABLE', 'VIEW');\n `);\n return res.rows;\n });\n}\n\n/**\n * Raised when the exclude list could not be built. `db push` MUST abort on\n * this rather than continue: the exclude list is the only thing shielding\n * non-collection (user/system) tables from the auto-approved declarative\n * apply. A partial list — the old fail-open behaviour — meant a transient\n * introspection hiccup dropped every table not present in `schema.sql`.\n */\nexport class ExcludeIntrospectionError extends Error {\n constructor(message: string, readonly cause?: unknown) {\n super(message);\n this.name = \"ExcludeIntrospectionError\";\n }\n}\n\n/**\n * Build the `--exclude` list that protects tables Rebase doesn't manage from\n * the declarative apply. Anything not backing a collection (or its M2M\n * junctions) is excluded so Atlas never drops it.\n *\n * Fails **closed**: if the database can't be introspected we cannot know\n * which tables to protect, so we throw {@link ExcludeIntrospectionError}\n * instead of returning a near-empty list and letting the caller drop\n * everything.\n *\n * `deps` is injectable for tests; production uses the real pg-backed queries.\n */\nexport async function getTableExcludes(\n databaseUrl: string,\n collectionsPath: string,\n deps: {\n queryExistingTables?: (databaseUrl: string) => Promise<ExistingTable[]>;\n getIncludes?: (collectionsPath: string) => Promise<string[]>;\n } = {}\n): Promise<string[]> {\n const getIncludes = deps.getIncludes ?? getTableIncludes;\n const queryTables = deps.queryExistingTables ?? queryExistingTables;\n\n const includes = await getIncludes(collectionsPath);\n // Schemas the declarative apply must never touch. `rebase` holds the auth\n // tables (users, refresh_tokens, …), the RLS helper functions and Atlas's\n // own revision table. Excluding both the schema object AND its contents\n // keeps Atlas from planning a `DROP SCHEMA … CASCADE` — which on a live\n // database would take the user/auth tables with it.\n // `atlas_schema_revisions.*` covers a revision table living outside the\n // `rebase` schema — an externally managed Atlas setup — which the apply\n // must not plan changes against either.\n //\n // `auth` stays on this list even though Rebase no longer creates it, and\n // that is the point: on a database still holding the pre-1.0 schema it must\n // survive until the guarded cleanup retires it, and on a Supabase database\n // it is somebody else's and must never be in scope at all. Removing it here\n // would let a `db push` plan `DROP SCHEMA auth CASCADE` against a live\n // Supabase installation.\n const excludes: string[] = [\"atlas_schema_revisions.*\", \"auth\", \"auth.*\", \"rebase\", \"rebase.*\"];\n\n let existingTables: ExistingTable[];\n try {\n existingTables = await queryTables(databaseUrl);\n } catch (err) {\n throw new ExcludeIntrospectionError(\n `Failed to introspect the database for unmapped tables: ${err instanceof Error ? err.message : String(err)}`,\n err\n );\n }\n\n for (const { schema, table } of existingTables) {\n if (!includes.includes(`${schema}.${table}`)) {\n excludes.push(atlasExcludePattern(schema, table));\n }\n }\n\n return excludes;\n}\n","import chalk from \"chalk\";\nimport { outWarn, outError } from \"./cli-output\";\nimport { extractCauseMessage } from \"./utils/error-cause\";\nimport { redactConnectionPasswords } from \"./utils/connection-string\";\nimport { detectProjectPackageManager, withAppClient, type ProjectPackageManager } from \"./cli-helpers\";\n\n/**\n * Detect whether an error (or AggregateError wrapping multiple attempts)\n * represents an ECONNREFUSED — i.e. the database is simply not running.\n *\n * Handles:\n * - Direct `{ code: \"ECONNREFUSED\" }` errors from Node `net`\n * - `AggregateError` from dual-stack IPv4+IPv6 connection attempts\n * - Drizzle's `cause`-wrapped pg errors\n */\nexport function isEconnrefused(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { code?: string; cause?: unknown; errors?: unknown[] };\n if (e.code === \"ECONNREFUSED\") return true;\n // AggregateError from Node net (dual-stack IPv4 + IPv6)\n if (Array.isArray(e.errors)) {\n return e.errors.some(inner =>\n inner && typeof inner === \"object\" && (inner as { code?: string }).code === \"ECONNREFUSED\"\n );\n }\n // Drizzle wraps the pg error in `cause`\n if (e.cause && typeof e.cause === \"object\") {\n return isEconnrefused(e.cause);\n }\n return false;\n}\n\n/**\n * Detect PostgreSQL authentication failures.\n * PG error codes: 28P01 (invalid_password), 28000 (invalid_authorization_specification)\n */\nexport function isAuthFailure(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { code?: string; cause?: unknown };\n if (e.code === \"28P01\" || e.code === \"28000\") return true;\n if (e.cause && typeof e.cause === \"object\") {\n return isAuthFailure(e.cause);\n }\n // Also check the message for common pg auth failure text\n if (\"message\" in e && typeof (e as { message?: string }).message === \"string\") {\n const msg = (e as { message: string }).message.toLowerCase();\n if (msg.includes(\"password authentication failed\") || msg.includes(\"no pg_hba.conf entry\")) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Detect the \"SSL is not enabled on the server\" failure — the client attempted\n * an SSL handshake against a Postgres server that doesn't support it (common\n * with a plain local dev database). The fix is `?sslmode=disable` on the URL.\n */\nexport function isSslNotEnabled(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { message?: string; cause?: unknown };\n if (typeof e.message === \"string\" && e.message.toLowerCase().includes(\"ssl is not enabled on the server\")) {\n return true;\n }\n if (e.cause && typeof e.cause === \"object\") {\n return isSslNotEnabled(e.cause);\n }\n return false;\n}\n\n/**\n * The database a connection was refused by because it is closed to new\n * connections (`ALLOW_CONNECTIONS false`), or null.\n *\n * Postgres says it as `database \"x\" is not currently accepting connections`,\n * SQLSTATE 55000. That code also covers failures that have nothing to do with\n * connecting (`lastval is not yet defined in this session`), so the sentence is\n * what is matched, through Drizzle's `cause` and a dual-stack `AggregateError`.\n */\nexport function closedDatabaseName(err: unknown): string | null {\n if (!err || typeof err !== \"object\") return null;\n if (\"message\" in err && typeof err.message === \"string\") {\n const named = /database \"(.+)\" is not currently accepting connections/.exec(err.message);\n if (named) return named[1];\n }\n if (err instanceof AggregateError) {\n for (const inner of err.errors) {\n const named = closedDatabaseName(inner);\n if (named) return named;\n }\n }\n return \"cause\" in err ? closedDatabaseName(err.cause) : null;\n}\n\n/**\n * Detect PostgreSQL \"cannot drop ... because other objects depend on it\"\n * (error code 2BP01, dependent_objects_still_exist). This is the failure that\n * strands a declarative `db push` half-applied when a collection is removed but\n * an enum type it defined is still referenced by another object.\n */\nexport function isDependencyDropError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { code?: string; message?: string; cause?: unknown };\n if (e.code === \"2BP01\") return true;\n if (typeof e.message === \"string\") {\n const msg = e.message.toLowerCase();\n if (msg.includes(\"other objects depend on it\") || msg.includes(\"cannot drop type\")) {\n return true;\n }\n }\n if (e.cause && typeof e.cause === \"object\") {\n return isDependencyDropError(e.cause);\n }\n return false;\n}\n\n/**\n * Parse host:port from a DATABASE_URL for display purposes.\n *\n * Exported because every message about a connection has to name the thing it\n * could not reach, and the boot path needs the same rendering the CLI banners\n * use — including the same refusal to print the URL itself, which carries the\n * password.\n */\nexport function parseHostInfo(databaseUrl: string): string {\n try {\n const parsed = new URL(databaseUrl);\n return `${parsed.hostname}:${parsed.port || 5432}`;\n } catch {\n return \"unknown\";\n }\n}\n\n/**\n * The sentence the operating system actually produced, dug out of the wrappers.\n *\n * `connect ECONNREFUSED 127.0.0.1:5432` is written by `net`, then wrapped by\n * `pg`, then wrapped again by Drizzle as `Failed query: …` — and on a\n * dual-stack host it is not in `.cause` at all but inside the\n * `AggregateError.errors` array of one attempt per resolved address. Printing\n * the banner without it loses the one token every search engine, runbook and\n * colleague recognises.\n */\nexport function deepestErrorMessage(err: unknown): string | null {\n if (!err || typeof err !== \"object\") return null;\n const e = err as { message?: string; code?: string; cause?: unknown; errors?: unknown[] };\n\n if (Array.isArray(e.errors)) {\n for (const inner of e.errors) {\n const deeper = deepestErrorMessage(inner);\n if (deeper) return deeper;\n }\n }\n if (e.cause) {\n const deeper = deepestErrorMessage(e.cause);\n if (deeper) return deeper;\n }\n if (typeof e.message === \"string\" && e.message && !e.message.startsWith(\"Failed query:\")) {\n return e.code ? `${e.message} (${e.code})` : e.message;\n }\n return null;\n}\n\n/** One indented line naming the driver's own reason, or nothing. */\nfunction driverReasonLine(err: unknown): string {\n const reason = err === undefined ? null : deepestErrorMessage(err);\n return reason ? ` The driver said: ${reason}\\n\\n` : \"\";\n}\n\n/**\n * What named the database a db command connects to, as the CLI resolved it\n * and says in `REBASE_DATABASE_SOURCE`: `flag` (`--database-url`),\n * `environment` (the shell's DATABASE_URL), `env-file` (`.env`), `branch` (a\n * `db branch switch`), `docker` / `last-dev` (the compose `db` service) or\n * `managed` (the managed development database). `undefined` when nothing\n * says — a boot, or the driver run by hand.\n */\nfunction databaseSource(): string | undefined {\n const source = process.env.REBASE_DATABASE_SOURCE?.trim();\n return source ? source : undefined;\n}\n\n/** Where the connection string is, in the words a reader looks for it by. */\nfunction connectionStringPlace(source: string | undefined): string {\n switch (source) {\n case \"flag\": return \"the URL passed with --database-url\";\n case \"environment\": return \"DATABASE_URL in this shell's environment\";\n default: return \"DATABASE_URL\";\n }\n}\n\n/** The fixes for a database that does not answer, for what named it. */\nfunction connectionRefusedFixes(source: string | undefined): string[] {\n switch (source) {\n case \"flag\": return [\"Check the URL passed with --database-url, and that its server is running\"];\n case \"environment\": return [\"Check DATABASE_URL in this shell's environment, and that its server is running\"];\n case \"env-file\": return [\"Verify DATABASE_URL in your .env file, and that its server is running\"];\n case \"branch\": return [\n \"The branch's database is on the server DATABASE_URL in .env names: start that server\",\n \"Or leave the branch: rebase db branch switch --off\"\n ];\n case \"docker\":\n case \"last-dev\": return [\"docker compose up -d db (the project's Postgres in Docker)\"];\n case \"managed\": return [\"Restart the managed development database: rebase db stop, then run the command again\"];\n default: return [\n \"docker compose up -d db (the service a Rebase scaffold ships)\",\n \"brew services start postgresql@18\",\n \"Verify DATABASE_URL in your .env file\"\n ];\n }\n}\n\n/**\n * Format a diagnostic banner for ECONNREFUSED errors.\n */\nfunction formatConnectionRefusedBanner(databaseUrl: string, err?: unknown): string {\n const hostInfo = parseHostInfo(databaseUrl);\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Cannot connect to PostgreSQL at ${hostInfo}\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n driverReasonLine(err) +\n ` The database server is not running or is not accepting\\n` +\n ` connections. Common fixes:\\n` +\n `\\n` +\n connectionRefusedFixes(databaseSource()).map((fix) => ` • ${fix}\\n`).join(\"\") +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for authentication failures.\n */\nfunction formatAuthFailureBanner(databaseUrl: string, err?: unknown): string {\n const hostInfo = parseHostInfo(databaseUrl);\n let username = \"unknown\";\n try {\n username = new URL(databaseUrl).username || \"unknown\";\n } catch { /* ignore */ }\n\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Authentication failed for user \"${username}\" at ${hostInfo}\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n driverReasonLine(err) +\n ` PostgreSQL rejected the credentials. Common fixes:\\n` +\n `\\n` +\n ` • Check the username and password in ${connectionStringPlace(databaseSource())}\\n` +\n ` • Verify the user exists: psql -c \"\\\\du\"\\n` +\n ` • Reset the password: ALTER USER ${username} PASSWORD 'new_password';\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for \"SSL is not enabled on the server\".\n */\nfunction formatSslNotEnabledBanner(databaseUrl: string): string {\n const hostInfo = parseHostInfo(databaseUrl);\n const suggestion = databaseUrl.includes(\"?\") ? \"&sslmode=disable\" : \"?sslmode=disable\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ SSL is not enabled on the PostgreSQL server at ${hostInfo}\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` The client tried to connect over SSL, but the server does not\\n` +\n ` support it. This is normal for a plain local dev database.\\n` +\n `\\n` +\n ` Fix: append ${chalk.bold(\"sslmode=disable\")} to DATABASE_URL, e.g.\\n` +\n `\\n` +\n ` DATABASE_URL=...${suggestion}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for a database closed to new connections.\n *\n * Nothing in a development database closes one but `rebase db branch create\n * --force`, which does it for the length of the copy; one killed before it\n * opened it again leaves the project's database refusing everything. Every\n * `rebase db branch` command opens it again before it does anything else, so\n * that is the command named — with the statement itself, for a server the CLI\n * cannot reach that way.\n */\nfunction formatClosedDatabaseBanner(databaseUrl: string, database: string, err?: unknown): string {\n const hostInfo = parseHostInfo(databaseUrl);\n const reopen = databaseSource() === \"flag\"\n ? \"rebase db branch list --database-url <the same URL>\"\n : \"rebase db branch list\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Database \"${database}\" at ${hostInfo} is closed to new connections\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n driverReasonLine(err) +\n ` It is set to refuse every new connection (ALLOW_CONNECTIONS false).\\n` +\n ` \\`rebase db branch create --force\\` closes its source for the copy, and\\n` +\n ` one stopped before it finished (a closed terminal, a kill) leaves it\\n` +\n ` closed. Nothing in it is lost. Open it again with:\\n` +\n `\\n` +\n ` ${chalk.bold.green(reopen)}\\n` +\n ` (every rebase db branch command opens it before anything else)\\n` +\n `\\n` +\n ` or run this, connected to the server's postgres database:\\n` +\n `\\n` +\n ` ALTER DATABASE \"${database.replace(/\"/g, '\"\"')}\" ALLOW_CONNECTIONS true;\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for a dependency-drop failure during `db push`.\n * Explains that the database may be left partially migrated and how to recover.\n *\n * The DROP reads the connection string from `rebase db url`, as the NOT NULL\n * box's backfill does: on the managed development database `.env` leaves\n * `DATABASE_URL` unset, and the shell has none.\n */\nfunction formatDependencyDropBanner(manager: ProjectPackageManager = detectProjectPackageManager()): string {\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Schema push failed: a type/table could not be dropped\\n` +\n ` because other objects still depend on it.\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` ${chalk.yellow(\"The database may now be partially migrated.\")} Atlas applies\\n` +\n ` statements individually, so earlier changes in this push may\\n` +\n ` already be committed while later ones failed.\\n` +\n `\\n` +\n ` This commonly happens when a collection is removed but an enum\\n` +\n ` type it defined is still referenced. To recover:\\n` +\n `\\n` +\n ` 1. Inspect the leftover object named in the error above.\\n` +\n ` 2. Drop it with CASCADE, e.g.:\\n` +\n ` psql \"$(${projectRebaseCommand(manager)} db url)\" -c 'DROP TYPE \"<name>\" CASCADE;'\\n` +\n ` 3. Re-run: ${chalk.bold.green(\"rebase db push\")}\\n` +\n `\\n` +\n ` Prefer a safe, versioned workflow? Use ${chalk.bold(\"rebase db generate\")}\\n` +\n ` + ${chalk.bold(\"rebase db migrate\")} instead of push for destructive changes.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Pre-flight check: verify that the database is reachable before running\n * a heavy subprocess (Atlas, migrations, etc.).\n *\n * Exits with code 1 and a friendly banner on known failure modes.\n * On unknown errors, logs a warning and allows the caller to proceed.\n */\nexport async function checkDatabaseConnectivity(databaseUrl: string): Promise<void> {\n try {\n // On the command's own connection, which every step after this uses\n // too — so a probe that passes leaves it open rather than closing a\n // connection the next step would reopen. See `withAppClient`.\n await withAppClient(databaseUrl, client => client.query(\"SELECT 1\"), { connectionTimeoutMillis: 5000 });\n } catch (err: unknown) {\n if (isEconnrefused(err)) {\n outError(formatConnectionRefusedBanner(databaseUrl, err));\n process.exit(1);\n }\n if (isAuthFailure(err)) {\n outError(formatAuthFailureBanner(databaseUrl, err));\n process.exit(1);\n }\n if (isSslNotEnabled(err)) {\n outError(formatSslNotEnabledBanner(databaseUrl));\n process.exit(1);\n }\n const closed = closedDatabaseName(err);\n if (closed) {\n outError(formatClosedDatabaseBanner(databaseUrl, closed, err));\n process.exit(1);\n }\n // Unknown error — warn but don't block; let the downstream tool surface details\n outWarn(chalk.yellow(` ⚠ Could not verify database connectivity: ${err instanceof Error ? err.message : String(err)}`));\n outWarn(chalk.gray(\" Proceeding anyway — the command may fail if the database is unreachable.\"));\n }\n}\n\n/**\n * Post-hoc error diagnosis for direct database operations (e.g. applyPolicies).\n * Returns a formatted diagnostic string if the error matches a known pattern,\n * or null if unrecognized.\n */\nexport function diagnoseDbError(err: unknown, databaseUrl?: string): string | null {\n if (isEconnrefused(err)) {\n return formatConnectionRefusedBanner(databaseUrl || \"\", err);\n }\n if (isAuthFailure(err)) {\n return formatAuthFailureBanner(databaseUrl || \"\", err);\n }\n if (isSslNotEnabled(err)) {\n return formatSslNotEnabledBanner(databaseUrl || \"\");\n }\n const closed = closedDatabaseName(err);\n if (closed) {\n return formatClosedDatabaseBanner(databaseUrl || \"\", closed, err);\n }\n if (isDependencyDropError(err)) {\n return formatDependencyDropBanner();\n }\n return null;\n}\n\n/**\n * Say why the command failed, on the way out.\n *\n * The entry point below used to be `.catch(() => process.exit(1))`, which threw\n * the error away. Every message this file and its services raise — \"Branch\n * \\\"x\\\" already exists.\", \"the source database has active connections\", \"Branch\n * name is too long\" — was written, wrapped in the right PG error code, and then\n * discarded one frame before it reached a terminal. What a developer saw was a\n * header line, no error, and exit 1.\n *\n * Two shapes are deliberately kept quiet:\n *\n * - **A child process that already spoke.** Atlas, `pg_dump` and `psql` run\n * with inherited stdio, so their diagnosis is on the terminal already and\n * execa's wrapper adds only `Command failed with exit code 1: atlas …`.\n * `packages/cli` filters exactly these two phrasings one level up\n * (`runDbCommand`), and this is that filter, for the process that is actually\n * throwing.\n *\n * - **A message that is only a query.** Drizzle reports failures as\n * `Failed query: <sql> params:` and hides the real PostgreSQL error in\n * `cause`, so the wrapper alone tells a reader nothing they can act on. The\n * cause is appended when it says something the message does not.\n *\n * - **An error that has already printed its own diagnosis**, marked\n * `alreadyReported`. `CollectionsPathMissing` is one: it prints the path, what\n * it resolved to and the cwd it resolved against, and then throws so the entry\n * point owns the exit code. Repeating its one-line summary underneath would\n * undo the \"printed once\" this whole path exists for.\n *\n * Whatever is printed has its connection-string passwords replaced (see\n * `redactConnectionPasswords`): a driver or tool error can quote the URL it was\n * given, and this line lands in terminals and CI logs.\n */\nexport function reportCommandFailure(error: unknown): void {\n const message = redactConnectionPasswords(error instanceof Error ? error.message : String(error ?? \"\"));\n\n if ((error as { alreadyReported?: boolean } | null)?.alreadyReported) return;\n if (!message || /Command failed|exited with code/i.test(message)) return;\n\n outError(\"\");\n outError(chalk.red(` ✗ ${message}`));\n\n const rawCause = extractCauseMessage(error);\n const cause = rawCause ? redactConnectionPasswords(rawCause) : rawCause;\n if (cause && !message.includes(cause)) {\n outError(chalk.gray(` ${cause}`));\n }\n outError(\"\");\n}\n\n/* ------------------------------------------------------------------------- *\n * Atlas failures that have a remedy\n *\n * Atlas runs with a teed stderr, so what reaches these is its output as text,\n * not a `pg` error object carrying a `code`. They are deliberately NOT part of\n * `diagnoseDbError`: that one is shared with the boot path, and \"record a\n * baseline with `rebase db migrate`\" is advice for somebody standing at a\n * terminal, not for a container that has just failed to start.\n * ------------------------------------------------------------------------- */\n\n/** The table Atlas keeps its migration history in, in the `rebase` schema. */\nconst ATLAS_REVISIONS_TABLE = \"atlas_schema_revisions\";\n\n/**\n * `migrate apply` refusing because the database already has the schema.\n *\n * `42710` (duplicate_object), `42P07` (duplicate_table), `42701`\n * (duplicate_column) and `42P06` (duplicate_schema) are what a migration hits\n * when boot-ensure — or a `db push` — has already provisioned the objects it\n * was going to create. Since boot-ensure provisions *every* production\n * database, this is the normal case rather than the exotic one, and the raw\n * failure (`pq: type \"posts_status\" already exists (42710)`, then `sql/migrate:\n * write revision: … current transaction is aborted`) names no way forward.\n *\n * `42701` is the everyday one in development: `rebase dev` adds a new\n * column at boot, `db generate` writes the migration that adds it, and\n * `db migrate` then finds it there. A column is named `table.column`.\n */\nexport function parseAlreadyProvisioned(text: string): { object: string; code: string } | null {\n const column = /column \"([^\"]+)\" of relation \"([^\"]+)\" already exists/.exec(text);\n if (column && /42701/.test(text)) return { object: `${column[2]}.${column[1]}`, code: \"42701\" };\n\n const withCode = /(?:type|relation|constraint|schema) \"([^\"]+)\" already exists \\((42710|42P07|42P06)\\)/.exec(text);\n const bare = /(?:type|relation|constraint|schema) \"([^\"]+)\" already exists/.exec(text);\n // Atlas's own revisions table is not schema a migration creates: Atlas\n // failed to set up the history `--baseline` writes into, so a baseline\n // fails the same way.\n if ((withCode ?? bare)?.[1] === ATLAS_REVISIONS_TABLE) return null;\n if (withCode) return { object: withCode[1], code: withCode[2] };\n // Some Atlas builds keep the SQLSTATE on a following line instead.\n const code = /42P07|42P06|42710/.exec(text)?.[0];\n if (bare && code) return { object: bare[1], code };\n return null;\n}\n\n/**\n * The migration Atlas was running when it failed: `from version \"<v>\"` in\n * `sql/migrate: executing statement \"…\" from version \"20261013201353\": …`.\n */\nexport function parseFailedMigrationVersion(text: string): string | null {\n return /from version \"([^\"]+)\"/.exec(text)?.[1] ?? null;\n}\n\n/**\n * What to do about it: tell Atlas the database is already at a version.\n *\n * Atlas's own mechanism (`migrate apply --baseline <version>`), not a ledger of\n * ours — it writes the revision row Atlas reads, so every later `rebase db\n * migrate` is an ordinary one.\n */\nexport function formatBaselineRemedy(version: string | null, object?: string, failedVersion?: string | null): string {\n const versionToken = version ?? \"<version>\";\n // Named too when it is not the newest: marking the newest also skips the\n // migrations after the one that failed, which boot may not have covered\n // (a data migration, an index written by hand).\n const onlyTheFailedOne = failedVersion && failedVersion !== version\n ? ` To mark only the migration that failed, and run the ones after it:\\n` +\n `\\n` +\n ` ${chalk.bold.green(`rebase db migrate --baseline ${failedVersion}`)}\\n` +\n `\\n`\n : \"\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ This database already has the schema this migration creates\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n (object ? ` ${chalk.gray(`\"${object}\" is already there, so the migration cannot create it.`)}\\n\\n` : \"\") +\n ` Nothing is wrong with the migration — the database was provisioned\\n` +\n ` another way. Every Rebase boot ensures the schema, and ${chalk.bold(\"rebase db push\")}\\n` +\n ` applies it directly, so a database that has ever run either one is\\n` +\n ` ahead of the migrations recorded on it.\\n` +\n `\\n` +\n ` Record where it already is, then migrate normally:\\n` +\n `\\n` +\n ` ${chalk.bold.green(`rebase db migrate --baseline ${versionToken}`)}\\n` +\n ` ${chalk.bold.green(\"rebase db migrate\")}\\n` +\n `\\n` +\n ` The baseline is the version already applied — the numeric prefix of\\n` +\n ` the migration file describing what is in the database now. Migrations\\n` +\n ` after it run; it and everything before it are marked done.\\n` +\n `\\n` +\n onlyTheFailedOne +\n ` On a database nothing has ever booted against, none of this is needed.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Atlas's whole report when a connection was closed before it answered:\n * `Error: EOF`, sometimes with the step in front (`Error: postgres: querying\n * system variables: EOF`). The line names neither the database nor the\n * connection, so on its own it reads like a crash.\n */\nexport function parseConnectionEof(text: string): boolean {\n return /^Error: (?:.*: )?EOF\\s*$/m.test(text);\n}\n\n/**\n * What a closed connection means, and that running the command again is safe.\n *\n * On the managed development database it is almost always the connection\n * limit. PGlite is one session behind a socket server that accepts only a few\n * connections; a running `rebase dev` holds some, `atlas schema apply` opens\n * two, and the one over the limit is closed unanswered. Measured: with the\n * daemon's limit of four and three held, `db push --dry-run` failed with a\n * bare `Error: EOF`; with two held it passed. The dev server's pool lets an\n * idle connection go after 30 seconds (`idleTimeoutMillis` in connection.ts),\n * which is why a later attempt usually works.\n */\nexport function formatConnectionEofBanner(managed: boolean): string {\n const why = managed\n ? ` The managed development database accepts only a few connections at a\\n` +\n ` time, and a running ${chalk.bold(\"rebase dev\")} holds some of them. Atlas opened one more,\\n` +\n ` and it was closed before the database answered.\\n`\n : ` The server, or a proxy or pooler in front of it, closed the connection:\\n` +\n ` a restart, a connection limit, or a reaper for idle connections.\\n`;\n const next = managed\n ? ` Run the command again in a moment: it is safe to repeat, and the dev\\n` +\n ` server lets an idle connection go after 30 seconds. If it keeps failing,\\n` +\n ` stop ${chalk.bold(\"rebase dev\")} while you run it.\\n`\n : ` Run the command again: it is safe to repeat. If it keeps failing, check\\n` +\n ` the server's connection limit and that it is up: ${chalk.bold(\"rebase doctor\")}.\\n`;\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Atlas lost its connection to the database (EOF)\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n why +\n `\\n` +\n next +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/** `ALTER TABLE … SET NOT NULL` on a table that already holds rows (23502). */\nexport function parseNotNullViolation(text: string): { table: string; column: string } | null {\n const match = /column \"([^\"]+)\" of relation \"([^\"]+)\" contains null values/.exec(text);\n return match ? { table: match[2], column: match[1] } : null;\n}\n\n/**\n * Whether the column that refused NOT NULL was being added by this push, read\n * from the statement Atlas was running: `true` for `ADD COLUMN \"<column>\"`,\n * `false` for any other statement that names it (`ALTER COLUMN … SET NOT\n * NULL`), `null` when the error carried no statement.\n */\nexport function notNullColumnIsNew(stderr: string, column: string): boolean | null {\n const statement = parseFailedStatement(stderr);\n if (!statement) return null;\n const quoted = column.replace(/\"/g, \"\\\"\\\"\").replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(`ADD\\\\s+COLUMN\\\\s+(?:IF\\\\s+NOT\\\\s+EXISTS\\\\s+)?\"${quoted}\"`, \"i\").test(statement);\n}\n\n/**\n * `rebase`, spelled the way a project runs it from its own shell: a scaffold\n * keeps the CLI in `node_modules/.bin`, not on PATH, so it is `pnpm exec\n * rebase`, as `rebase start`'s hint and `rebase init`'s next steps spell it.\n */\nfunction projectRebaseCommand(manager: ProjectPackageManager): string {\n switch (manager) {\n case \"pnpm\": return \"pnpm exec rebase\";\n case \"npm\": return \"npx rebase\";\n case \"yarn\": return \"yarn rebase\";\n case \"bun\": return \"bunx rebase\";\n default: return \"rebase\";\n }\n}\n\n/**\n * The ways out, and only the ones that work for this column.\n *\n * A `defaultValue` backfills only a column the push is adding: PostgreSQL\n * fills `ADD COLUMN … NOT NULL DEFAULT …` for every existing row, but `ALTER\n * COLUMN … SET DEFAULT` touches no row that is already there, so for an\n * existing column that remedy fails exactly as before. When the error does not\n * say which it was (`isNew` null), the default is offered with that caveat.\n *\n * Boot-ensure handles this case — it adds the column nullable and sets NOT NULL\n * only when the table is empty — so a push that dies here is strictly worse\n * than the boot that would have run instead.\n */\nexport function formatNotNullViolationBanner(\n violation: { table: string; column: string },\n rowCount: number | null,\n isNew: boolean | null = null,\n manager: ProjectPackageManager = detectProjectPackageManager()\n): string {\n const { table, column } = violation;\n const rows = rowCount === null\n ? ` \"${table}\" already holds rows, and none of them has a value for \"${column}\".\\n`\n : ` \"${table}\" already holds ${rowCount} row${rowCount === 1 ? \"\" : \"s\"}, and they have no value for \"${column}\".\\n`;\n const backfill =\n `Backfill by hand first, then push again:\\n` +\n // `rebase db url`, not `$DATABASE_URL`: on the managed development\n // database `.env` leaves DATABASE_URL unset, and the shell has none.\n ` ${chalk.gray(`psql \"$(${projectRebaseCommand(manager)} db url)\" -c 'UPDATE \"${table}\" SET \"${column}\" = … WHERE \"${column}\" IS NULL;'`)}\\n`;\n const leaveOptional = `Leave it optional — take \\`validation.required\\` off the property.\\n`;\n const defaultLine = (caveat: string) =>\n `Give the property a default, so the push can backfill${caveat}:\\n` +\n ` ${chalk.gray(`{ type: \"string\", defaultValue: \"…\", validation: { required: true } }`)}\\n`;\n const remedies = isNew === false\n ? [backfill, leaveOptional]\n : isNew === true\n ? [defaultLine(\"\"), backfill, leaveOptional]\n : [backfill, defaultLine(\" (only if this push adds the column — a default never fills rows that already exist)\"), leaveOptional];\n const count = [\"\", \"One way\", \"Two ways\", \"Three ways\"][remedies.length];\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Cannot make \"${table}\".\"${column}\" required: existing rows are null\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n rows +\n ` PostgreSQL will not add the NOT NULL until every one of them does.\\n` +\n `\\n` +\n ` ${count} forward:\\n` +\n `\\n` +\n remedies.map((r, i) => ` ${i + 1}. ${r}`).join(\"\") +\n `\\n` +\n ` ${chalk.gray(\"Nothing in this push was applied: Atlas applies it as one transaction.\")}\\n` +\n ` ${chalk.gray(\"`rebase dev` would have kept the column nullable instead:\")}\\n` +\n ` ${chalk.gray(\"boot sets NOT NULL only when it is safe.\")}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/** Atlas refusing to remove a label from an enum — a renamed `enum` option id. */\nexport function parseEnumLabelDrop(text: string): { label: string; enumType: string } | null {\n const match = /dropping (?:enum )?value \"([^\"]+)\" from enum \"([^\"]+)\" is not supported/.exec(text);\n return match ? { label: match[1], enumType: match[2] } : null;\n}\n\n/**\n * Why the two paths disagree, and what retiring an option id actually costs.\n *\n * Boot-ensure adds enum labels and never removes one, so `rebase dev` accepts\n * the very edit that stops `db push` dead. PostgreSQL has no `ALTER TYPE …\n * DROP VALUE`: a label goes only by rewriting the type, which is a data\n * migration and not a schema push.\n */\nexport function formatEnumLabelDropBanner(drop: { label: string; enumType: string }): string {\n const { label, enumType } = drop;\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ \"${label}\" cannot be removed from the enum \"${enumType}\"\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` PostgreSQL has no ALTER TYPE … DROP VALUE. An option id can be added\\n` +\n ` to an enum but never taken out of one, so renaming an id reads as a\\n` +\n ` removal and a push cannot carry it.\\n` +\n `\\n` +\n ` ${chalk.yellow(\"Nothing was applied.\")} ${chalk.bold(\"rebase dev\")} accepts this same edit — boot adds\\n` +\n ` new labels and never removes one — so the two paths disagree by\\n` +\n ` design, and the old label would simply have stayed behind, unused.\\n` +\n `\\n` +\n ` To keep the id: put \"${label}\" back and change only its label text.\\n` +\n `\\n` +\n ` To retire it for real, it is a data migration:\\n` +\n `\\n` +\n ` 1. Add the new id alongside the old one, and push.\\n` +\n ` 2. Move the rows: ${chalk.gray(`UPDATE … SET … = '<new>' WHERE … = '${label}';`)}\\n` +\n ` 3. Rewrite the type, which is what actually drops the label:\\n` +\n ` ${chalk.gray(\"rebase db generate retire_option && rebase db migrate\")}\\n` +\n ` ${chalk.gray(\"(a migration can CREATE TYPE …, ALTER TABLE … TYPE … USING, DROP TYPE)\")}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * The statement Atlas was running when it failed, unescaped.\n *\n * Two spellings, one per phase: the apply quotes it\n * (`executing statement \"ALTER TABLE \\\"public\\\".\\\"people\\\" …;\": pq: …`), and\n * the dry-run's analysis does not\n * (`analyzing plan: executing statement: ALTER TABLE \"public\".\"people\" …;: pq: …`).\n */\nexport function parseFailedStatement(text: string): string | null {\n const quoted = /executing statement \"((?:[^\"\\\\]|\\\\.)*)\"/.exec(text);\n if (quoted) return quoted[1].replace(/\\\\(.)/g, \"$1\");\n const bare = /executing statement: ([\\s\\S]*?;): pq:/.exec(text);\n return bare ? bare[1] : null;\n}\n\n/** `ALTER TABLE \"public\".\"people\" …` → `{ schema: \"public\", table: \"people\" }`. */\nexport function failedStatementTable(statement: string): { schema?: string; table: string } | null {\n const ident = String.raw`(?:\"((?:[^\"]|\"\")+)\"|([A-Za-z_][A-Za-z0-9_$]*))`;\n const match = new RegExp(String.raw`^\\s*(?:ALTER\\s+TABLE|CREATE\\s+UNIQUE\\s+INDEX\\s+\\S+\\s+ON)\\s+(?:ONLY\\s+)?${ident}(?:\\.${ident})?`, \"i\")\n .exec(statement);\n if (!match) return null;\n const first = (match[1] ?? match[2]).replace(/\"\"/g, \"\\\"\");\n const second = match[3] ?? match[4];\n return second === undefined\n ? { table: first }\n : { schema: first, table: second.replace(/\"\"/g, \"\\\"\") };\n}\n\n/** A generated column standing in the way of a retype, as the catalogue has it. */\nexport interface GeneratedColumnBlocker {\n schema: string;\n table: string;\n /** The generated column. */\n column: string;\n /** The column it reads, which the statement retypes. */\n reads: string;\n}\n\n/**\n * PostgreSQL refusing to retype a column a `GENERATED ALWAYS AS … STORED`\n * expression reads (0A000). `db push` moves Rebase's own search columns out of\n * the way before the apply; this is what is left when it could not.\n */\nexport function parseGeneratedColumnRefusal(text: string): { statement: string | null } | null {\n if (!/cannot alter type of a column used by a generated column/.test(text)) return null;\n return { statement: parseFailedStatement(text) };\n}\n\n/**\n * Which generated columns read a column the failed statement retypes — read\n * from the catalogue, because PostgreSQL's DETAIL naming them does not survive\n * Atlas.\n */\nasync function generatedColumnsBlocking(\n databaseUrl: string | undefined,\n statement: string | null\n): Promise<GeneratedColumnBlocker[]> {\n if (!databaseUrl || !statement) return [];\n const target = failedStatementTable(statement);\n if (!target) return [];\n const retyped = [...statement.matchAll(/ALTER\\s+(?:COLUMN\\s+)?\"((?:[^\"]|\"\")+)\"\\s+(?:SET\\s+DATA\\s+)?TYPE\\b/gi)]\n .map(match => match[1].replace(/\"\"/g, \"\\\"\"));\n if (retyped.length === 0) return [];\n try {\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 5000 });\n await client.connect();\n try {\n const res = await client.query<GeneratedColumnBlocker>(`\n SELECT n.nspname AS \"schema\", c.relname AS \"table\", gen.attname AS \"column\", dep.attname AS \"reads\"\n FROM pg_attrdef ad\n JOIN pg_class c ON c.oid = ad.adrelid\n JOIN pg_namespace n ON n.oid = c.relnamespace\n JOIN pg_attribute gen ON gen.attrelid = ad.adrelid AND gen.attnum = ad.adnum\n JOIN pg_depend d ON d.classid = 'pg_attrdef'::regclass AND d.objid = ad.oid\n AND d.refclassid = 'pg_class'::regclass AND d.refobjid = ad.adrelid\n JOIN pg_attribute dep ON dep.attrelid = ad.adrelid AND dep.attnum = d.refobjsubid\n WHERE gen.attgenerated <> '' AND c.relname = $1\n AND ($2::text IS NULL OR n.nspname = $2) AND dep.attname = ANY($3::text[])\n ORDER BY 1, 2, 3, 4`, [target.table, target.schema ?? null, retyped]);\n return res.rows;\n } finally {\n await client.end();\n }\n } catch {\n return [];\n }\n}\n\n/**\n * What to do when a generated column stops a retype.\n *\n * A collection's own search column should never reach this — `db push` drops\n * it before the apply and rebuilds it after — so seeing one named here means\n * that machinery missed it, which is a bug worth a report. Any other generated\n * column is somebody's, with an expression Rebase holds no copy of.\n */\nexport function formatGeneratedColumnRefusalBanner(\n refusal: { statement: string | null },\n blockers: GeneratedColumnBlocker[]\n): string {\n const named = blockers.length > 0\n ? blockers.map(b => ` ${chalk.bold(`${b.schema}.${b.table}.${b.column}`)} reads \"${b.reads}\"`).join(\"\\n\") + \"\\n\\n\"\n : refusal.statement\n ? ` The statement:\\n ${chalk.gray(refusal.statement.replace(/\\s+/g, \" \"))}\\n\\n`\n : \"\";\n const first = blockers[0];\n const drop = first\n ? `ALTER TABLE \"${first.schema}\".\"${first.table}\" DROP COLUMN \"${first.column}\";`\n : \"ALTER TABLE … DROP COLUMN <the generated column>;\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ A generated column reads a column this push retypes\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n named +\n ` PostgreSQL will not retype a column while a GENERATED ALWAYS AS …\\n` +\n ` STORED expression reads it, and Atlas applies a push as one\\n` +\n ` transaction — ${chalk.yellow(\"nothing in this push was applied.\")}\\n` +\n `\\n` +\n ` A collection's ${chalk.bold(\"search\")} column is dropped and rebuilt around the apply\\n` +\n ` by the push itself. If that is the column named above, run the push\\n` +\n ` again, and please report it: it should not have got this far.\\n` +\n `\\n` +\n ` Any other generated column is yours. Drop it, push, and put it back\\n` +\n ` with its original expression:\\n` +\n `\\n` +\n ` ${chalk.gray(drop)}\\n` +\n ` ${chalk.gray(\"rebase db push\")}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * PostgreSQL refusing a retype it has no implicit cast for (42804): `string` →\n * `number`, `time` → `date`, an array back to a string. Atlas reports it from\n * the dry-run's analysis as well as from the apply, so without this a\n * `--dry-run` died with the raw error and showed no plan at all.\n */\nexport function parseUncastableRetype(text: string): { column: string; type: string; statement: string | null } | null {\n const match = /column \"((?:[^\"]|\"\")+)\" cannot be cast automatically to type ([^\\n(]+?)\\s*(?:\\(42804\\)|$|\\n)/m.exec(text);\n if (!match) return null;\n return { column: match[1], type: match[2].trim(), statement: parseFailedStatement(text) };\n}\n\n/** The `ALTER … USING` a developer would run, and the other ways past it. Boot's drift report says the same. */\nexport function formatUncastableRetypeBanner(refusal: { column: string; type: string; statement: string | null }): string {\n const { column, type, statement } = refusal;\n const target = statement ? failedStatementTable(statement) : null;\n const table = target ? `${target.schema ? `\"${target.schema}\".` : \"\"}\"${target.table}\"` : \"<table>\";\n const quotedColumn = `\"${column.replace(/\"/g, \"\\\"\\\"\")}\"`;\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ ${table}.${quotedColumn} cannot be converted to ${type} automatically\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` The collection now declares this column as ${type}, and PostgreSQL has\\n` +\n ` no implicit conversion from what the column holds today. Atlas stops\\n` +\n ` at the first such statement — ${chalk.yellow(\"nothing in this push was applied\")}, and a\\n` +\n ` --dry-run cannot show the rest of the plan until this one is settled.\\n` +\n `\\n` +\n ` Three ways forward:\\n` +\n `\\n` +\n ` 1. Convert the values yourself, saying how, then push again:\\n` +\n ` ${chalk.gray(`ALTER TABLE ${table} ALTER COLUMN ${quotedColumn} TYPE ${type} USING ${quotedColumn}::${type};`)}\\n` +\n ` ${chalk.gray(\"(adjust the USING expression to whatever turns each old value into a new one)\")}\\n` +\n ` 2. Add a new property for the new type, copy the values over with an\\n` +\n ` UPDATE, then remove the old property.\\n` +\n ` 3. Keep the column as it is — put the property's type back, or pin the\\n` +\n ` database type with ${chalk.bold(\"columnType\")}.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * A unique constraint or index PostgreSQL could not build because the rows\n * already repeat a value (23505). The DETAIL naming the value does not survive\n * Atlas, so the duplicates are read back from the table.\n */\nexport function parseUniqueBuildFailure(text: string): { index: string; statement: string | null; columns: string[] } | null {\n const match = /could not create unique index \"((?:[^\"]|\"\")+)\"/.exec(text);\n if (!match) return null;\n const statement = parseFailedStatement(text);\n let columns: string[] = [];\n if (statement) {\n const list = /UNIQUE\\s*\\(([^)]*)\\)/i.exec(statement)\n ?? /\\bON\\s+(?:ONLY\\s+)?(?:\"(?:[^\"]|\"\")+\"|[A-Za-z_][\\w$]*)(?:\\.(?:\"(?:[^\"]|\"\")+\"|[A-Za-z_][\\w$]*))?\\s*(?:USING\\s+\\w+\\s*)?\\(([^)]*)\\)/i.exec(statement);\n if (list) {\n columns = [...list[1].matchAll(/\"((?:[^\"]|\"\")+)\"|([A-Za-z_][\\w$]*)/g)]\n .map(m => (m[1] ?? m[2]).replace(/\"\"/g, \"\\\"\"));\n }\n }\n return { index: match[1], statement, columns };\n}\n\n/** Up to five repeated values, read from the database, or `[]` when we cannot say. */\nasync function duplicateValues(\n databaseUrl: string | undefined,\n target: { schema?: string; table: string } | null,\n columns: string[]\n): Promise<{ values: string; count: number }[]> {\n if (!databaseUrl || !target || columns.length === 0) return [];\n const ident = (name: string) => `\"${name.replace(/\"/g, \"\\\"\\\"\")}\"`;\n const table = `${target.schema ? `${ident(target.schema)}.` : \"\"}${ident(target.table)}`;\n const cols = columns.map(ident).join(\", \");\n try {\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 5000 });\n await client.connect();\n try {\n // Identifiers from the failed statement, quoted; no value is interpolated.\n const res = await client.query<{ values: string; count: number }>(\n `SELECT concat_ws(', ', ${columns.map(c => `${ident(c)}::text`).join(\", \")}) AS \"values\", count(*)::int AS \"count\"\n FROM ${table} GROUP BY ${cols} HAVING count(*) > 1 ORDER BY count(*) DESC LIMIT 5`);\n return res.rows;\n } finally {\n await client.end();\n }\n } catch {\n return [];\n }\n}\n\nexport function formatUniqueBuildFailureBanner(\n failure: { index: string; statement: string | null; columns: string[] },\n duplicates: { values: string; count: number }[]\n): string {\n const target = failure.statement ? failedStatementTable(failure.statement) : null;\n const table = target ? `${target.schema ? `\"${target.schema}\".` : \"\"}\"${target.table}\"` : \"<table>\";\n const cols = failure.columns.length > 0 ? failure.columns.map(c => `\"${c}\"`).join(\", \") : \"<column>\";\n const listed = duplicates.length > 0\n ? ` Repeated values:\\n` + duplicates.map(d => ` ${chalk.bold(d.values)} ${chalk.gray(`× ${d.count}`)}`).join(\"\\n\") + `\\n\\n`\n : \"\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Cannot make ${table} (${cols}) unique: rows already repeat a value\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n listed +\n ` PostgreSQL will not build \"${failure.index}\" while two rows share a\\n` +\n ` value, and Atlas applies a push as one transaction — ${chalk.yellow(\"nothing in\")}\\n` +\n ` ${chalk.yellow(\"this push was applied.\")}\\n` +\n `\\n` +\n ` Find them:\\n` +\n ` ${chalk.gray(`SELECT ${cols}, count(*) FROM ${table} GROUP BY ${cols} HAVING count(*) > 1;`)}\\n` +\n `\\n` +\n ` Then change or delete the repeats and push again, or take ${chalk.bold(\"unique\")} off\\n` +\n ` the property.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/** How many rows a table holds, or `null` when we cannot say. */\nasync function countRows(databaseUrl: string | undefined, table: string): Promise<number | null> {\n if (!databaseUrl) return null;\n try {\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 5000 });\n await client.connect();\n try {\n // Identifier interpolation, and it has to be: the name comes from\n // PostgreSQL's own error text, and `count(*)` takes no parameter in\n // that position. Quoted, with any embedded quote doubled.\n const res = await client.query(`SELECT count(*)::int AS n FROM \"${table.replace(/\"/g, \"\\\"\\\"\")}\"`);\n return (res.rows[0] as { n: number } | undefined)?.n ?? null;\n } finally {\n await client.end();\n }\n } catch {\n return null;\n }\n}\n\n/**\n * A hand-written generated column reading a column the plan wants to retype or\n * drop.\n *\n * Rebase's own generated columns are dropped and rebuilt around the apply — it\n * holds their definition in `search.sql`. This one it does not: PostgreSQL\n * stores a generated expression only in the catalogue, so dropping it to let\n * the apply through would destroy the only copy. Refuse instead, and say what\n * the operator can do about it.\n */\nexport function formatForeignGeneratedColumnBanner(\n conflicts: { schema: string; table: string; column: string; blocking: { column: string; kind: string }[] }[]\n): string {\n const lines = conflicts.map(c => {\n const reads = c.blocking.map(b => `\"${b.column}\"`).join(\", \");\n return ` ${chalk.bold(`${c.schema}.${c.table}.${c.column}`)} reads ${reads}`;\n });\n const first = conflicts[0];\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ A generated column depends on a column this push changes\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n lines.join(\"\\n\") + `\\n` +\n `\\n` +\n ` PostgreSQL refuses to retype or drop a column while a GENERATED\\n` +\n ` ALWAYS AS … STORED expression reads it, and an Atlas apply is one\\n` +\n ` transaction — so this refusal would roll back every other statement\\n` +\n ` in the push with it.\\n` +\n `\\n` +\n ` ${chalk.yellow(\"Nothing was applied.\")} Rebase rebuilds the generated columns it made\\n` +\n ` itself (a collection's ${chalk.bold(\"search\")} block) around the apply. It has no\\n` +\n ` definition for this one, and a dropped generated expression is not\\n` +\n ` recoverable from anywhere but your own migration.\\n` +\n `\\n` +\n ` Drop it, push, and put it back:\\n` +\n `\\n` +\n ` ${chalk.gray(`ALTER TABLE \"${first.schema}\".\"${first.table}\" DROP COLUMN \"${first.column}\";`)}\\n` +\n ` ${chalk.gray(\"rebase db push\")}\\n` +\n ` ${chalk.gray(\"-- then re-create the column with its original expression\")}\\n` +\n `\\n` +\n ` Or declare it through a ${chalk.bold(\"search\")} block, and Rebase will own it.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * The remedy for an Atlas invocation that failed, or `null`.\n *\n * Scoped by the invocation, because the same database state means different\n * things to different subcommands: \"already exists\" under `migrate apply` wants\n * a baseline, while under `schema apply` it is a genuine conflict.\n */\nexport async function diagnoseAtlasFailure(context: {\n domain: string;\n args: string[];\n stderr: string;\n databaseUrl?: string;\n /** The newest migration version on disk, named in the baseline remedy. */\n latestMigrationVersion?: string | null;\n /** DATABASE_URL is the managed development database (PGlite). */\n managedDatabase?: boolean;\n}): Promise<string | null> {\n const { domain, args, stderr, databaseUrl } = context;\n\n if (parseConnectionEof(stderr)) return formatConnectionEofBanner(context.managedDatabase === true);\n\n if (domain === \"migrate\" && args.includes(\"apply\")) {\n const provisioned = parseAlreadyProvisioned(stderr);\n if (provisioned) {\n return formatBaselineRemedy(\n context.latestMigrationVersion ?? null,\n provisioned.object,\n parseFailedMigrationVersion(stderr)\n );\n }\n }\n\n if (domain === \"schema\" && args.includes(\"apply\")) {\n const enumDrop = parseEnumLabelDrop(stderr);\n if (enumDrop) return formatEnumLabelDropBanner(enumDrop);\n\n const notNull = parseNotNullViolation(stderr);\n if (notNull) {\n return formatNotNullViolationBanner(notNull, await countRows(databaseUrl, notNull.table), notNullColumnIsNew(stderr, notNull.column));\n }\n\n const uncastable = parseUncastableRetype(stderr);\n if (uncastable) return formatUncastableRetypeBanner(uncastable);\n\n const unique = parseUniqueBuildFailure(stderr);\n if (unique) {\n const target = unique.statement ? failedStatementTable(unique.statement) : null;\n return formatUniqueBuildFailureBanner(unique, await duplicateValues(databaseUrl, target, unique.columns));\n }\n\n const generated = parseGeneratedColumnRefusal(stderr);\n if (generated) {\n return formatGeneratedColumnRefusalBanner(generated, await generatedColumnsBlocking(databaseUrl, generated.statement));\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
|