@rebasepro/server-postgres 0.25.0-canary.ge06257e → 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-DNg4txyf.js → BranchService-iXXZeVYD.js} +222 -19
- package/dist/BranchService-iXXZeVYD.js.map +1 -0
- package/dist/backup/backup-cli.d.ts +33 -1
- package/dist/backup/backup-service.d.ts +68 -0
- package/dist/backup/pg-binary.d.ts +2 -2
- package/dist/backup/pg-tools.d.ts +60 -1
- package/dist/{backup-cli-HCEkPcQj.js → backup-cli-B2CAn9fR.js} +105 -14
- package/dist/backup-cli-B2CAn9fR.js.map +1 -0
- package/dist/{backup-service-D-UpT7DH.js → backup-service-Bb39plam.js} +358 -21
- package/dist/backup-service-Bb39plam.js.map +1 -0
- package/dist/{boot-ddl-Dnvpmm_T.js → boot-ddl-s_3Hkz94.js} +2 -2
- package/dist/{boot-ddl-Dnvpmm_T.js.map → boot-ddl-s_3Hkz94.js.map} +1 -1
- package/dist/branch-prune.d.ts +18 -0
- package/dist/{cli-console-C_ISGwJo.js → cli-console-DQ_K-sdM.js} +17 -10
- package/dist/cli-console-DQ_K-sdM.js.map +1 -0
- package/dist/cli-console.d.ts +4 -3
- package/dist/{cli-errors-mx4sSjB8.js → cli-errors-DwQnNATs.js} +80 -12
- package/dist/{cli-errors-mx4sSjB8.js.map → cli-errors-DwQnNATs.js.map} +1 -1
- package/dist/cli-errors.d.ts +12 -1
- package/dist/{cli-flags-DUozBFyK.js → cli-flags-D-b0qL9T.js} +3 -2
- package/dist/{cli-flags-DUozBFyK.js.map → cli-flags-D-b0qL9T.js.map} +1 -1
- package/dist/cli.js +106 -40
- package/dist/cli.js.map +1 -1
- package/dist/collections/validate-relations.d.ts +8 -2
- package/dist/{deferred-index-build-Ca6OqKyR.js → deferred-index-build-Cis_HoGn.js} +6 -6
- package/dist/{deferred-index-build-Ca6OqKyR.js.map → deferred-index-build-Cis_HoGn.js.map} +1 -1
- package/dist/{doctor-CJyuhV1n.js → doctor-DvJH-IQG.js} +6 -6
- package/dist/doctor-DvJH-IQG.js.map +1 -0
- package/dist/{ensure-collection-policies-C1ux3V_z.js → ensure-collection-policies-CCqayoUz.js} +54 -8
- package/dist/ensure-collection-policies-CCqayoUz.js.map +1 -0
- package/dist/{ensure-collection-tables-Do-Bvb64.js → ensure-collection-tables-DvvImXDe.js} +7 -7
- package/dist/{ensure-collection-tables-Do-Bvb64.js.map → ensure-collection-tables-DvvImXDe.js.map} +1 -1
- package/dist/{ensure-tables-Dl_Eh9ow.js → ensure-tables-Dpgwx5sy.js} +2 -2
- package/dist/{ensure-tables-Dl_Eh9ow.js.map → ensure-tables-Dpgwx5sy.js.map} +1 -1
- package/dist/{enum-labels-kept-D9eTIWCz.js → enum-labels-kept-CUlUlRkO.js} +3 -3
- package/dist/{enum-labels-kept-D9eTIWCz.js.map → enum-labels-kept-CUlUlRkO.js.map} +1 -1
- package/dist/{generate-drizzle-schema-Bf8canaR.js → generate-drizzle-schema-C3EAP9HE.js} +3 -3
- package/dist/{generate-drizzle-schema-Bf8canaR.js.map → generate-drizzle-schema-C3EAP9HE.js.map} +1 -1
- package/dist/{generate-drizzle-schema-logic-BaT0H7Gq.js → generate-drizzle-schema-logic-DA3zM8zc.js} +3 -3
- package/dist/{generate-drizzle-schema-logic-BaT0H7Gq.js.map → generate-drizzle-schema-logic-DA3zM8zc.js.map} +1 -1
- package/dist/{generate-files-D50Pxw1G.js → generate-files-ClmOUHXj.js} +5 -5
- package/dist/{generate-files-D50Pxw1G.js.map → generate-files-ClmOUHXj.js.map} +1 -1
- package/dist/{generate-next-step-DCd7yiJ2.js → generate-next-step-DCeS4LrJ.js} +22 -14
- package/dist/{generate-next-step-DCd7yiJ2.js.map → generate-next-step-DCeS4LrJ.js.map} +1 -1
- package/dist/{generate-postgres-ddl-logic-B4k9djAV.js → generate-postgres-ddl-logic-4eUP4t8w.js} +3 -3
- package/dist/{generate-postgres-ddl-logic-B4k9djAV.js.map → generate-postgres-ddl-logic-4eUP4t8w.js.map} +1 -1
- package/dist/index.es.js +8 -8
- package/dist/{introspect-db-logic-j2vYhmdP.js → introspect-db-logic-CI5CU_n0.js} +2 -2
- package/dist/{introspect-db-logic-j2vYhmdP.js.map → introspect-db-logic-CI5CU_n0.js.map} +1 -1
- package/dist/{open-rebase-D8rytTP0.js → open-rebase-BKA7Nyoe.js} +47 -27
- package/dist/{open-rebase-D8rytTP0.js.map → open-rebase-BKA7Nyoe.js.map} +1 -1
- package/dist/{pg-error-utils-CdsGYMTm.js → pg-error-utils-Bv5n2PLn.js} +11 -4
- package/dist/pg-error-utils-Bv5n2PLn.js.map +1 -0
- package/dist/{plan-schema-CsNgK0qi.js → plan-schema-CXOucCw-.js} +2 -2
- package/dist/{plan-schema-CsNgK0qi.js.map → plan-schema-CXOucCw-.js.map} +1 -1
- package/dist/{policy-drift-CY5P1ZRu.js → policy-drift-vm0BcvcE.js} +2 -2
- package/dist/{policy-drift-CY5P1ZRu.js.map → policy-drift-vm0BcvcE.js.map} +1 -1
- package/dist/{push-index-prebuild-BDFcfRPo.js → push-index-prebuild-DIequ4bV.js} +4 -4
- package/dist/{push-index-prebuild-BDFcfRPo.js.map → push-index-prebuild-DIequ4bV.js.map} +1 -1
- package/dist/{push-legacy-renames-D4bvMWAb.js → push-legacy-renames-BqJut4F3.js} +4 -4
- package/dist/{push-legacy-renames-D4bvMWAb.js.map → push-legacy-renames-BqJut4F3.js.map} +1 -1
- package/dist/{push-policy-plan-DTXE6aVa.js → push-policy-plan-CvX8hiPb.js} +22 -6
- package/dist/push-policy-plan-CvX8hiPb.js.map +1 -0
- package/dist/{render-ddl-CrfEe__B.js → render-ddl-CQNwZdse.js} +3 -3
- package/dist/{render-ddl-CrfEe__B.js.map → render-ddl-CQNwZdse.js.map} +1 -1
- package/dist/schema/atlas-argv.d.ts +3 -1
- package/dist/schema/doctor-cli.js +4 -4
- package/dist/schema/ensure-collection-policies.d.ts +24 -0
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-next-step.d.ts +20 -8
- package/dist/schema/generate-postgres-ddl.js +2 -2
- package/dist/schema/introspect-db.js +5 -4
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/push-policy-plan.d.ts +5 -2
- package/dist/services/BranchService.d.ts +67 -0
- package/dist/{source-DB6xJv3p.js → source-Bv5KPeIx.js} +45 -2
- package/dist/source-Bv5KPeIx.js.map +1 -0
- package/dist/{websocket-DMeXOoyv.js → websocket-MC0zTBar.js} +3 -3
- package/dist/{websocket-DMeXOoyv.js.map → websocket-MC0zTBar.js.map} +1 -1
- package/package.json +7 -7
- package/dist/BranchService-DNg4txyf.js.map +0 -1
- package/dist/backup-cli-HCEkPcQj.js.map +0 -1
- package/dist/backup-service-D-UpT7DH.js.map +0 -1
- package/dist/cli-console-C_ISGwJo.js.map +0 -1
- package/dist/doctor-CJyuhV1n.js.map +0 -1
- package/dist/ensure-collection-policies-C1ux3V_z.js.map +0 -1
- package/dist/pg-error-utils-CdsGYMTm.js.map +0 -1
- package/dist/push-policy-plan-DTXE6aVa.js.map +0 -1
- package/dist/source-DB6xJv3p.js.map +0 -1
package/dist/cli-errors.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ProjectPackageManager } from "./cli-helpers.js";
|
|
1
2
|
/**
|
|
2
3
|
* Detect whether an error (or AggregateError wrapping multiple attempts)
|
|
3
4
|
* represents an ECONNREFUSED — i.e. the database is simply not running.
|
|
@@ -19,6 +20,16 @@ export declare function isAuthFailure(err: unknown): boolean;
|
|
|
19
20
|
* with a plain local dev database). The fix is `?sslmode=disable` on the URL.
|
|
20
21
|
*/
|
|
21
22
|
export declare function isSslNotEnabled(err: unknown): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The database a connection was refused by because it is closed to new
|
|
25
|
+
* connections (`ALLOW_CONNECTIONS false`), or null.
|
|
26
|
+
*
|
|
27
|
+
* Postgres says it as `database "x" is not currently accepting connections`,
|
|
28
|
+
* SQLSTATE 55000. That code also covers failures that have nothing to do with
|
|
29
|
+
* connecting (`lastval is not yet defined in this session`), so the sentence is
|
|
30
|
+
* what is matched, through Drizzle's `cause` and a dual-stack `AggregateError`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function closedDatabaseName(err: unknown): string | null;
|
|
22
33
|
/**
|
|
23
34
|
* Detect PostgreSQL "cannot drop ... because other objects depend on it"
|
|
24
35
|
* (error code 2BP01, dependent_objects_still_exist). This is the failure that
|
|
@@ -175,7 +186,7 @@ export declare function notNullColumnIsNew(stderr: string, column: string): bool
|
|
|
175
186
|
export declare function formatNotNullViolationBanner(violation: {
|
|
176
187
|
table: string;
|
|
177
188
|
column: string;
|
|
178
|
-
}, rowCount: number | null, isNew?: boolean | null): string;
|
|
189
|
+
}, rowCount: number | null, isNew?: boolean | null, manager?: ProjectPackageManager): string;
|
|
179
190
|
/** Atlas refusing to remove a label from an enum — a renamed `enum` option id. */
|
|
180
191
|
export declare function parseEnumLabelDrop(text: string): {
|
|
181
192
|
label: string;
|
|
@@ -140,7 +140,8 @@ function assertKnownFlags(domain, subcommand, args) {
|
|
|
140
140
|
...RELAYED_FLAGS,
|
|
141
141
|
...spec
|
|
142
142
|
});
|
|
143
|
-
|
|
143
|
+
const own = Object.keys(spec).filter((flag) => flag.startsWith("--"));
|
|
144
|
+
throw new Error(`\`rebase ${domain} ${subcommand}\` has no option ${unknown.split("=")[0]}` + (suggestion ? ` — did you mean ${suggestion}?` : ` — it takes ${own.join(", ")}.`) + ` Nothing was run. See \`rebase ${domain} --help\`.`);
|
|
144
145
|
}
|
|
145
146
|
throw err;
|
|
146
147
|
}
|
|
@@ -202,4 +203,4 @@ function parseCommandFlags(spec, argv, command) {
|
|
|
202
203
|
//#endregion
|
|
203
204
|
export { parseCommandFlags as a, collectionsPathIn as i, assertKnownFlags as n, parseDriverLine as o, closestWord as r, RELAYED_FLAGS as t };
|
|
204
205
|
|
|
205
|
-
//# sourceMappingURL=cli-flags-
|
|
206
|
+
//# sourceMappingURL=cli-flags-D-b0qL9T.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-flags-DUozBFyK.js","names":[],"sources":["../src/cli-flags.ts"],"sourcesContent":["/**\n * The flags each driver command takes, in one place, and the check that\n * enforces them.\n *\n * Every parser in `cli.ts` runs `arg(..., { permissive: true })`, which does not\n * mean \"be lenient\" — it means **an undeclared flag becomes a positional**. So\n * `rebase db push --alow-destructive` did not fail: the typo landed in `_`, the\n * push ran with the destructive gate still closed, and the developer read the\n * refusal as Rebase ignoring the flag they had just typed. `rebase schema\n * generate --ouput src/schema.ts` was worse — it wrote the default path and\n * said nothing, so the next build compiled a file nobody had regenerated.\n *\n * The check has to live at the entry point rather than in those parsers, and\n * that is not a detail: `db push` and `db generate` re-enter `schemaCommand`\n * and `generatePostgresDdlCommand` with the *db* line, so a strict parser\n * inside either of them would reject `--allow-destructive` on a line where it\n * is correct. One validation, once, against the spec for the command the user\n * actually named; the inner parsers keep reading an already-validated line.\n *\n * Commands with their own argument handling — `db branch` (`unexpectedBranchArgs`),\n * `db backup`, `db restore`, `db backups` — are deliberately absent: they own\n * their spec, and a second list here would be one more thing to keep in step.\n * An absent entry is \"not checked here\", never \"takes nothing\".\n */\nimport arg from \"arg\";\nimport { suggestNearMiss } from \"@rebasepro/utils\";\n\n/**\n * Flags that reach this process on a line it did not compose.\n *\n * `rebase db …` hands the driver the user's whole line: the CLI *reads*\n * `--database-url` and `--docker` to resolve the database and then relays them\n * verbatim, and `--debug` is what `bin/rebase.js` prints after every failure as\n * the thing to re-run with. Rejecting any of the three would make the driver\n * refuse commands the CLI documents.\n */\nexport const RELAYED_FLAGS = {\n \"--database-url\": String,\n \"--docker\": Boolean,\n \"--debug\": Boolean,\n \"--help\": Boolean,\n \"-h\": \"--help\"\n} satisfies arg.Spec;\n\n/**\n * Parse a driver command's line — `[\"db\", \"<subcommand>\", …]` — with the\n * relayed flags declared alongside the command's own.\n *\n * Every driver parser is permissive, and an undeclared flag in a permissive\n * parse is a positional. The positionals are what `db migrate` hands\n * `atlas migrate apply`, what `db generate` names its migration after and\n * which backup `db restore` restores, so a relayed flag left undeclared was\n * read as one of those: `rebase db migrate --docker` died on Atlas's\n * `unknown flag: --docker` after it had already written the auth schema.\n */\nexport function parseDriverLine<S extends arg.Spec>(\n spec: S,\n args: string[]\n): arg.Result<typeof RELAYED_FLAGS & S> {\n return arg({ ...RELAYED_FLAGS, ...spec }, { argv: args.slice(2), permissive: true });\n}\n\n/** Keyed by `\"<domain> <subcommand>\"`, the way the user types it. */\nexport const DRIVER_FLAG_SPECS: Record<string, arg.Spec> = {\n \"db push\": {\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--dry-run\": Boolean,\n \"--allow-destructive\": Boolean,\n \"--yes\": Boolean,\n \"-y\": \"--yes\"\n },\n \"db generate\": {\n \"--collections\": String,\n \"-c\": \"--collections\"\n },\n // Bare positionals still pass through to `atlas migrate apply`, which takes\n // an optional amount; flags do not, apart from `--baseline`.\n //\n // `--baseline <version>` is the escape hatch for the normal case, not an\n // exotic one: every Rebase boot ensures the schema, so a production\n // database has the tables before its first migration ever runs, and\n // `migrate apply` then dies on `type \"…\" already exists (42710)`. It maps\n // onto Atlas's own `migrate apply --baseline`, which writes the revision\n // row Atlas reads — no ledger of ours.\n //\n // `--refuse-destructive` is opt-in: it dry-runs the pending files and exits\n // 1, applying nothing, when one drops or narrows data — the gate `db push`\n // has always had, for the path that had none.\n \"db migrate\": {\n \"--baseline\": String,\n \"--refuse-destructive\": Boolean\n },\n \"schema generate\": {\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--output\": String,\n \"-o\": \"--output\",\n \"--out\": \"--output\",\n \"--watch\": Boolean,\n \"-w\": \"--watch\"\n },\n \"schema introspect\": {\n \"--output\": String,\n \"-o\": \"--output\",\n \"--out\": \"--output\",\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--force\": Boolean,\n \"-f\": \"--force\",\n \"--schema\": String,\n // Whether rows are sampled at all: without a terminal the prompt is\n // skipped, and the skip line names `--data-inference`.\n \"--data-inference\": Boolean,\n \"--no-data-inference\": Boolean\n },\n \"schema stale\": {\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--output\": String,\n \"-o\": \"--output\",\n \"--out\": \"--output\",\n \"--fix\": Boolean\n }\n};\n\n/**\n * One destination flag, two spellings, everywhere.\n *\n * `--out` is the primary on `rebase build`, an alias on `generate-sdk`, `db\n * backup` and `cloud env pull`, and was refused outright by the three `schema`\n * commands — so the spelling a user learned on one command was an\n * \"unknown or unexpected option\" on the next. Neither name can be retired (both\n * are shipped), so both are accepted, and {@link assertOutputAliasesPaired}\n * makes that the rule rather than a habit.\n */\nexport const OUTPUT_FLAG_ALIASES = [\"--out\", \"--output\"] as const;\n\n/**\n * Every spec that names one of the pair names both.\n *\n * Exported so the CLI's own specs can be held to it too: the drift this fixes\n * ran across two packages, and a check that only reads this file would let the\n * next `--out`-only command through.\n */\nexport function assertOutputAliasesPaired(specs: Record<string, arg.Spec>): string[] {\n const problems: string[] = [];\n for (const [command, spec] of Object.entries(specs)) {\n const present = OUTPUT_FLAG_ALIASES.filter(flag => flag in spec);\n if (present.length === 0 || present.length === OUTPUT_FLAG_ALIASES.length) continue;\n const missing = OUTPUT_FLAG_ALIASES.filter(flag => !(flag in spec));\n problems.push(`\\`${command}\\` takes ${present.join(\", \")} but not ${missing.join(\", \")}`);\n }\n return problems;\n}\n\n/**\n * The `--collections` path the user typed, or `null` if they typed none.\n *\n * Read once, at the entry point, because every command that takes it re-enters\n * the generators with the same line and each of them resolved it again — which\n * is how \"Collections path not found\" came to be printed four times before the\n * real output, and how a path that does not exist got as far as *writing* an\n * empty schema.\n *\n * Permissive, and it has to be: {@link assertKnownFlags} has already judged the\n * line, and `db push` carries flags the `--collections` spec does not name.\n */\nexport function collectionsPathIn(args: string[]): string | null {\n try {\n const parsed = arg(\n { \"--collections\": String, \"-c\": \"--collections\" },\n { argv: args.slice(2), permissive: true }\n );\n\n return parsed[\"--collections\"] ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * The long flags a `--help` usage line documents.\n *\n * `\"rebase db push [--collections <dir>] [--dry-run] …\"` → `[\"--collections\",\n * \"--dry-run\"]`. Placeholders (`<dir>`) and the alternation inside a positional\n * (`<create|list|switch>`) are not flags and are not returned.\n */\nexport function flagsInUsage(usage: string): string[] {\n return [...new Set(usage.match(/--[a-z][a-z0-9-]*/g) ?? [])];\n}\n\n/**\n * Every flag the help documents is accepted, and every flag accepted is documented.\n *\n * The drift this catches shipped: `rebase db push --help` has printed\n * `[--dry-run]` since the flag was written, `DRIVER_FLAG_SPECS[\"db push\"]` never\n * listed it, and {@link assertKnownFlags} — added later to stop typos being\n * swallowed — turned the documented flag into `unknown or unexpected option`.\n * The only way to see a push's SQL was to trip the destructive gate, which is\n * the exact problem `--dry-run` was written to solve. Worse in a project on an\n * older driver, whose permissive parser *applied* the schema on that line.\n *\n * Two hand-maintained lists in two packages cannot be kept in step by care, so\n * they are held to each other instead: `usages` is keyed the way\n * {@link DRIVER_FLAG_SPECS} is (`\"db push\"`), and comes from the CLI's own help\n * pages. A key in only one of the two is not this function's business — the\n * help covers commands that parse their own lines (`db branch`, `db backup`),\n * and an absent spec entry means \"not checked here\".\n *\n * Aliases (`\"-c\": \"--collections\"`, `\"--out\": \"--output\"`) need no line of their\n * own: they are spellings of a documented flag. Neither do {@link RELAYED_FLAGS},\n * which every driver command accepts because the CLI relays them.\n */\nexport function assertSpecMatchesUsage(\n specs: Record<string, arg.Spec>,\n usages: Record<string, string>\n): string[] {\n const problems: string[] = [];\n for (const [command, spec] of Object.entries(specs)) {\n const usage = usages[command];\n if (usage === undefined) continue;\n\n const documented = flagsInUsage(usage);\n const accepted = Object.entries(spec);\n // An alias resolves to the flag it spells; only the target needs a line.\n const canonical = new Set(\n accepted\n .map(([flag, kind]) => (typeof kind === \"string\" ? kind : flag))\n .filter(flag => flag.startsWith(\"--\"))\n );\n\n for (const flag of documented) {\n if (flag in spec || flag in RELAYED_FLAGS) continue;\n problems.push(`\\`${command}\\` documents ${flag} in its usage line but the spec rejects it`);\n }\n for (const flag of canonical) {\n if (documented.includes(flag) || flag in RELAYED_FLAGS) continue;\n problems.push(`\\`${command}\\` accepts ${flag} but no usage line documents it`);\n }\n }\n return problems;\n}\n\n/**\n * Reject a flag the named command does not take.\n *\n * `args` is the driver's whole line — `[\"db\", \"push\", …]` — so the flags are\n * read from `args.slice(2)`, past the two command words.\n *\n * Throws rather than exiting: `runPluginCommand`'s caller already turns a\n * thrown error into one red line and exit 1, and a thrown error is what the\n * tests can see.\n */\nexport function assertKnownFlags(domain: string, subcommand: string | undefined, args: string[]): void {\n if (!subcommand) return;\n const spec = DRIVER_FLAG_SPECS[`${domain} ${subcommand}`];\n if (!spec) return;\n\n try {\n arg({ ...RELAYED_FLAGS, ...spec }, { argv: args.slice(2) });\n } catch (err) {\n const unknown = unknownFlagIn(err);\n if (unknown !== null && err instanceof Error) {\n const suggestion = closestFlag(unknown, { ...RELAYED_FLAGS, ...spec });\n throw new Error(\n `${err.message}${suggestion ? ` (did you mean ${suggestion}?)` : \"\"} — run ` +\n `\\`rebase ${domain} --help\\` for the options \\`${domain} ${subcommand}\\` takes.`\n );\n }\n throw err;\n }\n}\n\n/** The flag `arg` refused as undeclared, or `null` for any other failure. */\nfunction unknownFlagIn(err: unknown): string | null {\n if (!(err instanceof Error) || !(\"code\" in err) || err.code !== \"ARG_UNKNOWN_OPTION\") return null;\n // arg's wording: `unknown or unexpected option: --targt-db`.\n return /option: (\\S+)$/.exec(err.message)?.[1] ?? null;\n}\n\n/** The declared long flag `typed` is one edit or one swap away from, if there is one. */\nfunction closestFlag(typed: string, spec: arg.Spec): string | undefined {\n const name = typed.split(\"=\")[0];\n return closestWord(name, Object.keys(spec).filter(flag => flag.startsWith(\"--\")));\n}\n\n/**\n * The word in `known` that `typed` was probably meant to be: one edit away\n * (`--targt-db`), or two neighbouring letters swapped (`--clena`, `--froce`),\n * which `suggestNearMiss` counts as two edits and so never offers.\n */\nexport function closestWord(typed: string, known: readonly string[]): string | undefined {\n return suggestNearMiss(known, typed) ?? transposedFrom(known, typed);\n}\n\nfunction transposedFrom(known: readonly string[], typed: string): string | undefined {\n return known.find((candidate) => {\n if (candidate.length !== typed.length || candidate === typed) return false;\n const differ = [...candidate].flatMap((letter, index) => (letter === typed[index] ? [] : [index]));\n return differ.length === 2 && differ[1] === differ[0] + 1\n && candidate[differ[0]] === typed[differ[1]] && candidate[differ[1]] === typed[differ[0]];\n });\n}\n\n/**\n * Parse a command that owns its argument handling, refusing what it does not\n * declare.\n *\n * `db backup`, `db restore` and `db backups list` parsed permissively, so an\n * undeclared flag became a positional and was ignored: `db restore <dump>\n * --targt-db x --create-db --yes` dropped the misspelled target and restored\n * into the live database. A typo is refused here before anything runs, naming\n * the declared flag it is one edit from, or every flag the command takes when\n * none is that close.\n *\n * @param argv the words after the command, e.g. `rawArgs.slice(2)`.\n * @param command the command as typed, for the message: `\"db restore\"`.\n */\nexport function parseCommandFlags<S extends arg.Spec>(\n spec: S,\n argv: string[],\n command: string\n): arg.Result<typeof RELAYED_FLAGS & S> {\n const full = { ...RELAYED_FLAGS, ...spec };\n try {\n return arg(full, { argv });\n } catch (err) {\n const unknown = unknownFlagIn(err);\n if (unknown === null) throw err;\n const suggestion = closestFlag(unknown, full);\n const flags = Object.keys(spec).filter(flag => flag.startsWith(\"--\"));\n throw new Error(\n `\\`rebase ${command}\\` has no option ${unknown}` +\n (suggestion ? ` — did you mean ${suggestion}?` : ` — it takes ${flags.join(\", \")}.`) +\n ` Nothing was run. See \\`rebase ${command} --help\\`.`\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoCA,IAAa,gBAAgB;CACzB,kBAAkB;CAClB,YAAY;CACZ,WAAW;CACX,UAAU;CACV,MAAM;AACV;;;;;;;;;;;;AAaA,SAAgB,gBACZ,MACA,MACoC;CACpC,QAAA,GAAA,WAAA,QAAA,CAAW;EAAE,GAAG;EAAe,GAAG;CAAK,GAAG;EAAE,MAAM,KAAK,MAAM,CAAC;EAAG,YAAY;CAAK,CAAC;AACvF;;AAGA,IAAa,oBAA8C;CACvD,WAAW;EACP,iBAAiB;EACjB,MAAM;EACN,aAAa;EACb,uBAAuB;EACvB,SAAS;EACT,MAAM;CACV;CACA,eAAe;EACX,iBAAiB;EACjB,MAAM;CACV;CAcA,cAAc;EACV,cAAc;EACd,wBAAwB;CAC5B;CACA,mBAAmB;EACf,iBAAiB;EACjB,MAAM;EACN,YAAY;EACZ,MAAM;EACN,SAAS;EACT,WAAW;EACX,MAAM;CACV;CACA,qBAAqB;EACjB,YAAY;EACZ,MAAM;EACN,SAAS;EACT,iBAAiB;EACjB,MAAM;EACN,WAAW;EACX,MAAM;EACN,YAAY;EAGZ,oBAAoB;EACpB,uBAAuB;CAC3B;CACA,gBAAgB;EACZ,iBAAiB;EACjB,MAAM;EACN,YAAY;EACZ,MAAM;EACN,SAAS;EACT,SAAS;CACb;AACJ;;;;;;;;;;;;;AA4CA,SAAgB,kBAAkB,MAA+B;CAC7D,IAAI;EAMA,QAAA,GAAA,WAAA,QAAA,CAJI;GAAE,iBAAiB;GAAQ,MAAM;EAAgB,GACjD;GAAE,MAAM,KAAK,MAAM,CAAC;GAAG,YAAY;EAAK,CAGrC,CAAA,CAAO,oBAAoB;CACtC,QAAQ;EACJ,OAAO;CACX;AACJ;;;;;;;;;;;AA2EA,SAAgB,iBAAiB,QAAgB,YAAgC,MAAsB;CACnG,IAAI,CAAC,YAAY;CACjB,MAAM,OAAO,kBAAkB,GAAG,OAAO,GAAG;CAC5C,IAAI,CAAC,MAAM;CAEX,IAAI;EACA,CAAA,GAAA,WAAA,QAAA,CAAI;GAAE,GAAG;GAAe,GAAG;EAAK,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,EAAE,CAAC;CAC9D,SAAS,KAAK;EACV,MAAM,UAAU,cAAc,GAAG;EACjC,IAAI,YAAY,QAAQ,eAAe,OAAO;GAC1C,MAAM,aAAa,YAAY,SAAS;IAAE,GAAG;IAAe,GAAG;GAAK,CAAC;GACrE,MAAM,IAAI,MACN,GAAG,IAAI,UAAU,aAAa,kBAAkB,WAAW,MAAM,GAAG,kBACxD,OAAO,8BAA8B,OAAO,GAAG,WAAW,UAC1E;EACJ;EACA,MAAM;CACV;AACJ;;AAGA,SAAS,cAAc,KAA6B;CAChD,IAAI,EAAE,eAAe,UAAU,EAAE,UAAU,QAAQ,IAAI,SAAS,sBAAsB,OAAO;CAE7F,OAAO,iBAAiB,KAAK,IAAI,OAAO,CAAC,GAAG,MAAM;AACtD;;AAGA,SAAS,YAAY,OAAe,MAAoC;CACpE,MAAM,OAAO,MAAM,MAAM,GAAG,CAAC,CAAC;CAC9B,OAAO,YAAY,MAAM,OAAO,KAAK,IAAI,CAAC,CAAC,QAAO,SAAQ,KAAK,WAAW,IAAI,CAAC,CAAC;AACpF;;;;;;AAOA,SAAgB,YAAY,OAAe,OAA8C;CACrF,OAAO,gBAAgB,OAAO,KAAK,KAAK,eAAe,OAAO,KAAK;AACvE;AAEA,SAAS,eAAe,OAA0B,OAAmC;CACjF,OAAO,MAAM,MAAM,cAAc;EAC7B,IAAI,UAAU,WAAW,MAAM,UAAU,cAAc,OAAO,OAAO;EACrE,MAAM,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,SAAS,QAAQ,UAAW,WAAW,MAAM,SAAS,CAAC,IAAI,CAAC,KAAK,CAAE;EACjG,OAAO,OAAO,WAAW,KAAK,OAAO,OAAO,OAAO,KAAK,KACjD,UAAU,OAAO,QAAQ,MAAM,OAAO,OAAO,UAAU,OAAO,QAAQ,MAAM,OAAO;CAC9F,CAAC;AACL;;;;;;;;;;;;;;;AAgBA,SAAgB,kBACZ,MACA,MACA,SACoC;CACpC,MAAM,OAAO;EAAE,GAAG;EAAe,GAAG;CAAK;CACzC,IAAI;EACA,QAAA,GAAA,WAAA,QAAA,CAAW,MAAM,EAAE,KAAK,CAAC;CAC7B,SAAS,KAAK;EACV,MAAM,UAAU,cAAc,GAAG;EACjC,IAAI,YAAY,MAAM,MAAM;EAC5B,MAAM,aAAa,YAAY,SAAS,IAAI;EAC5C,MAAM,QAAQ,OAAO,KAAK,IAAI,CAAC,CAAC,QAAO,SAAQ,KAAK,WAAW,IAAI,CAAC;EACpE,MAAM,IAAI,MACN,YAAY,QAAQ,mBAAmB,aACtC,aAAa,mBAAmB,WAAW,KAAK,eAAe,MAAM,KAAK,IAAI,EAAE,MACjF,kCAAkC,QAAQ,WAC9C;CACJ;AACJ"}
|
|
1
|
+
{"version":3,"file":"cli-flags-D-b0qL9T.js","names":[],"sources":["../src/cli-flags.ts"],"sourcesContent":["/**\n * The flags each driver command takes, in one place, and the check that\n * enforces them.\n *\n * Every parser in `cli.ts` runs `arg(..., { permissive: true })`, which does not\n * mean \"be lenient\" — it means **an undeclared flag becomes a positional**. So\n * `rebase db push --alow-destructive` did not fail: the typo landed in `_`, the\n * push ran with the destructive gate still closed, and the developer read the\n * refusal as Rebase ignoring the flag they had just typed. `rebase schema\n * generate --ouput src/schema.ts` was worse — it wrote the default path and\n * said nothing, so the next build compiled a file nobody had regenerated.\n *\n * The check has to live at the entry point rather than in those parsers, and\n * that is not a detail: `db push` and `db generate` re-enter `schemaCommand`\n * and `generatePostgresDdlCommand` with the *db* line, so a strict parser\n * inside either of them would reject `--allow-destructive` on a line where it\n * is correct. One validation, once, against the spec for the command the user\n * actually named; the inner parsers keep reading an already-validated line.\n *\n * Commands with their own argument handling — `db branch` (`unexpectedBranchArgs`),\n * `db backup`, `db restore`, `db backups` — are deliberately absent: they own\n * their spec, and a second list here would be one more thing to keep in step.\n * An absent entry is \"not checked here\", never \"takes nothing\".\n */\nimport arg from \"arg\";\nimport { suggestNearMiss } from \"@rebasepro/utils\";\n\n/**\n * Flags that reach this process on a line it did not compose.\n *\n * `rebase db …` hands the driver the user's whole line: the CLI *reads*\n * `--database-url` and `--docker` to resolve the database and then relays them\n * verbatim, and `--debug` is what `bin/rebase.js` prints after every failure as\n * the thing to re-run with. Rejecting any of the three would make the driver\n * refuse commands the CLI documents.\n */\nexport const RELAYED_FLAGS = {\n \"--database-url\": String,\n \"--docker\": Boolean,\n \"--debug\": Boolean,\n \"--help\": Boolean,\n \"-h\": \"--help\"\n} satisfies arg.Spec;\n\n/**\n * Parse a driver command's line — `[\"db\", \"<subcommand>\", …]` — with the\n * relayed flags declared alongside the command's own.\n *\n * Every driver parser is permissive, and an undeclared flag in a permissive\n * parse is a positional. The positionals are what `db migrate` hands\n * `atlas migrate apply`, what `db generate` names its migration after and\n * which backup `db restore` restores, so a relayed flag left undeclared was\n * read as one of those: `rebase db migrate --docker` died on Atlas's\n * `unknown flag: --docker` after it had already written the auth schema.\n */\nexport function parseDriverLine<S extends arg.Spec>(\n spec: S,\n args: string[]\n): arg.Result<typeof RELAYED_FLAGS & S> {\n return arg({ ...RELAYED_FLAGS, ...spec }, { argv: args.slice(2), permissive: true });\n}\n\n/** Keyed by `\"<domain> <subcommand>\"`, the way the user types it. */\nexport const DRIVER_FLAG_SPECS: Record<string, arg.Spec> = {\n \"db push\": {\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--dry-run\": Boolean,\n \"--allow-destructive\": Boolean,\n \"--yes\": Boolean,\n \"-y\": \"--yes\"\n },\n \"db generate\": {\n \"--collections\": String,\n \"-c\": \"--collections\"\n },\n // Bare positionals still pass through to `atlas migrate apply`, which takes\n // an optional amount; flags do not, apart from `--baseline`.\n //\n // `--baseline <version>` is the escape hatch for the normal case, not an\n // exotic one: every Rebase boot ensures the schema, so a production\n // database has the tables before its first migration ever runs, and\n // `migrate apply` then dies on `type \"…\" already exists (42710)`. It maps\n // onto Atlas's own `migrate apply --baseline`, which writes the revision\n // row Atlas reads — no ledger of ours.\n //\n // `--refuse-destructive` is opt-in: it dry-runs the pending files and exits\n // 1, applying nothing, when one drops or narrows data — the gate `db push`\n // has always had, for the path that had none.\n \"db migrate\": {\n \"--baseline\": String,\n \"--refuse-destructive\": Boolean\n },\n \"schema generate\": {\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--output\": String,\n \"-o\": \"--output\",\n \"--out\": \"--output\",\n \"--watch\": Boolean,\n \"-w\": \"--watch\"\n },\n \"schema introspect\": {\n \"--output\": String,\n \"-o\": \"--output\",\n \"--out\": \"--output\",\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--force\": Boolean,\n \"-f\": \"--force\",\n \"--schema\": String,\n // Whether rows are sampled at all: without a terminal the prompt is\n // skipped, and the skip line names `--data-inference`.\n \"--data-inference\": Boolean,\n \"--no-data-inference\": Boolean\n },\n \"schema stale\": {\n \"--collections\": String,\n \"-c\": \"--collections\",\n \"--output\": String,\n \"-o\": \"--output\",\n \"--out\": \"--output\",\n \"--fix\": Boolean\n }\n};\n\n/**\n * One destination flag, two spellings, everywhere.\n *\n * `--out` is the primary on `rebase build`, an alias on `generate-sdk`, `db\n * backup` and `cloud env pull`, and was refused outright by the three `schema`\n * commands — so the spelling a user learned on one command was an\n * \"unknown or unexpected option\" on the next. Neither name can be retired (both\n * are shipped), so both are accepted, and {@link assertOutputAliasesPaired}\n * makes that the rule rather than a habit.\n */\nexport const OUTPUT_FLAG_ALIASES = [\"--out\", \"--output\"] as const;\n\n/**\n * Every spec that names one of the pair names both.\n *\n * Exported so the CLI's own specs can be held to it too: the drift this fixes\n * ran across two packages, and a check that only reads this file would let the\n * next `--out`-only command through.\n */\nexport function assertOutputAliasesPaired(specs: Record<string, arg.Spec>): string[] {\n const problems: string[] = [];\n for (const [command, spec] of Object.entries(specs)) {\n const present = OUTPUT_FLAG_ALIASES.filter(flag => flag in spec);\n if (present.length === 0 || present.length === OUTPUT_FLAG_ALIASES.length) continue;\n const missing = OUTPUT_FLAG_ALIASES.filter(flag => !(flag in spec));\n problems.push(`\\`${command}\\` takes ${present.join(\", \")} but not ${missing.join(\", \")}`);\n }\n return problems;\n}\n\n/**\n * The `--collections` path the user typed, or `null` if they typed none.\n *\n * Read once, at the entry point, because every command that takes it re-enters\n * the generators with the same line and each of them resolved it again — which\n * is how \"Collections path not found\" came to be printed four times before the\n * real output, and how a path that does not exist got as far as *writing* an\n * empty schema.\n *\n * Permissive, and it has to be: {@link assertKnownFlags} has already judged the\n * line, and `db push` carries flags the `--collections` spec does not name.\n */\nexport function collectionsPathIn(args: string[]): string | null {\n try {\n const parsed = arg(\n { \"--collections\": String, \"-c\": \"--collections\" },\n { argv: args.slice(2), permissive: true }\n );\n\n return parsed[\"--collections\"] ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * The long flags a `--help` usage line documents.\n *\n * `\"rebase db push [--collections <dir>] [--dry-run] …\"` → `[\"--collections\",\n * \"--dry-run\"]`. Placeholders (`<dir>`) and the alternation inside a positional\n * (`<create|list|switch>`) are not flags and are not returned.\n */\nexport function flagsInUsage(usage: string): string[] {\n return [...new Set(usage.match(/--[a-z][a-z0-9-]*/g) ?? [])];\n}\n\n/**\n * Every flag the help documents is accepted, and every flag accepted is documented.\n *\n * The drift this catches shipped: `rebase db push --help` has printed\n * `[--dry-run]` since the flag was written, `DRIVER_FLAG_SPECS[\"db push\"]` never\n * listed it, and {@link assertKnownFlags} — added later to stop typos being\n * swallowed — turned the documented flag into `unknown or unexpected option`.\n * The only way to see a push's SQL was to trip the destructive gate, which is\n * the exact problem `--dry-run` was written to solve. Worse in a project on an\n * older driver, whose permissive parser *applied* the schema on that line.\n *\n * Two hand-maintained lists in two packages cannot be kept in step by care, so\n * they are held to each other instead: `usages` is keyed the way\n * {@link DRIVER_FLAG_SPECS} is (`\"db push\"`), and comes from the CLI's own help\n * pages. A key in only one of the two is not this function's business — the\n * help covers commands that parse their own lines (`db branch`, `db backup`),\n * and an absent spec entry means \"not checked here\".\n *\n * Aliases (`\"-c\": \"--collections\"`, `\"--out\": \"--output\"`) need no line of their\n * own: they are spellings of a documented flag. Neither do {@link RELAYED_FLAGS},\n * which every driver command accepts because the CLI relays them.\n */\nexport function assertSpecMatchesUsage(\n specs: Record<string, arg.Spec>,\n usages: Record<string, string>\n): string[] {\n const problems: string[] = [];\n for (const [command, spec] of Object.entries(specs)) {\n const usage = usages[command];\n if (usage === undefined) continue;\n\n const documented = flagsInUsage(usage);\n const accepted = Object.entries(spec);\n // An alias resolves to the flag it spells; only the target needs a line.\n const canonical = new Set(\n accepted\n .map(([flag, kind]) => (typeof kind === \"string\" ? kind : flag))\n .filter(flag => flag.startsWith(\"--\"))\n );\n\n for (const flag of documented) {\n if (flag in spec || flag in RELAYED_FLAGS) continue;\n problems.push(`\\`${command}\\` documents ${flag} in its usage line but the spec rejects it`);\n }\n for (const flag of canonical) {\n if (documented.includes(flag) || flag in RELAYED_FLAGS) continue;\n problems.push(`\\`${command}\\` accepts ${flag} but no usage line documents it`);\n }\n }\n return problems;\n}\n\n/**\n * Reject a flag the named command does not take.\n *\n * `args` is the driver's whole line — `[\"db\", \"push\", …]` — so the flags are\n * read from `args.slice(2)`, past the two command words.\n *\n * Throws rather than exiting: `runPluginCommand`'s caller already turns a\n * thrown error into one red line and exit 1, and a thrown error is what the\n * tests can see.\n */\nexport function assertKnownFlags(domain: string, subcommand: string | undefined, args: string[]): void {\n if (!subcommand) return;\n const spec = DRIVER_FLAG_SPECS[`${domain} ${subcommand}`];\n if (!spec) return;\n\n try {\n arg({ ...RELAYED_FLAGS, ...spec }, { argv: args.slice(2) });\n } catch (err) {\n const unknown = unknownFlagIn(err);\n if (unknown !== null && err instanceof Error) {\n // In the words `parseCommandFlags` and the CLI's own parser use.\n const suggestion = closestFlag(unknown, { ...RELAYED_FLAGS, ...spec });\n const own = Object.keys(spec).filter(flag => flag.startsWith(\"--\"));\n throw new Error(\n `\\`rebase ${domain} ${subcommand}\\` has no option ${unknown.split(\"=\")[0]}` +\n (suggestion ? ` — did you mean ${suggestion}?` : ` — it takes ${own.join(\", \")}.`) +\n ` Nothing was run. See \\`rebase ${domain} --help\\`.`\n );\n }\n throw err;\n }\n}\n\n/** The flag `arg` refused as undeclared, or `null` for any other failure. */\nfunction unknownFlagIn(err: unknown): string | null {\n if (!(err instanceof Error) || !(\"code\" in err) || err.code !== \"ARG_UNKNOWN_OPTION\") return null;\n // arg's wording: `unknown or unexpected option: --targt-db`.\n return /option: (\\S+)$/.exec(err.message)?.[1] ?? null;\n}\n\n/** The declared long flag `typed` is one edit or one swap away from, if there is one. */\nfunction closestFlag(typed: string, spec: arg.Spec): string | undefined {\n const name = typed.split(\"=\")[0];\n return closestWord(name, Object.keys(spec).filter(flag => flag.startsWith(\"--\")));\n}\n\n/**\n * The word in `known` that `typed` was probably meant to be: one edit away\n * (`--targt-db`), or two neighbouring letters swapped (`--clena`, `--froce`),\n * which `suggestNearMiss` counts as two edits and so never offers.\n */\nexport function closestWord(typed: string, known: readonly string[]): string | undefined {\n return suggestNearMiss(known, typed) ?? transposedFrom(known, typed);\n}\n\nfunction transposedFrom(known: readonly string[], typed: string): string | undefined {\n return known.find((candidate) => {\n if (candidate.length !== typed.length || candidate === typed) return false;\n const differ = [...candidate].flatMap((letter, index) => (letter === typed[index] ? [] : [index]));\n return differ.length === 2 && differ[1] === differ[0] + 1\n && candidate[differ[0]] === typed[differ[1]] && candidate[differ[1]] === typed[differ[0]];\n });\n}\n\n/**\n * Parse a command that owns its argument handling, refusing what it does not\n * declare.\n *\n * `db backup`, `db restore` and `db backups list` parsed permissively, so an\n * undeclared flag became a positional and was ignored: `db restore <dump>\n * --targt-db x --create-db --yes` dropped the misspelled target and restored\n * into the live database. A typo is refused here before anything runs, naming\n * the declared flag it is one edit from, or every flag the command takes when\n * none is that close.\n *\n * @param argv the words after the command, e.g. `rawArgs.slice(2)`.\n * @param command the command as typed, for the message: `\"db restore\"`.\n */\nexport function parseCommandFlags<S extends arg.Spec>(\n spec: S,\n argv: string[],\n command: string\n): arg.Result<typeof RELAYED_FLAGS & S> {\n const full = { ...RELAYED_FLAGS, ...spec };\n try {\n return arg(full, { argv });\n } catch (err) {\n const unknown = unknownFlagIn(err);\n if (unknown === null) throw err;\n const suggestion = closestFlag(unknown, full);\n const flags = Object.keys(spec).filter(flag => flag.startsWith(\"--\"));\n throw new Error(\n `\\`rebase ${command}\\` has no option ${unknown}` +\n (suggestion ? ` — did you mean ${suggestion}?` : ` — it takes ${flags.join(\", \")}.`) +\n ` Nothing was run. See \\`rebase ${command} --help\\`.`\n );\n }\n}\n"],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
__createRequire(import.meta.url);
|
|
3
3
|
import { i as __toESM } from "./rolldown-runtime-V7rFbMRe.js";
|
|
4
|
-
import {
|
|
5
|
-
import { a as outWarn, i as outError, o as atlasExcludePattern, r as out, s as excludePatternParts, t as chalk } from "./source-
|
|
6
|
-
import { t as nextStepAfterDbGenerate } from "./generate-next-step-
|
|
4
|
+
import { A as getUndeclaredConstraintExcludes, B as withAppClient, C as dropDevDatabase, D as getSearchExcludes, E as getForeignIndexExcludes, F as queryUndeclarableConstraints, G as prepareGeneratedSqlDir, H as DESIRED_STATE_URL, I as readSearchDdl, J as detectDestructiveStatements, K as decidePushSafety, L as readTriggersDdl, M as loadCollectionsForCli, N as queryColumnTypes, O as getTableExcludes, P as queryGeneratedColumnDependencies, Q as forAtlas, R as readVectorDdl, S as diagnoseMissingBin, T as ensureDevDatabaseExists, U as GENERATED_SQL_DIR, V as workspaceMemberDir, W as generatedSqlDir, X as fromAlterTable, Y as extractPlanStatements, Z as isSyncedPlan, _ as closeAppClients, a as diagnoseDbError, b as describeReinstallCommand, c as isEconnrefused, d as reportCommandFailure, et as redactConnectionPasswords, f as ExcludeIntrospectionError, g as atlasDevDatabaseUrl, h as applyVectorDdl, i as diagnoseAtlasFailure, j as getVectorExcludes, k as getTriggerExcludes, l as isSslNotEnabled, m as applyTriggersDdl, o as formatForeignGeneratedColumnBanner, p as applySearchDdl, q as destructiveOptInFlag, s as isAuthFailure, t as checkDatabaseConnectivity, v as describeBuildScriptRemedy, w as dropGeneratedColumns, x as detectProjectPackageManager, y as describeDevAddCommand, z as resolveLocalBin } from "./cli-errors-DwQnNATs.js";
|
|
5
|
+
import { a as outWarn, i as outError, o as atlasExcludePattern, r as out, s as excludePatternParts, t as chalk } from "./source-Bv5KPeIx.js";
|
|
6
|
+
import { t as nextStepAfterDbGenerate } from "./generate-next-step-DCeS4LrJ.js";
|
|
7
7
|
import { o as execa, r as dropLegacyAuthSchema, t as RLS_BOOTSTRAP_SQL } from "./rls-bootstrap-sql-Bd-hJk08.js";
|
|
8
8
|
import { n as require_arg, t as promptConfirm } from "./cli-prompt-egtNKkZl.js";
|
|
9
|
-
import { i as collectionsPathIn, n as assertKnownFlags, o as parseDriverLine, r as closestWord, t as RELAYED_FLAGS } from "./cli-flags-
|
|
9
|
+
import { i as collectionsPathIn, n as assertKnownFlags, o as parseDriverLine, r as closestWord, t as RELAYED_FLAGS } from "./cli-flags-D-b0qL9T.js";
|
|
10
10
|
import { formatRelativeTime } from "@rebasepro/utils";
|
|
11
11
|
import path from "path";
|
|
12
12
|
import fs from "fs";
|
|
@@ -656,7 +656,9 @@ function readsRevisions(domain, args) {
|
|
|
656
656
|
* all of them, so it excludes no one; what it does cost there is a connection.
|
|
657
657
|
* The managed database accepts four and a running `rebase dev` holds two, so
|
|
658
658
|
* `migrate apply`'s third — its own, the lock's, the revisions table's — was
|
|
659
|
-
* closed unanswered and Atlas said only `Error: EOF`.
|
|
659
|
+
* closed unanswered and Atlas said only `Error: EOF`. `migrate set`, which has
|
|
660
|
+
* no `--skip-lock`, is bound to the revisions schema instead, which spares it
|
|
661
|
+
* the revisions table's connection.
|
|
660
662
|
*/
|
|
661
663
|
function buildAtlasArgs(invocation) {
|
|
662
664
|
const { domain, args, url, devUrl, excludes = [], singleSession = false } = invocation;
|
|
@@ -666,6 +668,7 @@ function buildAtlasArgs(invocation) {
|
|
|
666
668
|
else if (args.includes("clean") || args.includes("inspect")) argv.push("--url", url);
|
|
667
669
|
} else if (domain === "migrate") {
|
|
668
670
|
if (args.includes("diff")) argv.push("--dev-url", devUrl);
|
|
671
|
+
else if (args.includes("set") && singleSession) argv.push("--url", withSearchPath(url, "rebase"));
|
|
669
672
|
else if (readsRevisions(domain, args)) {
|
|
670
673
|
argv.push("--url", url, "--revisions-schema", "rebase");
|
|
671
674
|
if (args.includes("apply") && !args.includes("--baseline")) argv.push("--allow-dirty");
|
|
@@ -676,6 +679,23 @@ function buildAtlasArgs(invocation) {
|
|
|
676
679
|
return argv;
|
|
677
680
|
}
|
|
678
681
|
/**
|
|
682
|
+
* The URL with its `search_path` set to `schema`: an Atlas URL bound to that
|
|
683
|
+
* schema. Every other parameter is kept as written. A URL that does not
|
|
684
|
+
* parse is returned unchanged.
|
|
685
|
+
*/
|
|
686
|
+
function withSearchPath(url, schema) {
|
|
687
|
+
let parsed;
|
|
688
|
+
try {
|
|
689
|
+
parsed = new URL(url);
|
|
690
|
+
} catch {
|
|
691
|
+
return url;
|
|
692
|
+
}
|
|
693
|
+
const params = Array.from(parsed.searchParams.entries()).filter(([key]) => key !== "search_path");
|
|
694
|
+
params.push(["search_path", schema]);
|
|
695
|
+
parsed.search = params.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
|
|
696
|
+
return parsed.toString();
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
679
699
|
* `atlas migrate apply`'s own arguments for `rebase db migrate`.
|
|
680
700
|
*
|
|
681
701
|
* `positionals` are what is left of the user's line once every flag the
|
|
@@ -1252,6 +1272,13 @@ function backupActionOf(rawArgs) {
|
|
|
1252
1272
|
}
|
|
1253
1273
|
//#endregion
|
|
1254
1274
|
//#region src/branch-prune.ts
|
|
1275
|
+
/**
|
|
1276
|
+
* The variable the CLI hands the driver the database its checkout is switched
|
|
1277
|
+
* to in. The pointer is the CLI's (`dev-db/branch-pointer.ts`); the driver is
|
|
1278
|
+
* a child process that cannot read it. Spelled the same in
|
|
1279
|
+
* `packages/cli/src/commands/db.ts`.
|
|
1280
|
+
*/
|
|
1281
|
+
var ACTIVE_BRANCH_DATABASE_ENV = "REBASE_ACTIVE_BRANCH_DATABASE";
|
|
1255
1282
|
/** True when nothing at all needs doing. */
|
|
1256
1283
|
function planIsEmpty(plan, includeDevDiff) {
|
|
1257
1284
|
return plan.staleRows.length === 0 && plan.orphanDatabases.length === 0 && plan.expired.length === 0 && (!includeDevDiff || plan.devDiff.length === 0);
|
|
@@ -1284,14 +1311,31 @@ function planPrune(input, options = {}) {
|
|
|
1284
1311
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
1285
1312
|
const present = new Set(input.databases);
|
|
1286
1313
|
const registered = new Set(input.rows.map((row) => row.dbName));
|
|
1314
|
+
const isActive = (database) => options.activeDatabase != null && database === options.activeDatabase;
|
|
1315
|
+
let active = null;
|
|
1287
1316
|
return {
|
|
1288
1317
|
staleRows: input.rows.filter((row) => !present.has(row.dbName)),
|
|
1289
|
-
orphanDatabases: input.databases.filter((name) => name.startsWith(input.branchPrefix) && !registered.has(name)).
|
|
1318
|
+
orphanDatabases: input.databases.filter((name) => name.startsWith(input.branchPrefix) && !registered.has(name)).filter((name) => {
|
|
1319
|
+
if (!isActive(name)) return true;
|
|
1320
|
+
active = {
|
|
1321
|
+
name,
|
|
1322
|
+
database: name
|
|
1323
|
+
};
|
|
1324
|
+
return false;
|
|
1325
|
+
}).sort(),
|
|
1290
1326
|
expired: options.olderThanDays == null ? [] : input.rows.filter((row) => present.has(row.dbName)).map((branch) => ({
|
|
1291
1327
|
branch,
|
|
1292
1328
|
ageDays: ageInDays(branch.createdAt, now)
|
|
1293
|
-
})).filter((entry) => entry.ageDays >= options.olderThanDays).
|
|
1294
|
-
|
|
1329
|
+
})).filter((entry) => entry.ageDays >= options.olderThanDays).filter((entry) => {
|
|
1330
|
+
if (!isActive(entry.branch.dbName)) return true;
|
|
1331
|
+
active = {
|
|
1332
|
+
name: entry.branch.name,
|
|
1333
|
+
database: entry.branch.dbName
|
|
1334
|
+
};
|
|
1335
|
+
return false;
|
|
1336
|
+
}).sort((a, b) => b.ageDays - a.ageDays),
|
|
1337
|
+
devDiff: input.databases.filter((name) => name.endsWith("_dev_diff")).sort(),
|
|
1338
|
+
active
|
|
1295
1339
|
};
|
|
1296
1340
|
}
|
|
1297
1341
|
//#endregion
|
|
@@ -1382,7 +1426,7 @@ function generatesInProcess() {
|
|
|
1382
1426
|
async function generateInProcess(what, label, run) {
|
|
1383
1427
|
let generators;
|
|
1384
1428
|
try {
|
|
1385
|
-
generators = await import("./generate-files-
|
|
1429
|
+
generators = await import("./generate-files-ClmOUHXj.js").then((n) => n.t);
|
|
1386
1430
|
} catch (err) {
|
|
1387
1431
|
outError(chalk.red(`✗ Failed to run ${what}: ${err instanceof Error ? err.message : String(err)}`));
|
|
1388
1432
|
process.exit(1);
|
|
@@ -1428,7 +1472,7 @@ async function runPluginCommand(args) {
|
|
|
1428
1472
|
else if (domain === "schema") await schemaCommand(subcommand, args);
|
|
1429
1473
|
else if (domain === "doctor") await doctorPluginCommand(args);
|
|
1430
1474
|
else if (domain === "console") {
|
|
1431
|
-
const { consoleCommand } = await import("./cli-console-
|
|
1475
|
+
const { consoleCommand } = await import("./cli-console-DQ_K-sdM.js");
|
|
1432
1476
|
process.exitCode = await consoleCommand(args);
|
|
1433
1477
|
} else {
|
|
1434
1478
|
outError(chalk.red(`Unknown domain command: ${domain}`));
|
|
@@ -1604,7 +1648,7 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1604
1648
|
return;
|
|
1605
1649
|
}
|
|
1606
1650
|
if (subcommand === "backup" || subcommand === "restore" || subcommand === "backups") {
|
|
1607
|
-
const { backupCommand, restoreCommand, backupsCommand } = await import("./backup-cli-
|
|
1651
|
+
const { backupCommand, restoreCommand, backupsCommand } = await import("./backup-cli-B2CAn9fR.js");
|
|
1608
1652
|
if (subcommand === "backup" && backupActionOf(rawArgs) === "list") await backupsCommand(rawArgs);
|
|
1609
1653
|
else if (subcommand === "backup") await backupCommand(rawArgs);
|
|
1610
1654
|
else if (subcommand === "restore") await restoreCommand(rawArgs);
|
|
@@ -1651,7 +1695,7 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1651
1695
|
}
|
|
1652
1696
|
const searchContent = readSearchDdl();
|
|
1653
1697
|
if (searchContent) {
|
|
1654
|
-
const { searchColumnDependencies } = await import("./generate-postgres-ddl-logic-
|
|
1698
|
+
const { searchColumnDependencies } = await import("./generate-postgres-ddl-logic-4eUP4t8w.js").then((n) => n.o);
|
|
1655
1699
|
const migrationConflicts = findGeneratedColumnConflicts(parseColumnMutations(migrationContent), searchColumnDependencies(await loadCollectionsForCli(collectionsPath)));
|
|
1656
1700
|
const preamble = migrationDropPreamble(migrationConflicts);
|
|
1657
1701
|
if (preamble) {
|
|
@@ -1699,9 +1743,14 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1699
1743
|
Search, vector, trigger and RLS DDL are applied by \`rebase db push\`, and at
|
|
1700
1744
|
boot by the schema ensure. For a migration-only deployment, add
|
|
1701
1745
|
${GENERATED_SQL_DIR}/search.sql, vector.sql, triggers.sql and\n policies.sql to a migration by hand.`));
|
|
1746
|
+
const nextStep = nextStepAfterDbGenerate({
|
|
1747
|
+
newestVersion: latestMigrationVersion(),
|
|
1748
|
+
wrote: wroteNewMigration,
|
|
1749
|
+
first: migrationsBefore.length === 0
|
|
1750
|
+
});
|
|
1702
1751
|
out("");
|
|
1703
|
-
for (const line of
|
|
1704
|
-
out("");
|
|
1752
|
+
for (const line of nextStep) out(line);
|
|
1753
|
+
if (nextStep.length > 0) out("");
|
|
1705
1754
|
} else {
|
|
1706
1755
|
out("");
|
|
1707
1756
|
out(chalk.bold(` 🗄️ Rebase DB ${subcommand.charAt(0).toUpperCase() + subcommand.slice(1)}`));
|
|
@@ -1930,8 +1979,8 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1930
1979
|
async function renameLegacyForeignKeys(databaseUrl, collectionsPath, dryRun) {
|
|
1931
1980
|
let renames;
|
|
1932
1981
|
try {
|
|
1933
|
-
const { loadCollections } = await import("./doctor-
|
|
1934
|
-
const { legacyForeignKeyRenames } = await import("./push-legacy-renames-
|
|
1982
|
+
const { loadCollections } = await import("./doctor-DvJH-IQG.js").then((n) => n.t);
|
|
1983
|
+
const { legacyForeignKeyRenames } = await import("./push-legacy-renames-BqJut4F3.js");
|
|
1935
1984
|
const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
|
|
1936
1985
|
renames = await withAppClient(databaseUrl, (client) => legacyForeignKeyRenames({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, collections));
|
|
1937
1986
|
} catch {
|
|
@@ -1950,8 +1999,8 @@ async function renameLegacyForeignKeys(databaseUrl, collectionsPath, dryRun) {
|
|
|
1950
1999
|
/** Names the enum labels the database keeps after the collections dropped them. See `schema/enum-labels-kept.ts`. */
|
|
1951
2000
|
async function reportKeptEnumLabels(databaseUrl, collectionsPath) {
|
|
1952
2001
|
try {
|
|
1953
|
-
const { loadCollections } = await import("./doctor-
|
|
1954
|
-
const { keptEnumLabels, formatKeptEnumLabelsNotice } = await import("./enum-labels-kept-
|
|
2002
|
+
const { loadCollections } = await import("./doctor-DvJH-IQG.js").then((n) => n.t);
|
|
2003
|
+
const { keptEnumLabels, formatKeptEnumLabelsNotice } = await import("./enum-labels-kept-CUlUlRkO.js");
|
|
1955
2004
|
const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
|
|
1956
2005
|
const kept = await withAppClient(databaseUrl, (client) => keptEnumLabels({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, collections));
|
|
1957
2006
|
if (kept.length > 0) outWarn(chalk.yellow(formatKeptEnumLabelsNotice(kept)));
|
|
@@ -1960,8 +2009,8 @@ async function reportKeptEnumLabels(databaseUrl, collectionsPath) {
|
|
|
1960
2009
|
async function prebuildForeignKeyIndexes(databaseUrl, collectionsPath, dryRun) {
|
|
1961
2010
|
let missing;
|
|
1962
2011
|
try {
|
|
1963
|
-
const { loadCollections } = await import("./doctor-
|
|
1964
|
-
const { missingForeignKeyIndexes } = await import("./push-index-prebuild-
|
|
2012
|
+
const { loadCollections } = await import("./doctor-DvJH-IQG.js").then((n) => n.t);
|
|
2013
|
+
const { missingForeignKeyIndexes } = await import("./push-index-prebuild-DIequ4bV.js");
|
|
1965
2014
|
const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
|
|
1966
2015
|
missing = await withAppClient(databaseUrl, (client) => missingForeignKeyIndexes({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, collections));
|
|
1967
2016
|
} catch {
|
|
@@ -1971,7 +2020,7 @@ async function prebuildForeignKeyIndexes(databaseUrl, collectionsPath, dryRun) {
|
|
|
1971
2020
|
if (missing.length === 0 || dryRun) return names;
|
|
1972
2021
|
out(chalk.gray(` Building ${missing.length} foreign-key index(es) on existing tables first — CONCURRENTLY, so writes go on:`));
|
|
1973
2022
|
try {
|
|
1974
|
-
const { buildDeferredIndexes } = await import("./deferred-index-build-
|
|
2023
|
+
const { buildDeferredIndexes } = await import("./deferred-index-build-Cis_HoGn.js").then((n) => n.r);
|
|
1975
2024
|
const outcome = await withAppClient(databaseUrl, (client) => buildDeferredIndexes(async () => ({
|
|
1976
2025
|
dedicated: true,
|
|
1977
2026
|
query: async (text) => ({ rows: (await client.query(text)).rows }),
|
|
@@ -2019,8 +2068,8 @@ async function lockManagedTables(databaseUrl, collectionsPath) {
|
|
|
2019
2068
|
let unsecured;
|
|
2020
2069
|
let userRole;
|
|
2021
2070
|
try {
|
|
2022
|
-
const { lockCollectionTables } = await import("./ensure-collection-policies-
|
|
2023
|
-
const { loadCollections } = await import("./doctor-
|
|
2071
|
+
const { lockCollectionTables } = await import("./ensure-collection-policies-CCqayoUz.js");
|
|
2072
|
+
const { loadCollections } = await import("./doctor-DvJH-IQG.js").then((n) => n.t);
|
|
2024
2073
|
userRole = (await import("./rls-enforcement-D7P4-ZYX.js").then((n) => n.c)).REBASE_USER_ROLE;
|
|
2025
2074
|
const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
|
|
2026
2075
|
({unsecured} = await withAppClient(databaseUrl, (client) => lockCollectionTables({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, collections)));
|
|
@@ -2056,8 +2105,8 @@ async function lockManagedTables(databaseUrl, collectionsPath) {
|
|
|
2056
2105
|
async function ensureAuthTables(databaseUrl, collectionsPath) {
|
|
2057
2106
|
try {
|
|
2058
2107
|
const { drizzle } = await import("drizzle-orm/node-postgres");
|
|
2059
|
-
const { ensureAuthTablesExist } = await import("./ensure-tables-
|
|
2060
|
-
const { loadCollections } = await import("./doctor-
|
|
2108
|
+
const { ensureAuthTablesExist } = await import("./ensure-tables-Dpgwx5sy.js").then((n) => n.n);
|
|
2109
|
+
const { loadCollections } = await import("./doctor-DvJH-IQG.js").then((n) => n.t);
|
|
2061
2110
|
const authCollection = (await loadCollections(path.resolve(process.cwd(), collectionsPath))).find((c) => {
|
|
2062
2111
|
const a = c.auth;
|
|
2063
2112
|
return a === true || typeof a === "object" && a !== null && a.enabled === true;
|
|
@@ -2137,9 +2186,9 @@ async function applyPolicies(databaseUrl, collectionsPath) {
|
|
|
2137
2186
|
*/
|
|
2138
2187
|
async function policyStamping(databaseUrl, collectionsPath, policiesSql) {
|
|
2139
2188
|
try {
|
|
2140
|
-
const { loadCollections } = await import("./doctor-
|
|
2141
|
-
const { planCollectionPolicies } = await import("./generate-postgres-ddl-logic-
|
|
2142
|
-
const { policyStampSql } = await import("./ensure-collection-policies-
|
|
2189
|
+
const { loadCollections } = await import("./doctor-DvJH-IQG.js").then((n) => n.t);
|
|
2190
|
+
const { planCollectionPolicies } = await import("./generate-postgres-ddl-logic-4eUP4t8w.js").then((n) => n.o);
|
|
2191
|
+
const { policyStampSql } = await import("./ensure-collection-policies-CCqayoUz.js");
|
|
2143
2192
|
const { pinSearchPath } = await import("./connection-C938nR3E.js").then((n) => n.n);
|
|
2144
2193
|
return {
|
|
2145
2194
|
statements: stampableTables(planCollectionPolicies(await loadCollections(path.resolve(process.cwd(), collectionsPath))), policiesSql).map((plan) => policyStampSql(plan)).filter((statement) => statement !== void 0),
|
|
@@ -2157,8 +2206,8 @@ async function policyStamping(databaseUrl, collectionsPath, policiesSql) {
|
|
|
2157
2206
|
*/
|
|
2158
2207
|
async function readPolicyPushPlan(databaseUrl, collectionsPath) {
|
|
2159
2208
|
try {
|
|
2160
|
-
const { formatPolicyPushPlan, planPolicyPush, summarizePolicyPushPlan } = await import("./push-policy-plan-
|
|
2161
|
-
const { loadCollections } = await import("./doctor-
|
|
2209
|
+
const { formatPolicyPushPlan, planPolicyPush, summarizePolicyPushPlan } = await import("./push-policy-plan-CvX8hiPb.js");
|
|
2210
|
+
const { loadCollections } = await import("./doctor-DvJH-IQG.js").then((n) => n.t);
|
|
2162
2211
|
const { pinSearchPath } = await import("./connection-C938nR3E.js").then((n) => n.n);
|
|
2163
2212
|
const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
|
|
2164
2213
|
const plan = await withAppClient(databaseUrl, (client) => planPolicyPush({ query: async (text, values) => ({ rows: (await client.query(text, values)).rows }) }, collections, { searchPath: pinSearchPath(databaseUrl) === databaseUrl ? void 0 : "public" }));
|
|
@@ -2186,8 +2235,8 @@ async function readPolicyPushPlan(databaseUrl, collectionsPath) {
|
|
|
2186
2235
|
*/
|
|
2187
2236
|
async function reconcilePolicies(databaseUrl, collectionsPath) {
|
|
2188
2237
|
try {
|
|
2189
|
-
const { checkPolicyDrift, dropOrphanedPolicies, formatPolicyDrift, hasDrift } = await import("./policy-drift-
|
|
2190
|
-
const { loadCollections } = await import("./doctor-
|
|
2238
|
+
const { checkPolicyDrift, dropOrphanedPolicies, formatPolicyDrift, hasDrift } = await import("./policy-drift-vm0BcvcE.js").then((n) => n.s);
|
|
2239
|
+
const { loadCollections } = await import("./doctor-DvJH-IQG.js").then((n) => n.t);
|
|
2191
2240
|
const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
|
|
2192
2241
|
await withAppClient(databaseUrl, async (client) => {
|
|
2193
2242
|
const drift = await checkPolicyDrift(client, collections);
|
|
@@ -2233,6 +2282,11 @@ var BRANCH_FLAGS = {
|
|
|
2233
2282
|
"--yes": Boolean,
|
|
2234
2283
|
"-y": "--yes"
|
|
2235
2284
|
};
|
|
2285
|
+
/** The source `branch create --from <source>` names, if it names one. */
|
|
2286
|
+
function createSource(rawArgs) {
|
|
2287
|
+
const at = rawArgs.indexOf("--from");
|
|
2288
|
+
return at !== -1 && rawArgs[at + 1] ? rawArgs[at + 1] : void 0;
|
|
2289
|
+
}
|
|
2236
2290
|
async function branchCommand(rawArgs) {
|
|
2237
2291
|
const branchAction = rawArgs[2];
|
|
2238
2292
|
if (!branchAction || branchAction === "--help") {
|
|
@@ -2265,9 +2319,11 @@ async function branchCommand(rawArgs) {
|
|
|
2265
2319
|
process.exit(1);
|
|
2266
2320
|
}
|
|
2267
2321
|
const { DatabasePoolManager } = await import("./databasePoolManager-Yqdw14wY.js").then((n) => n.n);
|
|
2268
|
-
const { BranchService } = await import("./BranchService-
|
|
2322
|
+
const { BranchService, reopenClosedDatabases, reopenedDatabasesNotice } = await import("./BranchService-iXXZeVYD.js").then((n) => n.n);
|
|
2269
2323
|
const { drizzle } = await import("drizzle-orm/node-postgres");
|
|
2270
2324
|
const { Pool } = await import("pg");
|
|
2325
|
+
const healed = await reopenClosedDatabases(databaseUrl, [branchAction === "create" ? createSource(rawArgs) : void 0, process.env[ACTIVE_BRANCH_DATABASE_ENV]]);
|
|
2326
|
+
for (const line of reopenedDatabasesNotice(healed)) outWarn(chalk.yellow(` ${line}`));
|
|
2271
2327
|
await checkDatabaseConnectivity(databaseUrl);
|
|
2272
2328
|
await closeAppClients();
|
|
2273
2329
|
const pool = new Pool({
|
|
@@ -2287,9 +2343,7 @@ async function branchCommand(rawArgs) {
|
|
|
2287
2343
|
out(chalk.gray(" Usage: rebase db branch create <name> [--from <source>]"));
|
|
2288
2344
|
process.exit(1);
|
|
2289
2345
|
}
|
|
2290
|
-
|
|
2291
|
-
const fromIdx = rawArgs.indexOf("--from");
|
|
2292
|
-
if (fromIdx !== -1 && rawArgs[fromIdx + 1]) source = rawArgs[fromIdx + 1];
|
|
2346
|
+
const source = createSource(rawArgs);
|
|
2293
2347
|
const force = rawArgs.includes("--force");
|
|
2294
2348
|
out("");
|
|
2295
2349
|
out(chalk.bold(" 🌿 Creating database branch..."));
|
|
@@ -2314,10 +2368,12 @@ async function branchCommand(rawArgs) {
|
|
|
2314
2368
|
else {
|
|
2315
2369
|
out(chalk.bold(` 🌿 ${branches.length} branch(es):`));
|
|
2316
2370
|
out("");
|
|
2371
|
+
const current = process.env[ACTIVE_BRANCH_DATABASE_ENV]?.trim();
|
|
2317
2372
|
for (const b of branches) {
|
|
2318
2373
|
const size = b.sizeBytes != null ? chalk.gray(` (${formatBytes(b.sizeBytes)})`) : "";
|
|
2319
2374
|
const age = chalk.gray(` — created ${timeAgo(b.createdAt)}`);
|
|
2320
|
-
|
|
2375
|
+
const here = current && b.database === current ? chalk.cyan(" (current — this checkout is on it)") : "";
|
|
2376
|
+
out(` ${chalk.green("●")} ${chalk.bold(b.name)}${here}${size}${age}`);
|
|
2321
2377
|
out(chalk.gray(` from ${b.parentDatabase}`));
|
|
2322
2378
|
}
|
|
2323
2379
|
}
|
|
@@ -2379,9 +2435,18 @@ async function branchCommand(rawArgs) {
|
|
|
2379
2435
|
rows,
|
|
2380
2436
|
databases,
|
|
2381
2437
|
branchPrefix: "rb_"
|
|
2382
|
-
}, {
|
|
2438
|
+
}, {
|
|
2439
|
+
olderThanDays,
|
|
2440
|
+
activeDatabase: process.env["REBASE_ACTIVE_BRANCH_DATABASE"]?.trim() || null
|
|
2441
|
+
});
|
|
2442
|
+
const keptActive = () => {
|
|
2443
|
+
if (!plan.active) return;
|
|
2444
|
+
out(` ${chalk.gray("○")} ${chalk.bold(plan.active.name)} ${chalk.gray("— this checkout is switched to it, so it is kept")}`);
|
|
2445
|
+
out(chalk.gray(` To prune it, leave it first: ${chalk.cyan("rebase db branch switch --off")}`));
|
|
2446
|
+
};
|
|
2383
2447
|
out("");
|
|
2384
2448
|
if (planIsEmpty(plan, includeDevDiff)) {
|
|
2449
|
+
keptActive();
|
|
2385
2450
|
out(chalk.gray(" Nothing to prune."));
|
|
2386
2451
|
if (!olderThanDays && rows.length > 0) out(chalk.gray(` ${rows.length} branch(es) are healthy — --older-than 14 also removes old ones.`));
|
|
2387
2452
|
if (!includeDevDiff && plan.devDiff.length > 0) {
|
|
@@ -2396,6 +2461,7 @@ async function branchCommand(rawArgs) {
|
|
|
2396
2461
|
for (const row of plan.staleRows) out(` ${chalk.yellow("○")} ${chalk.bold(row.name)} ${chalk.gray("— registered, but its database is gone; the entry will be removed")}`);
|
|
2397
2462
|
for (const dbName of plan.orphanDatabases) out(` ${chalk.yellow("○")} ${chalk.bold(dbName)} ${chalk.gray("— a branch database with no entry; the database will be dropped")}`);
|
|
2398
2463
|
for (const { branch, ageDays } of plan.expired) out(` ${chalk.red("●")} ${chalk.bold(branch.name)} ${chalk.gray(`— ${ageDays} days old; branch and database will be dropped`)}`);
|
|
2464
|
+
keptActive();
|
|
2399
2465
|
if (includeDevDiff) for (const dbName of plan.devDiff) out(` ${chalk.red("●")} ${chalk.bold(dbName)} ${chalk.gray("— Atlas scratch database; will be dropped")}`);
|
|
2400
2466
|
else if (plan.devDiff.length > 0) out(chalk.gray(` (${plan.devDiff.length} Atlas scratch database(s) not listed — --include-dev-diff)`));
|
|
2401
2467
|
out("");
|
|
@@ -2762,9 +2828,9 @@ async function schemaStaleCommand(rawArgs) {
|
|
|
2762
2828
|
const schemaFile = path.resolve(process.cwd(), outputPath);
|
|
2763
2829
|
const fix = Boolean(argsList["--fix"]);
|
|
2764
2830
|
const generatedExists = fs.existsSync(schemaFile);
|
|
2765
|
-
const { loadCollections } = await import("./doctor-
|
|
2831
|
+
const { loadCollections } = await import("./doctor-DvJH-IQG.js").then((n) => n.t);
|
|
2766
2832
|
const { findLegacyForeignKeyNames, compareGeneratedDeclarations, staleVerdict } = await import("./generated-schema-staleness-FlxM2i4i.js").then((n) => n.r);
|
|
2767
|
-
const { generateSchema } = await import("./generate-drizzle-schema-logic-
|
|
2833
|
+
const { generateSchema } = await import("./generate-drizzle-schema-logic-DA3zM8zc.js").then((n) => n.n);
|
|
2768
2834
|
const { relationalCollections } = await import("@rebasepro/common");
|
|
2769
2835
|
let stale = [];
|
|
2770
2836
|
let differences = [];
|