@rebasepro/server-postgres 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BranchService-CucnFcSE.js → BranchService-BRt78gfa.js} +35 -18
- package/dist/BranchService-BRt78gfa.js.map +1 -0
- package/dist/PostgresBackendDriver.d.ts +116 -4
- package/dist/auth/services.d.ts +99 -22
- package/dist/{auth-users-columns-D2LBFrMH.js → auth-users-columns-C72EMoDJ.js} +17 -1
- package/dist/{auth-users-columns-D2LBFrMH.js.map → auth-users-columns-C72EMoDJ.js.map} +1 -1
- package/dist/backup/backup-cli.d.ts +22 -0
- package/dist/{backup-cli-DqBakiMO.js → backup-cli-DW5p9_zv.js} +17 -14
- package/dist/backup-cli-DW5p9_zv.js.map +1 -0
- package/dist/{backup-service-CaMOS76G.js → backup-service-EwcDVG-8.js} +7 -9
- package/dist/{backup-service-CaMOS76G.js.map → backup-service-EwcDVG-8.js.map} +1 -1
- package/dist/{cli-errors-Dka89exj.js → cli-errors-DsA-K9uP.js} +101 -1
- package/dist/cli-errors-DsA-K9uP.js.map +1 -0
- package/dist/cli-errors.d.ts +42 -0
- package/dist/cli-flags-BglvpjHv.js +138 -0
- package/dist/cli-flags-BglvpjHv.js.map +1 -0
- package/dist/cli-flags.d.ts +28 -0
- package/dist/cli-helpers.d.ts +18 -12
- package/dist/cli-scratch-database.d.ts +34 -0
- package/dist/cli.js +177 -287
- package/dist/cli.js.map +1 -1
- package/dist/{column-plan-helpers-CpILzHJS.js → column-plan-helpers-1-LQD0yI.js} +44 -38
- package/dist/column-plan-helpers-1-LQD0yI.js.map +1 -0
- package/dist/data-transformer.d.ts +0 -8
- package/dist/{doctor-CU9IogdL.js → doctor-C-sYWbmt.js} +228 -46
- package/dist/doctor-C-sYWbmt.js.map +1 -0
- package/dist/{ensure-collection-policies-Dzd-2S81.js → ensure-collection-policies-B1ureSIV.js} +73 -18
- package/dist/ensure-collection-policies-B1ureSIV.js.map +1 -0
- package/dist/{ensure-collection-tables-CpAgy51F.js → ensure-collection-tables-kkkHk8oo.js} +124 -37
- package/dist/ensure-collection-tables-kkkHk8oo.js.map +1 -0
- package/dist/{ensure-tables-Cr5B4UmH.js → ensure-tables-BmI_tRxc.js} +10 -3
- package/dist/ensure-tables-BmI_tRxc.js.map +1 -0
- package/dist/{generate-drizzle-schema-B537GIvz.js → generate-drizzle-schema-93M0lUxK.js} +2 -2
- package/dist/{generate-drizzle-schema-B537GIvz.js.map → generate-drizzle-schema-93M0lUxK.js.map} +1 -1
- package/dist/{generate-drizzle-schema-logic-BcMl7VSy.js → generate-drizzle-schema-logic-sSFDp6LR.js} +26 -8
- package/dist/generate-drizzle-schema-logic-sSFDp6LR.js.map +1 -0
- package/dist/generate-postgres-ddl-logic-BJsLaVNX.js +152 -0
- package/dist/generate-postgres-ddl-logic-BJsLaVNX.js.map +1 -0
- package/dist/generated-sql.d.ts +28 -0
- package/dist/index.es.js +4768 -1117
- package/dist/index.es.js.map +1 -1
- package/dist/{introspect-db-logic-C6LQdTxj.js → introspect-db-logic-kCETE8TY.js} +532 -39
- package/dist/introspect-db-logic-kCETE8TY.js.map +1 -0
- package/dist/introspect-db-queries-C_Q5VgQw.js +317 -0
- package/dist/introspect-db-queries-C_Q5VgQw.js.map +1 -0
- package/dist/{plan-schema-CboAIwLN.js → plan-schema-DU9exq6C.js} +291 -529
- package/dist/plan-schema-DU9exq6C.js.map +1 -0
- package/dist/{policy-drift-xJfy9xG7.js → policy-drift-B-J2hhm0.js} +3 -3
- package/dist/policy-drift-B-J2hhm0.js.map +1 -0
- package/dist/{generate-postgres-ddl-logic-D7imhYV8.js → render-ddl-Ds2t_d9V.js} +11 -149
- package/dist/render-ddl-Ds2t_d9V.js.map +1 -0
- package/dist/{rls-bootstrap-sql-H3rCFi3F.js → rls-bootstrap-sql-_KNnjanK.js} +562 -35
- package/dist/rls-bootstrap-sql-_KNnjanK.js.map +1 -0
- package/dist/{rls-enforcement-C6Xk0lA6.js → rls-enforcement-CfXOJJaW.js} +10 -2
- package/dist/rls-enforcement-CfXOJJaW.js.map +1 -0
- package/dist/schema/atlas-argv.d.ts +15 -0
- package/dist/schema/auth-schema.d.ts +170 -0
- package/dist/schema/classify-change.d.ts +29 -1
- package/dist/schema/column-plan-helpers.d.ts +51 -20
- package/dist/schema/destructive-sql.d.ts +71 -1
- package/dist/schema/doctor-cli.js +4 -4
- package/dist/schema/doctor.d.ts +34 -1
- package/dist/schema/ensure-collection-policies.d.ts +22 -0
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -5
- package/dist/schema/generate-postgres-ddl.js +1 -1
- package/dist/schema/generate-schema-commit.d.ts +12 -0
- package/dist/schema/introspect-db-logic.d.ts +31 -0
- package/dist/schema/introspect-db-queries.d.ts +1 -1
- package/dist/schema/introspect-db-search.d.ts +22 -0
- package/dist/schema/introspect-db-storage.d.ts +83 -0
- package/dist/schema/introspect-db.js +15 -314
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/plan/diff-plan.d.ts +4 -3
- package/dist/schema/plan/plan-schema.d.ts +26 -11
- package/dist/schema/plan/render-ddl.d.ts +6 -0
- package/dist/schema/plan/types.d.ts +43 -12
- package/dist/search-column-BM-GV6vH.js +442 -0
- package/dist/search-column-BM-GV6vH.js.map +1 -0
- package/dist/security/policy-drift.d.ts +1 -1
- package/dist/security/rls-enforcement.d.ts +7 -0
- package/dist/services/BranchService.d.ts +22 -1
- package/dist/services/FetchService.d.ts +102 -32
- package/dist/services/PersistService.d.ts +41 -5
- package/dist/services/RelationService.d.ts +29 -0
- package/dist/services/RelationWriteService.d.ts +6 -0
- package/dist/services/cdc/CdcListener.d.ts +15 -5
- package/dist/services/cdc/identity-columns.d.ts +19 -0
- package/dist/services/cdc/trigger-cdc.d.ts +45 -7
- package/dist/services/channel-bus/PostgresChannelBus.d.ts +12 -7
- package/dist/services/channel-history.d.ts +17 -1
- package/dist/services/collection-helpers.d.ts +21 -0
- package/dist/services/dataService.d.ts +3 -20
- package/dist/services/field-op-sql.d.ts +71 -0
- package/dist/services/junction-writes.d.ts +19 -3
- package/dist/services/pg-notify-listener.d.ts +95 -6
- package/dist/services/read-field-access.d.ts +19 -0
- package/dist/services/realtimeService.d.ts +232 -44
- package/dist/services/row-pipeline.d.ts +5 -0
- package/dist/services/socket-liveness.d.ts +45 -0
- package/dist/services/soft-delete.d.ts +12 -2
- package/dist/services/sql-script.d.ts +71 -0
- package/dist/services/write-depth.d.ts +16 -0
- package/dist/services/write-transaction-scope.d.ts +42 -0
- package/dist/utils/drizzle-conditions.d.ts +49 -4
- package/dist/utils/sql-redaction.d.ts +21 -0
- package/dist/websocket.d.ts +57 -18
- package/package.json +9 -9
- package/dist/BranchService-CucnFcSE.js.map +0 -1
- package/dist/backup-cli-DqBakiMO.js.map +0 -1
- package/dist/cli-errors-Dka89exj.js.map +0 -1
- package/dist/column-plan-helpers-CpILzHJS.js.map +0 -1
- package/dist/doctor-CU9IogdL.js.map +0 -1
- package/dist/ensure-collection-policies-Dzd-2S81.js.map +0 -1
- package/dist/ensure-collection-tables-CpAgy51F.js.map +0 -1
- package/dist/ensure-tables-Cr5B4UmH.js.map +0 -1
- package/dist/generate-drizzle-schema-logic-BcMl7VSy.js.map +0 -1
- package/dist/generate-postgres-ddl-logic-D7imhYV8.js.map +0 -1
- package/dist/introspect-db-logic-C6LQdTxj.js.map +0 -1
- package/dist/plan-schema-CboAIwLN.js.map +0 -1
- package/dist/policy-drift-xJfy9xG7.js.map +0 -1
- package/dist/rls-bootstrap-sql-H3rCFi3F.js.map +0 -1
- package/dist/rls-enforcement-C6Xk0lA6.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,105 +1,19 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
__createRequire(import.meta.url);
|
|
3
3
|
import { a as __toESM } from "./__vite-browser-external-BnuHet1e.js";
|
|
4
|
-
import { a as formatForeignGeneratedColumnBanner, c as reportCommandFailure, i as diagnoseDbError, r as diagnoseAtlasFailure, t as checkDatabaseConnectivity } from "./cli-errors-
|
|
4
|
+
import { a as formatForeignGeneratedColumnBanner, c as reportCommandFailure, i as diagnoseDbError, r as diagnoseAtlasFailure, t as checkDatabaseConnectivity } from "./cli-errors-DsA-K9uP.js";
|
|
5
5
|
import { n as outError, r as outWarn, t as out } from "./cli-output-CNdMql-L.js";
|
|
6
|
-
import { A as
|
|
6
|
+
import { A as resolveLocalBin, B as forLibpq, C as loadCollectionsForCli, D as readSearchDdl, E as queryGeneratedColumnDependencies, F as decidePushSafety, I as detectDestructiveStatements, L as extractPlanStatements, M as GENERATED_SQL_DIR, N as generatedSqlDir, O as readTriggersDdl, P as prepareGeneratedSqlDir, R as fromAlterTable, S as getVectorExcludes, T as queryColumnTypes, _ as getDevDatabaseUrl, a as ExcludeIntrospectionError, b as getTableExcludes, c as applyVectorDdl, d as describeReinstallCommand, f as detectProjectPackageManager, g as ensureDevDatabaseExists, h as dropGeneratedColumns, j as DESIRED_STATE_URL, k as readVectorDdl, l as describeBuildScriptRemedy, m as dropDevDatabase, o as applySearchDdl, p as diagnoseMissingBin, r as dropLegacyAuthSchema, s as applyTriggersDdl, t as RLS_BOOTSTRAP_SQL, u as describeDevAddCommand, v as getForeignIndexExcludes, w as promptConfirm, x as getTriggerExcludes, y as getSearchExcludes, z as execa } from "./rls-bootstrap-sql-_KNnjanK.js";
|
|
7
7
|
import { t as chalk } from "./source-Br7L7GOI.js";
|
|
8
8
|
import { t as require_arg } from "./arg-Dni7MzLB.js";
|
|
9
|
+
import { n as collectionsPathIn, r as parseDriverLine, t as assertKnownFlags } from "./cli-flags-BglvpjHv.js";
|
|
9
10
|
import { logger } from "@rebasepro/server";
|
|
10
11
|
import { formatRelativeTime } from "@rebasepro/utils";
|
|
11
12
|
import fs from "fs";
|
|
12
13
|
import path from "path";
|
|
13
14
|
import { fileURLToPath } from "url";
|
|
14
|
-
//#region src/schema/destructive-sql.ts
|
|
15
|
-
var import_arg = /* @__PURE__ */ __toESM(require_arg(), 1);
|
|
16
|
-
/**
|
|
17
|
-
* Pure helpers that classify an Atlas declarative-apply plan as destructive
|
|
18
|
-
* or not, and decide what `rebase db push` should do about it.
|
|
19
|
-
*
|
|
20
|
-
* `db push` runs `atlas schema apply` to make the live database match the
|
|
21
|
-
* generated `schema.sql`. Removing a collection field compiles to
|
|
22
|
-
* `DROP COLUMN`; renaming compiles to drop-then-add — either destroys data.
|
|
23
|
-
* We first run the apply with `--dry-run` to obtain the planned SQL, scan it
|
|
24
|
-
* here, and refuse to auto-approve anything destructive without an explicit
|
|
25
|
-
* opt-in.
|
|
26
|
-
*
|
|
27
|
-
* Everything in this file is side-effect free so it can be unit-tested
|
|
28
|
-
* without Atlas or a database.
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* SQL fragments that destroy data or data-bearing objects. Matched
|
|
32
|
-
* case-insensitively against each statement of the plan. `IF EXISTS` /
|
|
33
|
-
* whitespace variations are tolerated by the regexes below.
|
|
34
|
-
*/
|
|
35
|
-
var DESTRUCTIVE_PATTERNS = [
|
|
36
|
-
{
|
|
37
|
-
label: "DROP TABLE",
|
|
38
|
-
re: /\bDROP\s+TABLE\b/i
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
label: "DROP COLUMN",
|
|
42
|
-
re: /\bDROP\s+COLUMN\b/i
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
label: "DROP SCHEMA",
|
|
46
|
-
re: /\bDROP\s+SCHEMA\b/i
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
label: "DROP VIEW",
|
|
50
|
-
re: /\bDROP\s+(MATERIALIZED\s+)?VIEW\b/i
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
label: "DROP TYPE",
|
|
54
|
-
re: /\bDROP\s+TYPE\b/i
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
label: "TRUNCATE",
|
|
58
|
-
re: /\bTRUNCATE\b/i
|
|
59
|
-
}
|
|
60
|
-
];
|
|
61
|
-
/**
|
|
62
|
-
* Split a SQL script into individual statements, dropping blank lines and
|
|
63
|
-
* `--` comment lines. Deliberately simple: Atlas emits one plain statement
|
|
64
|
-
* per `;`, without string literals that contain semicolons in a schema DDL
|
|
65
|
-
* plan, so a naive split is safe and keeps this dependency-free.
|
|
66
|
-
*/
|
|
67
|
-
function splitSqlStatements(sql) {
|
|
68
|
-
return sql.split("\n").filter((line) => !line.trim().startsWith("--")).join("\n").split(";").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Scan an Atlas plan (the SQL printed by `schema apply --dry-run`) and return
|
|
72
|
-
* the statements that would destroy data. An empty array means the plan is
|
|
73
|
-
* safe to auto-approve.
|
|
74
|
-
*/
|
|
75
|
-
function detectDestructiveStatements(planSql) {
|
|
76
|
-
const found = [];
|
|
77
|
-
for (const statement of splitSqlStatements(planSql)) for (const { label, re } of DESTRUCTIVE_PATTERNS) if (re.test(statement)) {
|
|
78
|
-
found.push({
|
|
79
|
-
statement,
|
|
80
|
-
kind: label
|
|
81
|
-
});
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
return found;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Decide how `db push` should proceed given the plan's destructiveness and
|
|
88
|
-
* the invocation context.
|
|
89
|
-
*
|
|
90
|
-
* - No destructive statements → `apply` (safe to auto-approve).
|
|
91
|
-
* - Destructive + `--allow-destructive` → `apply` (operator opted in).
|
|
92
|
-
* - Destructive + interactive TTY → `confirm` (prompt before applying).
|
|
93
|
-
* - Destructive + non-interactive → `refuse` (never silently drop data in
|
|
94
|
-
* CI / scripts / agents).
|
|
95
|
-
*/
|
|
96
|
-
function decidePushSafety(opts) {
|
|
97
|
-
if (opts.destructiveCount === 0) return "apply";
|
|
98
|
-
if (opts.allowDestructive) return "apply";
|
|
99
|
-
return opts.interactive ? "confirm" : "refuse";
|
|
100
|
-
}
|
|
101
|
-
//#endregion
|
|
102
15
|
//#region src/schema/generated-column-conflicts.ts
|
|
16
|
+
var import_arg = /* @__PURE__ */ __toESM(require_arg(), 1);
|
|
103
17
|
/**
|
|
104
18
|
* Which columns an Atlas plan cannot touch, because a generated column reads
|
|
105
19
|
* them.
|
|
@@ -137,16 +51,6 @@ var ALTER_COLUMN_RE = new RegExp(String.raw`\bALTER\s+(?:COLUMN\s+)?(${IDENT$1})
|
|
|
137
51
|
var DROP_COLUMN_RE$1 = new RegExp(String.raw`\bDROP\s+COLUMN\s+(?:IF\s+EXISTS\s+)?(${IDENT$1})`, "gi");
|
|
138
52
|
var unquote = (ident) => ident.startsWith("\"") ? ident.slice(1, -1) : ident;
|
|
139
53
|
/**
|
|
140
|
-
* Strip Atlas's plan rendering from a statement.
|
|
141
|
-
*
|
|
142
|
-
* What `schema apply --dry-run` prints is not a SQL file: each statement is
|
|
143
|
-
* indented under a `-- modify "posts" table` heading and prefixed with `-> `,
|
|
144
|
-
* with an `-- ok (12µs)` line after it. `splitSqlStatements` drops the comment
|
|
145
|
-
* lines, and this drops the arrow — without it an anchored `^ALTER TABLE`
|
|
146
|
-
* matches nothing at all, which is a silent no-op rather than an error.
|
|
147
|
-
*/
|
|
148
|
-
var stripPlanMarker = (statement) => statement.replace(/^\s*->\s*/, "").trim();
|
|
149
|
-
/**
|
|
150
54
|
* Every column an Atlas plan retypes or drops, with the table it belongs to.
|
|
151
55
|
*
|
|
152
56
|
* One statement carries many clauses — Atlas emits `ALTER TABLE "public"."posts"
|
|
@@ -155,8 +59,8 @@ var stripPlanMarker = (statement) => statement.replace(/^\s*->\s*/, "").trim();
|
|
|
155
59
|
*/
|
|
156
60
|
function parseColumnMutations(planSql) {
|
|
157
61
|
const mutations = [];
|
|
158
|
-
for (const raw of
|
|
159
|
-
const statement =
|
|
62
|
+
for (const raw of extractPlanStatements(planSql)) {
|
|
63
|
+
const statement = fromAlterTable(raw);
|
|
160
64
|
const table = TABLE_RE.exec(statement);
|
|
161
65
|
if (!table) continue;
|
|
162
66
|
const [schema, name] = table[2] ? [unquote(table[1]), unquote(table[2])] : [void 0, unquote(table[1])];
|
|
@@ -650,6 +554,15 @@ function tidy(sql) {
|
|
|
650
554
|
function acceptsExcludeFlag(domain, args) {
|
|
651
555
|
return domain === "schema" && args.includes("apply");
|
|
652
556
|
}
|
|
557
|
+
/**
|
|
558
|
+
* Does this Atlas invocation plan against the scratch database (`--dev-url`)?
|
|
559
|
+
*
|
|
560
|
+
* `schema apply` and `migrate diff` do; `migrate apply`, `hash` and `status`
|
|
561
|
+
* never read it, so `db migrate` has no reason to create one.
|
|
562
|
+
*/
|
|
563
|
+
function takesDevUrl(domain, args) {
|
|
564
|
+
return domain === "schema" && args.includes("apply") || domain === "migrate" && args.includes("diff");
|
|
565
|
+
}
|
|
653
566
|
/** Assemble the full argv, connection flags and all. */
|
|
654
567
|
function buildAtlasArgs(invocation) {
|
|
655
568
|
const { domain, args, url, devUrl, excludes = [] } = invocation;
|
|
@@ -667,6 +580,52 @@ function buildAtlasArgs(invocation) {
|
|
|
667
580
|
if (acceptsExcludeFlag(domain, args)) for (const exclude of excludes) argv.push("--exclude", exclude);
|
|
668
581
|
return argv;
|
|
669
582
|
}
|
|
583
|
+
/**
|
|
584
|
+
* `atlas migrate apply`'s own arguments for `rebase db migrate`.
|
|
585
|
+
*
|
|
586
|
+
* `positionals` are what is left of the user's line once every flag the
|
|
587
|
+
* driver knows has been consumed — at most the optional amount Atlas takes. A
|
|
588
|
+
* flag reaching here is a flag Atlas rejects before doing any work.
|
|
589
|
+
*/
|
|
590
|
+
function migrateApplyArgs(positionals, baseline) {
|
|
591
|
+
return [
|
|
592
|
+
"apply",
|
|
593
|
+
"--dir",
|
|
594
|
+
"file://drizzle/migrations",
|
|
595
|
+
...baseline ? ["--baseline", baseline] : [],
|
|
596
|
+
...positionals.filter((positional) => positional !== "migrate")
|
|
597
|
+
];
|
|
598
|
+
}
|
|
599
|
+
//#endregion
|
|
600
|
+
//#region src/cli-scratch-database.ts
|
|
601
|
+
var ScratchDatabase = class {
|
|
602
|
+
ops;
|
|
603
|
+
created;
|
|
604
|
+
constructor(ops) {
|
|
605
|
+
this.ops = ops;
|
|
606
|
+
}
|
|
607
|
+
/** Make sure it exists before an Atlas call that plans against it. */
|
|
608
|
+
async ensure(databaseUrl, devDatabaseUrl) {
|
|
609
|
+
if (await this.ops.create(databaseUrl, devDatabaseUrl)) this.created = {
|
|
610
|
+
databaseUrl,
|
|
611
|
+
devDatabaseUrl
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Run a command, then drop the scratch database if the command created it.
|
|
616
|
+
* A command that throws skips the drop; one that exits the process never
|
|
617
|
+
* returns here, which is the same thing.
|
|
618
|
+
*/
|
|
619
|
+
async around(command) {
|
|
620
|
+
const result = await command();
|
|
621
|
+
const created = this.created;
|
|
622
|
+
if (created) {
|
|
623
|
+
this.created = void 0;
|
|
624
|
+
await this.ops.drop(created.databaseUrl, created.devDatabaseUrl);
|
|
625
|
+
}
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
};
|
|
670
629
|
//#endregion
|
|
671
630
|
//#region src/branch-argv.ts
|
|
672
631
|
/**
|
|
@@ -728,140 +687,6 @@ var TAKES_A_VALUE = /* @__PURE__ */ new Set([
|
|
|
728
687
|
"--database-url"
|
|
729
688
|
]);
|
|
730
689
|
//#endregion
|
|
731
|
-
//#region src/cli-flags.ts
|
|
732
|
-
/**
|
|
733
|
-
* The flags each driver command takes, in one place, and the check that
|
|
734
|
-
* enforces them.
|
|
735
|
-
*
|
|
736
|
-
* Every parser in `cli.ts` runs `arg(..., { permissive: true })`, which does not
|
|
737
|
-
* mean "be lenient" — it means **an undeclared flag becomes a positional**. So
|
|
738
|
-
* `rebase db push --alow-destructive` did not fail: the typo landed in `_`, the
|
|
739
|
-
* push ran with the destructive gate still closed, and the developer read the
|
|
740
|
-
* refusal as Rebase ignoring the flag they had just typed. `rebase schema
|
|
741
|
-
* generate --ouput src/schema.ts` was worse — it wrote the default path and
|
|
742
|
-
* said nothing, so the next build compiled a file nobody had regenerated.
|
|
743
|
-
*
|
|
744
|
-
* The check has to live at the entry point rather than in those parsers, and
|
|
745
|
-
* that is not a detail: `db push` and `db generate` re-enter `schemaCommand`
|
|
746
|
-
* and `generatePostgresDdlCommand` with the *db* line, so a strict parser
|
|
747
|
-
* inside either of them would reject `--allow-destructive` on a line where it
|
|
748
|
-
* is correct. One validation, once, against the spec for the command the user
|
|
749
|
-
* actually named; the inner parsers keep reading an already-validated line.
|
|
750
|
-
*
|
|
751
|
-
* Commands with their own argument handling — `db branch` (`unexpectedBranchArgs`),
|
|
752
|
-
* `db backup`, `db restore`, `db backups` — are deliberately absent: they own
|
|
753
|
-
* their spec, and a second list here would be one more thing to keep in step.
|
|
754
|
-
* An absent entry is "not checked here", never "takes nothing".
|
|
755
|
-
*/
|
|
756
|
-
/**
|
|
757
|
-
* Flags that reach this process on a line it did not compose.
|
|
758
|
-
*
|
|
759
|
-
* `rebase db …` hands the driver the user's whole line: the CLI *reads*
|
|
760
|
-
* `--database-url` and `--docker` to resolve the database and then relays them
|
|
761
|
-
* verbatim, and `--debug` is what `bin/rebase.js` prints after every failure as
|
|
762
|
-
* the thing to re-run with. Rejecting any of the three would make the driver
|
|
763
|
-
* refuse commands the CLI documents.
|
|
764
|
-
*/
|
|
765
|
-
var RELAYED_FLAGS = {
|
|
766
|
-
"--database-url": String,
|
|
767
|
-
"--docker": Boolean,
|
|
768
|
-
"--debug": Boolean,
|
|
769
|
-
"--help": Boolean,
|
|
770
|
-
"-h": "--help"
|
|
771
|
-
};
|
|
772
|
-
/** Keyed by `"<domain> <subcommand>"`, the way the user types it. */
|
|
773
|
-
var DRIVER_FLAG_SPECS = {
|
|
774
|
-
"db push": {
|
|
775
|
-
"--collections": String,
|
|
776
|
-
"-c": "--collections",
|
|
777
|
-
"--dry-run": Boolean,
|
|
778
|
-
"--allow-destructive": Boolean,
|
|
779
|
-
"--yes": Boolean,
|
|
780
|
-
"-y": "--yes"
|
|
781
|
-
},
|
|
782
|
-
"db generate": {
|
|
783
|
-
"--collections": String,
|
|
784
|
-
"-c": "--collections"
|
|
785
|
-
},
|
|
786
|
-
"db migrate": { "--baseline": String },
|
|
787
|
-
"schema generate": {
|
|
788
|
-
"--collections": String,
|
|
789
|
-
"-c": "--collections",
|
|
790
|
-
"--output": String,
|
|
791
|
-
"-o": "--output",
|
|
792
|
-
"--out": "--output",
|
|
793
|
-
"--watch": Boolean,
|
|
794
|
-
"-w": "--watch"
|
|
795
|
-
},
|
|
796
|
-
"schema introspect": {
|
|
797
|
-
"--output": String,
|
|
798
|
-
"-o": "--output",
|
|
799
|
-
"--out": "--output",
|
|
800
|
-
"--collections": String,
|
|
801
|
-
"-c": "--collections",
|
|
802
|
-
"--force": Boolean,
|
|
803
|
-
"-f": "--force",
|
|
804
|
-
"--schema": String
|
|
805
|
-
},
|
|
806
|
-
"schema stale": {
|
|
807
|
-
"--collections": String,
|
|
808
|
-
"-c": "--collections",
|
|
809
|
-
"--output": String,
|
|
810
|
-
"-o": "--output",
|
|
811
|
-
"--out": "--output",
|
|
812
|
-
"--fix": Boolean
|
|
813
|
-
}
|
|
814
|
-
};
|
|
815
|
-
/**
|
|
816
|
-
* The `--collections` path the user typed, or `null` if they typed none.
|
|
817
|
-
*
|
|
818
|
-
* Read once, at the entry point, because every command that takes it re-enters
|
|
819
|
-
* the generators with the same line and each of them resolved it again — which
|
|
820
|
-
* is how "Collections path not found" came to be printed four times before the
|
|
821
|
-
* real output, and how a path that does not exist got as far as *writing* an
|
|
822
|
-
* empty schema.
|
|
823
|
-
*
|
|
824
|
-
* Permissive, and it has to be: {@link assertKnownFlags} has already judged the
|
|
825
|
-
* line, and `db push` carries flags the `--collections` spec does not name.
|
|
826
|
-
*/
|
|
827
|
-
function collectionsPathIn(args) {
|
|
828
|
-
try {
|
|
829
|
-
return (0, import_arg.default)({
|
|
830
|
-
"--collections": String,
|
|
831
|
-
"-c": "--collections"
|
|
832
|
-
}, {
|
|
833
|
-
argv: args.slice(2),
|
|
834
|
-
permissive: true
|
|
835
|
-
})["--collections"] ?? null;
|
|
836
|
-
} catch {
|
|
837
|
-
return null;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* Reject a flag the named command does not take.
|
|
842
|
-
*
|
|
843
|
-
* `args` is the driver's whole line — `["db", "push", …]` — so the flags are
|
|
844
|
-
* read from `args.slice(2)`, past the two command words.
|
|
845
|
-
*
|
|
846
|
-
* Throws rather than exiting: `runPluginCommand`'s caller already turns a
|
|
847
|
-
* thrown error into one red line and exit 1, and a thrown error is what the
|
|
848
|
-
* tests can see.
|
|
849
|
-
*/
|
|
850
|
-
function assertKnownFlags(domain, subcommand, args) {
|
|
851
|
-
if (!subcommand) return;
|
|
852
|
-
const spec = DRIVER_FLAG_SPECS[`${domain} ${subcommand}`];
|
|
853
|
-
if (!spec) return;
|
|
854
|
-
try {
|
|
855
|
-
(0, import_arg.default)({
|
|
856
|
-
...RELAYED_FLAGS,
|
|
857
|
-
...spec
|
|
858
|
-
}, { argv: args.slice(2) });
|
|
859
|
-
} catch (err) {
|
|
860
|
-
if (err instanceof Error && err.code === "ARG_UNKNOWN_OPTION") throw new Error(`${err.message} — run \`rebase ${domain} --help\` for the options \`${domain} ${subcommand}\` takes.`);
|
|
861
|
-
throw err;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
//#endregion
|
|
865
690
|
//#region src/cli-collections-path.ts
|
|
866
691
|
/**
|
|
867
692
|
* `--collections <dir>`, checked once, before anything is written.
|
|
@@ -870,8 +695,8 @@ function assertKnownFlags(domain, subcommand, args) {
|
|
|
870
695
|
* `schemaCommand`, `generatePostgresDdlCommand` and the Atlas argv assembly
|
|
871
696
|
* each re-enter the loader with the same line — and then both generators wrote
|
|
872
697
|
* an *empty* schema: `drizzle/schema.sql` truncated to `CREATE SCHEMA IF NOT
|
|
873
|
-
* EXISTS "rebase";`, `src/schema.generated.ts` to ten lines. Both
|
|
874
|
-
* artifacts. The push that followed planned a `DROP TABLE` for every table in
|
|
698
|
+
* EXISTS "rebase";`, `src/schema.generated.ts` to ten lines. Both were committed
|
|
699
|
+
* artifacts then. The push that followed planned a `DROP TABLE` for every table in
|
|
875
700
|
* the database, and the only thing between a typo and that was the destructive
|
|
876
701
|
* gate.
|
|
877
702
|
*
|
|
@@ -1069,7 +894,7 @@ async function runPluginCommand(args) {
|
|
|
1069
894
|
const subcommand = args[1];
|
|
1070
895
|
assertKnownFlags(domain, subcommand, args);
|
|
1071
896
|
assertCollectionsPathExists(collectionsPathIn(args));
|
|
1072
|
-
if (domain === "db") await dbCommand(subcommand, args);
|
|
897
|
+
if (domain === "db") await scratchDatabase.around(() => dbCommand(subcommand, args));
|
|
1073
898
|
else if (domain === "schema") await schemaCommand(subcommand, args);
|
|
1074
899
|
else if (domain === "doctor") await doctorPluginCommand(args);
|
|
1075
900
|
else {
|
|
@@ -1146,6 +971,20 @@ async function stripCarvedOutFromNewestMigration(collectionsPath) {
|
|
|
1146
971
|
], collectionsPath);
|
|
1147
972
|
return !result.empty;
|
|
1148
973
|
}
|
|
974
|
+
/**
|
|
975
|
+
* The flags `rebase db` reads for the subcommands it parses itself. Hoisted
|
|
976
|
+
* and named `…FLAGS` so the documentation verifier, which reads specs out of
|
|
977
|
+
* this source, sees them.
|
|
978
|
+
*/
|
|
979
|
+
var DB_FLAGS = {
|
|
980
|
+
"--collections": String,
|
|
981
|
+
"--allow-destructive": Boolean,
|
|
982
|
+
"--dry-run": Boolean,
|
|
983
|
+
"--yes": Boolean,
|
|
984
|
+
"--baseline": String,
|
|
985
|
+
"-c": "--collections",
|
|
986
|
+
"-y": "--yes"
|
|
987
|
+
};
|
|
1149
988
|
async function dbCommand(subcommand, rawArgs) {
|
|
1150
989
|
const VALID_ACTIONS = [
|
|
1151
990
|
"push",
|
|
@@ -1172,25 +1011,14 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1172
1011
|
return;
|
|
1173
1012
|
}
|
|
1174
1013
|
if (subcommand === "backup" || subcommand === "restore" || subcommand === "backups") {
|
|
1175
|
-
const { backupCommand, restoreCommand, backupsCommand } = await import("./backup-cli-
|
|
1014
|
+
const { backupCommand, restoreCommand, backupsCommand } = await import("./backup-cli-DW5p9_zv.js");
|
|
1176
1015
|
if (subcommand === "backup" && backupActionOf(rawArgs) === "list") await backupsCommand(rawArgs);
|
|
1177
1016
|
else if (subcommand === "backup") await backupCommand(rawArgs);
|
|
1178
1017
|
else if (subcommand === "restore") await restoreCommand(rawArgs);
|
|
1179
1018
|
else await backupsCommand(rawArgs);
|
|
1180
1019
|
return;
|
|
1181
1020
|
}
|
|
1182
|
-
const argsList = (
|
|
1183
|
-
"--collections": String,
|
|
1184
|
-
"--allow-destructive": Boolean,
|
|
1185
|
-
"--dry-run": Boolean,
|
|
1186
|
-
"--yes": Boolean,
|
|
1187
|
-
"--baseline": String,
|
|
1188
|
-
"-c": "--collections",
|
|
1189
|
-
"-y": "--yes"
|
|
1190
|
-
}, {
|
|
1191
|
-
argv: rawArgs.slice(2),
|
|
1192
|
-
permissive: true
|
|
1193
|
-
});
|
|
1021
|
+
const argsList = parseDriverLine(DB_FLAGS, rawArgs);
|
|
1194
1022
|
const collectionsPath = argsList["--collections"] || path.join("..", "config", "collections");
|
|
1195
1023
|
if (subcommand === "generate") {
|
|
1196
1024
|
out("");
|
|
@@ -1210,7 +1038,7 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1210
1038
|
"--dir",
|
|
1211
1039
|
"file://drizzle/migrations",
|
|
1212
1040
|
"--to",
|
|
1213
|
-
|
|
1041
|
+
DESIRED_STATE_URL
|
|
1214
1042
|
], collectionsPath);
|
|
1215
1043
|
let wroteNewMigration = listMigrationFiles().length > migrationsBefore.length;
|
|
1216
1044
|
if (wroteNewMigration) wroteNewMigration = await stripCarvedOutFromNewestMigration(collectionsPath);
|
|
@@ -1229,7 +1057,7 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1229
1057
|
}
|
|
1230
1058
|
const searchContent = readSearchDdl();
|
|
1231
1059
|
if (searchContent) {
|
|
1232
|
-
const { searchColumnDependencies } = await import("./generate-postgres-ddl-logic-
|
|
1060
|
+
const { searchColumnDependencies } = await import("./generate-postgres-ddl-logic-BJsLaVNX.js").then((n) => n.o);
|
|
1233
1061
|
const migrationConflicts = findGeneratedColumnConflicts(parseColumnMutations(migrationContent), searchColumnDependencies(await loadCollectionsForCli(collectionsPath)));
|
|
1234
1062
|
const preamble = migrationDropPreamble(migrationConflicts);
|
|
1235
1063
|
if (preamble) {
|
|
@@ -1245,7 +1073,7 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1245
1073
|
out(chalk.gray(" ✓ Appended `updated_at` triggers to the migration"));
|
|
1246
1074
|
}
|
|
1247
1075
|
fs.writeFileSync(newestMigrationFile, migrationContent, "utf-8");
|
|
1248
|
-
const policiesFile = path.
|
|
1076
|
+
const policiesFile = path.join(generatedSqlDir(), "policies.sql");
|
|
1249
1077
|
if (fs.existsSync(policiesFile)) {
|
|
1250
1078
|
const policiesContent = fs.readFileSync(policiesFile, "utf-8");
|
|
1251
1079
|
fs.appendFileSync(newestMigrationFile, "\n\n" + RLS_BOOTSTRAP_SQL + "\n" + policiesContent);
|
|
@@ -1263,7 +1091,10 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1263
1091
|
} catch (err) {
|
|
1264
1092
|
outWarn(chalk.yellow(` ⚠️ Failed to append policies or re-hash migration: ${err instanceof Error ? err.message : String(err)}`));
|
|
1265
1093
|
}
|
|
1266
|
-
if (!wroteNewMigration) out(chalk.gray(
|
|
1094
|
+
if (!wroteNewMigration) out(chalk.gray(` ℹ No migration written — nothing in this change is visible to Atlas.
|
|
1095
|
+
Search, vector, trigger and RLS DDL are applied by \`rebase db push\`, and at
|
|
1096
|
+
boot by the schema ensure. For a migration-only deployment, add
|
|
1097
|
+
${GENERATED_SQL_DIR}/search.sql, vector.sql, triggers.sql and\n policies.sql to a migration by hand.`));
|
|
1267
1098
|
out("");
|
|
1268
1099
|
out(` You can now run ${chalk.bold.green("rebase db migrate")} to apply the migrations to your database.`);
|
|
1269
1100
|
out("");
|
|
@@ -1285,17 +1116,20 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1285
1116
|
const plan = await runAtlas("schema", [
|
|
1286
1117
|
"apply",
|
|
1287
1118
|
"--to",
|
|
1288
|
-
|
|
1119
|
+
DESIRED_STATE_URL,
|
|
1289
1120
|
"--dry-run"
|
|
1290
1121
|
], collectionsPath, { captureStdout: true });
|
|
1291
|
-
const destructive = detectDestructiveStatements(plan);
|
|
1122
|
+
const destructive = detectDestructiveStatements(plan, databaseUrl ? await queryColumnTypes(databaseUrl) : []);
|
|
1123
|
+
const unreadable = destructive.some((d) => d.kind === "UNREADABLE PLAN");
|
|
1124
|
+
const planStatements = extractPlanStatements(plan);
|
|
1125
|
+
const planListing = unreadable ? plan.trim() : planStatements.map((statement) => `${statement};`).join("\n");
|
|
1292
1126
|
const { managed: rebuildable, foreign: unowned } = partitionByOwnership(databaseUrl ? findGeneratedColumnConflicts(parseColumnMutations(plan), await queryGeneratedColumnDependencies(databaseUrl)) : [], collectionsPath ? await getSearchExcludes(collectionsPath) : []);
|
|
1293
1127
|
if (dryRun) {
|
|
1294
1128
|
out(chalk.gray(" Step 3/3: --dry-run — nothing was applied."));
|
|
1295
1129
|
out("");
|
|
1296
|
-
if (
|
|
1130
|
+
if (planStatements.length > 0 || unreadable) {
|
|
1297
1131
|
out(chalk.bold(" Planned changes:"));
|
|
1298
|
-
out(chalk.gray(
|
|
1132
|
+
out(chalk.gray(planListing.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
1299
1133
|
} else out(chalk.green(" ✓ No changes: the database already matches these collections."));
|
|
1300
1134
|
out("");
|
|
1301
1135
|
if (rebuildable.length > 0) {
|
|
@@ -1308,7 +1142,7 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1308
1142
|
if (unowned.length > 0) outWarn(formatForeignGeneratedColumnBanner(unowned));
|
|
1309
1143
|
if (destructive.length > 0) {
|
|
1310
1144
|
outWarn(chalk.yellow(` ⚠️ ${destructive.length} of those DESTROY data:`));
|
|
1311
|
-
for (const d of destructive) outWarn(chalk.red(` ${d.kind}: `) + chalk.gray(d.statement.replace(/\s+/g, " ")));
|
|
1145
|
+
for (const d of destructive) outWarn(chalk.red(` ${d.kind}${d.detail ? ` (${d.detail})` : ""}: `) + chalk.gray(d.statement.replace(/\s+/g, " ")));
|
|
1312
1146
|
outWarn("");
|
|
1313
1147
|
outWarn(chalk.yellow(" Applying them needs `rebase db push --allow-destructive`. Back up first: rebase db backup"));
|
|
1314
1148
|
outWarn("");
|
|
@@ -1330,10 +1164,10 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1330
1164
|
if (destructive.length > 0) {
|
|
1331
1165
|
outWarn(chalk.yellow(` ⚠️ This push includes ${destructive.length} destructive change(s) that will DESTROY data:`));
|
|
1332
1166
|
outWarn("");
|
|
1333
|
-
for (const d of destructive) outWarn(chalk.red(` ${d.kind}: `) + chalk.gray(d.statement.replace(/\s+/g, " ")));
|
|
1167
|
+
for (const d of destructive) outWarn(chalk.red(` ${d.kind}${d.detail ? ` (${d.detail})` : ""}: `) + chalk.gray(d.statement.replace(/\s+/g, " ")));
|
|
1334
1168
|
outWarn("");
|
|
1335
1169
|
outWarn(chalk.yellow(" Full planned changes:"));
|
|
1336
|
-
outWarn(chalk.gray(
|
|
1170
|
+
outWarn(chalk.gray(planListing.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
1337
1171
|
outWarn("");
|
|
1338
1172
|
if (decision === "refuse") {
|
|
1339
1173
|
outError(chalk.red(" ✗ Aborting: destructive changes require confirmation."));
|
|
@@ -1355,9 +1189,9 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1355
1189
|
await runAtlas("schema", [
|
|
1356
1190
|
"apply",
|
|
1357
1191
|
"--to",
|
|
1358
|
-
|
|
1192
|
+
DESIRED_STATE_URL,
|
|
1359
1193
|
"--auto-approve"
|
|
1360
|
-
], collectionsPath);
|
|
1194
|
+
], collectionsPath, { throwOnFailure: true });
|
|
1361
1195
|
} catch (err) {
|
|
1362
1196
|
if (rebuildable.length > 0 && databaseUrl) {
|
|
1363
1197
|
out(chalk.gray(" The apply failed — rebuilding the generated columns it needed out of the way."));
|
|
@@ -1367,6 +1201,7 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1367
1201
|
}
|
|
1368
1202
|
out("");
|
|
1369
1203
|
if (databaseUrl) {
|
|
1204
|
+
await lockManagedTables(databaseUrl, collectionsPath);
|
|
1370
1205
|
await ensureAuthTables(databaseUrl, collectionsPath);
|
|
1371
1206
|
await applyVectorDdl(databaseUrl);
|
|
1372
1207
|
await applySearchDdl(databaseUrl);
|
|
@@ -1375,24 +1210,16 @@ async function dbCommand(subcommand, rawArgs) {
|
|
|
1375
1210
|
await reconcilePolicies(databaseUrl, collectionsPath);
|
|
1376
1211
|
await ensureRlsUserRole(databaseUrl);
|
|
1377
1212
|
await retireLegacyAuthSchema(databaseUrl);
|
|
1378
|
-
if (scratchDatabaseCreatedHere) await dropDevDatabase(databaseUrl, getDevDatabaseUrl(databaseUrl));
|
|
1379
1213
|
} else outWarn(chalk.yellow(" ⚠️ DATABASE_URL not found in environment, skipping RLS policies application."));
|
|
1380
1214
|
} else if (subcommand === "migrate") {
|
|
1381
1215
|
const databaseUrl = process.env.DATABASE_URL;
|
|
1382
1216
|
if (databaseUrl) await ensureAuthSchemaAndFunctions(databaseUrl);
|
|
1383
|
-
const extraArgs = argsList._.filter((arg) => arg !== "migrate");
|
|
1384
1217
|
const baseline = argsList["--baseline"];
|
|
1385
1218
|
if (baseline) {
|
|
1386
1219
|
out(chalk.gray(` Recording ${baseline} as already applied, then migrating from the next one.`));
|
|
1387
1220
|
out("");
|
|
1388
1221
|
}
|
|
1389
|
-
await runAtlas("migrate",
|
|
1390
|
-
"apply",
|
|
1391
|
-
"--dir",
|
|
1392
|
-
"file://drizzle/migrations",
|
|
1393
|
-
...baseline ? ["--baseline", baseline] : [],
|
|
1394
|
-
...extraArgs
|
|
1395
|
-
], collectionsPath);
|
|
1222
|
+
await runAtlas("migrate", migrateApplyArgs(argsList._, baseline), collectionsPath);
|
|
1396
1223
|
if (databaseUrl) {
|
|
1397
1224
|
await ensureRlsUserRole(databaseUrl);
|
|
1398
1225
|
await retireLegacyAuthSchema(databaseUrl);
|
|
@@ -1418,6 +1245,58 @@ async function ensureAuthSchemaAndFunctions(databaseUrl) {
|
|
|
1418
1245
|
}
|
|
1419
1246
|
}
|
|
1420
1247
|
/**
|
|
1248
|
+
* Switch row-level security on for every table the collections declare, right
|
|
1249
|
+
* after Atlas has applied the schema.
|
|
1250
|
+
*
|
|
1251
|
+
* Atlas cannot carry `ENABLE ROW LEVEL SECURITY` in `schema.sql`: its free tier
|
|
1252
|
+
* neither plans it nor reads it back, so a table it creates is born with RLS
|
|
1253
|
+
* off — and `rebase_user`, the role every authenticated request runs as, holds
|
|
1254
|
+
* DML on it from that moment through the schema's default privileges. RLS used
|
|
1255
|
+
* to arrive with the policies, the last step of the push; a refusal from the
|
|
1256
|
+
* vector, search or trigger step in between left the new table readable and
|
|
1257
|
+
* writable by every caller, with no policy, until a later push or boot fixed
|
|
1258
|
+
* it — and a deployment with `REBASE_MIGRATE_ON_BOOT=none` never did. Locked
|
|
1259
|
+
* first, a failure later leaves a table that denies, not one that is open.
|
|
1260
|
+
*
|
|
1261
|
+
* Fatal when a table cannot be locked, and before the steps that follow: the
|
|
1262
|
+
* role provisioning at the end re-grants on every table in the schema, which
|
|
1263
|
+
* would undo the revoke that is the fallback here.
|
|
1264
|
+
*/
|
|
1265
|
+
async function lockManagedTables(databaseUrl, collectionsPath) {
|
|
1266
|
+
let unsecured;
|
|
1267
|
+
let userRole;
|
|
1268
|
+
try {
|
|
1269
|
+
const { lockCollectionTables } = await import("./ensure-collection-policies-B1ureSIV.js");
|
|
1270
|
+
const { loadCollections } = await import("./doctor-C-sYWbmt.js").then((n) => n.t);
|
|
1271
|
+
userRole = (await import("./rls-enforcement-CfXOJJaW.js").then((n) => n.o)).REBASE_USER_ROLE;
|
|
1272
|
+
const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
|
|
1273
|
+
const { Client } = await import("pg");
|
|
1274
|
+
const client = new Client({ connectionString: databaseUrl });
|
|
1275
|
+
await client.connect();
|
|
1276
|
+
try {
|
|
1277
|
+
({unsecured} = await lockCollectionTables({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, collections));
|
|
1278
|
+
} finally {
|
|
1279
|
+
await client.end();
|
|
1280
|
+
}
|
|
1281
|
+
} catch (err) {
|
|
1282
|
+
outError(chalk.red(`\n ✗ The schema was applied, but row-level security could not be switched on for its tables: ${err instanceof Error ? err.message : String(err)}`));
|
|
1283
|
+
outError(chalk.gray(" A table this push created may be readable and writable by every authenticated request\n until it is. Re-run `rebase db push` — it locks every collection table before anything else."));
|
|
1284
|
+
process.exit(1);
|
|
1285
|
+
}
|
|
1286
|
+
if (unsecured.length === 0) return;
|
|
1287
|
+
outError(chalk.red("\n ✗ The schema was applied, but row-level security could not be switched on for:"));
|
|
1288
|
+
for (const u of unsecured) {
|
|
1289
|
+
outError(chalk.red(` ${u.table}: ${u.error}`));
|
|
1290
|
+
outError(chalk.gray(u.grantWithdrawn ? ` Its privileges were taken back from ${userRole}, so it is unreachable rather than unprotected.` : ` ${userRole} still holds its privileges: every authenticated request can read and write every row.`));
|
|
1291
|
+
}
|
|
1292
|
+
outError(chalk.gray(" Nothing after the schema apply ran. Fix the cause — the connection role must own these\n tables, or be able to ALTER them — and run `rebase db push` again, or as their owner:"));
|
|
1293
|
+
for (const u of unsecured) {
|
|
1294
|
+
const [schema, ...table] = u.table.split(".");
|
|
1295
|
+
outError(chalk.bold(` ALTER TABLE "${schema}"."${table.join(".")}" ENABLE ROW LEVEL SECURITY;`));
|
|
1296
|
+
}
|
|
1297
|
+
process.exit(1);
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1421
1300
|
* Create the framework auth tables (e.g. `rebase.users`) that the generated RLS
|
|
1422
1301
|
* policies reference, before `applyPolicies` runs. Mirrors what the server does
|
|
1423
1302
|
* at boot (`PostgresBootstrapper.initializeAuth` → `ensureAuthTablesExist`).
|
|
@@ -1431,8 +1310,8 @@ async function ensureAuthSchemaAndFunctions(databaseUrl) {
|
|
|
1431
1310
|
async function ensureAuthTables(databaseUrl, collectionsPath) {
|
|
1432
1311
|
try {
|
|
1433
1312
|
const { drizzle } = await import("drizzle-orm/node-postgres");
|
|
1434
|
-
const { ensureAuthTablesExist } = await import("./ensure-tables-
|
|
1435
|
-
const { loadCollections } = await import("./doctor-
|
|
1313
|
+
const { ensureAuthTablesExist } = await import("./ensure-tables-BmI_tRxc.js").then((n) => n.n);
|
|
1314
|
+
const { loadCollections } = await import("./doctor-C-sYWbmt.js").then((n) => n.t);
|
|
1436
1315
|
const { Client } = await import("pg");
|
|
1437
1316
|
const authCollection = (await loadCollections(path.resolve(process.cwd(), collectionsPath))).find((c) => {
|
|
1438
1317
|
const a = c.auth;
|
|
@@ -1459,7 +1338,7 @@ async function ensureRlsUserRole(databaseUrl) {
|
|
|
1459
1338
|
const schemas = ["public", "rebase"];
|
|
1460
1339
|
let rls;
|
|
1461
1340
|
try {
|
|
1462
|
-
rls = await import("./rls-enforcement-
|
|
1341
|
+
rls = await import("./rls-enforcement-CfXOJJaW.js").then((n) => n.o);
|
|
1463
1342
|
} catch (err) {
|
|
1464
1343
|
outError(chalk.red(`\n ✗ Could not load the RLS provisioning module: ${err instanceof Error ? err.message : String(err)}`));
|
|
1465
1344
|
return;
|
|
@@ -1508,7 +1387,7 @@ async function retireLegacyAuthSchema(databaseUrl) {
|
|
|
1508
1387
|
}
|
|
1509
1388
|
async function applyPolicies(databaseUrl) {
|
|
1510
1389
|
try {
|
|
1511
|
-
const policiesPath = path.
|
|
1390
|
+
const policiesPath = path.join(generatedSqlDir(), "policies.sql");
|
|
1512
1391
|
if (!fs.existsSync(policiesPath)) return;
|
|
1513
1392
|
out(chalk.gray(" Step 3/3: Applying RLS policies to database..."));
|
|
1514
1393
|
out("");
|
|
@@ -1544,8 +1423,8 @@ async function applyPolicies(databaseUrl) {
|
|
|
1544
1423
|
*/
|
|
1545
1424
|
async function reconcilePolicies(databaseUrl, collectionsPath) {
|
|
1546
1425
|
try {
|
|
1547
|
-
const { checkPolicyDrift, dropOrphanedPolicies, formatPolicyDrift, hasDrift } = await import("./policy-drift-
|
|
1548
|
-
const { loadCollections } = await import("./doctor-
|
|
1426
|
+
const { checkPolicyDrift, dropOrphanedPolicies, formatPolicyDrift, hasDrift } = await import("./policy-drift-B-J2hhm0.js").then((n) => n.a);
|
|
1427
|
+
const { loadCollections } = await import("./doctor-C-sYWbmt.js").then((n) => n.t);
|
|
1549
1428
|
const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
|
|
1550
1429
|
const { Client } = await import("pg");
|
|
1551
1430
|
const client = new Client({ connectionString: databaseUrl });
|
|
@@ -1627,7 +1506,7 @@ async function branchCommand(rawArgs) {
|
|
|
1627
1506
|
process.exit(1);
|
|
1628
1507
|
}
|
|
1629
1508
|
const { DatabasePoolManager } = await import("./databasePoolManager-Bj5FbeAs.js").then((n) => n.n);
|
|
1630
|
-
const { BranchService } = await import("./BranchService-
|
|
1509
|
+
const { BranchService } = await import("./BranchService-BRt78gfa.js").then((n) => n.n);
|
|
1631
1510
|
const { drizzle } = await import("drizzle-orm/node-postgres");
|
|
1632
1511
|
const { Pool } = await import("pg");
|
|
1633
1512
|
const pool = new Pool({
|
|
@@ -1857,8 +1736,14 @@ function formatBytes(bytes) {
|
|
|
1857
1736
|
function timeAgo(date) {
|
|
1858
1737
|
return formatRelativeTime(date, { maxMs: Number.POSITIVE_INFINITY }) ?? "unknown";
|
|
1859
1738
|
}
|
|
1860
|
-
/**
|
|
1861
|
-
|
|
1739
|
+
/**
|
|
1740
|
+
* The Atlas scratch database this process created, dropped once the db
|
|
1741
|
+
* command that needed it succeeds — see `ScratchDatabase`.
|
|
1742
|
+
*/
|
|
1743
|
+
var scratchDatabase = new ScratchDatabase({
|
|
1744
|
+
create: ensureDevDatabaseExists,
|
|
1745
|
+
drop: dropDevDatabase
|
|
1746
|
+
});
|
|
1862
1747
|
async function runAtlas(domain, args, collectionsPath, opts = {}) {
|
|
1863
1748
|
const atlasBin = resolveLocalBin("atlas");
|
|
1864
1749
|
if (!atlasBin) {
|
|
@@ -1911,7 +1796,7 @@ async function runAtlas(domain, args, collectionsPath, opts = {}) {
|
|
|
1911
1796
|
}
|
|
1912
1797
|
await checkDatabaseConnectivity(databaseUrl);
|
|
1913
1798
|
const devDatabaseUrl = getDevDatabaseUrl(databaseUrl);
|
|
1914
|
-
if (await
|
|
1799
|
+
if (takesDevUrl(domain, args)) await scratchDatabase.ensure(databaseUrl, devDatabaseUrl);
|
|
1915
1800
|
const atlasUrl = forLibpq(databaseUrl);
|
|
1916
1801
|
const atlasDevUrl = forLibpq(devDatabaseUrl);
|
|
1917
1802
|
const excludes = [];
|
|
@@ -1980,6 +1865,7 @@ async function runAtlas(domain, args, collectionsPath, opts = {}) {
|
|
|
1980
1865
|
latestMigrationVersion: latestMigrationVersion()
|
|
1981
1866
|
}) ?? diagnoseDbError({ message: stderrText }, databaseUrl);
|
|
1982
1867
|
if (hint) outError(hint);
|
|
1868
|
+
if (opts.throwOnFailure) throw Object.assign(/* @__PURE__ */ new Error(`atlas ${domain} ${args.join(" ")} failed`), { alreadyReported: true });
|
|
1983
1869
|
process.exit(1);
|
|
1984
1870
|
}
|
|
1985
1871
|
if (opts.captureStdout) return stdoutText.trim().length > 0 ? stdoutText : stderrText;
|
|
@@ -2001,7 +1887,11 @@ async function generatePostgresDdlCommand(rawArgs) {
|
|
|
2001
1887
|
process.exit(1);
|
|
2002
1888
|
}
|
|
2003
1889
|
const collectionsPath = argsList["--collections"] || path.join("..", "config", "collections");
|
|
2004
|
-
const outputPath = argsList["--output"] || path.join("
|
|
1890
|
+
const outputPath = argsList["--output"] || path.join(".rebase/sql", "schema.sql");
|
|
1891
|
+
if (!argsList["--output"]) {
|
|
1892
|
+
const retired = prepareGeneratedSqlDir();
|
|
1893
|
+
if (retired.length > 0) out(chalk.gray(` Generated SQL is written to ${GENERATED_SQL_DIR}/ now, which is not committed. Removed the old copies — commit the deletion:\n ${retired.join(", ")}`));
|
|
1894
|
+
}
|
|
2005
1895
|
const cmdParts = [
|
|
2006
1896
|
ddl.bin,
|
|
2007
1897
|
ddl.script,
|
|
@@ -2053,9 +1943,9 @@ async function schemaStaleCommand(rawArgs) {
|
|
|
2053
1943
|
const schemaFile = path.resolve(process.cwd(), outputPath);
|
|
2054
1944
|
const fix = Boolean(argsList["--fix"]);
|
|
2055
1945
|
const generatedExists = fs.existsSync(schemaFile);
|
|
2056
|
-
const { loadCollections } = await import("./doctor-
|
|
1946
|
+
const { loadCollections } = await import("./doctor-C-sYWbmt.js").then((n) => n.t);
|
|
2057
1947
|
const { findLegacyForeignKeyNames, compareGeneratedDeclarations, staleVerdict } = await import("./generated-schema-staleness-Di9c4ZxN.js").then((n) => n.r);
|
|
2058
|
-
const { generateSchema } = await import("./generate-drizzle-schema-logic-
|
|
1948
|
+
const { generateSchema } = await import("./generate-drizzle-schema-logic-sSFDp6LR.js").then((n) => n.n);
|
|
2059
1949
|
const { relationalCollections } = await import("@rebasepro/common");
|
|
2060
1950
|
let stale = [];
|
|
2061
1951
|
let differences = [];
|