@rebasepro/server-postgres 0.24.1-canary.gcb918fc → 0.25.0-canary.ge06257e

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.
Files changed (137) hide show
  1. package/dist/{BranchService-Bsn2wfaj.js → BranchService-DNg4txyf.js} +14 -4
  2. package/dist/BranchService-DNg4txyf.js.map +1 -0
  3. package/dist/PostgresBackendDriver.d.ts +13 -0
  4. package/dist/PostgresBootstrapper.d.ts +10 -0
  5. package/dist/backup/backup-cli.d.ts +88 -2
  6. package/dist/backup/backup-service.d.ts +11 -1
  7. package/dist/backup/pg-tools.d.ts +18 -0
  8. package/dist/{backup-cli-DnpJi6I7.js → backup-cli-HCEkPcQj.js} +215 -79
  9. package/dist/backup-cli-HCEkPcQj.js.map +1 -0
  10. package/dist/{backup-service-BTBB7RXn.js → backup-service-D-UpT7DH.js} +121 -11
  11. package/dist/backup-service-D-UpT7DH.js.map +1 -0
  12. package/dist/{boot-ddl-DVpsFN6r.js → boot-ddl-Dnvpmm_T.js} +3 -3
  13. package/dist/{boot-ddl-DVpsFN6r.js.map → boot-ddl-Dnvpmm_T.js.map} +1 -1
  14. package/dist/branch-argv.d.ts +18 -6
  15. package/dist/cli-console-C_ISGwJo.js +255 -0
  16. package/dist/cli-console-C_ISGwJo.js.map +1 -0
  17. package/dist/cli-console.d.ts +10 -0
  18. package/dist/{cli-errors-CaI4ntyD.js → cli-errors-mx4sSjB8.js} +477 -103
  19. package/dist/cli-errors-mx4sSjB8.js.map +1 -0
  20. package/dist/cli-errors.d.ts +40 -4
  21. package/dist/{cli-flags-JNI-pwSf.js → cli-flags-DUozBFyK.js} +70 -6
  22. package/dist/cli-flags-DUozBFyK.js.map +1 -0
  23. package/dist/cli-flags.d.ts +21 -0
  24. package/dist/cli-helpers.d.ts +53 -24
  25. package/dist/{arg-Dni7MzLB.js → cli-prompt-egtNKkZl.js} +49 -3
  26. package/dist/cli-prompt-egtNKkZl.js.map +1 -0
  27. package/dist/cli-prompt.d.ts +19 -0
  28. package/dist/cli-scratch-database.d.ts +47 -8
  29. package/dist/cli.js +548 -94
  30. package/dist/cli.js.map +1 -1
  31. package/dist/{column-plan-helpers-drZIsSNf.js → column-plan-helpers-D_V4oaZh.js} +2 -2
  32. package/dist/{column-plan-helpers-drZIsSNf.js.map → column-plan-helpers-D_V4oaZh.js.map} +1 -1
  33. package/dist/{connection-C9mT8bgf.js → connection-C938nR3E.js} +2 -2
  34. package/dist/{connection-C9mT8bgf.js.map → connection-C938nR3E.js.map} +1 -1
  35. package/dist/{databasePoolManager-BIuewidb.js → databasePoolManager-Yqdw14wY.js} +4 -4
  36. package/dist/{databasePoolManager-BIuewidb.js.map → databasePoolManager-Yqdw14wY.js.map} +1 -1
  37. package/dist/{deferred-index-build-DP1_SNFo.js → deferred-index-build-Ca6OqKyR.js} +8 -8
  38. package/dist/{deferred-index-build-DP1_SNFo.js.map → deferred-index-build-Ca6OqKyR.js.map} +1 -1
  39. package/dist/{doctor-C4WiftHm.js → doctor-CJyuhV1n.js} +8 -8
  40. package/dist/{doctor-C4WiftHm.js.map → doctor-CJyuhV1n.js.map} +1 -1
  41. package/dist/{ensure-collection-policies-CXpbeNag.js → ensure-collection-policies-C1ux3V_z.js} +79 -18
  42. package/dist/ensure-collection-policies-C1ux3V_z.js.map +1 -0
  43. package/dist/{ensure-collection-tables-BhsU6qUE.js → ensure-collection-tables-Do-Bvb64.js} +10 -10
  44. package/dist/{ensure-collection-tables-BhsU6qUE.js.map → ensure-collection-tables-Do-Bvb64.js.map} +1 -1
  45. package/dist/{ensure-tables-CVm_yvzb.js → ensure-tables-Dl_Eh9ow.js} +5 -5
  46. package/dist/{ensure-tables-CVm_yvzb.js.map → ensure-tables-Dl_Eh9ow.js.map} +1 -1
  47. package/dist/{enum-labels-kept-D8fI1MoX.js → enum-labels-kept-D9eTIWCz.js} +3 -3
  48. package/dist/{enum-labels-kept-D8fI1MoX.js.map → enum-labels-kept-D9eTIWCz.js.map} +1 -1
  49. package/dist/{generate-drizzle-schema-Bz52jH2h.js → generate-drizzle-schema-Bf8canaR.js} +3 -3
  50. package/dist/{generate-drizzle-schema-Bz52jH2h.js.map → generate-drizzle-schema-Bf8canaR.js.map} +1 -1
  51. package/dist/{generate-drizzle-schema-logic-B74WN37S.js → generate-drizzle-schema-logic-BaT0H7Gq.js} +4 -4
  52. package/dist/{generate-drizzle-schema-logic-B74WN37S.js.map → generate-drizzle-schema-logic-BaT0H7Gq.js.map} +1 -1
  53. package/dist/{generate-files-CdEckX6c.js → generate-files-D50Pxw1G.js} +6 -47
  54. package/dist/generate-files-D50Pxw1G.js.map +1 -0
  55. package/dist/generate-next-step-DCd7yiJ2.js +66 -0
  56. package/dist/generate-next-step-DCd7yiJ2.js.map +1 -0
  57. package/dist/{generate-postgres-ddl-logic-B64O2oZF.js → generate-postgres-ddl-logic-B4k9djAV.js} +4 -4
  58. package/dist/{generate-postgres-ddl-logic-B64O2oZF.js.map → generate-postgres-ddl-logic-B4k9djAV.js.map} +1 -1
  59. package/dist/{generated-schema-staleness-Di9c4ZxN.js → generated-schema-staleness-FlxM2i4i.js} +102 -12
  60. package/dist/{generated-schema-staleness-Di9c4ZxN.js.map → generated-schema-staleness-FlxM2i4i.js.map} +1 -1
  61. package/dist/index.es.js +11 -11
  62. package/dist/{introspect-db-logic-C-Di8dA9.js → introspect-db-logic-j2vYhmdP.js} +145 -9
  63. package/dist/introspect-db-logic-j2vYhmdP.js.map +1 -0
  64. package/dist/{introspect-db-queries-D6kAqReI.js → introspect-db-queries-DevUrzll.js} +41 -6
  65. package/dist/introspect-db-queries-DevUrzll.js.map +1 -0
  66. package/dist/{open-rebase-CbOur0TU.js → open-rebase-D8rytTP0.js} +95 -45
  67. package/dist/open-rebase-D8rytTP0.js.map +1 -0
  68. package/dist/open-rebase.d.ts +12 -4
  69. package/dist/{pg-error-utils-vJezlXdI.js → pg-error-utils-CdsGYMTm.js} +10 -3
  70. package/dist/pg-error-utils-CdsGYMTm.js.map +1 -0
  71. package/dist/{plan-schema-B1YF_tYq.js → plan-schema-CsNgK0qi.js} +4 -4
  72. package/dist/{plan-schema-B1YF_tYq.js.map → plan-schema-CsNgK0qi.js.map} +1 -1
  73. package/dist/{policy-drift-Dm8i-s1J.js → policy-drift-CY5P1ZRu.js} +28 -14
  74. package/dist/{policy-drift-Dm8i-s1J.js.map → policy-drift-CY5P1ZRu.js.map} +1 -1
  75. package/dist/{push-index-prebuild-Db4AQF4i.js → push-index-prebuild-BDFcfRPo.js} +4 -4
  76. package/dist/{push-index-prebuild-Db4AQF4i.js.map → push-index-prebuild-BDFcfRPo.js.map} +1 -1
  77. package/dist/{push-legacy-renames-BK6ilQq2.js → push-legacy-renames-D4bvMWAb.js} +4 -4
  78. package/dist/{push-legacy-renames-BK6ilQq2.js.map → push-legacy-renames-D4bvMWAb.js.map} +1 -1
  79. package/dist/push-policy-plan-DTXE6aVa.js +111 -0
  80. package/dist/push-policy-plan-DTXE6aVa.js.map +1 -0
  81. package/dist/{render-ddl-CtC3y9Ol.js → render-ddl-CrfEe__B.js} +10 -9
  82. package/dist/render-ddl-CrfEe__B.js.map +1 -0
  83. package/dist/{rls-bootstrap-sql-RQUljnJ1.js → rls-bootstrap-sql-Bd-hJk08.js} +3 -58
  84. package/dist/rls-bootstrap-sql-Bd-hJk08.js.map +1 -0
  85. package/dist/{rls-enforcement-DvzqlaL1.js → rls-enforcement-D7P4-ZYX.js} +2 -2
  86. package/dist/{rls-enforcement-DvzqlaL1.js.map → rls-enforcement-D7P4-ZYX.js.map} +1 -1
  87. package/dist/{__vite-browser-external-BnuHet1e.js → rolldown-runtime-V7rFbMRe.js} +1 -8
  88. package/dist/schema/atlas-apply-report.d.ts +23 -0
  89. package/dist/schema/atlas-argv.d.ts +22 -1
  90. package/dist/schema/atlas-exclude.d.ts +42 -0
  91. package/dist/schema/atlas-revisions.d.ts +4 -0
  92. package/dist/schema/carved-out-migration.d.ts +5 -4
  93. package/dist/schema/destructive-sql.d.ts +23 -1
  94. package/dist/schema/doctor-cli.js +5 -5
  95. package/dist/schema/ensure-collection-policies.d.ts +50 -0
  96. package/dist/schema/generate-drizzle-schema.js +1 -1
  97. package/dist/schema/generate-next-step.d.ts +11 -0
  98. package/dist/schema/generate-postgres-ddl.js +2 -2
  99. package/dist/schema/generated-schema-diff.d.ts +7 -0
  100. package/dist/schema/generated-schema-staleness.d.ts +7 -1
  101. package/dist/schema/introspect-db-logic.d.ts +32 -1
  102. package/dist/schema/introspect-db-queries.d.ts +18 -5
  103. package/dist/schema/introspect-db.js +53 -52
  104. package/dist/schema/introspect-db.js.map +1 -1
  105. package/dist/schema/migrate-baseline.d.ts +51 -0
  106. package/dist/schema/plan/render-ddl.d.ts +2 -1
  107. package/dist/schema/push-policy-plan.d.ts +67 -0
  108. package/dist/schema/undeclarable-constraints.d.ts +59 -0
  109. package/dist/security/policy-drift.d.ts +5 -0
  110. package/dist/services/BranchService.d.ts +7 -0
  111. package/dist/{source-DBDbBTL4.js → source-DB6xJv3p.js} +65 -3
  112. package/dist/source-DB6xJv3p.js.map +1 -0
  113. package/dist/utils/connection-string.d.ts +47 -0
  114. package/dist/{vector-index-CMJKb3C9.js → vector-index-BFhyEaiz.js} +2 -2
  115. package/dist/{vector-index-CMJKb3C9.js.map → vector-index-BFhyEaiz.js.map} +1 -1
  116. package/dist/{websocket-BLGc5yhs.js → websocket-DMeXOoyv.js} +41 -13
  117. package/dist/websocket-DMeXOoyv.js.map +1 -0
  118. package/package.json +7 -7
  119. package/dist/BranchService-Bsn2wfaj.js.map +0 -1
  120. package/dist/__vite-browser-external-BnuHet1e.js.map +0 -1
  121. package/dist/arg-Dni7MzLB.js.map +0 -1
  122. package/dist/backup-cli-DnpJi6I7.js.map +0 -1
  123. package/dist/backup-service-BTBB7RXn.js.map +0 -1
  124. package/dist/cli-console-Ca6ZlAVU.js +0 -154
  125. package/dist/cli-console-Ca6ZlAVU.js.map +0 -1
  126. package/dist/cli-errors-CaI4ntyD.js.map +0 -1
  127. package/dist/cli-flags-JNI-pwSf.js.map +0 -1
  128. package/dist/ensure-collection-policies-CXpbeNag.js.map +0 -1
  129. package/dist/generate-files-CdEckX6c.js.map +0 -1
  130. package/dist/introspect-db-logic-C-Di8dA9.js.map +0 -1
  131. package/dist/introspect-db-queries-D6kAqReI.js.map +0 -1
  132. package/dist/open-rebase-CbOur0TU.js.map +0 -1
  133. package/dist/pg-error-utils-vJezlXdI.js.map +0 -1
  134. package/dist/render-ddl-CtC3y9Ol.js.map +0 -1
  135. package/dist/rls-bootstrap-sql-RQUljnJ1.js.map +0 -1
  136. package/dist/source-DBDbBTL4.js.map +0 -1
  137. package/dist/websocket-BLGc5yhs.js.map +0 -1
@@ -1,7 +1,8 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  __createRequire(import.meta.url);
3
- import { a as __toESM } from "./__vite-browser-external-BnuHet1e.js";
4
- import { t as require_arg } from "./arg-Dni7MzLB.js";
3
+ import { i as __toESM } from "./rolldown-runtime-V7rFbMRe.js";
4
+ import { n as require_arg } from "./cli-prompt-egtNKkZl.js";
5
+ import { suggestNearMiss } from "@rebasepro/utils";
5
6
  //#region src/cli-flags.ts
6
7
  var import_arg = /* @__PURE__ */ __toESM(require_arg(), 1);
7
8
  /**
@@ -75,7 +76,9 @@ var DRIVER_FLAG_SPECS = {
75
76
  "-c": "--collections",
76
77
  "--force": Boolean,
77
78
  "-f": "--force",
78
- "--schema": String
79
+ "--schema": String,
80
+ "--data-inference": Boolean,
81
+ "--no-data-inference": Boolean
79
82
  },
80
83
  "schema stale": {
81
84
  "--collections": String,
@@ -131,11 +134,72 @@ function assertKnownFlags(domain, subcommand, args) {
131
134
  ...spec
132
135
  }, { argv: args.slice(2) });
133
136
  } catch (err) {
134
- if (err instanceof Error && err.code === "ARG_UNKNOWN_OPTION") throw new Error(`${err.message} — run \`rebase ${domain} --help\` for the options \`${domain} ${subcommand}\` takes.`);
137
+ const unknown = unknownFlagIn(err);
138
+ if (unknown !== null && err instanceof Error) {
139
+ const suggestion = closestFlag(unknown, {
140
+ ...RELAYED_FLAGS,
141
+ ...spec
142
+ });
143
+ throw new Error(`${err.message}${suggestion ? ` (did you mean ${suggestion}?)` : ""} — run \`rebase ${domain} --help\` for the options \`${domain} ${subcommand}\` takes.`);
144
+ }
135
145
  throw err;
136
146
  }
137
147
  }
148
+ /** The flag `arg` refused as undeclared, or `null` for any other failure. */
149
+ function unknownFlagIn(err) {
150
+ if (!(err instanceof Error) || !("code" in err) || err.code !== "ARG_UNKNOWN_OPTION") return null;
151
+ return /option: (\S+)$/.exec(err.message)?.[1] ?? null;
152
+ }
153
+ /** The declared long flag `typed` is one edit or one swap away from, if there is one. */
154
+ function closestFlag(typed, spec) {
155
+ const name = typed.split("=")[0];
156
+ return closestWord(name, Object.keys(spec).filter((flag) => flag.startsWith("--")));
157
+ }
158
+ /**
159
+ * The word in `known` that `typed` was probably meant to be: one edit away
160
+ * (`--targt-db`), or two neighbouring letters swapped (`--clena`, `--froce`),
161
+ * which `suggestNearMiss` counts as two edits and so never offers.
162
+ */
163
+ function closestWord(typed, known) {
164
+ return suggestNearMiss(known, typed) ?? transposedFrom(known, typed);
165
+ }
166
+ function transposedFrom(known, typed) {
167
+ return known.find((candidate) => {
168
+ if (candidate.length !== typed.length || candidate === typed) return false;
169
+ const differ = [...candidate].flatMap((letter, index) => letter === typed[index] ? [] : [index]);
170
+ return differ.length === 2 && differ[1] === differ[0] + 1 && candidate[differ[0]] === typed[differ[1]] && candidate[differ[1]] === typed[differ[0]];
171
+ });
172
+ }
173
+ /**
174
+ * Parse a command that owns its argument handling, refusing what it does not
175
+ * declare.
176
+ *
177
+ * `db backup`, `db restore` and `db backups list` parsed permissively, so an
178
+ * undeclared flag became a positional and was ignored: `db restore <dump>
179
+ * --targt-db x --create-db --yes` dropped the misspelled target and restored
180
+ * into the live database. A typo is refused here before anything runs, naming
181
+ * the declared flag it is one edit from, or every flag the command takes when
182
+ * none is that close.
183
+ *
184
+ * @param argv the words after the command, e.g. `rawArgs.slice(2)`.
185
+ * @param command the command as typed, for the message: `"db restore"`.
186
+ */
187
+ function parseCommandFlags(spec, argv, command) {
188
+ const full = {
189
+ ...RELAYED_FLAGS,
190
+ ...spec
191
+ };
192
+ try {
193
+ return (0, import_arg.default)(full, { argv });
194
+ } catch (err) {
195
+ const unknown = unknownFlagIn(err);
196
+ if (unknown === null) throw err;
197
+ const suggestion = closestFlag(unknown, full);
198
+ const flags = Object.keys(spec).filter((flag) => flag.startsWith("--"));
199
+ throw new Error(`\`rebase ${command}\` has no option ${unknown}` + (suggestion ? ` — did you mean ${suggestion}?` : ` — it takes ${flags.join(", ")}.`) + ` Nothing was run. See \`rebase ${command} --help\`.`);
200
+ }
201
+ }
138
202
  //#endregion
139
- export { parseDriverLine as i, assertKnownFlags as n, collectionsPathIn as r, RELAYED_FLAGS as t };
203
+ export { parseCommandFlags as a, collectionsPathIn as i, assertKnownFlags as n, parseDriverLine as o, closestWord as r, RELAYED_FLAGS as t };
140
204
 
141
- //# sourceMappingURL=cli-flags-JNI-pwSf.js.map
205
+ //# sourceMappingURL=cli-flags-DUozBFyK.js.map
@@ -0,0 +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":"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"}
@@ -127,3 +127,24 @@ export declare function assertSpecMatchesUsage(specs: Record<string, arg.Spec>,
127
127
  * tests can see.
128
128
  */
129
129
  export declare function assertKnownFlags(domain: string, subcommand: string | undefined, args: string[]): void;
130
+ /**
131
+ * The word in `known` that `typed` was probably meant to be: one edit away
132
+ * (`--targt-db`), or two neighbouring letters swapped (`--clena`, `--froce`),
133
+ * which `suggestNearMiss` counts as two edits and so never offers.
134
+ */
135
+ export declare function closestWord(typed: string, known: readonly string[]): string | undefined;
136
+ /**
137
+ * Parse a command that owns its argument handling, refusing what it does not
138
+ * declare.
139
+ *
140
+ * `db backup`, `db restore` and `db backups list` parsed permissively, so an
141
+ * undeclared flag became a positional and was ignored: `db restore <dump>
142
+ * --targt-db x --create-db --yes` dropped the misspelled target and restored
143
+ * into the live database. A typo is refused here before anything runs, naming
144
+ * the declared flag it is one edit from, or every flag the command takes when
145
+ * none is that close.
146
+ *
147
+ * @param argv the words after the command, e.g. `rawArgs.slice(2)`.
148
+ * @param command the command as typed, for the message: `"db restore"`.
149
+ */
150
+ export declare function parseCommandFlags<S extends arg.Spec>(spec: S, argv: string[], command: string): arg.Result<typeof RELAYED_FLAGS & S>;
@@ -1,5 +1,6 @@
1
1
  import type { GeneratedColumnDependency } from "./schema/generated-column-conflicts.js";
2
2
  import { type ExistingColumnType } from "./schema/destructive-sql.js";
3
+ import { type ExistingConstraint } from "./schema/undeclarable-constraints.js";
3
4
  import type { CollectionConfig } from "@rebasepro/types";
4
5
  /**
5
6
  * Why is a dependency's binary missing — never installed, or installed with its
@@ -58,8 +59,23 @@ export declare function describeBuildScriptRemedy(packageName: string, manager?:
58
59
  * relink rather than decide the tree is already up to date.
59
60
  */
60
61
  export declare function describeReinstallCommand(manager?: ProjectPackageManager): string;
61
- /** `add <pkg>` as a dev dependency, in the reader's own package manager. */
62
- export declare function describeDevAddCommand(packageName: string, manager?: ProjectPackageManager): string;
62
+ /**
63
+ * The workspace member a command runs in, relative to the workspace's root —
64
+ * `backend` in a scaffolded project — or null at that root and outside any
65
+ * workspace.
66
+ *
67
+ * The member is the nearest directory at or above `startDir` holding a
68
+ * `package.json`; the root is the nearest one above it holding a
69
+ * `pnpm-workspace.yaml`, or a `package.json` that declares `workspaces`.
70
+ */
71
+ export declare function workspaceMemberDir(startDir?: string): string | null;
72
+ /**
73
+ * `add <pkg>` as a dev dependency, in the reader's own package manager, typed
74
+ * from the project root: into `workspaceDir` when the command runs in a
75
+ * workspace member, where a bare `pnpm add -D` at the root refuses with
76
+ * `ERR_PNPM_ADDING_TO_ROOT`.
77
+ */
78
+ export declare function describeDevAddCommand(packageName: string, manager?: ProjectPackageManager, workspaceDir?: string | null): string;
63
79
  /**
64
80
  * Why a dependency's binary is not on `PATH`.
65
81
  *
@@ -277,15 +293,21 @@ export declare function getTriggerExcludes(collectionsPath: string): Promise<str
277
293
  * catalogs. Separated from {@link getTableExcludes} so its failure mode can
278
294
  * be handled explicitly (fail closed) and so tests can inject a stub.
279
295
  */
296
+ /** An index as the catalogue names it: its schema, its table and its own name. */
297
+ export interface ExistingIndex {
298
+ schema: string;
299
+ table: string;
300
+ index: string;
301
+ }
280
302
  /**
281
- * Every index Postgres holds on a table Rebase manages, as
282
- * `schema.table.index`.
303
+ * Every index Postgres holds on a table, by its parts — never joined with `.`,
304
+ * which a name may itself contain.
283
305
  *
284
306
  * Constraint-backed indexes are left out: a `PRIMARY KEY` or a `UNIQUE` is a
285
307
  * *constraint* to Atlas, diffed from the constraint declaration, and naming its
286
308
  * index in an exclude would shield the constraint itself.
287
309
  */
288
- export declare function queryExistingIndexes(databaseUrl: string): Promise<string[]>;
310
+ export declare function queryExistingIndexes(databaseUrl: string): Promise<ExistingIndex[]>;
289
311
  /**
290
312
  * Glob patterns keeping Atlas away from indexes that are not Rebase's.
291
313
  *
@@ -312,13 +334,35 @@ export declare function queryExistingIndexes(databaseUrl: string): Promise<strin
312
334
  *
313
335
  * Three-part `schema.table.index`, never two: a two-part pattern reads as a
314
336
  * *table* named `<index>` in a *schema* named `<table>`, matches nothing, and
315
- * reports no error — the trap already recorded for the search excludes.
337
+ * reports no error — the trap already recorded for the search excludes. And
338
+ * spelled by `atlasExcludePattern`: a raw `orders_idx[v1]` is a glob that
339
+ * matches `orders_idxv`, so that index was dropped with no prompt.
316
340
  */
317
341
  export declare function getForeignIndexExcludes(databaseUrl: string, collectionsPath: string, deps?: {
318
- queryExistingIndexes?: (databaseUrl: string) => Promise<string[]>;
342
+ queryExistingIndexes?: (databaseUrl: string) => Promise<ExistingIndex[]>;
319
343
  getManagedIndexNames?: (collectionsPath: string) => Promise<Set<string>>;
320
344
  }): Promise<string[]>;
321
- export declare function queryExistingTables(databaseUrl: string): Promise<string[]>;
345
+ /** Every CHECK and EXCLUDE constraint the database holds. @see UNDECLARABLE_CONSTRAINTS_SQL */
346
+ export declare function queryUndeclarableConstraints(databaseUrl: string): Promise<ExistingConstraint[]>;
347
+ /**
348
+ * Patterns keeping Atlas away from the CHECK and EXCLUDE constraints the
349
+ * collections do not declare — which, since a collection cannot declare
350
+ * either, is all of them. See `schema/undeclarable-constraints.ts`.
351
+ *
352
+ * Fails CLOSED, like {@link getTableExcludes}: without the catalogue nothing
353
+ * says which constraints are there, and a partial list lets the push drop a
354
+ * rule the database enforces. `desiredSql` is the `schema.sql` Atlas is about
355
+ * to diff against; a constraint it declares is Atlas's to manage.
356
+ */
357
+ export declare function getUndeclaredConstraintExcludes(databaseUrl: string, desiredSql: string, deps?: {
358
+ queryConstraints?: (databaseUrl: string) => Promise<ExistingConstraint[]>;
359
+ }): Promise<string[]>;
360
+ /** A table or view as the catalogue names it. */
361
+ export interface ExistingTable {
362
+ schema: string;
363
+ table: string;
364
+ }
365
+ export declare function queryExistingTables(databaseUrl: string): Promise<ExistingTable[]>;
322
366
  /**
323
367
  * Raised when the exclude list could not be built. `db push` MUST abort on
324
368
  * this rather than continue: the exclude list is the only thing shielding
@@ -343,22 +387,7 @@ export declare class ExcludeIntrospectionError extends Error {
343
387
  * `deps` is injectable for tests; production uses the real pg-backed queries.
344
388
  */
345
389
  export declare function getTableExcludes(databaseUrl: string, collectionsPath: string, deps?: {
346
- queryExistingTables?: (databaseUrl: string) => Promise<string[]>;
390
+ queryExistingTables?: (databaseUrl: string) => Promise<ExistingTable[]>;
347
391
  getIncludes?: (collectionsPath: string) => Promise<string[]>;
348
392
  }): Promise<string[]>;
349
- /**
350
- * Ask a yes/no question on an interactive terminal.
351
- *
352
- * The `isTTY` guard is the contract, not an optimisation: non-interactive
353
- * shells (CI, pipes, agents) can never answer, and `readline` on a
354
- * non-TTY stdin resolves with whatever the pipe happens to contain — or never
355
- * resolves at all. Returning false there is what makes an unattended
356
- * `db push` abort instead of auto-confirming a destructive change. Callers
357
- * should already have gone through {@link decidePushSafety}, which decides
358
- * whether interactive confirmation is even possible; this is the backstop.
359
- *
360
- * Lives here rather than in cli.ts so it is reachable from a test — cli.ts uses
361
- * `import.meta` and cannot be imported by the jest runner.
362
- */
363
- export declare function promptConfirm(question: string): Promise<boolean>;
364
393
  export {};
@@ -1,6 +1,7 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  __createRequire(import.meta.url);
3
- import { n as __commonJSMin } from "./__vite-browser-external-BnuHet1e.js";
3
+ import { t as __commonJSMin } from "./rolldown-runtime-V7rFbMRe.js";
4
+ import readline from "readline";
4
5
  //#region ../../node_modules/.pnpm/arg@5.0.2/node_modules/arg/index.js
5
6
  var require_arg = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6
7
  var flagSymbol = Symbol("arg flag");
@@ -82,6 +83,51 @@ var require_arg = /* @__PURE__ */ __commonJSMin(((exports, module) => {
82
83
  module.exports = arg;
83
84
  }));
84
85
  //#endregion
85
- export { require_arg as t };
86
+ //#region src/cli-prompt.ts
87
+ /**
88
+ * The one yes/no question the driver CLI asks: before a destructive `db push`,
89
+ * before a `db restore` without `--yes`, before `db branch prune` removes
90
+ * anything, and before `schema introspect` samples rows.
91
+ */
92
+ /**
93
+ * Ask a yes/no question on an interactive terminal; `true` only for "y" or
94
+ * "yes".
95
+ *
96
+ * The `isTTY` guard is the contract, not an optimisation: non-interactive
97
+ * shells (CI, pipes, agents) can never answer, and `readline` on a non-TTY
98
+ * stdin resolves with whatever the pipe happens to contain, or never resolves
99
+ * at all. Returning false there is what makes an unattended `db push` abort
100
+ * instead of auto-confirming a destructive change. Callers decide first
101
+ * whether interactive confirmation is possible (`decidePushSafety` for a
102
+ * push); this is the backstop.
103
+ *
104
+ * Ctrl-C and Ctrl-D answer no. On a terminal readline reads Ctrl-C as a key,
105
+ * not as a signal, and with nothing listening it only pauses the input: the
106
+ * question never resolves, and the open database connections behind it keep
107
+ * the process alive, so the command hung instead of stopping. Answering no
108
+ * sends it down the path that says "Aborted" and cleans up.
109
+ */
110
+ async function promptConfirm(question) {
111
+ if (!process.stdin.isTTY) return false;
112
+ const rl = readline.createInterface({
113
+ input: process.stdin,
114
+ output: process.stdout
115
+ });
116
+ try {
117
+ const answer = await new Promise((resolve) => {
118
+ rl.once("SIGINT", () => {
119
+ process.stdout.write("\n");
120
+ resolve("");
121
+ });
122
+ rl.once("close", () => resolve(""));
123
+ rl.question(question, resolve);
124
+ });
125
+ return /^y(es)?$/i.test(answer.trim());
126
+ } finally {
127
+ rl.close();
128
+ }
129
+ }
130
+ //#endregion
131
+ export { require_arg as n, promptConfirm as t };
86
132
 
87
- //# sourceMappingURL=arg-Dni7MzLB.js.map
133
+ //# sourceMappingURL=cli-prompt-egtNKkZl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-prompt-egtNKkZl.js","names":[],"sources":["../../../node_modules/.pnpm/arg@5.0.2/node_modules/arg/index.js","../src/cli-prompt.ts"],"sourcesContent":["const flagSymbol = Symbol('arg flag');\n\nclass ArgError extends Error {\n\tconstructor(msg, code) {\n\t\tsuper(msg);\n\t\tthis.name = 'ArgError';\n\t\tthis.code = code;\n\n\t\tObject.setPrototypeOf(this, ArgError.prototype);\n\t}\n}\n\nfunction arg(\n\topts,\n\t{\n\t\targv = process.argv.slice(2),\n\t\tpermissive = false,\n\t\tstopAtPositional = false\n\t} = {}\n) {\n\tif (!opts) {\n\t\tthrow new ArgError(\n\t\t\t'argument specification object is required',\n\t\t\t'ARG_CONFIG_NO_SPEC'\n\t\t);\n\t}\n\n\tconst result = { _: [] };\n\n\tconst aliases = {};\n\tconst handlers = {};\n\n\tfor (const key of Object.keys(opts)) {\n\t\tif (!key) {\n\t\t\tthrow new ArgError(\n\t\t\t\t'argument key cannot be an empty string',\n\t\t\t\t'ARG_CONFIG_EMPTY_KEY'\n\t\t\t);\n\t\t}\n\n\t\tif (key[0] !== '-') {\n\t\t\tthrow new ArgError(\n\t\t\t\t`argument key must start with '-' but found: '${key}'`,\n\t\t\t\t'ARG_CONFIG_NONOPT_KEY'\n\t\t\t);\n\t\t}\n\n\t\tif (key.length === 1) {\n\t\t\tthrow new ArgError(\n\t\t\t\t`argument key must have a name; singular '-' keys are not allowed: ${key}`,\n\t\t\t\t'ARG_CONFIG_NONAME_KEY'\n\t\t\t);\n\t\t}\n\n\t\tif (typeof opts[key] === 'string') {\n\t\t\taliases[key] = opts[key];\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet type = opts[key];\n\t\tlet isFlag = false;\n\n\t\tif (\n\t\t\tArray.isArray(type) &&\n\t\t\ttype.length === 1 &&\n\t\t\ttypeof type[0] === 'function'\n\t\t) {\n\t\t\tconst [fn] = type;\n\t\t\ttype = (value, name, prev = []) => {\n\t\t\t\tprev.push(fn(value, name, prev[prev.length - 1]));\n\t\t\t\treturn prev;\n\t\t\t};\n\t\t\tisFlag = fn === Boolean || fn[flagSymbol] === true;\n\t\t} else if (typeof type === 'function') {\n\t\t\tisFlag = type === Boolean || type[flagSymbol] === true;\n\t\t} else {\n\t\t\tthrow new ArgError(\n\t\t\t\t`type missing or not a function or valid array type: ${key}`,\n\t\t\t\t'ARG_CONFIG_VAD_TYPE'\n\t\t\t);\n\t\t}\n\n\t\tif (key[1] !== '-' && key.length > 2) {\n\t\t\tthrow new ArgError(\n\t\t\t\t`short argument keys (with a single hyphen) must have only one character: ${key}`,\n\t\t\t\t'ARG_CONFIG_SHORTOPT_TOOLONG'\n\t\t\t);\n\t\t}\n\n\t\thandlers[key] = [type, isFlag];\n\t}\n\n\tfor (let i = 0, len = argv.length; i < len; i++) {\n\t\tconst wholeArg = argv[i];\n\n\t\tif (stopAtPositional && result._.length > 0) {\n\t\t\tresult._ = result._.concat(argv.slice(i));\n\t\t\tbreak;\n\t\t}\n\n\t\tif (wholeArg === '--') {\n\t\t\tresult._ = result._.concat(argv.slice(i + 1));\n\t\t\tbreak;\n\t\t}\n\n\t\tif (wholeArg.length > 1 && wholeArg[0] === '-') {\n\t\t\t/* eslint-disable operator-linebreak */\n\t\t\tconst separatedArguments =\n\t\t\t\twholeArg[1] === '-' || wholeArg.length === 2\n\t\t\t\t\t? [wholeArg]\n\t\t\t\t\t: wholeArg\n\t\t\t\t\t\t\t.slice(1)\n\t\t\t\t\t\t\t.split('')\n\t\t\t\t\t\t\t.map((a) => `-${a}`);\n\t\t\t/* eslint-enable operator-linebreak */\n\n\t\t\tfor (let j = 0; j < separatedArguments.length; j++) {\n\t\t\t\tconst arg = separatedArguments[j];\n\t\t\t\tconst [originalArgName, argStr] =\n\t\t\t\t\targ[1] === '-' ? arg.split(/=(.*)/, 2) : [arg, undefined];\n\n\t\t\t\tlet argName = originalArgName;\n\t\t\t\twhile (argName in aliases) {\n\t\t\t\t\targName = aliases[argName];\n\t\t\t\t}\n\n\t\t\t\tif (!(argName in handlers)) {\n\t\t\t\t\tif (permissive) {\n\t\t\t\t\t\tresult._.push(arg);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new ArgError(\n\t\t\t\t\t\t\t`unknown or unexpected option: ${originalArgName}`,\n\t\t\t\t\t\t\t'ARG_UNKNOWN_OPTION'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst [type, isFlag] = handlers[argName];\n\n\t\t\t\tif (!isFlag && j + 1 < separatedArguments.length) {\n\t\t\t\t\tthrow new ArgError(\n\t\t\t\t\t\t`option requires argument (but was followed by another short argument): ${originalArgName}`,\n\t\t\t\t\t\t'ARG_MISSING_REQUIRED_SHORTARG'\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (isFlag) {\n\t\t\t\t\tresult[argName] = type(true, argName, result[argName]);\n\t\t\t\t} else if (argStr === undefined) {\n\t\t\t\t\tif (\n\t\t\t\t\t\targv.length < i + 2 ||\n\t\t\t\t\t\t(argv[i + 1].length > 1 &&\n\t\t\t\t\t\t\targv[i + 1][0] === '-' &&\n\t\t\t\t\t\t\t!(\n\t\t\t\t\t\t\t\targv[i + 1].match(/^-?\\d*(\\.(?=\\d))?\\d*$/) &&\n\t\t\t\t\t\t\t\t(type === Number ||\n\t\t\t\t\t\t\t\t\t// eslint-disable-next-line no-undef\n\t\t\t\t\t\t\t\t\t(typeof BigInt !== 'undefined' && type === BigInt))\n\t\t\t\t\t\t\t))\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst extended =\n\t\t\t\t\t\t\toriginalArgName === argName ? '' : ` (alias for ${argName})`;\n\t\t\t\t\t\tthrow new ArgError(\n\t\t\t\t\t\t\t`option requires argument: ${originalArgName}${extended}`,\n\t\t\t\t\t\t\t'ARG_MISSING_REQUIRED_LONGARG'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tresult[argName] = type(argv[i + 1], argName, result[argName]);\n\t\t\t\t\t++i;\n\t\t\t\t} else {\n\t\t\t\t\tresult[argName] = type(argStr, argName, result[argName]);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tresult._.push(wholeArg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\narg.flag = (fn) => {\n\tfn[flagSymbol] = true;\n\treturn fn;\n};\n\n// Utility types\narg.COUNT = arg.flag((v, name, existingCount) => (existingCount || 0) + 1);\n\n// Expose error class\narg.ArgError = ArgError;\n\nmodule.exports = arg;\n","/**\n * The one yes/no question the driver CLI asks: before a destructive `db push`,\n * before a `db restore` without `--yes`, before `db branch prune` removes\n * anything, and before `schema introspect` samples rows.\n */\nimport readline from \"readline\";\n\n/**\n * Ask a yes/no question on an interactive terminal; `true` only for \"y\" or\n * \"yes\".\n *\n * The `isTTY` guard is the contract, not an optimisation: non-interactive\n * shells (CI, pipes, agents) can never answer, and `readline` on a non-TTY\n * stdin resolves with whatever the pipe happens to contain, or never resolves\n * at all. Returning false there is what makes an unattended `db push` abort\n * instead of auto-confirming a destructive change. Callers decide first\n * whether interactive confirmation is possible (`decidePushSafety` for a\n * push); this is the backstop.\n *\n * Ctrl-C and Ctrl-D answer no. On a terminal readline reads Ctrl-C as a key,\n * not as a signal, and with nothing listening it only pauses the input: the\n * question never resolves, and the open database connections behind it keep\n * the process alive, so the command hung instead of stopping. Answering no\n * sends it down the path that says \"Aborted\" and cleans up.\n */\nexport async function promptConfirm(question: string): Promise<boolean> {\n if (!process.stdin.isTTY) return false;\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer = await new Promise<string>((resolve) => {\n rl.once(\"SIGINT\", () => {\n process.stdout.write(\"\\n\");\n resolve(\"\");\n });\n rl.once(\"close\", () => resolve(\"\"));\n rl.question(question, resolve);\n });\n return /^y(es)?$/i.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n"],"x_google_ignoreList":[0],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Ask a yes/no question on an interactive terminal; `true` only for "y" or
3
+ * "yes".
4
+ *
5
+ * The `isTTY` guard is the contract, not an optimisation: non-interactive
6
+ * shells (CI, pipes, agents) can never answer, and `readline` on a non-TTY
7
+ * stdin resolves with whatever the pipe happens to contain, or never resolves
8
+ * at all. Returning false there is what makes an unattended `db push` abort
9
+ * instead of auto-confirming a destructive change. Callers decide first
10
+ * whether interactive confirmation is possible (`decidePushSafety` for a
11
+ * push); this is the backstop.
12
+ *
13
+ * Ctrl-C and Ctrl-D answer no. On a terminal readline reads Ctrl-C as a key,
14
+ * not as a signal, and with nothing listening it only pauses the input: the
15
+ * question never resolves, and the open database connections behind it keep
16
+ * the process alive, so the command hung instead of stopping. Answering no
17
+ * sends it down the path that says "Aborted" and cleans up.
18
+ */
19
+ export declare function promptConfirm(question: string): Promise<boolean>;
@@ -8,11 +8,17 @@
8
8
  * `db push`: a `--dry-run` that left one behind while saying nothing was
9
9
  * applied, and `db generate`, left one per target on the server.
10
10
  *
11
- * Two cases keep it. A command that fails leaves it as the state Atlas was
12
- * planning against, which is the one thing worth inspecting. And one this
13
- * process did not create — made by hand, the remedy for a role without
14
- * `CREATEDB` — is the user's, and dropping it would put them back at the same
15
- * refusal next time.
11
+ * It is dropped on every way out of the command: success, a destructive push
12
+ * the user declined or that had no terminal to ask, and a crash. A crash that
13
+ * kept it left it for good, because the next push finds it already there and
14
+ * takes it for one made by hand.
15
+ *
16
+ * Two cases keep it. An Atlas run that failed while planning against it leaves
17
+ * the state Atlas was planning, which is the one thing worth inspecting. And
18
+ * one this process did not create (made by hand, the remedy for a role without
19
+ * `CREATEDB`) is the user's, and dropping it would put them back at the same
20
+ * refusal next time. An Atlas run that failed before planning (see
21
+ * {@link atlasFailedBeforePlanning}) left nothing in it, and it is dropped.
16
22
  */
17
23
  export interface ScratchDatabaseOps {
18
24
  /** Create it if it is missing; `true` only when this call created it. */
@@ -22,13 +28,46 @@ export interface ScratchDatabaseOps {
22
28
  export declare class ScratchDatabase {
23
29
  private readonly ops;
24
30
  private created;
31
+ private keptAsEvidence;
25
32
  constructor(ops: ScratchDatabaseOps);
26
33
  /** Make sure it exists before an Atlas call that plans against it. */
27
34
  ensure(databaseUrl: string, devDatabaseUrl: string): Promise<void>;
35
+ /** An Atlas run against it failed: keep it, whatever the command does next. */
36
+ keepAsEvidence(): void;
37
+ /**
38
+ * An Atlas run failed with `stderr`. Failing before it planned, it left
39
+ * nothing here to read, so the database is dropped now — before the
40
+ * command exits, which may not unwind through {@link around}. Otherwise
41
+ * it is kept as the evidence.
42
+ */
43
+ afterAtlasFailure(stderr: string): Promise<void>;
28
44
  /**
29
- * Run a command, then drop the scratch database if the command created it.
30
- * A command that throws skips the drop; one that exits the process never
31
- * returns here, which is the same thing.
45
+ * Run a command, then drop the scratch database if the command created it,
46
+ * whether the command returned or threw. A throw after
47
+ * {@link keepAsEvidence} keeps it. A command that wants to stop with exit 1
48
+ * throws {@link CommandStopped} rather than calling `process.exit`, which
49
+ * would skip this.
32
50
  */
33
51
  around<T>(command: () => Promise<T>): Promise<T>;
52
+ /** Drop it now if this process created it. Safe to call twice. */
53
+ release(): Promise<void>;
54
+ }
55
+ /**
56
+ * Whether Atlas failed before it planned anything: on connecting, which it
57
+ * reports as `querying system variables: …` — SSL the server does not offer, a
58
+ * login refused, a socket that closed or never opened.
59
+ */
60
+ export declare function atlasFailedBeforePlanning(stderr: string): boolean;
61
+ /**
62
+ * A db command stopping with exit 1 after it has said why: a destructive push
63
+ * the user declined, or one with no terminal to ask on.
64
+ *
65
+ * Thrown instead of `process.exit(1)`, so it unwinds through
66
+ * {@link ScratchDatabase.around} and the scratch database is dropped on the way
67
+ * out. `alreadyReported` tells `reportCommandFailure` the explanation is on the
68
+ * terminal already; the entry point sets the exit code.
69
+ */
70
+ export declare class CommandStopped extends Error {
71
+ readonly alreadyReported = true;
72
+ constructor(reason: string);
34
73
  }