@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.
- package/LICENSE +0 -1
- package/README.md +31 -0
- package/dist/backup-argv.d.ts +19 -0
- package/dist/branch-argv.d.ts +28 -0
- package/dist/branch-prune.d.ts +77 -0
- package/dist/cli-collections-path.d.ts +31 -0
- package/dist/cli-errors.d.ts +123 -0
- package/dist/cli-flags.d.ts +101 -0
- package/dist/cli-helpers.d.ts +94 -10
- package/dist/collection-index-C7LopMyX.js +247 -0
- package/dist/collection-index-C7LopMyX.js.map +1 -0
- package/dist/databasePoolManager.d.ts +27 -0
- package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-CagOzEPS.js} +6 -6
- package/dist/{ensure-collection-policies-UCqgv_8c.js.map → ensure-collection-policies-CagOzEPS.js.map} +1 -1
- package/dist/{ensure-collection-tables-DgVixhX3.js → ensure-collection-tables-DbAhrjnG.js} +614 -52
- package/dist/ensure-collection-tables-DbAhrjnG.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.es.js +19307 -3059
- package/dist/index.es.js.map +1 -1
- package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-ClJoTboT.js} +53 -189
- package/dist/rls-enforcement-ClJoTboT.js.map +1 -0
- package/dist/schema/column-type-drift.d.ts +79 -0
- package/dist/schema/doctor.d.ts +42 -0
- package/dist/schema/ensure-collection-tables.d.ts +90 -0
- package/dist/schema/generate-next-step.d.ts +30 -0
- package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -0
- package/dist/schema/generated-schema-staleness.d.ts +37 -0
- package/dist/schema/introspect-db-queries.d.ts +34 -0
- package/dist/security/rls-enforcement.d.ts +63 -2
- package/dist/services/BranchService.d.ts +86 -1
- package/dist/services/collection-helpers.d.ts +1 -1
- package/dist/services/realtimeService.d.ts +30 -2
- package/dist/services/row-pipeline.d.ts +12 -1
- package/dist/utils/pg-error-utils.d.ts +3 -1
- package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
- package/package.json +32 -27
- package/dist/backup-service-DCk7KhhL.js +0 -9044
- package/dist/backup-service-DCk7KhhL.js.map +0 -1
- package/dist/collection-index-BRUg10H5.js +0 -5690
- package/dist/collection-index-BRUg10H5.js.map +0 -1
- package/dist/connection-GOKU3Hu5.js +0 -277
- package/dist/connection-GOKU3Hu5.js.map +0 -1
- package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
- package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
- package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
- package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
- package/dist/src-DiB5RP2Z.js +0 -541
- package/dist/src-DiB5RP2Z.js.map +0 -1
- package/dist/websocket-BZ4H5wUz.js +0 -8198
- package/dist/websocket-BZ4H5wUz.js.map +0 -1
- package/src/PostgresAdapter.ts +0 -91
- package/src/PostgresBackendDriver.ts +0 -1857
- package/src/PostgresBootstrapper.ts +0 -1157
- package/src/auth/ensure-tables.ts +0 -896
- package/src/auth/schema-version.ts +0 -260
- package/src/auth/services.ts +0 -1610
- package/src/backup/backup-cli.ts +0 -520
- package/src/backup/backup-cron.ts +0 -212
- package/src/backup/backup-logic.ts +0 -93
- package/src/backup/backup-service.ts +0 -485
- package/src/backup/index.ts +0 -12
- package/src/backup/pg-tools.ts +0 -403
- package/src/backup/retention.ts +0 -86
- package/src/cli-errors.ts +0 -265
- package/src/cli-helpers.ts +0 -569
- package/src/cli-output.ts +0 -43
- package/src/cli.ts +0 -1339
- package/src/collections/PostgresCollectionRegistry.ts +0 -108
- package/src/collections/buildRegistry.ts +0 -68
- package/src/collections/validate-relations.ts +0 -402
- package/src/connection.ts +0 -333
- package/src/data-transformer.ts +0 -845
- package/src/databasePoolManager.ts +0 -95
- package/src/history/HistoryService.ts +0 -254
- package/src/history/ensure-history-table.ts +0 -53
- package/src/index.ts +0 -19
- package/src/interfaces.ts +0 -60
- package/src/module-dir.ts +0 -7
- package/src/schema/atlas-argv.ts +0 -94
- package/src/schema/auth-schema.ts +0 -298
- package/src/schema/auth-users-columns.ts +0 -131
- package/src/schema/carved-out-migration.ts +0 -404
- package/src/schema/classify-change.ts +0 -436
- package/src/schema/collection-index.ts +0 -427
- package/src/schema/collections-schema-version.ts +0 -103
- package/src/schema/destructive-sql.ts +0 -94
- package/src/schema/doctor-cli.ts +0 -66
- package/src/schema/doctor-policy-checks.ts +0 -105
- package/src/schema/doctor.ts +0 -837
- package/src/schema/drizzle-ddl.ts +0 -33
- package/src/schema/dynamic-tables.test.ts +0 -302
- package/src/schema/dynamic-tables.ts +0 -293
- package/src/schema/ensure-collection-policies.ts +0 -198
- package/src/schema/ensure-collection-tables.test.ts +0 -518
- package/src/schema/ensure-collection-tables.ts +0 -1267
- package/src/schema/generate-drizzle-schema-logic.ts +0 -957
- package/src/schema/generate-drizzle-schema.ts +0 -143
- package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
- package/src/schema/generate-postgres-ddl.ts +0 -180
- package/src/schema/generate-schema-commit.ts +0 -267
- package/src/schema/generated-schema-staleness.ts +0 -284
- package/src/schema/introspect-db-constraints.ts +0 -385
- package/src/schema/introspect-db-inference.ts +0 -255
- package/src/schema/introspect-db-logic.ts +0 -1387
- package/src/schema/introspect-db-naming.ts +0 -15
- package/src/schema/introspect-db-project.ts +0 -78
- package/src/schema/introspect-db-queries.ts +0 -326
- package/src/schema/introspect-db-structure.ts +0 -670
- package/src/schema/introspect-db-types.ts +0 -56
- package/src/schema/introspect-db.ts +0 -257
- package/src/schema/introspect-runtime.test.ts +0 -260
- package/src/schema/introspect-runtime.ts +0 -327
- package/src/schema/non-sql-collections.test.ts +0 -131
- package/src/schema/rls-bootstrap-sql.ts +0 -288
- package/src/schema/search-column.ts +0 -643
- package/src/schema/test-schema.ts +0 -11
- package/src/schema/vector-index.ts +0 -491
- package/src/security/anonymous-grants.test.ts +0 -73
- package/src/security/policy-drift.test.ts +0 -435
- package/src/security/policy-drift.ts +0 -473
- package/src/security/rls-enforcement.ts +0 -505
- package/src/services/BranchService.ts +0 -326
- package/src/services/FetchService.ts +0 -2115
- package/src/services/PersistService.ts +0 -557
- package/src/services/RelationService.ts +0 -978
- package/src/services/RelationWriteService.ts +0 -653
- package/src/services/cdc/CdcListener.ts +0 -103
- package/src/services/cdc/junction-tables.ts +0 -91
- package/src/services/cdc/trigger-cdc.ts +0 -173
- package/src/services/channel-bus/ChannelBus.ts +0 -44
- package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
- package/src/services/channel-bus/index.ts +0 -114
- package/src/services/channel-history.ts +0 -424
- package/src/services/channel-presence.ts +0 -185
- package/src/services/collection-helpers.ts +0 -420
- package/src/services/dataService.ts +0 -256
- package/src/services/index.ts +0 -14
- package/src/services/junction-writes.ts +0 -295
- package/src/services/nested-path.ts +0 -145
- package/src/services/pg-notify-listener.ts +0 -151
- package/src/services/realtimeService.ts +0 -2545
- package/src/services/row-pipeline.ts +0 -240
- package/src/services/write-denial.ts +0 -55
- package/src/types.ts +0 -4
- package/src/utils/connection-string.ts +0 -58
- package/src/utils/drizzle-conditions.ts +0 -2743
- package/src/utils/pg-array-null-patch.ts +0 -42
- package/src/utils/pg-error-utils.ts +0 -391
- package/src/websocket.ts +0 -855
package/LICENSE
CHANGED
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;
|
package/dist/cli-errors.d.ts
CHANGED
|
@@ -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;
|
package/dist/cli-helpers.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
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
|
*
|