@rebasepro/server-postgres 0.17.3 → 0.18.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.
Files changed (149) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +31 -0
  3. package/dist/backup-argv.d.ts +19 -0
  4. package/dist/branch-argv.d.ts +28 -0
  5. package/dist/branch-prune.d.ts +77 -0
  6. package/dist/cli-collections-path.d.ts +31 -0
  7. package/dist/cli-errors.d.ts +123 -0
  8. package/dist/cli-flags.d.ts +101 -0
  9. package/dist/cli-helpers.d.ts +94 -10
  10. package/dist/collection-index-C7LopMyX.js +247 -0
  11. package/dist/collection-index-C7LopMyX.js.map +1 -0
  12. package/dist/databasePoolManager.d.ts +27 -0
  13. package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-CagOzEPS.js} +6 -6
  14. package/dist/{ensure-collection-policies-UCqgv_8c.js.map → ensure-collection-policies-CagOzEPS.js.map} +1 -1
  15. package/dist/{ensure-collection-tables-DgVixhX3.js → ensure-collection-tables-DbAhrjnG.js} +614 -52
  16. package/dist/ensure-collection-tables-DbAhrjnG.js.map +1 -0
  17. package/dist/index.d.ts +12 -0
  18. package/dist/index.es.js +19307 -3059
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-ClJoTboT.js} +53 -189
  21. package/dist/rls-enforcement-ClJoTboT.js.map +1 -0
  22. package/dist/schema/column-type-drift.d.ts +79 -0
  23. package/dist/schema/doctor.d.ts +42 -0
  24. package/dist/schema/ensure-collection-tables.d.ts +90 -0
  25. package/dist/schema/generate-next-step.d.ts +30 -0
  26. package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -0
  27. package/dist/schema/generated-schema-staleness.d.ts +37 -0
  28. package/dist/schema/introspect-db-queries.d.ts +34 -0
  29. package/dist/security/rls-enforcement.d.ts +63 -2
  30. package/dist/services/BranchService.d.ts +86 -1
  31. package/dist/services/collection-helpers.d.ts +1 -1
  32. package/dist/services/realtimeService.d.ts +30 -2
  33. package/dist/services/row-pipeline.d.ts +12 -1
  34. package/dist/utils/pg-error-utils.d.ts +3 -1
  35. package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
  36. package/package.json +32 -27
  37. package/dist/backup-service-DCk7KhhL.js +0 -9044
  38. package/dist/backup-service-DCk7KhhL.js.map +0 -1
  39. package/dist/collection-index-BRUg10H5.js +0 -5690
  40. package/dist/collection-index-BRUg10H5.js.map +0 -1
  41. package/dist/connection-GOKU3Hu5.js +0 -277
  42. package/dist/connection-GOKU3Hu5.js.map +0 -1
  43. package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
  44. package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
  45. package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
  46. package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
  47. package/dist/src-DiB5RP2Z.js +0 -541
  48. package/dist/src-DiB5RP2Z.js.map +0 -1
  49. package/dist/websocket-BZ4H5wUz.js +0 -8198
  50. package/dist/websocket-BZ4H5wUz.js.map +0 -1
  51. package/src/PostgresAdapter.ts +0 -91
  52. package/src/PostgresBackendDriver.ts +0 -1857
  53. package/src/PostgresBootstrapper.ts +0 -1157
  54. package/src/auth/ensure-tables.ts +0 -896
  55. package/src/auth/schema-version.ts +0 -260
  56. package/src/auth/services.ts +0 -1610
  57. package/src/backup/backup-cli.ts +0 -520
  58. package/src/backup/backup-cron.ts +0 -212
  59. package/src/backup/backup-logic.ts +0 -93
  60. package/src/backup/backup-service.ts +0 -485
  61. package/src/backup/index.ts +0 -12
  62. package/src/backup/pg-tools.ts +0 -403
  63. package/src/backup/retention.ts +0 -86
  64. package/src/cli-errors.ts +0 -265
  65. package/src/cli-helpers.ts +0 -569
  66. package/src/cli-output.ts +0 -43
  67. package/src/cli.ts +0 -1339
  68. package/src/collections/PostgresCollectionRegistry.ts +0 -108
  69. package/src/collections/buildRegistry.ts +0 -68
  70. package/src/collections/validate-relations.ts +0 -402
  71. package/src/connection.ts +0 -333
  72. package/src/data-transformer.ts +0 -845
  73. package/src/databasePoolManager.ts +0 -95
  74. package/src/history/HistoryService.ts +0 -254
  75. package/src/history/ensure-history-table.ts +0 -53
  76. package/src/index.ts +0 -19
  77. package/src/interfaces.ts +0 -60
  78. package/src/module-dir.ts +0 -7
  79. package/src/schema/atlas-argv.ts +0 -94
  80. package/src/schema/auth-schema.ts +0 -298
  81. package/src/schema/auth-users-columns.ts +0 -131
  82. package/src/schema/carved-out-migration.ts +0 -404
  83. package/src/schema/classify-change.ts +0 -436
  84. package/src/schema/collection-index.ts +0 -427
  85. package/src/schema/collections-schema-version.ts +0 -103
  86. package/src/schema/destructive-sql.ts +0 -94
  87. package/src/schema/doctor-cli.ts +0 -66
  88. package/src/schema/doctor-policy-checks.ts +0 -105
  89. package/src/schema/doctor.ts +0 -837
  90. package/src/schema/drizzle-ddl.ts +0 -33
  91. package/src/schema/dynamic-tables.test.ts +0 -302
  92. package/src/schema/dynamic-tables.ts +0 -293
  93. package/src/schema/ensure-collection-policies.ts +0 -198
  94. package/src/schema/ensure-collection-tables.test.ts +0 -518
  95. package/src/schema/ensure-collection-tables.ts +0 -1267
  96. package/src/schema/generate-drizzle-schema-logic.ts +0 -957
  97. package/src/schema/generate-drizzle-schema.ts +0 -143
  98. package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
  99. package/src/schema/generate-postgres-ddl.ts +0 -180
  100. package/src/schema/generate-schema-commit.ts +0 -267
  101. package/src/schema/generated-schema-staleness.ts +0 -284
  102. package/src/schema/introspect-db-constraints.ts +0 -385
  103. package/src/schema/introspect-db-inference.ts +0 -255
  104. package/src/schema/introspect-db-logic.ts +0 -1387
  105. package/src/schema/introspect-db-naming.ts +0 -15
  106. package/src/schema/introspect-db-project.ts +0 -78
  107. package/src/schema/introspect-db-queries.ts +0 -326
  108. package/src/schema/introspect-db-structure.ts +0 -670
  109. package/src/schema/introspect-db-types.ts +0 -56
  110. package/src/schema/introspect-db.ts +0 -257
  111. package/src/schema/introspect-runtime.test.ts +0 -260
  112. package/src/schema/introspect-runtime.ts +0 -327
  113. package/src/schema/non-sql-collections.test.ts +0 -131
  114. package/src/schema/rls-bootstrap-sql.ts +0 -288
  115. package/src/schema/search-column.ts +0 -643
  116. package/src/schema/test-schema.ts +0 -11
  117. package/src/schema/vector-index.ts +0 -491
  118. package/src/security/anonymous-grants.test.ts +0 -73
  119. package/src/security/policy-drift.test.ts +0 -435
  120. package/src/security/policy-drift.ts +0 -473
  121. package/src/security/rls-enforcement.ts +0 -505
  122. package/src/services/BranchService.ts +0 -326
  123. package/src/services/FetchService.ts +0 -2115
  124. package/src/services/PersistService.ts +0 -557
  125. package/src/services/RelationService.ts +0 -978
  126. package/src/services/RelationWriteService.ts +0 -653
  127. package/src/services/cdc/CdcListener.ts +0 -103
  128. package/src/services/cdc/junction-tables.ts +0 -91
  129. package/src/services/cdc/trigger-cdc.ts +0 -173
  130. package/src/services/channel-bus/ChannelBus.ts +0 -44
  131. package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
  132. package/src/services/channel-bus/index.ts +0 -114
  133. package/src/services/channel-history.ts +0 -424
  134. package/src/services/channel-presence.ts +0 -185
  135. package/src/services/collection-helpers.ts +0 -420
  136. package/src/services/dataService.ts +0 -256
  137. package/src/services/index.ts +0 -14
  138. package/src/services/junction-writes.ts +0 -295
  139. package/src/services/nested-path.ts +0 -145
  140. package/src/services/pg-notify-listener.ts +0 -151
  141. package/src/services/realtimeService.ts +0 -2545
  142. package/src/services/row-pipeline.ts +0 -240
  143. package/src/services/write-denial.ts +0 -55
  144. package/src/types.ts +0 -4
  145. package/src/utils/connection-string.ts +0 -58
  146. package/src/utils/drizzle-conditions.ts +0 -2743
  147. package/src/utils/pg-array-null-patch.ts +0 -42
  148. package/src/utils/pg-error-utils.ts +0 -391
  149. package/src/websocket.ts +0 -855
package/LICENSE CHANGED
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
-
package/README.md CHANGED
@@ -8,6 +8,37 @@ PostgreSQL database driver for Rebase, built on Drizzle ORM.
8
8
  pnpm add @rebasepro/server-postgres
9
9
  ```
10
10
 
11
+ This package is ESM-only (`"type": "module"`, no CommonJS build), so it is
12
+ loaded with `import`. `require()` of it from a CJS file works only on Node
13
+ 22.12+, which supports `require(esm)`.
14
+
15
+ ### Allow `@ariga/atlas` to run its install script
16
+
17
+ `db push`, `db generate` and `db migrate` shell out to the `atlas` binary, and
18
+ `@ariga/atlas` downloads that binary in a `preinstall` script. pnpm 10+ and npm
19
+ 12+ refuse a dependency's lifecycle scripts unless the project allowlists them —
20
+ and the install still exits 0, so the only sign is
21
+ `ERR_PNPM_IGNORED_BUILDS: @ariga/atlas` several screens up, followed by
22
+ `Failed to create bin … ENOENT`. Nothing then fails until the first schema push.
23
+
24
+ A project scaffolded by `rebase init` already carries the entry. Adding this
25
+ package to an existing project needs it:
26
+
27
+ ```yaml
28
+ # pnpm-workspace.yaml
29
+ allowBuilds:
30
+ "@ariga/atlas": true
31
+ ```
32
+
33
+ ```json
34
+ // package.json, for npm
35
+ { "allowScripts": { "@ariga/atlas": true } }
36
+ ```
37
+
38
+ `rebase doctor` reports the state of that binary, and tells the three apart:
39
+ not installed, installed with its script blocked, and on disk with only the
40
+ `node_modules/.bin` link missing.
41
+
11
42
  ## What This Package Does
12
43
 
13
44
  Implements the Rebase `DatabaseAdapter` / `BackendBootstrapper` interfaces for PostgreSQL. It provides connection pooling, a Drizzle-based data driver, Postgres LISTEN/NOTIFY realtime, auth table management, snapshot history, schema generation, branching, read replicas, and WebSocket support. Plug it into `@rebasepro/server` via `createPostgresAdapter()` or `createPostgresBootstrapper()`.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Which of the two things `rebase db backup …` means.
3
+ *
4
+ * The cloud family spells the listing `rebase cloud db backup list`. Locally
5
+ * the same words *created a backup*: `backup` dispatched straight to
6
+ * `backupCommand`, which parsed permissively and dropped "list" into the
7
+ * positionals it never reads. One CLI, two spellings, and the wrong guess wrote
8
+ * a dump instead of reading one — quiet, slow, and on a large database not
9
+ * free.
10
+ *
11
+ * A separate module for the same reason `branch-argv.ts` is one: the decision
12
+ * is pure, and the dispatch it lives in cannot be imported without loading the
13
+ * environment and reaching for a database.
14
+ */
15
+ export type BackupAction = "create" | "list";
16
+ /**
17
+ * @param rawArgs the driver's whole line, `["db", "backup", …]`.
18
+ */
19
+ export declare function backupActionOf(rawArgs: readonly string[]): BackupAction;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Argument shapes for `rebase db branch`, with no dependencies.
3
+ *
4
+ * Its own module for the same reason `schema/atlas-argv.ts` is: `cli.ts` pulls
5
+ * in chalk, execa and the driver runtime, so a unit test that imports it cannot
6
+ * run. Argv parsing is the part most worth testing and the part least in need
7
+ * of any of that.
8
+ */
9
+ /**
10
+ * Words on a `db branch` command line that no argument accounts for.
11
+ *
12
+ * `rebase db branch create alpha beta` created a branch called `alpha` and
13
+ * threw `beta` away without a word. The shapes that produces are all quiet and
14
+ * all wrong: an unquoted name (`create my feature` → `my`), a flag written
15
+ * without its dashes (`create feat from main` → `feat`), a shell that split
16
+ * something you thought was one token. In each case the command succeeds and
17
+ * the branch is not the one asked for — and branch names are the thing you
18
+ * later type to switch, delete, or point a deploy at.
19
+ *
20
+ * Anything starting with `-` is left alone rather than validated. This runs on
21
+ * every branch subcommand, including ones added later with flags this function
22
+ * has never heard of, and rejecting an unrecognised flag here would break them
23
+ * from a distance. The value after a flag that takes one is skipped for the
24
+ * same reason: it is that flag's argument, not a stray word.
25
+ *
26
+ * @param words The command's own arguments: action, name, then the rest.
27
+ */
28
+ export declare function unexpectedBranchArgs(words: readonly string[]): string[];
@@ -0,0 +1,77 @@
1
+ /**
2
+ * What `rebase db branch prune` should remove, decided without a database.
3
+ *
4
+ * Branching had no cleanup story at all: no TTL, no prune, no `delete --all`,
5
+ * and every branch is a **full-size copy** — `CREATE DATABASE ... TEMPLATE`
6
+ * duplicates the files on disk, so five branches of a 100 GB database cost
7
+ * 500 GB. The only way to reclaim any of it was to remember every name you had
8
+ * ever typed.
9
+ *
10
+ * Three things drift apart, and they are not the same problem:
11
+ *
12
+ * 1. **Stale rows** — `rebase.branches` says a branch exists and its database
13
+ * is gone. Someone dropped it with plain SQL, or restored the cluster from
14
+ * a backup taken before it. The row is what `list` reads, so the branch goes
15
+ * on being reported forever; `switch` and `info` then fail against a
16
+ * database nothing can find.
17
+ *
18
+ * 2. **Orphan databases** — an `rb_*` database with no row. `createBranch`
19
+ * creates the database first and records it second, so a crash between the
20
+ * two leaves exactly this: disk consumed by something no Rebase command will
21
+ * ever mention again.
22
+ *
23
+ * 3. **Expired branches** — alive, registered, and older than you meant to keep.
24
+ * This is the ordinary case and the reason the command exists.
25
+ *
26
+ * Atlas's scratch databases are reported alongside but never removed by
27
+ * default. `rebase db push` creates `<db>_dev_diff` to compute its diff against
28
+ * and does not always clean it up, so they accumulate next to the branches and
29
+ * look like them. They are not branches, and one may belong to an Atlas run
30
+ * happening right now — so this names them and leaves the decision to a flag.
31
+ */
32
+ /** A branch as `rebase.branches` records it. */
33
+ export interface BranchRow {
34
+ name: string;
35
+ dbName: string;
36
+ createdAt: Date;
37
+ }
38
+ export interface PrunePlan {
39
+ /** Registered, but the database is gone. Remove the row only. */
40
+ staleRows: BranchRow[];
41
+ /** An `rb_*` database with no row. Drop the database only. */
42
+ orphanDatabases: string[];
43
+ /** Alive, registered, older than the cutoff. Drop both. */
44
+ expired: {
45
+ branch: BranchRow;
46
+ ageDays: number;
47
+ }[];
48
+ /** Atlas scratch databases. Reported; removed only when asked. */
49
+ devDiff: string[];
50
+ }
51
+ /** True when nothing at all needs doing. */
52
+ export declare function planIsEmpty(plan: PrunePlan, includeDevDiff: boolean): boolean;
53
+ /**
54
+ * Age in whole days, floored.
55
+ *
56
+ * Floored rather than rounded so `--older-than 7` never catches something six
57
+ * and a half days old: a prune that removes more than it said it would is worse
58
+ * than one that waits another twelve hours.
59
+ */
60
+ export declare function ageInDays(createdAt: Date, now: Date): number;
61
+ /**
62
+ * Parse `--older-than`. Accepts a bare number of days, or `7d` / `2w`.
63
+ *
64
+ * Returns null for anything else, so the caller refuses rather than guessing —
65
+ * silently reading `--older-than 7h` as seven *days* would delete a week of
66
+ * work.
67
+ */
68
+ export declare function parseOlderThan(value: string): number | null;
69
+ export declare function planPrune(input: {
70
+ rows: readonly BranchRow[];
71
+ /** Every database name on the server. */
72
+ databases: readonly string[];
73
+ branchPrefix: string;
74
+ }, options?: {
75
+ olderThanDays?: number | null;
76
+ now?: Date;
77
+ }): PrunePlan;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Thrown rather than exited, so the tests can see it and the entry point owns
3
+ * the exit code.
4
+ *
5
+ * `alreadyReported` is read by `reportCommandFailure`: the full diagnosis is on
6
+ * stderr by the time this is thrown, and the point of the whole change is that
7
+ * it is printed once.
8
+ */
9
+ export declare class CollectionsPathMissing extends Error {
10
+ readonly typed: string;
11
+ readonly resolved: string;
12
+ readonly alreadyReported = true;
13
+ constructor(typed: string, resolved: string);
14
+ }
15
+ /**
16
+ * The whole message, in one place, because it is printed by two callers.
17
+ *
18
+ * Names what was typed, what it resolved to, and the working directory it
19
+ * resolved against — a relative `--collections` is resolved against the cwd,
20
+ * and the generated npm script runs with `cwd: backend/`, so "the path is
21
+ * right and the directory is wrong" is the commonest way to arrive here.
22
+ */
23
+ export declare function describeMissingCollectionsPath(typed: string, resolved: string): string;
24
+ /**
25
+ * Refuse a `--collections` path that does not exist.
26
+ *
27
+ * `null` means the flag was not given: the default (`../config/collections`) is
28
+ * left to the loader, which warns and continues — a project may legitimately
29
+ * have no collections directory, and a headless scaffold does.
30
+ */
31
+ export declare function assertCollectionsPathExists(typed: string | null): void;
@@ -26,6 +26,26 @@ export declare function isSslNotEnabled(err: unknown): boolean;
26
26
  * an enum type it defined is still referenced by another object.
27
27
  */
28
28
  export declare function isDependencyDropError(err: unknown): boolean;
29
+ /**
30
+ * Parse host:port from a DATABASE_URL for display purposes.
31
+ *
32
+ * Exported because every message about a connection has to name the thing it
33
+ * could not reach, and the boot path needs the same rendering the CLI banners
34
+ * use — including the same refusal to print the URL itself, which carries the
35
+ * password.
36
+ */
37
+ export declare function parseHostInfo(databaseUrl: string): string;
38
+ /**
39
+ * The sentence the operating system actually produced, dug out of the wrappers.
40
+ *
41
+ * `connect ECONNREFUSED 127.0.0.1:5432` is written by `net`, then wrapped by
42
+ * `pg`, then wrapped again by Drizzle as `Failed query: …` — and on a
43
+ * dual-stack host it is not in `.cause` at all but inside the
44
+ * `AggregateError.errors` array of one attempt per resolved address. Printing
45
+ * the banner without it loses the one token every search engine, runbook and
46
+ * colleague recognises.
47
+ */
48
+ export declare function deepestErrorMessage(err: unknown): string | null;
29
49
  /**
30
50
  * Pre-flight check: verify that the database is reachable before running
31
51
  * a heavy subprocess (Atlas, migrations, etc.).
@@ -40,3 +60,106 @@ export declare function checkDatabaseConnectivity(databaseUrl: string): Promise<
40
60
  * or null if unrecognized.
41
61
  */
42
62
  export declare function diagnoseDbError(err: unknown, databaseUrl?: string): string | null;
63
+ /**
64
+ * Say why the command failed, on the way out.
65
+ *
66
+ * The entry point below used to be `.catch(() => process.exit(1))`, which threw
67
+ * the error away. Every message this file and its services raise — "Branch
68
+ * \"x\" already exists.", "the source database has active connections", "Branch
69
+ * name is too long" — was written, wrapped in the right PG error code, and then
70
+ * discarded one frame before it reached a terminal. What a developer saw was a
71
+ * header line, no error, and exit 1.
72
+ *
73
+ * Two shapes are deliberately kept quiet:
74
+ *
75
+ * - **A child process that already spoke.** Atlas, `pg_dump` and `psql` run
76
+ * with inherited stdio, so their diagnosis is on the terminal already and
77
+ * execa's wrapper adds only `Command failed with exit code 1: atlas …`.
78
+ * `packages/cli` filters exactly these two phrasings one level up
79
+ * (`runDbCommand`), and this is that filter, for the process that is actually
80
+ * throwing.
81
+ *
82
+ * - **A message that is only a query.** Drizzle reports failures as
83
+ * `Failed query: <sql> params:` and hides the real PostgreSQL error in
84
+ * `cause`, so the wrapper alone tells a reader nothing they can act on. The
85
+ * cause is appended when it says something the message does not.
86
+ *
87
+ * - **An error that has already printed its own diagnosis**, marked
88
+ * `alreadyReported`. `CollectionsPathMissing` is one: it prints the path, what
89
+ * it resolved to and the cwd it resolved against, and then throws so the entry
90
+ * point owns the exit code. Repeating its one-line summary underneath would
91
+ * undo the "printed once" this whole path exists for.
92
+ */
93
+ export declare function reportCommandFailure(error: unknown): void;
94
+ /**
95
+ * `migrate apply` refusing because the database already has the schema.
96
+ *
97
+ * `42710` (duplicate_object) and `42P07` (duplicate_table) are what a migration
98
+ * hits when boot-ensure — or a `db push` — has already provisioned the objects
99
+ * it was going to create. Since boot-ensure provisions *every* production
100
+ * database, this is the normal case rather than the exotic one, and the raw
101
+ * failure (`pq: type "posts_status" already exists (42710)`, then `sql/migrate:
102
+ * write revision: … current transaction is aborted`) names no way forward.
103
+ */
104
+ export declare function parseAlreadyProvisioned(text: string): {
105
+ object: string;
106
+ code: string;
107
+ } | null;
108
+ /**
109
+ * What to do about it: tell Atlas the database is already at a version.
110
+ *
111
+ * Atlas's own mechanism (`migrate apply --baseline <version>`), not a ledger of
112
+ * ours — it writes the revision row Atlas reads, so every later `rebase db
113
+ * migrate` is an ordinary one.
114
+ */
115
+ export declare function formatBaselineRemedy(version: string | null, object?: string): string;
116
+ /** `ALTER TABLE … SET NOT NULL` on a table that already holds rows (23502). */
117
+ export declare function parseNotNullViolation(text: string): {
118
+ table: string;
119
+ column: string;
120
+ } | null;
121
+ /**
122
+ * The three ways out, because there is no fourth.
123
+ *
124
+ * Boot-ensure handles this case — it adds the column nullable and sets NOT NULL
125
+ * only when the table is empty — so a push that dies here is strictly worse
126
+ * than the boot that would have run instead, and the developer deserves to be
127
+ * told which of the three they want rather than left with `pq: … contains null
128
+ * values (23502)` and no next step.
129
+ */
130
+ export declare function formatNotNullViolationBanner(violation: {
131
+ table: string;
132
+ column: string;
133
+ }, rowCount: number | null): string;
134
+ /** Atlas refusing to remove a label from an enum — a renamed `enum` option id. */
135
+ export declare function parseEnumLabelDrop(text: string): {
136
+ label: string;
137
+ enumType: string;
138
+ } | null;
139
+ /**
140
+ * Why the two paths disagree, and what retiring an option id actually costs.
141
+ *
142
+ * Boot-ensure adds enum labels and never removes one, so `rebase dev` accepts
143
+ * the very edit that stops `db push` dead. PostgreSQL has no `ALTER TYPE …
144
+ * DROP VALUE`: a label goes only by rewriting the type, which is a data
145
+ * migration and not a schema push.
146
+ */
147
+ export declare function formatEnumLabelDropBanner(drop: {
148
+ label: string;
149
+ enumType: string;
150
+ }): string;
151
+ /**
152
+ * The remedy for an Atlas invocation that failed, or `null`.
153
+ *
154
+ * Scoped by the invocation, because the same database state means different
155
+ * things to different subcommands: "already exists" under `migrate apply` wants
156
+ * a baseline, while under `schema apply` it is a genuine conflict.
157
+ */
158
+ export declare function diagnoseAtlasFailure(context: {
159
+ domain: string;
160
+ args: string[];
161
+ stderr: string;
162
+ databaseUrl?: string;
163
+ /** The newest migration version on disk, named in the baseline remedy. */
164
+ latestMigrationVersion?: string | null;
165
+ }): Promise<string | null>;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The flags each driver command takes, in one place, and the check that
3
+ * enforces them.
4
+ *
5
+ * Every parser in `cli.ts` runs `arg(..., { permissive: true })`, which does not
6
+ * mean "be lenient" — it means **an undeclared flag becomes a positional**. So
7
+ * `rebase db push --alow-destructive` did not fail: the typo landed in `_`, the
8
+ * push ran with the destructive gate still closed, and the developer read the
9
+ * refusal as Rebase ignoring the flag they had just typed. `rebase schema
10
+ * generate --ouput src/schema.ts` was worse — it wrote the default path and
11
+ * said nothing, so the next build compiled a file nobody had regenerated.
12
+ *
13
+ * The check has to live at the entry point rather than in those parsers, and
14
+ * that is not a detail: `db push` and `db generate` re-enter `schemaCommand`
15
+ * and `generatePostgresDdlCommand` with the *db* line, so a strict parser
16
+ * inside either of them would reject `--allow-destructive` on a line where it
17
+ * is correct. One validation, once, against the spec for the command the user
18
+ * actually named; the inner parsers keep reading an already-validated line.
19
+ *
20
+ * Commands with their own argument handling — `db branch` (`unexpectedBranchArgs`),
21
+ * `db backup`, `db restore`, `db backups` — are deliberately absent: they own
22
+ * their spec, and a second list here would be one more thing to keep in step.
23
+ * An absent entry is "not checked here", never "takes nothing".
24
+ */
25
+ import arg from "arg";
26
+ /** Keyed by `"<domain> <subcommand>"`, the way the user types it. */
27
+ export declare const DRIVER_FLAG_SPECS: Record<string, arg.Spec>;
28
+ /**
29
+ * One destination flag, two spellings, everywhere.
30
+ *
31
+ * `--out` is the primary on `rebase build`, an alias on `generate-sdk`, `db
32
+ * backup` and `cloud env pull`, and was refused outright by the three `schema`
33
+ * commands — so the spelling a user learned on one command was an
34
+ * "unknown or unexpected option" on the next. Neither name can be retired (both
35
+ * are shipped), so both are accepted, and {@link assertOutputAliasesPaired}
36
+ * makes that the rule rather than a habit.
37
+ */
38
+ export declare const OUTPUT_FLAG_ALIASES: readonly ["--out", "--output"];
39
+ /**
40
+ * Every spec that names one of the pair names both.
41
+ *
42
+ * Exported so the CLI's own specs can be held to it too: the drift this fixes
43
+ * ran across two packages, and a check that only reads this file would let the
44
+ * next `--out`-only command through.
45
+ */
46
+ export declare function assertOutputAliasesPaired(specs: Record<string, arg.Spec>): string[];
47
+ /**
48
+ * The `--collections` path the user typed, or `null` if they typed none.
49
+ *
50
+ * Read once, at the entry point, because every command that takes it re-enters
51
+ * the generators with the same line and each of them resolved it again — which
52
+ * is how "Collections path not found" came to be printed four times before the
53
+ * real output, and how a path that does not exist got as far as *writing* an
54
+ * empty schema.
55
+ *
56
+ * Permissive, and it has to be: {@link assertKnownFlags} has already judged the
57
+ * line, and `db push` carries flags the `--collections` spec does not name.
58
+ */
59
+ export declare function collectionsPathIn(args: string[]): string | null;
60
+ /**
61
+ * The long flags a `--help` usage line documents.
62
+ *
63
+ * `"rebase db push [--collections <dir>] [--dry-run] …"` → `["--collections",
64
+ * "--dry-run"]`. Placeholders (`<dir>`) and the alternation inside a positional
65
+ * (`<create|list|switch>`) are not flags and are not returned.
66
+ */
67
+ export declare function flagsInUsage(usage: string): string[];
68
+ /**
69
+ * Every flag the help documents is accepted, and every flag accepted is documented.
70
+ *
71
+ * The drift this catches shipped: `rebase db push --help` has printed
72
+ * `[--dry-run]` since the flag was written, `DRIVER_FLAG_SPECS["db push"]` never
73
+ * listed it, and {@link assertKnownFlags} — added later to stop typos being
74
+ * swallowed — turned the documented flag into `unknown or unexpected option`.
75
+ * The only way to see a push's SQL was to trip the destructive gate, which is
76
+ * the exact problem `--dry-run` was written to solve. Worse in a project on an
77
+ * older driver, whose permissive parser *applied* the schema on that line.
78
+ *
79
+ * Two hand-maintained lists in two packages cannot be kept in step by care, so
80
+ * they are held to each other instead: `usages` is keyed the way
81
+ * {@link DRIVER_FLAG_SPECS} is (`"db push"`), and comes from the CLI's own help
82
+ * pages. A key in only one of the two is not this function's business — the
83
+ * help covers commands that parse their own lines (`db branch`, `db backup`),
84
+ * and an absent spec entry means "not checked here".
85
+ *
86
+ * Aliases (`"-c": "--collections"`, `"--out": "--output"`) need no line of their
87
+ * own: they are spellings of a documented flag. Neither do {@link RELAYED_FLAGS},
88
+ * which every driver command accepts because the CLI relays them.
89
+ */
90
+ export declare function assertSpecMatchesUsage(specs: Record<string, arg.Spec>, usages: Record<string, string>): string[];
91
+ /**
92
+ * Reject a flag the named command does not take.
93
+ *
94
+ * `args` is the driver's whole line — `["db", "push", …]` — so the flags are
95
+ * read from `args.slice(2)`, past the two command words.
96
+ *
97
+ * Throws rather than exiting: `runPluginCommand`'s caller already turns a
98
+ * thrown error into one red line and exit 1, and a thrown error is what the
99
+ * tests can see.
100
+ */
101
+ export declare function assertKnownFlags(domain: string, subcommand: string | undefined, args: string[]): void;
@@ -22,22 +22,106 @@ import type { CollectionConfig } from "@rebasepro/types";
22
22
  * second, matching the order {@link resolveLocalBin} searches — the driver may
23
23
  * be installed a level up from where the command runs.
24
24
  */
25
- export declare function diagnoseMissingBin(packageName: string): "not-installed" | "build-script-blocked";
25
+ export type ProjectPackageManager = "pnpm" | "npm" | "yarn" | "bun" | "unknown";
26
+ /**
27
+ * Which package manager installed this project, read off its lockfile.
28
+ *
29
+ * Every remedy for a blocked build script is package-manager specific, and
30
+ * naming the wrong one is the same failure as naming none: `pnpm approve-builds`
31
+ * is not a thing an npm user can run, and `pnpm.onlyBuiltDependencies` is a key
32
+ * npm does not read. This existed as a pnpm-only message for long enough that
33
+ * npm 12 — which blocks dependency lifecycle scripts by default, the way pnpm 10
34
+ * does — could ship and leave every npm reader three unfollowable commands deep.
35
+ *
36
+ * The lockfile, not `npm_config_user_agent`: the question is how the project's
37
+ * `node_modules` was built, not which binary happens to be invoking us. Walks up
38
+ * because commands run from `backend/` in a scaffolded project, and the lockfile
39
+ * is at the workspace root.
40
+ */
41
+ export declare function detectProjectPackageManager(startDir?: string): ProjectPackageManager;
42
+ /**
43
+ * How to let `@ariga/atlas` run its `preinstall`, in the reader's own package
44
+ * manager.
45
+ *
46
+ * Returns the lines to print after "the binary is missing but the package is
47
+ * installed". Kept next to {@link detectProjectPackageManager} so the two move
48
+ * together, and returned rather than printed so it can be asserted on.
49
+ */
50
+ export declare function describeBuildScriptRemedy(packageName: string, manager?: ProjectPackageManager): string[];
51
+ /**
52
+ * "Install again, from scratch", in the reader's own package manager.
53
+ *
54
+ * For the `bin-link-missing` state: the tree is half-written, and re-resolving
55
+ * it is what recreates the `.bin` shim. `--force` on pnpm is what makes it
56
+ * relink rather than decide the tree is already up to date.
57
+ */
58
+ export declare function describeReinstallCommand(manager?: ProjectPackageManager): string;
59
+ /** `add <pkg>` as a dev dependency, in the reader's own package manager. */
60
+ export declare function describeDevAddCommand(packageName: string, manager?: ProjectPackageManager): string;
61
+ /**
62
+ * Why a dependency's binary is not on `PATH`.
63
+ *
64
+ * `bin-link-missing` is the third state, and the reason this is not a boolean.
65
+ * The `preinstall` did run and the binary IS on disk — only the
66
+ * `node_modules/.bin` shim that points at it is absent. pnpm produces exactly
67
+ * that when it writes the link before the script that creates the target
68
+ * ("Failed to create bin … ENOENT"), and so does a tree copied without its
69
+ * symlinks. Telling that reader their build scripts are blocked sends them to
70
+ * `approve-builds`, which does nothing, because nothing is blocked.
71
+ */
72
+ export type MissingBinDiagnosis = "not-installed" | "build-script-blocked" | "bin-link-missing";
73
+ /**
74
+ * The binaries a package's own manifest declares, as absolute paths.
75
+ *
76
+ * `bin` is a string for a single binary named after the package, or a map.
77
+ * Both forms appear in the wild and `@ariga/atlas` uses the map.
78
+ */
79
+ export declare function declaredBinaries(manifestPath: string): string[];
80
+ export declare function diagnoseMissingBin(packageName: string): MissingBinDiagnosis;
26
81
  export declare function resolveLocalBin(binName: string): string | null;
27
82
  export declare function getTableIncludesFromCollections(allCollections: CollectionConfig[]): Promise<string[]>;
28
83
  export declare function getTableIncludes(collectionsPath: string): Promise<string[]>;
84
+ export declare function loadCollectionsForCli(collectionsPath: string): Promise<CollectionConfig[]>;
85
+ export declare function getDevDatabaseUrl(databaseUrl: string): string;
29
86
  /**
30
- * Load a project's collections the way the Atlas-facing commands need them.
87
+ * Create the scratch database Atlas plans against, and say so when we cannot.
88
+ *
89
+ * Atlas needs a `--dev-url` — an empty database it can build the desired state
90
+ * in — and this makes `<db>_dev_diff` next to the real one. The whole body used
91
+ * to be wrapped in an empty catch commented "Ignore, let Atlas handle
92
+ * connection failures", and Atlas's version of handling it is `postgres:
93
+ * querying system variables: pq: database "app_dev_diff" does not exist
94
+ * (3D000)` — the symptom, four frames downstream of the cause, with the cause
95
+ * thrown away.
31
96
  *
32
- * Deliberately forgiving a file that fails to import is skipped rather than
33
- * fatal because the callers use this to *narrow* what Atlas may touch, and a
34
- * hard failure here would block a push over an unrelated broken file. Callers
35
- * that cannot tolerate a partial answer (the table excludes, which fail closed)
36
- * check the result themselves.
97
+ * The cause is almost always `42501`: `CREATE DATABASE` is a privilege managed
98
+ * providers do not grant, so this is the first thing a hosted user hits and the
99
+ * one they can do least with.
100
+ *
101
+ * @returns whether the scratch database was created by *this* call — the only
102
+ * case in which dropping it afterwards is ours to do.
37
103
  */
38
- export declare function loadCollectionsForCli(collectionsPath: string): Promise<CollectionConfig[]>;
39
- export declare function getDevDatabaseUrl(databaseUrl: string): string;
40
- export declare function ensureDevDatabaseExists(databaseUrl: string, devDatabaseUrl: string): Promise<void>;
104
+ export declare function ensureDevDatabaseExists(databaseUrl: string, devDatabaseUrl: string): Promise<boolean>;
105
+ /**
106
+ * What went wrong creating the scratch database, and what to do about it.
107
+ *
108
+ * Exported for its test: the `42501` branch is the one that matters and it
109
+ * cannot be provoked without a non-superuser role.
110
+ */
111
+ export declare function describeDevDatabaseFailure(err: unknown, devDbName: string): string;
112
+ /**
113
+ * Drop the scratch database again, after the command that needed it succeeded.
114
+ *
115
+ * It is a full copy of the schema — no rows, but every table, type and index —
116
+ * and it was left behind per target forever. The only notice anyone got was
117
+ * `rebase db branch prune` reporting "3 Atlas scratch database(s) left over
118
+ * from db push", and on a provider that bills per database it is a silent cost.
119
+ *
120
+ * Deliberately NOT called on the failure path: after a failed push the scratch
121
+ * database holds the state Atlas was planning against, which is the one thing
122
+ * worth inspecting.
123
+ */
124
+ export declare function dropDevDatabase(databaseUrl: string, devDatabaseUrl: string): Promise<void>;
41
125
  /**
42
126
  * The generated SQL for the project's `search` blocks, if it has any.
43
127
  *