@rebasepro/server-postgres 0.23.0 → 0.24.1-canary.g56af4a4
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-DGPL6G_C.js → BranchService-Bsn2wfaj.js} +36 -18
- package/dist/BranchService-Bsn2wfaj.js.map +1 -0
- package/dist/PostgresBackendDriver.d.ts +166 -13
- package/dist/auth/ensure-tables.d.ts +12 -0
- package/dist/auth/services.d.ts +78 -14
- 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 +14 -0
- package/dist/backup/backup-service.d.ts +2 -2
- package/dist/backup/pg-binary.d.ts +5 -0
- package/dist/{backup-cli-24v4OlSp.js → backup-cli-aUfRv16h.js} +37 -29
- package/dist/backup-cli-aUfRv16h.js.map +1 -0
- package/dist/{backup-service-HQ9GC4tN.js → backup-service-BTBB7RXn.js} +60 -28
- package/dist/backup-service-BTBB7RXn.js.map +1 -0
- package/dist/boot-ddl-DVpsFN6r.js +105 -0
- package/dist/boot-ddl-DVpsFN6r.js.map +1 -0
- package/dist/cli-errors-CaI4ntyD.js +1919 -0
- package/dist/cli-errors-CaI4ntyD.js.map +1 -0
- package/dist/cli-errors.d.ts +93 -5
- package/dist/{cli-flags-BglvpjHv.js → cli-flags-B-Dawocl.js} +5 -2
- package/dist/cli-flags-B-Dawocl.js.map +1 -0
- package/dist/cli-helpers.d.ts +52 -12
- package/dist/cli-output.d.ts +16 -28
- package/dist/cli-push.d.ts +87 -0
- package/dist/cli.js +625 -190
- package/dist/cli.js.map +1 -1
- package/dist/{column-plan-helpers-DF-8dTVa.js → column-plan-helpers-drZIsSNf.js} +42 -26
- package/dist/column-plan-helpers-drZIsSNf.js.map +1 -0
- package/dist/{databasePoolManager-Bj5FbeAs.js → connection-BPoNILnD.js} +19 -110
- package/dist/connection-BPoNILnD.js.map +1 -0
- package/dist/connection.d.ts +9 -1
- package/dist/data-transformer.d.ts +0 -8
- package/dist/databasePoolManager-BkEy2w18.js +119 -0
- package/dist/databasePoolManager-BkEy2w18.js.map +1 -0
- package/dist/deferred-index-build-DP1_SNFo.js +1051 -0
- package/dist/deferred-index-build-DP1_SNFo.js.map +1 -0
- package/dist/{doctor-Cb2thZ8s.js → doctor-C4WiftHm.js} +279 -87
- package/dist/doctor-C4WiftHm.js.map +1 -0
- package/dist/ensure-collection-policies-CXpbeNag.js +541 -0
- package/dist/ensure-collection-policies-CXpbeNag.js.map +1 -0
- package/dist/ensure-collection-tables-BhsU6qUE.js +532 -0
- package/dist/ensure-collection-tables-BhsU6qUE.js.map +1 -0
- package/dist/{ensure-tables-Dhn9KM3B.js → ensure-tables-CHsgyLqg.js} +178 -75
- package/dist/ensure-tables-CHsgyLqg.js.map +1 -0
- package/dist/enum-labels-kept-D8fI1MoX.js +56 -0
- package/dist/enum-labels-kept-D8fI1MoX.js.map +1 -0
- package/dist/generate-drizzle-schema-Bz52jH2h.js +57 -0
- package/dist/generate-drizzle-schema-Bz52jH2h.js.map +1 -0
- package/dist/{generate-drizzle-schema-logic-BfzK7UQd.js → generate-drizzle-schema-logic-B74WN37S.js} +28 -10
- package/dist/generate-drizzle-schema-logic-B74WN37S.js.map +1 -0
- package/dist/generate-files-CdEckX6c.js +199 -0
- package/dist/generate-files-CdEckX6c.js.map +1 -0
- package/dist/generate-postgres-ddl-logic-B64O2oZF.js +152 -0
- package/dist/generate-postgres-ddl-logic-B64O2oZF.js.map +1 -0
- package/dist/generated-sql.d.ts +34 -0
- package/dist/history/HistoryService.d.ts +38 -6
- package/dist/history/ensure-history-table.d.ts +42 -4
- package/dist/index.es.js +5557 -7612
- package/dist/index.es.js.map +1 -1
- package/dist/{introspect-db-logic-WMuAfxvw.js → introspect-db-logic-C-Di8dA9.js} +562 -350
- package/dist/introspect-db-logic-C-Di8dA9.js.map +1 -0
- package/dist/introspect-db-queries-D6kAqReI.js +339 -0
- package/dist/introspect-db-queries-D6kAqReI.js.map +1 -0
- package/dist/pg-error-utils-vJezlXdI.js +388 -0
- package/dist/pg-error-utils-vJezlXdI.js.map +1 -0
- package/dist/{plan-schema-C0fxM8dY.js → plan-schema-B1YF_tYq.js} +278 -533
- package/dist/plan-schema-B1YF_tYq.js.map +1 -0
- package/dist/{policy-drift-DljYdrpW.js → policy-drift-Dm8i-s1J.js} +24 -7
- package/dist/policy-drift-Dm8i-s1J.js.map +1 -0
- package/dist/push-index-prebuild-Db4AQF4i.js +45 -0
- package/dist/push-index-prebuild-Db4AQF4i.js.map +1 -0
- package/dist/push-legacy-renames-BK6ilQq2.js +34 -0
- package/dist/push-legacy-renames-BK6ilQq2.js.map +1 -0
- package/dist/{generate-postgres-ddl-logic-Bt2d2mRH.js → render-ddl-CtC3y9Ol.js} +17 -151
- package/dist/render-ddl-CtC3y9Ol.js.map +1 -0
- package/dist/{rls-bootstrap-sql-B8EclDyM.js → rls-bootstrap-sql-RQUljnJ1.js} +125 -1046
- package/dist/rls-bootstrap-sql-RQUljnJ1.js.map +1 -0
- package/dist/{rls-enforcement-C6Xk0lA6.js → rls-enforcement-DvzqlaL1.js} +137 -17
- package/dist/rls-enforcement-DvzqlaL1.js.map +1 -0
- package/dist/schema/auth-schema.d.ts +172 -2
- package/dist/schema/boot-ddl.d.ts +47 -0
- package/dist/schema/classify-change.d.ts +29 -1
- package/dist/schema/collection-index.d.ts +45 -0
- package/dist/schema/column-plan-helpers.d.ts +38 -21
- package/dist/schema/deferred-index-build.d.ts +131 -0
- package/dist/schema/destructive-sql.d.ts +27 -1
- package/dist/schema/doctor-cli.js +110 -6
- package/dist/schema/doctor-cli.js.map +1 -1
- package/dist/schema/doctor-search-scale.d.ts +45 -0
- package/dist/schema/doctor.d.ts +53 -1
- package/dist/schema/ensure-collection-policies.d.ts +95 -35
- package/dist/schema/ensure-collection-tables.d.ts +56 -3
- package/dist/schema/enum-labels-kept.d.ts +26 -0
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-files.d.ts +44 -0
- package/dist/schema/generate-next-step.d.ts +0 -13
- package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -5
- package/dist/schema/generate-postgres-ddl.js +10 -81
- package/dist/schema/generate-postgres-ddl.js.map +1 -1
- package/dist/schema/generate-schema-commit.d.ts +12 -0
- package/dist/schema/introspect-db-logic.d.ts +43 -0
- package/dist/schema/introspect-db-queries.d.ts +12 -2
- 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-structure.d.ts +10 -0
- package/dist/schema/introspect-db.js +17 -6
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/migration-post-process.d.ts +34 -0
- package/dist/schema/plan/diff-plan.d.ts +127 -1
- package/dist/schema/plan/plan-schema.d.ts +16 -8
- package/dist/schema/plan/render-ddl.d.ts +6 -0
- package/dist/schema/plan/types.d.ts +45 -11
- package/dist/schema/push-index-prebuild.d.ts +25 -0
- package/dist/schema/push-legacy-renames.d.ts +20 -0
- package/dist/schema/rls-bootstrap-sql.d.ts +15 -0
- 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 +39 -4
- package/dist/services/BranchService.d.ts +22 -1
- package/dist/services/FetchService.d.ts +63 -12
- package/dist/services/PersistService.d.ts +15 -0
- package/dist/services/RelationService.d.ts +106 -3
- package/dist/services/cdc/CdcListener.d.ts +26 -10
- package/dist/services/cdc/identity-columns.d.ts +19 -0
- package/dist/services/cdc/trigger-cdc.d.ts +180 -18
- package/dist/services/channel-bus/PostgresChannelBus.d.ts +12 -7
- package/dist/services/channel-history.d.ts +32 -5
- package/dist/services/collection-helpers.d.ts +26 -1
- package/dist/services/field-viewer.d.ts +15 -1
- package/dist/services/pg-notify-listener.d.ts +95 -6
- package/dist/services/realtimeService.d.ts +467 -53
- package/dist/services/row-pipeline.d.ts +16 -9
- package/dist/services/socket-liveness.d.ts +85 -0
- package/dist/services/soft-delete.d.ts +12 -0
- package/dist/services/sql-script.d.ts +275 -0
- package/dist/services/vector-iterative-scan.d.ts +40 -0
- package/dist/services/write-depth.d.ts +22 -0
- package/dist/services/write-transaction-scope.d.ts +15 -3
- package/dist/socket-liveness-D3UD5_BW.js +7570 -0
- package/dist/socket-liveness-D3UD5_BW.js.map +1 -0
- package/dist/socket-opt-ins.d.ts +64 -0
- package/dist/source-DBDbBTL4.js +811 -0
- package/dist/source-DBDbBTL4.js.map +1 -0
- package/dist/tsx-loader.d.ts +14 -0
- package/dist/utils/drizzle-conditions.d.ts +63 -3
- package/dist/utils/error-cause.d.ts +7 -0
- package/dist/utils/pg-error-utils.d.ts +2 -4
- package/dist/utils/result-rows.d.ts +10 -0
- package/dist/websocket.d.ts +63 -18
- package/package.json +7 -7
- package/dist/BranchService-DGPL6G_C.js.map +0 -1
- package/dist/backup-cli-24v4OlSp.js.map +0 -1
- package/dist/backup-service-HQ9GC4tN.js.map +0 -1
- package/dist/cli-errors-B8qHg02P.js +0 -798
- package/dist/cli-errors-B8qHg02P.js.map +0 -1
- package/dist/cli-flags-BglvpjHv.js.map +0 -1
- package/dist/cli-output-CNdMql-L.js +0 -47
- package/dist/cli-output-CNdMql-L.js.map +0 -1
- package/dist/collection-index-DdnsxiJ_.js +0 -286
- package/dist/collection-index-DdnsxiJ_.js.map +0 -1
- package/dist/column-plan-helpers-DF-8dTVa.js.map +0 -1
- package/dist/databasePoolManager-Bj5FbeAs.js.map +0 -1
- package/dist/doctor-Cb2thZ8s.js.map +0 -1
- package/dist/ensure-collection-policies-RHUcEp4v.js +0 -105
- package/dist/ensure-collection-policies-RHUcEp4v.js.map +0 -1
- package/dist/ensure-collection-tables-BEEjn5cn.js +0 -857
- package/dist/ensure-collection-tables-BEEjn5cn.js.map +0 -1
- package/dist/ensure-tables-Dhn9KM3B.js.map +0 -1
- package/dist/generate-drizzle-schema-logic-BfzK7UQd.js.map +0 -1
- package/dist/generate-drizzle-schema-yzY_BLhr.js +0 -123
- package/dist/generate-drizzle-schema-yzY_BLhr.js.map +0 -1
- package/dist/generate-postgres-ddl-logic-Bt2d2mRH.js.map +0 -1
- package/dist/introspect-db-logic-WMuAfxvw.js.map +0 -1
- package/dist/plan-schema-C0fxM8dY.js.map +0 -1
- package/dist/policy-drift-DljYdrpW.js.map +0 -1
- package/dist/rls-bootstrap-sql-B8EclDyM.js.map +0 -1
- package/dist/rls-enforcement-C6Xk0lA6.js.map +0 -1
- package/dist/source-Br7L7GOI.js +0 -410
- package/dist/source-Br7L7GOI.js.map +0 -1
package/dist/schema/doctor.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { CollectionConfig, Property } from "@rebasepro/types";
|
|
2
|
+
import type { PgType, SchemaPlan, TablePlan } from "./plan/types.js";
|
|
2
3
|
export type IssueSeverity = "error" | "warning" | "info";
|
|
3
4
|
export interface DoctorIssue {
|
|
4
5
|
severity: IssueSeverity;
|
|
5
|
-
category: "missing_table" | "missing_column" | "type_mismatch" | "missing_constraint" | "schema_stale" | "missing_enum" | "enum_value_mismatch" | "missing_foreign_key" | "sdk_stale" | "sdk_not_generated" | "sdk_ungeneratable";
|
|
6
|
+
category: "missing_table" | "missing_column" | "type_mismatch" | "missing_constraint" | "primary_key_mismatch" | "schema_stale" | "missing_enum" | "enum_value_mismatch" | "missing_foreign_key" | "orphaned_junction" | "sdk_stale" | "sdk_not_generated" | "sdk_ungeneratable";
|
|
6
7
|
table?: string;
|
|
7
8
|
column?: string;
|
|
8
9
|
expected?: string;
|
|
@@ -84,6 +85,14 @@ export declare function getExpectedColumnType(prop: Property): string | null;
|
|
|
84
85
|
export declare function loadCollections(collectionsPath: string): Promise<CollectionConfig[]>;
|
|
85
86
|
export declare function checkCollectionsVsSchema(collections: CollectionConfig[], schemaFilePath: string): Promise<DoctorPhase>;
|
|
86
87
|
export declare function checkCollectionsVsSdk(collections: CollectionConfig[], sdkFilePath: string): Promise<DoctorPhase>;
|
|
88
|
+
interface DbColumn {
|
|
89
|
+
table_schema: string;
|
|
90
|
+
table_name: string;
|
|
91
|
+
column_name: string;
|
|
92
|
+
data_type: string;
|
|
93
|
+
is_nullable: string;
|
|
94
|
+
udt_name: string;
|
|
95
|
+
}
|
|
87
96
|
/**
|
|
88
97
|
* Extensions the collections need, that the database does not have installed.
|
|
89
98
|
*
|
|
@@ -115,7 +124,49 @@ export declare function schemaStampIssues(pool: {
|
|
|
115
124
|
rows: T[];
|
|
116
125
|
}>;
|
|
117
126
|
}, collections: CollectionConfig[]): Promise<DoctorIssue[]>;
|
|
127
|
+
/**
|
|
128
|
+
* What to do about a column whose type is not the one the collection plans.
|
|
129
|
+
*
|
|
130
|
+
* Both ways out, because the doctor cannot know which side is right: the
|
|
131
|
+
* property moved, or the database did. Putting the property back always clears
|
|
132
|
+
* the finding; converting the column clears it when every value converts.
|
|
133
|
+
*/
|
|
134
|
+
export declare function typeMismatchFix(schema: string, table: string, column: string, actual: string, planned: PgType | undefined): string;
|
|
135
|
+
/**
|
|
136
|
+
* The table's primary key against the one the collection reads it by.
|
|
137
|
+
*
|
|
138
|
+
* Rebase addresses a row by the key the plan names — the `isId` property, or
|
|
139
|
+
* the implicit `id` — so a table keyed on other columns, or on none, serves
|
|
140
|
+
* reads and writes against a key that does not hold. An error, because that is
|
|
141
|
+
* what it is at runtime.
|
|
142
|
+
*/
|
|
143
|
+
export declare function primaryKeyIssues(tablePlan: TablePlan, dbPk: string[], displayName: string): DoctorIssue[];
|
|
144
|
+
/**
|
|
145
|
+
* A junction's two key columns: present, typed like the keys they point at,
|
|
146
|
+
* and the table keyed on the pair. Order is not compared — boot and `db push`
|
|
147
|
+
* have built the key in both orders, and either one serves the same rows.
|
|
148
|
+
*/
|
|
149
|
+
export declare function junctionKeyIssues(junctionPlan: TablePlan, dbColumns: DbColumn[], dbPk: string[], displayName: string): DoctorIssue[];
|
|
118
150
|
export declare function checkCollectionsVsDatabase(collections: CollectionConfig[], databaseUrl: string): Promise<DoctorPhase>;
|
|
151
|
+
/**
|
|
152
|
+
* A `public` table in the junction shape that no collection declares.
|
|
153
|
+
*
|
|
154
|
+
* The shape is the planner's: exactly two columns, together the primary key,
|
|
155
|
+
* each a single-column foreign key to a collection's table. Renaming or
|
|
156
|
+
* removing a many-to-many leaves such a table behind — push and boot only
|
|
157
|
+
* create — with every link row still in it and nothing reading it.
|
|
158
|
+
*
|
|
159
|
+
* Info, never more: the table may be data somebody still wants. The remedy is
|
|
160
|
+
* shown, never run.
|
|
161
|
+
*/
|
|
162
|
+
export declare function orphanedJunctionIssues(plan: SchemaPlan, columnsByTable: Map<string, {
|
|
163
|
+
column_name: string;
|
|
164
|
+
}[]>, pksByTable: Map<string, string[]>, fksByTable: Map<string, {
|
|
165
|
+
constraint_name: string;
|
|
166
|
+
column_name: string;
|
|
167
|
+
foreign_table_schema: string;
|
|
168
|
+
foreign_table_name: string;
|
|
169
|
+
}[]>): DoctorIssue[];
|
|
119
170
|
export declare function renderReport(report: DoctorReport): void;
|
|
120
171
|
export declare function runDoctor(options: {
|
|
121
172
|
collectionsPath: string;
|
|
@@ -123,3 +174,4 @@ export declare function runDoctor(options: {
|
|
|
123
174
|
sdkPath: string;
|
|
124
175
|
databaseUrl?: string;
|
|
125
176
|
}): Promise<DoctorReport>;
|
|
177
|
+
export {};
|
|
@@ -1,43 +1,17 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Applying a bundle's RLS policies to a database at boot, idempotently.
|
|
3
|
-
*
|
|
4
|
-
* ## Why this exists
|
|
5
|
-
*
|
|
6
|
-
* {@link ensureCollectionTables} creates the collection *tables* a managed
|
|
7
|
-
* runtime boots against, but a table with row-level security disabled and no
|
|
8
|
-
* policies is not servable: authenticated requests run as the restricted
|
|
9
|
-
* `rebase_user` role, so a read with no `SELECT` policy returns nothing (a
|
|
10
|
-
* public collection answered 401) and a write with no `INSERT`/`UPDATE` policy
|
|
11
|
-
* is denied. The policies live in the collections' `securityRules`; nothing at
|
|
12
|
-
* boot applied them. `rebase db push` does — but it drives Atlas against a
|
|
13
|
-
* local `DATABASE_URL`, and a managed tenant's database is reachable only from
|
|
14
|
-
* inside the cluster, by the runtime that is already connected to it. So the
|
|
15
|
-
* runtime is the only thing that *can* apply them, and this is where it does.
|
|
16
|
-
*
|
|
17
|
-
* ## Why this is safe to run on every boot
|
|
18
|
-
*
|
|
19
|
-
* Every statement is idempotent: `ENABLE ROW LEVEL SECURITY` is a no-op once
|
|
20
|
-
* enabled, and each policy is a `DROP POLICY IF EXISTS` immediately followed by
|
|
21
|
-
* a `CREATE POLICY`, so re-applying asserts exactly the declared state. It adds
|
|
22
|
-
* and replaces; it never drops data. (It does not *reconcile* — a policy a
|
|
23
|
-
* previous push left behind under an old name is not removed here; that stays a
|
|
24
|
-
* `db push` / `db migrate` concern, alongside destructive schema changes.)
|
|
25
|
-
*
|
|
26
|
-
* Unlike table creation, a failure here is not fatal: RLS stays enabled, so a
|
|
27
|
-
* table whose policies could not be applied fails **closed** (denies) rather
|
|
28
|
-
* than leaking rows. One collection's policy failing (e.g. a rule that
|
|
29
|
-
* references a table a real migration has not created yet) must not crash-loop
|
|
30
|
-
* the whole deployment and take the other collections' working routes down with
|
|
31
|
-
* it. Failures are reported loudly and per-table so the operator can see
|
|
32
|
-
* exactly which collection is not yet servable and why.
|
|
33
|
-
*/
|
|
34
1
|
import { type CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
import { type CollectionPolicyPlan } from "./generate-postgres-ddl-logic.js";
|
|
35
3
|
import { type Queryable } from "./ensure-collection-tables.js";
|
|
4
|
+
import { type BootLockOptions } from "./boot-ddl.js";
|
|
36
5
|
export interface PolicyEnsureResult {
|
|
37
6
|
/** `CREATE POLICY` statements that ran successfully. */
|
|
38
7
|
policiesApplied: number;
|
|
39
|
-
/** Tables that had RLS enabled. */
|
|
8
|
+
/** Tables that had RLS enabled — including the {@link unchanged} ones, which already had it. */
|
|
40
9
|
tablesSecured: number;
|
|
10
|
+
/**
|
|
11
|
+
* Tables whose policies were already exactly the declared ones, RLS on, by
|
|
12
|
+
* the stamp each policy carries — left alone: no statement, no lock.
|
|
13
|
+
*/
|
|
14
|
+
unchanged: number;
|
|
41
15
|
/** Declared tables absent from the database — left to a real migration. */
|
|
42
16
|
skipped: {
|
|
43
17
|
table: string;
|
|
@@ -80,5 +54,91 @@ export interface PolicyEnsureResult {
|
|
|
80
54
|
* database — which is the reason this module exists. So boot has to do it.
|
|
81
55
|
*/
|
|
82
56
|
orphansDropped: number;
|
|
57
|
+
/**
|
|
58
|
+
* Tables whose policies were not re-applied because another session held
|
|
59
|
+
* the table's lock for every attempt — a long transaction, a report, a
|
|
60
|
+
* dump. Each table's policy statements run in one transaction, so a table
|
|
61
|
+
* here was left exactly as it was: RLS on, its existing policies in force.
|
|
62
|
+
* `rulesChanged` says whether those differ from the declared ones by name
|
|
63
|
+
* (a rule whose policy is missing, or an orphan still granting), in which
|
|
64
|
+
* case the new rules are not in force on this table until a later boot.
|
|
65
|
+
*/
|
|
66
|
+
deferred: {
|
|
67
|
+
table: string;
|
|
68
|
+
error: string;
|
|
69
|
+
rulesChanged: boolean;
|
|
70
|
+
}[];
|
|
71
|
+
}
|
|
72
|
+
/** What every policy stamp starts with. See {@link policyStampSql}. */
|
|
73
|
+
export declare const POLICY_STAMP_PREFIX = "rebase:";
|
|
74
|
+
/**
|
|
75
|
+
* One statement that stamps every policy the plan just created on its table:
|
|
76
|
+
* `COMMENT ON POLICY … IS 'rebase:<hash>'`, the hash taken over the rendered
|
|
77
|
+
* statement's hash and the definition Postgres stored — read back here, after
|
|
78
|
+
* the `CREATE`s, in the same transaction. A `DO` block because the stored
|
|
79
|
+
* definition is only known once the policy exists, and a `COMMENT` takes a
|
|
80
|
+
* literal. One line, and it names only this table.
|
|
81
|
+
*
|
|
82
|
+
* `undefined` for a table the plan gives no policies.
|
|
83
|
+
*/
|
|
84
|
+
export declare function policyStampSql(plan: CollectionPolicyPlan): string | undefined;
|
|
85
|
+
export interface TableLockResult {
|
|
86
|
+
/** Tables RLS is now on for. */
|
|
87
|
+
locked: number;
|
|
88
|
+
/** Tables it could not be switched on for. See {@link PolicyEnsureResult.unsecured}. */
|
|
89
|
+
unsecured: PolicyEnsureResult["unsecured"];
|
|
83
90
|
}
|
|
84
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Switch RLS on for every table the collections declare — each collection's
|
|
93
|
+
* and each junction's — and do nothing else.
|
|
94
|
+
*
|
|
95
|
+
* The first thing `rebase db push` does once Atlas has committed. Atlas cannot
|
|
96
|
+
* carry `ENABLE ROW LEVEL SECURITY` (its free tier neither plans nor reads it),
|
|
97
|
+
* so a table it creates arrives with RLS off — while the user role already
|
|
98
|
+
* holds DML on it through the schema's default privileges. Everything push runs
|
|
99
|
+
* after Atlas can refuse: the vector, search and trigger DDL, the policies
|
|
100
|
+
* themselves. Locking first means none of those failures can leave a table
|
|
101
|
+
* open: RLS with no policy denies, which is a state a failed push may leave.
|
|
102
|
+
*
|
|
103
|
+
* A declared table that is not in the database is skipped — nothing can reach
|
|
104
|
+
* a table that does not exist, and the step that creates it locks it.
|
|
105
|
+
*/
|
|
106
|
+
export declare function lockCollectionTables(client: Queryable, collections: CollectionConfig[]): Promise<TableLockResult>;
|
|
107
|
+
/**
|
|
108
|
+
* Bring the declared collections' RLS policies up to date. Returns what it did.
|
|
109
|
+
*
|
|
110
|
+
* Only tables that already exist are touched: the boot-time table creator runs
|
|
111
|
+
* first, so anything still missing is a table this additive path did not
|
|
112
|
+
* create (it creates junctions too) — one a failed boot DDL left out, or a
|
|
113
|
+
* project whose schema a migration owns. Enabling RLS on a
|
|
114
|
+
* non-existent table would error, so those are recorded as skipped, not failed.
|
|
115
|
+
*
|
|
116
|
+
* ## One transaction per table
|
|
117
|
+
*
|
|
118
|
+
* Each table's statements — `ENABLE` when RLS is off, every `DROP POLICY IF
|
|
119
|
+
* EXISTS` / `CREATE POLICY` pair, the orphan drops — go to Postgres as one
|
|
120
|
+
* string, which it runs as one transaction (see `boot-ddl`). Before, each was
|
|
121
|
+
* its own autocommit: at 240 tables, 4,080 commits and 4,080 ACCESS EXCLUSIVE
|
|
122
|
+
* lock acquisitions per boot, 6–22 s on a disk that really flushes. And between
|
|
123
|
+
* a policy's DROP and its CREATE, every other replica evaluated the table with
|
|
124
|
+
* that policy missing — a user's continuous reads of their own row came back
|
|
125
|
+
* empty once per boot (measured, Postgres 18). Now a concurrent request sees the
|
|
126
|
+
* old policies or the new ones, never neither.
|
|
127
|
+
*
|
|
128
|
+
* If the transaction fails for any reason but a lock timeout, the table is
|
|
129
|
+
* applied again one statement at a time, exactly as before — so a rule that
|
|
130
|
+
* cannot be created (a function a migration has not added yet) leaves the
|
|
131
|
+
* table in the same deny-leaning partial state it always did, rather than
|
|
132
|
+
* rolling back to the old, possibly looser, policies.
|
|
133
|
+
*
|
|
134
|
+
* ## Lock budget
|
|
135
|
+
*
|
|
136
|
+
* The transaction runs under `lock_timeout` (`boot-ddl`), so a table that a
|
|
137
|
+
* long transaction, a report or a dump is holding costs live traffic one
|
|
138
|
+
* second, not the statement timeout. A contended table is retried after the
|
|
139
|
+
* others, with backoff, within a retry budget for the whole step; one still
|
|
140
|
+
* held after that is reported in `deferred` — its transaction rolled back, so
|
|
141
|
+
* it is exactly as it was before this boot — or, if RLS was off on it, closed
|
|
142
|
+
* by revoking the user role's grant, as any table RLS cannot be enabled on is.
|
|
143
|
+
*/
|
|
144
|
+
export declare function ensureCollectionPolicies(client: Queryable, collections: CollectionConfig[], log?: (message: string) => void, options?: BootLockOptions): Promise<PolicyEnsureResult>;
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
* See `diff-plan.ts` for the whole of that argument.
|
|
23
23
|
*/
|
|
24
24
|
import { type CollectionConfig } from "@rebasepro/types";
|
|
25
|
-
import { type ConstraintPolicy, type DiffOptions, type EnsureAction, type EnsurePlan, type ExistingSchema, type LegacyForeignKey, type OrphanedRequiredColumn, type SearchColumnDrift, type WithheldConstraint } from "./plan/diff-plan.js";
|
|
26
|
-
|
|
25
|
+
import { type ConstraintPolicy, type DeferredIndexBuild, type DiffOptions, type EnsureAction, type EnsurePlan, type ExistingSchema, type LegacyForeignKey, type OrphanedRequiredColumn, type SearchColumnDrift, type WithheldConstraint } from "./plan/diff-plan.js";
|
|
26
|
+
import { type DeferredIndexOutcome, type OpenIndexBuildSession } from "./deferred-index-build.js";
|
|
27
|
+
export type { ConstraintPolicy, DeferredIndexBuild, EnsureAction, EnsurePlan, ExistingSchema, LegacyForeignKey, OrphanedRequiredColumn, SearchColumnDrift, WithheldConstraint };
|
|
27
28
|
/**
|
|
28
29
|
* The subset of a database handle this needs: run a statement, get rows back.
|
|
29
30
|
*
|
|
@@ -51,6 +52,36 @@ export interface EnsureOptions extends DiffOptions {
|
|
|
51
52
|
* reads the world.
|
|
52
53
|
*/
|
|
53
54
|
databaseExtensions?: readonly string[];
|
|
55
|
+
/**
|
|
56
|
+
* Run each statement that changes an existing table under this
|
|
57
|
+
* `lock_timeout` (ms), retrying a timeout with backoff — see
|
|
58
|
+
* `schema/boot-ddl`. The boot sets it; absent, statements wait on locks for
|
|
59
|
+
* as long as the connection's `statement_timeout` allows, as the live schema
|
|
60
|
+
* editor's confirmed changes do.
|
|
61
|
+
*/
|
|
62
|
+
lockTimeoutMs?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Apply the statements that build tables this run creates — and the enum
|
|
65
|
+
* types they use — in transactions of up to {@link NEW_TABLE_BATCH}
|
|
66
|
+
* statements instead of one autocommit each. The boot sets it; see
|
|
67
|
+
* `ensureCollectionTables`.
|
|
68
|
+
*/
|
|
69
|
+
batchNewTables?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Leave the indexes a live table is missing (or holds INVALID) to the
|
|
72
|
+
* caller, in {@link EnsureOutcome.deferredIndexes}, instead of building
|
|
73
|
+
* them before returning. The boot sets it and builds them in the
|
|
74
|
+
* background once it serves — see `deferred-index-build`. A UNIQUE one is
|
|
75
|
+
* built before returning either way: see {@link DeferredIndexBuild.unique}.
|
|
76
|
+
*/
|
|
77
|
+
deferIndexBuilds?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* The connection those builds run on when they are not deferred. Absent,
|
|
80
|
+
* they share `client` — fine for a small table, and cut short by the
|
|
81
|
+
* pool's `statement_timeout` on a large one, which is why the boot hands
|
|
82
|
+
* over a dedicated connection.
|
|
83
|
+
*/
|
|
84
|
+
openIndexBuildSession?: OpenIndexBuildSession;
|
|
54
85
|
}
|
|
55
86
|
export interface EnsureOutcome extends EnsurePlan {
|
|
56
87
|
/**
|
|
@@ -68,6 +99,14 @@ export interface EnsureOutcome extends EnsurePlan {
|
|
|
68
99
|
target: string;
|
|
69
100
|
error: string;
|
|
70
101
|
}[];
|
|
102
|
+
/**
|
|
103
|
+
* What happened to the indexes built here rather than left to the caller —
|
|
104
|
+
* every one of {@link EnsurePlan.deferredIndexes}, or under
|
|
105
|
+
* `deferIndexBuilds` the UNIQUE ones only; absent when there were none.
|
|
106
|
+
* An index on a table that already exists reports here, not in
|
|
107
|
+
* {@link failures}.
|
|
108
|
+
*/
|
|
109
|
+
indexBuilds?: DeferredIndexOutcome;
|
|
71
110
|
}
|
|
72
111
|
/**
|
|
73
112
|
* Decide what to add. Pure — the caller supplies what exists and runs the
|
|
@@ -82,8 +121,22 @@ export interface EnsureOutcome extends EnsurePlan {
|
|
|
82
121
|
* with no developer in the loop.
|
|
83
122
|
*/
|
|
84
123
|
export declare function planCollectionSchemaEnsure(allCollections: CollectionConfig[], existing: ExistingSchema, options?: EnsureOptions): EnsurePlan;
|
|
124
|
+
export interface ReadSchemaOptions {
|
|
125
|
+
/**
|
|
126
|
+
* The existing tables worth probing for rows, given every table's columns.
|
|
127
|
+
*
|
|
128
|
+
* Whether a table holds rows decides one thing: whether a NOT NULL or a
|
|
129
|
+
* UNIQUE can be added with a column *being added* to it. So the boot asks
|
|
130
|
+
* only about tables gaining a column — none at all on a boot that changes
|
|
131
|
+
* nothing — rather than probing every table in the schema, adopted and
|
|
132
|
+
* unmanaged ones included, on every boot. A table that is not probed is
|
|
133
|
+
* reported as holding rows: the answer that never plans a constraint that
|
|
134
|
+
* could fail against data. Absent, every table is probed.
|
|
135
|
+
*/
|
|
136
|
+
probeTables?: (tables: ReadonlyMap<string, ReadonlySet<string>>) => Iterable<string>;
|
|
137
|
+
}
|
|
85
138
|
/** Read what the database has, for the schemas the collections live in. */
|
|
86
|
-
export declare function readExistingSchema(client: Queryable, schemas: string[]): Promise<ExistingSchema>;
|
|
139
|
+
export declare function readExistingSchema(client: Queryable, schemas: string[], options?: ReadSchemaOptions): Promise<ExistingSchema>;
|
|
87
140
|
/**
|
|
88
141
|
* Read what the database looks like, for the schemas a set of collections
|
|
89
142
|
* lives in.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enum labels the database holds and the collections no longer declare.
|
|
3
|
+
*
|
|
4
|
+
* PostgreSQL has no `ALTER TYPE … DROP VALUE`, and Atlas plans nothing for a
|
|
5
|
+
* removed label — `db push` reported success while the database kept the old
|
|
6
|
+
* label, so writes of it (by SQL, or an older client) went on succeeding, and
|
|
7
|
+
* `rebase doctor` sent the developer back to the same push. Boot behaves the
|
|
8
|
+
* same way by design: it adds labels and never removes one. So this is not a
|
|
9
|
+
* refusal — the push stays what boot would do — but the push says it, with the
|
|
10
|
+
* real way to retire a label.
|
|
11
|
+
*/
|
|
12
|
+
import { type CollectionConfig } from "@rebasepro/types";
|
|
13
|
+
import type { EnumPlan } from "./plan/types.js";
|
|
14
|
+
import { type Queryable } from "./ensure-collection-tables.js";
|
|
15
|
+
export interface KeptEnumLabels {
|
|
16
|
+
/** `schema.type`, as the catalogue names it. */
|
|
17
|
+
type: string;
|
|
18
|
+
/** Labels in the database that no collection declares, in the type's order. */
|
|
19
|
+
labels: string[];
|
|
20
|
+
}
|
|
21
|
+
/** Pure: the declared enums against the catalogue's labels, keyed `schema.type`. */
|
|
22
|
+
export declare function findKeptEnumLabels(declared: Pick<EnumPlan, "qualified" | "labels">[], existing: ReadonlyMap<string, string[]>): KeptEnumLabels[];
|
|
23
|
+
/** Plans the collections and reads the catalogue's enum labels. */
|
|
24
|
+
export declare function keptEnumLabels(client: Queryable, collections: CollectionConfig[]): Promise<KeptEnumLabels[]>;
|
|
25
|
+
/** What the push prints: which labels stay, and how one is actually retired. */
|
|
26
|
+
export declare function formatKeptEnumLabelsNotice(kept: KeptEnumLabels[]): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
__createRequire(import.meta.url);
|
|
3
|
-
import { t as main } from "../generate-drizzle-schema-
|
|
3
|
+
import { t as main } from "../generate-drizzle-schema-Bz52jH2h.js";
|
|
4
4
|
//#region src/schema/bin/generate-drizzle-schema.ts
|
|
5
5
|
/**
|
|
6
6
|
* The built entry point for `generate-drizzle-schema`.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two generators `rebase db push` runs as its step 1, as functions.
|
|
3
|
+
*
|
|
4
|
+
* They used to exist only as scripts, each its own `tsx` process: the driver
|
|
5
|
+
* (itself a `tsx` process) spawned one for the Drizzle schema and then one for
|
|
6
|
+
* the DDL, and each paid a Node start, tsx's loader and an import of the whole
|
|
7
|
+
* server package before reading a single collection — about 0.5 s apiece, a
|
|
8
|
+
* second per push. The driver process is one-shot, so a fresh module graph per
|
|
9
|
+
* generation bought nothing: the collections cannot change under it.
|
|
10
|
+
*
|
|
11
|
+
* The scripts remain (`generate-drizzle-schema.ts`, `generate-postgres-ddl.ts`)
|
|
12
|
+
* for `--watch`, which re-imports the collections on every save and does need a
|
|
13
|
+
* process of its own — Node's module cache would serve it the first version
|
|
14
|
+
* forever.
|
|
15
|
+
*
|
|
16
|
+
* Both functions print their own success lines and their own error, exactly as
|
|
17
|
+
* the scripts did, and throw {@link GenerationFailed} after printing — so a
|
|
18
|
+
* caller can tell "the generator said why" from "the generator never ran".
|
|
19
|
+
*/
|
|
20
|
+
/** Thrown once the generator has printed why it failed. */
|
|
21
|
+
export declare class GenerationFailed extends Error {
|
|
22
|
+
constructor(message: string, options?: {
|
|
23
|
+
cause?: unknown;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export interface GenerateFileOptions {
|
|
27
|
+
/** The collections directory (or single module), resolved against the cwd. */
|
|
28
|
+
collectionsPath: string;
|
|
29
|
+
/** Where to write. Absent: print to stdout instead. */
|
|
30
|
+
outputPath?: string;
|
|
31
|
+
/**
|
|
32
|
+
* End on the line that says what to run next. Only `rebase schema
|
|
33
|
+
* generate` itself wants it: inside `db push` it told the push to run
|
|
34
|
+
* `db generate`, and inside `db generate` to run itself.
|
|
35
|
+
*/
|
|
36
|
+
nextStep?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** `src/schema.generated.ts`, from the collections. */
|
|
39
|
+
export declare function writeDrizzleSchema(options: GenerateFileOptions): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* `schema.sql` (Atlas's desired state) and, beside it, `policies.sql`,
|
|
42
|
+
* `search.sql`, `vector.sql` and `triggers.sql` — the parts Atlas does not own.
|
|
43
|
+
*/
|
|
44
|
+
export declare function writePostgresDdl(options: GenerateFileOptions): Promise<void>;
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The line `rebase schema generate` ends on.
|
|
3
|
-
*
|
|
4
|
-
* Its own module because `generate-drizzle-schema.ts` uses `import.meta` to
|
|
5
|
-
* decide whether it was executed directly, which the driver's jest suite cannot
|
|
6
|
-
* load at all — so the one sentence in that file with a decision in it had no
|
|
7
|
-
* test and could not have one where it stood.
|
|
8
|
-
*/
|
|
9
|
-
export declare const formatTerminalText: (text: string, options?: {
|
|
10
|
-
bold?: boolean;
|
|
11
|
-
backgroundColor?: "blue" | "green" | "red" | "yellow" | "cyan" | "magenta";
|
|
12
|
-
textColor?: "white" | "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan";
|
|
13
|
-
}) => string;
|
|
14
1
|
/**
|
|
15
2
|
* What to do with the file that was just generated.
|
|
16
3
|
*
|
|
@@ -31,7 +31,7 @@ type ResolveCollection = (slug: string) => CollectionConfig | undefined;
|
|
|
31
31
|
*/
|
|
32
32
|
export declare const generatePolicyStatements: (collection: CollectionConfig, rule: SecurityRule, resolveCollection: ResolveCollection) => string[];
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* `.rebase/sql/schema.sql` — the desired state Atlas diffs the database against.
|
|
35
35
|
*
|
|
36
36
|
* Synchronous. It was `async` with nothing to await, which made every caller
|
|
37
37
|
* (the writer script, the doctor, the live editor, two gates) carry a promise
|
|
@@ -39,10 +39,10 @@ export declare const generatePolicyStatements: (collection: CollectionConfig, ru
|
|
|
39
39
|
* `[object Promise]`.
|
|
40
40
|
*/
|
|
41
41
|
export declare const generatePostgresDdl: (allCollections: CollectionConfig[], options?: DdlRenderOptions) => string;
|
|
42
|
-
/**
|
|
42
|
+
/** `.rebase/sql/policies.sql` — the RLS policies. Atlas has no model for a policy. */
|
|
43
43
|
export declare const generatePostgresPoliciesDdl: (allCollections: CollectionConfig[]) => string;
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* `.rebase/sql/search.sql` — everything a `search` block needs, as a file Rebase
|
|
46
46
|
* applies itself.
|
|
47
47
|
*
|
|
48
48
|
* Search is one of the parts of the schema Atlas does not own, for two
|
|
@@ -70,7 +70,7 @@ export declare const generatePostgresSearchDdl: (allCollections: CollectionConfi
|
|
|
70
70
|
*/
|
|
71
71
|
export declare const searchExcludePatterns: (allCollections: CollectionConfig[]) => string[];
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* `.rebase/sql/vector.sql` — the `vector` columns, their ANN indexes and, when a
|
|
74
74
|
* database declared it, the extension.
|
|
75
75
|
*
|
|
76
76
|
* Vector is out of Atlas's hands for a narrower reason than search: not that
|
|
@@ -86,7 +86,7 @@ export declare const generatePostgresVectorDdl: (allCollections: CollectionConfi
|
|
|
86
86
|
/** @see searchExcludePatterns */
|
|
87
87
|
export declare const vectorExcludePatterns: (allCollections: CollectionConfig[]) => string[];
|
|
88
88
|
/**
|
|
89
|
-
*
|
|
89
|
+
* `.rebase/sql/triggers.sql` — the `BEFORE UPDATE` triggers behind
|
|
90
90
|
* `autoValue: "on_update"`, and the one function they share.
|
|
91
91
|
*
|
|
92
92
|
* A trigger is a function plus a binding, so it is carved out of Atlas's view
|
|
@@ -1,94 +1,23 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
__createRequire(import.meta.url);
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { loadCollectionsFromDirectory } from "@rebasepro/server";
|
|
6
|
-
import { declaredDatabaseExtensions } from "@rebasepro/types";
|
|
7
|
-
import * as fs$1 from "fs";
|
|
8
|
-
import { promises } from "fs";
|
|
3
|
+
import { r as out } from "../source-DBDbBTL4.js";
|
|
4
|
+
import { r as writePostgresDdl } from "../generate-files-CdEckX6c.js";
|
|
9
5
|
import path from "path";
|
|
10
|
-
import { pathToFileURL } from "url";
|
|
11
6
|
//#region src/schema/generate-postgres-ddl.ts
|
|
12
|
-
/** Where a project declares its resources, relative to the config directory. */
|
|
13
|
-
var RESOURCE_ENTRIES = [
|
|
14
|
-
"resources.ts",
|
|
15
|
-
"resources.js",
|
|
16
|
-
"resources/index.ts",
|
|
17
|
-
"resources/index.js"
|
|
18
|
-
];
|
|
19
7
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* directory, so nothing else in it would ever import the resources module — and
|
|
25
|
-
* a permission that reads as absent because nobody loaded it is the worst shape
|
|
26
|
-
* this could take: the push succeeds, `vector.sql` quietly omits the
|
|
27
|
-
* `CREATE EXTENSION`, and the failure lands one statement later looking like a
|
|
28
|
-
* database problem.
|
|
29
|
-
*
|
|
30
|
-
* Imported directly rather than through the config index, for the reason
|
|
31
|
-
* `boot/resource-loading.ts` gives: re-exporting from the index would work only
|
|
32
|
-
* for projects that remember to.
|
|
33
|
-
*
|
|
34
|
-
* A project with no resources module is the normal case and declares nothing.
|
|
35
|
-
* An import that *throws* is not swallowed — a resources module that does not
|
|
36
|
-
* evaluate is a configuration error, and treating it as "declared nothing"
|
|
37
|
-
* would turn it into a silently missing extension.
|
|
8
|
+
* The generator as its own process — what `--watch` runs, because a watch has
|
|
9
|
+
* to re-import the collections on every save and Node's module cache would
|
|
10
|
+
* serve it the first version forever. One-shot generation runs in the driver's
|
|
11
|
+
* own process instead; see `generate-files.ts`.
|
|
38
12
|
*/
|
|
39
|
-
async function loadDeclaredResources(configDir) {
|
|
40
|
-
const entry = RESOURCE_ENTRIES.map((name) => path.join(configDir, name)).find((candidate) => fs$1.existsSync(candidate));
|
|
41
|
-
if (!entry) return;
|
|
42
|
-
await import(pathToFileURL(entry).href);
|
|
43
|
-
}
|
|
44
13
|
var runGeneration = async (collectionsFilePath, outputPath) => {
|
|
14
|
+
if (!collectionsFilePath) throw new Error("No collections file path provided.");
|
|
45
15
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let collections = await loadCollectionsFromDirectory(resolvedPath);
|
|
50
|
-
if (!collections || !Array.isArray(collections)) collections = [];
|
|
51
|
-
collections.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
52
|
-
const ddlContent = generatePostgresDdl(collections, {
|
|
53
|
-
includePolicies: false,
|
|
54
|
-
includeSearch: false,
|
|
55
|
-
includeVector: false
|
|
16
|
+
await writePostgresDdl({
|
|
17
|
+
collectionsPath: path.resolve(collectionsFilePath),
|
|
18
|
+
outputPath
|
|
56
19
|
});
|
|
57
|
-
const policiesContent = generatePostgresPoliciesDdl(collections);
|
|
58
|
-
const searchContent = generatePostgresSearchDdl(collections);
|
|
59
|
-
const vectorContent = generatePostgresVectorDdl(collections, { extensions: declaredDatabaseExtensions() });
|
|
60
|
-
const triggersContent = generatePostgresTriggersDdl(collections);
|
|
61
|
-
if (outputPath) {
|
|
62
|
-
const outputDir = path.dirname(outputPath);
|
|
63
|
-
await promises.mkdir(outputDir, { recursive: true });
|
|
64
|
-
await promises.writeFile(outputPath, ddlContent);
|
|
65
|
-
out(`✅ PostgreSQL DDL generated successfully at ${outputPath}`);
|
|
66
|
-
const policiesPath = path.join(outputDir, "policies.sql");
|
|
67
|
-
await promises.writeFile(policiesPath, policiesContent);
|
|
68
|
-
out(`✅ PostgreSQL Policies DDL generated successfully at ${policiesPath}`);
|
|
69
|
-
const searchPath = path.join(outputDir, "search.sql");
|
|
70
|
-
if (searchContent) {
|
|
71
|
-
await promises.writeFile(searchPath, searchContent);
|
|
72
|
-
out(`✅ PostgreSQL search DDL generated successfully at ${searchPath}`);
|
|
73
|
-
} else if (fs$1.existsSync(searchPath)) await promises.rm(searchPath);
|
|
74
|
-
const vectorPath = path.join(outputDir, "vector.sql");
|
|
75
|
-
if (vectorContent) {
|
|
76
|
-
await promises.writeFile(vectorPath, vectorContent);
|
|
77
|
-
out(`✅ PostgreSQL vector DDL generated successfully at ${vectorPath}`);
|
|
78
|
-
} else if (fs$1.existsSync(vectorPath)) await promises.rm(vectorPath);
|
|
79
|
-
const triggersPath = path.join(outputDir, "triggers.sql");
|
|
80
|
-
if (triggersContent) {
|
|
81
|
-
await promises.writeFile(triggersPath, triggersContent);
|
|
82
|
-
out(`✅ PostgreSQL trigger DDL generated successfully at ${triggersPath}`);
|
|
83
|
-
} else if (fs$1.existsSync(triggersPath)) await promises.rm(triggersPath);
|
|
84
|
-
} else {
|
|
85
|
-
out("✅ PostgreSQL DDL generated successfully.");
|
|
86
|
-
out(String(ddlContent));
|
|
87
|
-
out("\n✅ PostgreSQL Policies DDL generated successfully.");
|
|
88
|
-
out(String(policiesContent));
|
|
89
|
-
}
|
|
90
20
|
} catch (error) {
|
|
91
|
-
outError(`Error generating DDL schema: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
|
|
92
21
|
process.exitCode = 1;
|
|
93
22
|
throw error;
|
|
94
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-postgres-ddl.js","names":[],"sources":["../../src/schema/generate-postgres-ddl.ts","../../src/schema/bin/generate-postgres-ddl.ts"],"sourcesContent":["import { promises as fsPromises } from \"fs\";\nimport * as fs from \"fs\";\nimport path from \"path\";\nimport { pathToFileURL } from \"url\";\nimport {\n generatePostgresDdl,\n generatePostgresPoliciesDdl,\n generatePostgresSearchDdl,\n generatePostgresTriggersDdl,\n generatePostgresVectorDdl\n} from \"./generate-postgres-ddl-logic\";\nimport { CollectionConfig, declaredDatabaseExtensions } from \"@rebasepro/types\";\nimport { loadCollectionsFromDirectory } from \"@rebasepro/server\";\nimport { out, outError } from \"../cli-output\";\n\n/** Where a project declares its resources, relative to the config directory. */\nconst RESOURCE_ENTRIES = [\"resources.ts\", \"resources.js\", \"resources/index.ts\", \"resources/index.js\"];\n\n/**\n * Evaluate the project's `resources.ts`, so `declaredDatabaseExtensions()` can\n * answer.\n *\n * This generator runs as its own tsx subprocess and loads only the collections\n * directory, so nothing else in it would ever import the resources module — and\n * a permission that reads as absent because nobody loaded it is the worst shape\n * this could take: the push succeeds, `vector.sql` quietly omits the\n * `CREATE EXTENSION`, and the failure lands one statement later looking like a\n * database problem.\n *\n * Imported directly rather than through the config index, for the reason\n * `boot/resource-loading.ts` gives: re-exporting from the index would work only\n * for projects that remember to.\n *\n * A project with no resources module is the normal case and declares nothing.\n * An import that *throws* is not swallowed — a resources module that does not\n * evaluate is a configuration error, and treating it as \"declared nothing\"\n * would turn it into a silently missing extension.\n */\nasync function loadDeclaredResources(configDir: string): Promise<void> {\n const entry = RESOURCE_ENTRIES\n .map(name => path.join(configDir, name))\n .find(candidate => fs.existsSync(candidate));\n if (!entry) return;\n await import(pathToFileURL(entry).href);\n}\n\nconst runGeneration = async (collectionsFilePath?: string, outputPath?: string) => {\n try {\n if (!collectionsFilePath) {\n throw new Error(\"No collections file path provided.\");\n }\n\n const resolvedPath = path.resolve(collectionsFilePath);\n\n // `<config>/collections` is the path this is given, so the config\n // directory is its parent — the same relationship `rebase resources`\n // and the boot loader assume.\n await loadDeclaredResources(path.dirname(resolvedPath));\n\n // Shared with the runtime and the doctor: what gets generated here must\n // be exactly what the server serves, including directory-level defaults.\n let collections: CollectionConfig[] = await loadCollectionsFromDirectory(resolvedPath);\n\n\n if (!collections || !Array.isArray(collections)) {\n collections = [];\n }\n\n // Sort collections by slug alphabetically to ensure deterministic DDL generation\n collections.sort((a, b) => a.slug.localeCompare(b.slug));\n\n // `schema.sql` is Atlas's desired state, and it carries neither the RLS\n // policies, nor the search apparatus, nor the vector columns: Atlas\n // manages none of them, and in the search and vector cases cannot. All\n // three are written beside it and applied by the CLI in their own right.\n const ddlContent = generatePostgresDdl(collections, {\n includePolicies: false,\n includeSearch: false,\n includeVector: false\n });\n const policiesContent = generatePostgresPoliciesDdl(collections);\n const searchContent = generatePostgresSearchDdl(collections);\n const vectorContent = generatePostgresVectorDdl(collections, {\n extensions: declaredDatabaseExtensions()\n });\n const triggersContent = generatePostgresTriggersDdl(collections);\n\n if (outputPath) {\n const outputDir = path.dirname(outputPath);\n await fsPromises.mkdir(outputDir, { recursive: true });\n await fsPromises.writeFile(outputPath, ddlContent);\n out(`✅ PostgreSQL DDL generated successfully at ${outputPath}`);\n\n const policiesPath = path.join(outputDir, \"policies.sql\");\n await fsPromises.writeFile(policiesPath, policiesContent);\n out(`✅ PostgreSQL Policies DDL generated successfully at ${policiesPath}`);\n\n // Removed when the last `search` block goes: the CLI applies this\n // file whenever it exists, and a stale one would keep re-creating\n // functions for collections that no longer want them.\n const searchPath = path.join(outputDir, \"search.sql\");\n if (searchContent) {\n await fsPromises.writeFile(searchPath, searchContent);\n out(`✅ PostgreSQL search DDL generated successfully at ${searchPath}`);\n } else if (fs.existsSync(searchPath)) {\n await fsPromises.rm(searchPath);\n }\n\n // Removed when the last `vector` property goes, for the same reason\n // `search.sql` is: the CLI applies whichever of these files exists,\n // and a stale one would keep re-adding a column the collections no\n // longer declare — which Atlas, no longer told to exclude it, would\n // then plan a DROP for on the very next push.\n const vectorPath = path.join(outputDir, \"vector.sql\");\n if (vectorContent) {\n await fsPromises.writeFile(vectorPath, vectorContent);\n out(`✅ PostgreSQL vector DDL generated successfully at ${vectorPath}`);\n } else if (fs.existsSync(vectorPath)) {\n await fsPromises.rm(vectorPath);\n }\n\n // `autoValue: \"on_update\"`, on the same terms as the two above: a\n // trigger is a function plus a binding, which Atlas's free tier\n // will not parse, and a stale file would keep re-installing a\n // trigger for a column nothing declares any more.\n const triggersPath = path.join(outputDir, \"triggers.sql\");\n if (triggersContent) {\n await fsPromises.writeFile(triggersPath, triggersContent);\n out(`✅ PostgreSQL trigger DDL generated successfully at ${triggersPath}`);\n } else if (fs.existsSync(triggersPath)) {\n await fsPromises.rm(triggersPath);\n }\n } else {\n out(\"✅ PostgreSQL DDL generated successfully.\");\n out(String(ddlContent));\n out(\"\\n✅ PostgreSQL Policies DDL generated successfully.\");\n out(String(policiesContent));\n }\n\n } catch (error) {\n outError(`Error generating DDL schema: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);\n // Step 1 of `rebase db push` is this process, and its exit code is the\n // only thing the push reads. Printing and exiting 0 made a resources\n // module that does not evaluate — the single most common cause here —\n // report `✓ rebase db push completed successfully` while Atlas applied\n // whatever `drizzle/schema.sql` happened to be on disk, sometimes\n // minutes old. With no prior file the same run died inside Atlas on\n // `stat drizzle/schema.sql: no such file or directory`, which hides the\n // real cause two screens up.\n process.exitCode = 1;\n throw error;\n }\n};\n\nexport const main = async () => {\n const collectionsFilePathArg = process.argv.find(arg => arg.startsWith(\"--collections=\"));\n const collectionsFilePath = collectionsFilePathArg ? collectionsFilePathArg.split(\"=\")[1] : process.argv[2];\n\n const outputPathArg = process.argv.find(arg => arg.startsWith(\"--output=\"));\n const outputPath = outputPathArg ? outputPathArg.split(\"=\")[1] : undefined;\n\n const watch = process.argv.includes(\"--watch\");\n\n if (!collectionsFilePath) {\n out(\"Usage: ts-node generate-postgres-ddl.ts <path-to-collections-file> [--output <path-to-output-file>] [--watch]\");\n return;\n }\n\n const resolvedPath = path.resolve(process.cwd(), collectionsFilePath);\n const resolvedOutputPath = outputPath ? path.resolve(process.cwd(), outputPath) : undefined;\n\n if (watch) {\n out(`Watching for changes in ${resolvedPath}...`);\n // Imported here rather than at module scope, and this is not a style\n // choice: chokidar is needed only by `--watch`, which is a\n // schema-authoring path that never runs inside the runtime image. A\n // top-level import puts it on the boot path of the published driver\n // bundle, and the image installs a hand-listed set of runtime\n // dependencies that does not include it — so the whole driver failed to\n // load with \"Cannot find package 'chokidar'\", and every self-hosted\n // container answered 500 with a stack trace about a file watcher.\n //\n // Same reasoning the image already applies to @ariga/atlas: an\n // authoring-only dependency does not belong on a boot path.\n const { default: chokidar } = await import(\"chokidar\");\n const watcher = chokidar.watch(resolvedPath, {\n persistent: true,\n ignoreInitial: false\n });\n\n watcher.on(\"all\", (event, filePath) => {\n out(`[${event}] ${filePath}. Regenerating DDL schema...`);\n // A watch session outlives a bad edit: the cause is already on\n // stderr, and the next save is the retry. Only the one-shot run\n // below turns a failure into an exit code.\n void runGeneration(resolvedPath, resolvedOutputPath).catch(() => undefined);\n });\n } else {\n await runGeneration(resolvedPath, resolvedOutputPath);\n }\n};\n\n// This check ensures the script only runs when executed directly\nif (import.meta.url.endsWith(process.argv[1])) {\n // `runGeneration` has already printed the cause and set the exit code; the\n // handler is here so the last thing on the terminal is that message rather\n // than Node's unhandled-rejection dump of the same stack.\n main().catch(() => {\n process.exitCode = 1;\n });\n}\n","/**\n * The built entry point for `generate-postgres-ddl`.\n *\n * `generate-postgres-ddl.ts` guards its own `main()` with\n * `import.meta.url.endsWith(process.argv[1])`, because `src/index.ts` re-exports\n * the module and importing the driver must not run the generator. That guard is\n * correct when the file IS the process entry — which is how it runs from source\n * under tsx — and silently false once the module is bundled: the executed file\n * is a stub, the code lives in a shared chunk, and the chunk's `import.meta.url`\n * is not the stub's path. The generator then did nothing, exited 0, and the\n * caller reported success over an unwritten schema.\n *\n * So the built path gets an entry that says what it means.\n */\nimport { main } from \"../generate-postgres-ddl\";\n\nmain().catch(() => {\n process.exitCode = 1;\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generate-postgres-ddl.js","names":[],"sources":["../../src/schema/generate-postgres-ddl.ts","../../src/schema/bin/generate-postgres-ddl.ts"],"sourcesContent":["import path from \"path\";\nimport { out } from \"../cli-output\";\nimport { writePostgresDdl } from \"./generate-files\";\n\n/**\n * The generator as its own process — what `--watch` runs, because a watch has\n * to re-import the collections on every save and Node's module cache would\n * serve it the first version forever. One-shot generation runs in the driver's\n * own process instead; see `generate-files.ts`.\n */\nconst runGeneration = async (collectionsFilePath?: string, outputPath?: string) => {\n if (!collectionsFilePath) {\n throw new Error(\"No collections file path provided.\");\n }\n try {\n await writePostgresDdl({ collectionsPath: path.resolve(collectionsFilePath), outputPath });\n } catch (error) {\n // A caller that reads nothing but this process's exit code must not\n // see 0 after an error was printed — see `writePostgresDdl`.\n process.exitCode = 1;\n throw error;\n }\n};\n\nexport const main = async () => {\n const collectionsFilePathArg = process.argv.find(arg => arg.startsWith(\"--collections=\"));\n const collectionsFilePath = collectionsFilePathArg ? collectionsFilePathArg.split(\"=\")[1] : process.argv[2];\n\n const outputPathArg = process.argv.find(arg => arg.startsWith(\"--output=\"));\n const outputPath = outputPathArg ? outputPathArg.split(\"=\")[1] : undefined;\n\n const watch = process.argv.includes(\"--watch\");\n\n if (!collectionsFilePath) {\n out(\"Usage: ts-node generate-postgres-ddl.ts <path-to-collections-file> [--output <path-to-output-file>] [--watch]\");\n return;\n }\n\n const resolvedPath = path.resolve(process.cwd(), collectionsFilePath);\n const resolvedOutputPath = outputPath ? path.resolve(process.cwd(), outputPath) : undefined;\n\n if (watch) {\n out(`Watching for changes in ${resolvedPath}...`);\n // Imported here rather than at module scope, and this is not a style\n // choice: chokidar is needed only by `--watch`, which is a\n // schema-authoring path that never runs inside the runtime image. A\n // top-level import puts it on the boot path of the published driver\n // bundle, and the image installs a hand-listed set of runtime\n // dependencies that does not include it — so the whole driver failed to\n // load with \"Cannot find package 'chokidar'\", and every self-hosted\n // container answered 500 with a stack trace about a file watcher.\n //\n // Same reasoning the image already applies to @ariga/atlas: an\n // authoring-only dependency does not belong on a boot path.\n const { default: chokidar } = await import(\"chokidar\");\n const watcher = chokidar.watch(resolvedPath, {\n persistent: true,\n ignoreInitial: false\n });\n\n watcher.on(\"all\", (event, filePath) => {\n out(`[${event}] ${filePath}. Regenerating DDL schema...`);\n // A watch session outlives a bad edit: the cause is already on\n // stderr, and the next save is the retry. Only the one-shot run\n // below turns a failure into an exit code.\n void runGeneration(resolvedPath, resolvedOutputPath).catch(() => undefined);\n });\n } else {\n await runGeneration(resolvedPath, resolvedOutputPath);\n }\n};\n\n// This check ensures the script only runs when executed directly\nif (import.meta.url.endsWith(process.argv[1])) {\n // `runGeneration` has already printed the cause and set the exit code; the\n // handler is here so the last thing on the terminal is that message rather\n // than Node's unhandled-rejection dump of the same stack.\n main().catch(() => {\n process.exitCode = 1;\n });\n}\n","/**\n * The built entry point for `generate-postgres-ddl`.\n *\n * `generate-postgres-ddl.ts` guards its own `main()` with\n * `import.meta.url.endsWith(process.argv[1])`, because `src/index.ts` re-exports\n * the module and importing the driver must not run the generator. That guard is\n * correct when the file IS the process entry — which is how it runs from source\n * under tsx — and silently false once the module is bundled: the executed file\n * is a stub, the code lives in a shared chunk, and the chunk's `import.meta.url`\n * is not the stub's path. The generator then did nothing, exited 0, and the\n * caller reported success over an unwritten schema.\n *\n * So the built path gets an entry that says what it means.\n */\nimport { main } from \"../generate-postgres-ddl\";\n\nmain().catch(() => {\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;;;;AAUA,IAAM,gBAAgB,OAAO,qBAA8B,eAAwB;CAC/E,IAAI,CAAC,qBACD,MAAM,IAAI,MAAM,oCAAoC;CAExD,IAAI;EACA,MAAM,iBAAiB;GAAE,iBAAiB,KAAK,QAAQ,mBAAmB;GAAG;EAAW,CAAC;CAC7F,SAAS,OAAO;EAGZ,QAAQ,WAAW;EACnB,MAAM;CACV;AACJ;AAEA,IAAa,OAAO,YAAY;CAC5B,MAAM,yBAAyB,QAAQ,KAAK,MAAK,QAAO,IAAI,WAAW,gBAAgB,CAAC;CACxF,MAAM,sBAAsB,yBAAyB,uBAAuB,MAAM,GAAG,CAAC,CAAC,KAAK,QAAQ,KAAK;CAEzG,MAAM,gBAAgB,QAAQ,KAAK,MAAK,QAAO,IAAI,WAAW,WAAW,CAAC;CAC1E,MAAM,aAAa,gBAAgB,cAAc,MAAM,GAAG,CAAC,CAAC,KAAK,KAAA;CAEjE,MAAM,QAAQ,QAAQ,KAAK,SAAS,SAAS;CAE7C,IAAI,CAAC,qBAAqB;EACtB,IAAI,+GAA+G;EACnH;CACJ;CAEA,MAAM,eAAe,KAAK,QAAQ,QAAQ,IAAI,GAAG,mBAAmB;CACpE,MAAM,qBAAqB,aAAa,KAAK,QAAQ,QAAQ,IAAI,GAAG,UAAU,IAAI,KAAA;CAElF,IAAI,OAAO;EACP,IAAI,2BAA2B,aAAa,IAAI;EAYhD,MAAM,EAAE,SAAS,aAAa,MAAM,OAAO;EAM3C,SALyB,MAAM,cAAc;GACzC,YAAY;GACZ,eAAe;EACnB,CAEA,CAAA,CAAQ,GAAG,QAAQ,OAAO,aAAa;GACnC,IAAI,IAAI,MAAM,IAAI,SAAS,6BAA6B;GAIxD,cAAmB,cAAc,kBAAkB,CAAC,CAAC,YAAY,KAAA,CAAS;EAC9E,CAAC;CACL,OACI,MAAM,cAAc,cAAc,kBAAkB;AAE5D;AAGA,IAAI,OAAO,KAAK,IAAI,SAAS,QAAQ,KAAK,EAAE,GAIxC,KAAK,CAAC,CAAC,YAAY;CACf,QAAQ,WAAW;AACvB,CAAC;;;;;;;;;;;;;;;;;AC/DL,KAAK,CAAC,CAAC,YAAY;CACf,QAAQ,WAAW;AACvB,CAAC"}
|
|
@@ -72,6 +72,12 @@ export interface SchemaCommitInput {
|
|
|
72
72
|
* a statement that applies and one that is rejected.
|
|
73
73
|
*/
|
|
74
74
|
existing?: ExistingSchema;
|
|
75
|
+
/**
|
|
76
|
+
* Plan a commit of the source alone — "Edit source only". An unapplicable
|
|
77
|
+
* change is not refused: it is committed with no statements, under a
|
|
78
|
+
* message that names what the database keeps.
|
|
79
|
+
*/
|
|
80
|
+
sourceOnly?: boolean;
|
|
75
81
|
}
|
|
76
82
|
export interface SchemaCommit {
|
|
77
83
|
/** Every file the commit writes, source and generated alike. */
|
|
@@ -123,6 +129,12 @@ export declare class SchemaCommitError extends Error {
|
|
|
123
129
|
* where there is no database to describe.
|
|
124
130
|
*/
|
|
125
131
|
export declare function additiveStatements(before: CollectionConfig[], after: CollectionConfig[], existing?: ExistingSchema, options?: EnsureOptions): string[];
|
|
132
|
+
/**
|
|
133
|
+
* The commit of an "Edit source only": what changed in the source, and what
|
|
134
|
+
* the database keeps — `chore(schema): remove sku from products (source only —
|
|
135
|
+
* column products.sku kept)`. The body lists what each change leaves behind.
|
|
136
|
+
*/
|
|
137
|
+
export declare function sourceOnlyCommitMessage(classified: ClassifiedChanges): string;
|
|
126
138
|
/** A commit message that says what changed rather than that something did. */
|
|
127
139
|
export declare function commitMessage(classified: ClassifiedChanges): string;
|
|
128
140
|
/**
|