@rebasepro/server-postgres 0.17.3 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -1
- package/README.md +31 -0
- package/dist/BranchService-ByLfM_Tt.js +360 -0
- package/dist/BranchService-ByLfM_Tt.js.map +1 -0
- package/dist/__vite-browser-external-BnuHet1e.js +46 -0
- package/dist/__vite-browser-external-BnuHet1e.js.map +1 -0
- package/dist/arg-Dni7MzLB.js +87 -0
- package/dist/arg-Dni7MzLB.js.map +1 -0
- package/dist/auth-users-columns-D2LBFrMH.js +116 -0
- package/dist/auth-users-columns-D2LBFrMH.js.map +1 -0
- package/dist/backup-argv.d.ts +19 -0
- package/dist/backup-cli-oM9WQVlV.js +408 -0
- package/dist/backup-cli-oM9WQVlV.js.map +1 -0
- package/dist/backup-service-Br3tqwsy.js +715 -0
- package/dist/backup-service-Br3tqwsy.js.map +1 -0
- package/dist/branch-argv.d.ts +28 -0
- package/dist/branch-prune.d.ts +77 -0
- package/dist/cli-collections-path.d.ts +31 -0
- package/dist/cli-errors-DxN4yzdH.js +657 -0
- package/dist/cli-errors-DxN4yzdH.js.map +1 -0
- package/dist/cli-errors.d.ts +123 -0
- package/dist/cli-flags.d.ts +101 -0
- package/dist/cli-helpers.d.ts +94 -10
- package/dist/cli-output-CNdMql-L.js +47 -0
- package/dist/cli-output-CNdMql-L.js.map +1 -0
- package/dist/cli.js +2020 -0
- package/dist/cli.js.map +1 -0
- package/dist/collection-index-BHgT_TKd.js +276 -0
- package/dist/collection-index-BHgT_TKd.js.map +1 -0
- package/dist/{collections-schema-version-BMeu3cgv.js → collections-schema-version-T6cGvrDi.js} +1 -2
- package/dist/{collections-schema-version-BMeu3cgv.js.map → collections-schema-version-T6cGvrDi.js.map} +1 -1
- package/dist/{connection-GOKU3Hu5.js → databasePoolManager-Bj5FbeAs.js} +112 -40
- package/dist/databasePoolManager-Bj5FbeAs.js.map +1 -0
- package/dist/databasePoolManager.d.ts +27 -0
- package/dist/doctor-D7HWmGzs.js +974 -0
- package/dist/doctor-D7HWmGzs.js.map +1 -0
- package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-2c2NV8zX.js} +7 -27
- package/dist/ensure-collection-policies-2c2NV8zX.js.map +1 -0
- package/dist/ensure-collection-tables-D4wJzM4y.js +820 -0
- package/dist/ensure-collection-tables-D4wJzM4y.js.map +1 -0
- package/dist/ensure-tables-CIojK-Rx.js +650 -0
- package/dist/ensure-tables-CIojK-Rx.js.map +1 -0
- package/dist/generate-drizzle-schema-CwEgVyjO.js +123 -0
- package/dist/generate-drizzle-schema-CwEgVyjO.js.map +1 -0
- package/dist/generate-drizzle-schema-logic-so2Z_lyC.js +528 -0
- package/dist/generate-drizzle-schema-logic-so2Z_lyC.js.map +1 -0
- package/dist/{ensure-collection-tables-DgVixhX3.js → generate-postgres-ddl-logic-CtnpIBVv.js} +126 -984
- package/dist/generate-postgres-ddl-logic-CtnpIBVv.js.map +1 -0
- package/dist/generated-schema-staleness-DQdfJl2A.js +157 -0
- package/dist/generated-schema-staleness-DQdfJl2A.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.es.js +7423 -1133
- package/dist/index.es.js.map +1 -1
- package/dist/introspect-db-logic-C6LQdTxj.js +1378 -0
- package/dist/introspect-db-logic-C6LQdTxj.js.map +1 -0
- package/dist/policy-drift-kNJ6X2Uj.js +313 -0
- package/dist/policy-drift-kNJ6X2Uj.js.map +1 -0
- package/dist/{backup-service-DCk7KhhL.js → rls-bootstrap-sql-DLDnUjtx.js} +737 -2689
- package/dist/rls-bootstrap-sql-DLDnUjtx.js.map +1 -0
- package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-DeYEjYLt.js} +65 -190
- package/dist/rls-enforcement-DeYEjYLt.js.map +1 -0
- package/dist/schema/bin/generate-drizzle-schema.d.ts +1 -0
- package/dist/schema/bin/generate-postgres-ddl.d.ts +1 -0
- package/dist/schema/column-type-drift.d.ts +79 -0
- package/dist/schema/doctor-cli.js +129 -0
- package/dist/schema/doctor-cli.js.map +1 -0
- package/dist/schema/doctor.d.ts +42 -0
- package/dist/schema/ensure-collection-tables.d.ts +90 -0
- package/dist/schema/generate-drizzle-schema.d.ts +1 -1
- package/dist/schema/generate-drizzle-schema.js +24 -0
- package/dist/schema/generate-drizzle-schema.js.map +1 -0
- package/dist/schema/generate-next-step.d.ts +30 -0
- package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -0
- package/dist/schema/generate-postgres-ddl.d.ts +1 -1
- package/dist/schema/generate-postgres-ddl.js +138 -0
- package/dist/schema/generate-postgres-ddl.js.map +1 -0
- package/dist/schema/generated-schema-staleness.d.ts +37 -0
- package/dist/schema/introspect-db-queries.d.ts +34 -0
- package/dist/schema/introspect-db.js +866 -0
- package/dist/schema/introspect-db.js.map +1 -0
- package/dist/security/rls-enforcement.d.ts +63 -2
- package/dist/services/BranchService.d.ts +86 -1
- package/dist/services/collection-helpers.d.ts +1 -1
- package/dist/services/realtimeService.d.ts +30 -2
- package/dist/services/row-pipeline.d.ts +12 -1
- package/dist/source-Br7L7GOI.js +410 -0
- package/dist/source-Br7L7GOI.js.map +1 -0
- package/dist/utils/pg-error-utils.d.ts +3 -1
- package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
- package/dist/vector-index-DkWxm2de.js +309 -0
- package/dist/vector-index-DkWxm2de.js.map +1 -0
- package/package.json +32 -27
- package/dist/backup-service-DCk7KhhL.js.map +0 -1
- package/dist/collection-index-BRUg10H5.js +0 -5690
- package/dist/collection-index-BRUg10H5.js.map +0 -1
- package/dist/connection-GOKU3Hu5.js.map +0 -1
- package/dist/ensure-collection-policies-UCqgv_8c.js.map +0 -1
- package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
- package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
- package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
- package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
- package/dist/src-DiB5RP2Z.js +0 -541
- package/dist/src-DiB5RP2Z.js.map +0 -1
- package/dist/websocket-BZ4H5wUz.js +0 -8198
- package/dist/websocket-BZ4H5wUz.js.map +0 -1
- package/src/PostgresAdapter.ts +0 -91
- package/src/PostgresBackendDriver.ts +0 -1857
- package/src/PostgresBootstrapper.ts +0 -1157
- package/src/auth/ensure-tables.ts +0 -896
- package/src/auth/schema-version.ts +0 -260
- package/src/auth/services.ts +0 -1610
- package/src/backup/backup-cli.ts +0 -520
- package/src/backup/backup-cron.ts +0 -212
- package/src/backup/backup-logic.ts +0 -93
- package/src/backup/backup-service.ts +0 -485
- package/src/backup/index.ts +0 -12
- package/src/backup/pg-tools.ts +0 -403
- package/src/backup/retention.ts +0 -86
- package/src/cli-errors.ts +0 -265
- package/src/cli-helpers.ts +0 -569
- package/src/cli-output.ts +0 -43
- package/src/cli.ts +0 -1339
- package/src/collections/PostgresCollectionRegistry.ts +0 -108
- package/src/collections/buildRegistry.ts +0 -68
- package/src/collections/validate-relations.ts +0 -402
- package/src/connection.ts +0 -333
- package/src/data-transformer.ts +0 -845
- package/src/databasePoolManager.ts +0 -95
- package/src/history/HistoryService.ts +0 -254
- package/src/history/ensure-history-table.ts +0 -53
- package/src/index.ts +0 -19
- package/src/interfaces.ts +0 -60
- package/src/module-dir.ts +0 -7
- package/src/schema/atlas-argv.ts +0 -94
- package/src/schema/auth-schema.ts +0 -298
- package/src/schema/auth-users-columns.ts +0 -131
- package/src/schema/carved-out-migration.ts +0 -404
- package/src/schema/classify-change.ts +0 -436
- package/src/schema/collection-index.ts +0 -427
- package/src/schema/collections-schema-version.ts +0 -103
- package/src/schema/destructive-sql.ts +0 -94
- package/src/schema/doctor-cli.ts +0 -66
- package/src/schema/doctor-policy-checks.ts +0 -105
- package/src/schema/doctor.ts +0 -837
- package/src/schema/drizzle-ddl.ts +0 -33
- package/src/schema/dynamic-tables.test.ts +0 -302
- package/src/schema/dynamic-tables.ts +0 -293
- package/src/schema/ensure-collection-policies.ts +0 -198
- package/src/schema/ensure-collection-tables.test.ts +0 -518
- package/src/schema/ensure-collection-tables.ts +0 -1267
- package/src/schema/generate-drizzle-schema-logic.ts +0 -957
- package/src/schema/generate-drizzle-schema.ts +0 -143
- package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
- package/src/schema/generate-postgres-ddl.ts +0 -180
- package/src/schema/generate-schema-commit.ts +0 -267
- package/src/schema/generated-schema-staleness.ts +0 -284
- package/src/schema/introspect-db-constraints.ts +0 -385
- package/src/schema/introspect-db-inference.ts +0 -255
- package/src/schema/introspect-db-logic.ts +0 -1387
- package/src/schema/introspect-db-naming.ts +0 -15
- package/src/schema/introspect-db-project.ts +0 -78
- package/src/schema/introspect-db-queries.ts +0 -326
- package/src/schema/introspect-db-structure.ts +0 -670
- package/src/schema/introspect-db-types.ts +0 -56
- package/src/schema/introspect-db.ts +0 -257
- package/src/schema/introspect-runtime.test.ts +0 -260
- package/src/schema/introspect-runtime.ts +0 -327
- package/src/schema/non-sql-collections.test.ts +0 -131
- package/src/schema/rls-bootstrap-sql.ts +0 -288
- package/src/schema/search-column.ts +0 -643
- package/src/schema/test-schema.ts +0 -11
- package/src/schema/vector-index.ts +0 -491
- package/src/security/anonymous-grants.test.ts +0 -73
- package/src/security/policy-drift.test.ts +0 -435
- package/src/security/policy-drift.ts +0 -473
- package/src/security/rls-enforcement.ts +0 -505
- package/src/services/BranchService.ts +0 -326
- package/src/services/FetchService.ts +0 -2115
- package/src/services/PersistService.ts +0 -557
- package/src/services/RelationService.ts +0 -978
- package/src/services/RelationWriteService.ts +0 -653
- package/src/services/cdc/CdcListener.ts +0 -103
- package/src/services/cdc/junction-tables.ts +0 -91
- package/src/services/cdc/trigger-cdc.ts +0 -173
- package/src/services/channel-bus/ChannelBus.ts +0 -44
- package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
- package/src/services/channel-bus/index.ts +0 -114
- package/src/services/channel-history.ts +0 -424
- package/src/services/channel-presence.ts +0 -185
- package/src/services/collection-helpers.ts +0 -420
- package/src/services/dataService.ts +0 -256
- package/src/services/index.ts +0 -14
- package/src/services/junction-writes.ts +0 -295
- package/src/services/nested-path.ts +0 -145
- package/src/services/pg-notify-listener.ts +0 -151
- package/src/services/realtimeService.ts +0 -2545
- package/src/services/row-pipeline.ts +0 -240
- package/src/services/write-denial.ts +0 -55
- package/src/types.ts +0 -4
- package/src/utils/connection-string.ts +0 -58
- package/src/utils/drizzle-conditions.ts +0 -2743
- package/src/utils/pg-array-null-patch.ts +0 -42
- package/src/utils/pg-error-utils.ts +0 -391
- package/src/websocket.ts +0 -855
package/dist/schema/doctor.d.ts
CHANGED
|
@@ -41,6 +41,17 @@ export interface DoctorPhase {
|
|
|
41
41
|
* a comparison, and no comparison happened.
|
|
42
42
|
*/
|
|
43
43
|
notApplicable?: string;
|
|
44
|
+
/**
|
|
45
|
+
* The phase was skipped by a fault rather than by a choice.
|
|
46
|
+
*
|
|
47
|
+
* `skipped` covers both: "no DATABASE_URL, so nothing to compare against"
|
|
48
|
+
* is a local situation a developer may be perfectly happy with, and "the
|
|
49
|
+
* DATABASE_URL you set refuses connections" is not. The report renders them
|
|
50
|
+
* the same way — a check that did not run is a check that did not run — but
|
|
51
|
+
* the exit code must not, or `rebase doctor` in CI goes green against a
|
|
52
|
+
* database it never reached.
|
|
53
|
+
*/
|
|
54
|
+
blocked?: boolean;
|
|
44
55
|
}
|
|
45
56
|
export interface DoctorReport {
|
|
46
57
|
collectionsToSchema: DoctorPhase;
|
|
@@ -64,6 +75,37 @@ export declare function getExpectedColumnType(prop: Property): string | null;
|
|
|
64
75
|
export declare function loadCollections(collectionsPath: string): Promise<CollectionConfig[]>;
|
|
65
76
|
export declare function checkCollectionsVsSchema(collections: CollectionConfig[], schemaFilePath: string): Promise<DoctorPhase>;
|
|
66
77
|
export declare function checkCollectionsVsSdk(collections: CollectionConfig[], sdkFilePath: string): Promise<DoctorPhase>;
|
|
78
|
+
/**
|
|
79
|
+
* Extensions the collections need, that the database does not have installed.
|
|
80
|
+
*
|
|
81
|
+
* Only `vector` today, and only because a `{ type: "vector" }` property plans a
|
|
82
|
+
* column of a type that will not exist. Rebase installs it only where a project
|
|
83
|
+
* has said it may (`database({ extensions: ["vector"] })`), so the absence is
|
|
84
|
+
* often correct configuration and a wrong deployment — which is exactly why the
|
|
85
|
+
* report has to name it rather than the first failing INSERT.
|
|
86
|
+
*
|
|
87
|
+
* Exported for its test.
|
|
88
|
+
*/
|
|
89
|
+
export declare function missingExtensionIssues(collections: CollectionConfig[], installed: ReadonlySet<string>): DoctorIssue[];
|
|
90
|
+
/**
|
|
91
|
+
* The stamp the runtime wrote, against the collections on disk.
|
|
92
|
+
*
|
|
93
|
+
* A stamp that disagrees means this database was provisioned from a different
|
|
94
|
+
* set of collections than the ones in this checkout — a colleague's branch, an
|
|
95
|
+
* older deploy, a tenant somebody else migrated. Everything else in this report
|
|
96
|
+
* is then describing a schema somebody else created, which is worth knowing
|
|
97
|
+
* before acting on any of it.
|
|
98
|
+
*
|
|
99
|
+
* A warning, not an error, and never a direction: the stamp is a hash, so it
|
|
100
|
+
* can say the two disagree and never which is ahead. `null` — never stamped —
|
|
101
|
+
* is not a finding: every database provisioned before the stamp existed reads
|
|
102
|
+
* that way, and so does every fresh one.
|
|
103
|
+
*/
|
|
104
|
+
export declare function schemaStampIssues(pool: {
|
|
105
|
+
query<T>(text: string): Promise<{
|
|
106
|
+
rows: T[];
|
|
107
|
+
}>;
|
|
108
|
+
}, collections: CollectionConfig[]): Promise<DoctorIssue[]>;
|
|
67
109
|
export declare function checkCollectionsVsDatabase(collections: CollectionConfig[], databaseUrl: string): Promise<DoctorPhase>;
|
|
68
110
|
export declare function renderReport(report: DoctorReport): void;
|
|
69
111
|
export declare function runDoctor(options: {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { type CollectionConfig } from "@rebasepro/types";
|
|
29
29
|
import { type SkippedVectorIndex } from "./vector-index.js";
|
|
30
|
+
import { type ColumnTypeDrift } from "./column-type-drift.js";
|
|
30
31
|
/**
|
|
31
32
|
* The subset of a database handle this needs: run a statement, get rows back.
|
|
32
33
|
*
|
|
@@ -75,6 +76,15 @@ export interface ExistingSchema {
|
|
|
75
76
|
enumValues?: Map<string, string[]>;
|
|
76
77
|
/** `schema.table.column` for every column the database marks NOT NULL. */
|
|
77
78
|
notNullColumns?: Set<string>;
|
|
79
|
+
/**
|
|
80
|
+
* `schema.table.column` → the column's `udt_name` as the database reports
|
|
81
|
+
* it (`int4`, `jsonb`, `_numeric` for an array of numeric).
|
|
82
|
+
*
|
|
83
|
+
* The only evidence that a column's declared type and its real type have
|
|
84
|
+
* parted company. Absent is read as "unknown", which reports no drift —
|
|
85
|
+
* see `column-type-drift.ts` for why silence is the right unknown here.
|
|
86
|
+
*/
|
|
87
|
+
columnTypes?: Map<string, string>;
|
|
78
88
|
/**
|
|
79
89
|
* Tables known to hold at least one row.
|
|
80
90
|
*
|
|
@@ -85,6 +95,14 @@ export interface ExistingSchema {
|
|
|
85
95
|
* constraint rather than attempting one that aborts the boot.
|
|
86
96
|
*/
|
|
87
97
|
populatedTables?: Set<string>;
|
|
98
|
+
/**
|
|
99
|
+
* `schema.table.column` for every column the database gives a DEFAULT.
|
|
100
|
+
*
|
|
101
|
+
* Paired with {@link notNullColumns} to decide whether a column no property
|
|
102
|
+
* declares can still accept a write: NOT NULL with a default can, NOT NULL
|
|
103
|
+
* without one cannot. Absent is read as "unknown", which reports nothing.
|
|
104
|
+
*/
|
|
105
|
+
columnDefaults?: Set<string>;
|
|
88
106
|
}
|
|
89
107
|
/**
|
|
90
108
|
* How far the planner may go in making the database's constraints match the
|
|
@@ -179,6 +197,52 @@ export interface EnsurePlan {
|
|
|
179
197
|
* to read.
|
|
180
198
|
*/
|
|
181
199
|
withheldConstraints: WithheldConstraint[];
|
|
200
|
+
/**
|
|
201
|
+
* Columns whose type in the database is in a different family from the one
|
|
202
|
+
* the collection declares.
|
|
203
|
+
*
|
|
204
|
+
* Reported and never acted on — see `column-type-drift.ts`. This is the one
|
|
205
|
+
* divergence that presents as a *working* deploy: the statement was never
|
|
206
|
+
* planned, so nothing failed, and the first evidence is a write rejected
|
|
207
|
+
* hours later by whichever end validates first.
|
|
208
|
+
*/
|
|
209
|
+
columnTypeDrift: ColumnTypeDrift[];
|
|
210
|
+
/**
|
|
211
|
+
* Columns that are NOT NULL, have no default, and that no property declares
|
|
212
|
+
* any more — so every insert through the API is rejected for a field the
|
|
213
|
+
* author cannot name, because they already deleted it.
|
|
214
|
+
*
|
|
215
|
+
* This is what a **rename** looks like to an additive provisioner. Rename
|
|
216
|
+
* `title` to `headline` and boot does exactly what it promises: it adds
|
|
217
|
+
* `headline` (nullable, correctly, and says why) and leaves `title` alone,
|
|
218
|
+
* because dropping a column is destructive and this runs unattended. The
|
|
219
|
+
* table is then unwritable:
|
|
220
|
+
*
|
|
221
|
+
* POST /api/data/posts -> 400 PG_23502
|
|
222
|
+
* Missing required field: "title" in "posts" cannot be empty.
|
|
223
|
+
*
|
|
224
|
+
* naming a field that is no longer in the collection at all. On the default
|
|
225
|
+
* first-run path there is no way out inside the product either: the managed
|
|
226
|
+
* database is PGlite, and `db push` — the documented repair — refuses there
|
|
227
|
+
* because Atlas needs a second database to diff against.
|
|
228
|
+
*
|
|
229
|
+
* Reported, never acted on. Dropping the column is the right fix roughly
|
|
230
|
+
* always and destructive exactly once, which is not a decision to take
|
|
231
|
+
* unattended.
|
|
232
|
+
*/
|
|
233
|
+
orphanedRequiredColumns: OrphanedRequiredColumn[];
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* A NOT NULL column with no default that no property declares.
|
|
237
|
+
* @see EnsurePlan.orphanedRequiredColumns
|
|
238
|
+
*/
|
|
239
|
+
export interface OrphanedRequiredColumn {
|
|
240
|
+
/** `schema.table`. */
|
|
241
|
+
table: string;
|
|
242
|
+
/** The column left behind. */
|
|
243
|
+
column: string;
|
|
244
|
+
/** The collection that no longer declares it. */
|
|
245
|
+
slug: string;
|
|
182
246
|
}
|
|
183
247
|
/** A constraint the configuration asks for that the planner is not applying. */
|
|
184
248
|
export interface WithheldConstraint {
|
|
@@ -276,3 +340,29 @@ export declare function readSchemaFactsFor(client: Queryable, collections: Colle
|
|
|
276
340
|
* The error is surfaced with the statement that caused it.
|
|
277
341
|
*/
|
|
278
342
|
export declare function ensureCollectionTables(client: Queryable, collections: CollectionConfig[], log?: (message: string) => void, options?: EnsureOptions): Promise<EnsureOutcome>;
|
|
343
|
+
/**
|
|
344
|
+
* What actually went wrong, rather than what the ORM said about it.
|
|
345
|
+
*
|
|
346
|
+
* Drizzle wraps every driver error, and its `message` is literally
|
|
347
|
+
* ``Failed query: ${sql}\nparams: ${params}`` — the statement we already know,
|
|
348
|
+
* and not one word of Postgres's answer. Reporting `err.message` therefore
|
|
349
|
+
* produced a failure that named the statement twice and the *reason* zero
|
|
350
|
+
* times, so four unrelated boot failures — a missing extension, a wrong
|
|
351
|
+
* privilege, a bad index method, a constraint the rows violate — all read
|
|
352
|
+
* identically to whoever had to fix them.
|
|
353
|
+
*
|
|
354
|
+
* Two consequences, both observed in the field before this existed:
|
|
355
|
+
*
|
|
356
|
+
* 1. On a managed deployment the reason never reaches anybody. Boot dies, the
|
|
357
|
+
* pod never goes ready, and the deploy reports a readiness timeout — so the
|
|
358
|
+
* one line that says why is the one line that was thrown away here.
|
|
359
|
+
* 2. {@link vectorExtensionHint} matches on the error text. Against the
|
|
360
|
+
* wrapper it matched nothing, so the hint written specifically to explain a
|
|
361
|
+
* missing pgvector never fired on the path that raises it.
|
|
362
|
+
*
|
|
363
|
+
* `detail` and `hint` come along because they are the fields Postgres uses to
|
|
364
|
+
* say which object it meant and what to do about it — `CREATE EXTENSION` names
|
|
365
|
+
* the missing library there, and a rejected constraint names the row. This
|
|
366
|
+
* writes into the tenant's own boot log, about the tenant's own database.
|
|
367
|
+
*/
|
|
368
|
+
export declare function describeDriverError(err: unknown): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare const main: () => Promise<void>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "module";
|
|
2
|
+
__createRequire(import.meta.url);
|
|
3
|
+
import { t as main } from "../generate-drizzle-schema-CwEgVyjO.js";
|
|
4
|
+
//#region src/schema/bin/generate-drizzle-schema.ts
|
|
5
|
+
/**
|
|
6
|
+
* The built entry point for `generate-drizzle-schema`.
|
|
7
|
+
*
|
|
8
|
+
* `generate-drizzle-schema.ts` guards its own `main()` with
|
|
9
|
+
* `import.meta.url.endsWith(process.argv[1])`, because `src/index.ts` re-exports
|
|
10
|
+
* the module and importing the driver must not run the generator. That guard is
|
|
11
|
+
* correct when the file IS the process entry — which is how it runs from source
|
|
12
|
+
* under tsx — and silently false once the module is bundled: the executed file
|
|
13
|
+
* is a stub, the code lives in a shared chunk, and the chunk's `import.meta.url`
|
|
14
|
+
* is not the stub's path. The generator then did nothing, exited 0, and the
|
|
15
|
+
* caller reported success over an unwritten schema.
|
|
16
|
+
*
|
|
17
|
+
* So the built path gets an entry that says what it means.
|
|
18
|
+
*/
|
|
19
|
+
main().catch(() => {
|
|
20
|
+
process.exitCode = 1;
|
|
21
|
+
});
|
|
22
|
+
//#endregion
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=generate-drizzle-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-drizzle-schema.js","names":[],"sources":["../../src/schema/bin/generate-drizzle-schema.ts"],"sourcesContent":["/**\n * The built entry point for `generate-drizzle-schema`.\n *\n * `generate-drizzle-schema.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-drizzle-schema\";\n\nmain().catch(() => {\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgBA,KAAK,CAAC,CAAC,YAAY;CACf,QAAQ,WAAW;AACvB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
/**
|
|
15
|
+
* What to do with the file that was just generated.
|
|
16
|
+
*
|
|
17
|
+
* Not the same answer on the managed development database: `rebase db generate`
|
|
18
|
+
* plans with Atlas, Atlas needs a second empty database to diff against, and
|
|
19
|
+
* PGlite serves exactly one — so the CLI refuses that command outright there.
|
|
20
|
+
* This line recommended it unconditionally, which meant every stock scaffold
|
|
21
|
+
* was told, by the tool itself, to run something the same tool would refuse.
|
|
22
|
+
*
|
|
23
|
+
* The kind arrives as `REBASE_DEV_DATABASE_KIND`, set by the CLI from the
|
|
24
|
+
* ordered resolution in `dev-db/resolve.ts`. It is a pure decision — nothing is
|
|
25
|
+
* started to make it — and the driver cannot make it for itself: on the managed
|
|
26
|
+
* path `DATABASE_URL` is a perfectly ordinary connection string to a Postgres
|
|
27
|
+
* on loopback. Absent (the driver run directly, outside the CLI) keeps the
|
|
28
|
+
* original wording.
|
|
29
|
+
*/
|
|
30
|
+
export declare const nextStepAfterGenerate: () => string;
|
|
@@ -9,6 +9,34 @@ export declare const isNumericId: (collection: CollectionConfig) => boolean;
|
|
|
9
9
|
export declare const getPrimaryKeyName: (collection: CollectionConfig) => string;
|
|
10
10
|
export declare const isIdProperty: (propName: string, prop: Property, collection: CollectionConfig) => boolean;
|
|
11
11
|
type ResolveCollection = (slug: string) => CollectionConfig | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* The `ON DELETE` a foreign key gets when the author did not say.
|
|
14
|
+
*
|
|
15
|
+
* One rule for every property that emits one — `belongsTo` relations and
|
|
16
|
+
* `reference` properties alike. `reference` kept its own `CASCADE` default for
|
|
17
|
+
* a release after this rule was written, which is exactly the split this
|
|
18
|
+
* function exists to prevent: two spellings of the same link, two data-retention
|
|
19
|
+
* behaviours, and only one of them documented.
|
|
20
|
+
*
|
|
21
|
+
* An optional link is `SET NULL`: the column can hold NULL, so dropping the
|
|
22
|
+
* parent leaves the child row with an empty pointer, which is what "optional"
|
|
23
|
+
* already means.
|
|
24
|
+
*
|
|
25
|
+
* A required link is **`RESTRICT`**, not `CASCADE`. `NOT NULL` says the child
|
|
26
|
+
* cannot exist without a parent; it does not say deleting the parent should
|
|
27
|
+
* take the child with it. That second claim is a data-retention decision, and
|
|
28
|
+
* defaulting to it meant `onDelete` — a field nobody has to write — silently
|
|
29
|
+
* turned every `DELETE FROM authors` into a cascade through posts, comments and
|
|
30
|
+
* anything else that hung off them. `RESTRICT` fails the delete instead and
|
|
31
|
+
* names the constraint, so the author chooses `cascade` on purpose or clears
|
|
32
|
+
* the children first.
|
|
33
|
+
*
|
|
34
|
+
* Both DDL paths — the `CREATE TABLE` generator and {@link planRelationalColumns},
|
|
35
|
+
* which boot-ensure compares the live database against — call this, so a
|
|
36
|
+
* default that appears in one and not the other cannot make `db push` plan a
|
|
37
|
+
* constraint rewrite on every run.
|
|
38
|
+
*/
|
|
39
|
+
export declare const defaultBelongsToOnDelete: (required: boolean | undefined) => "RESTRICT" | "SET NULL";
|
|
12
40
|
/**
|
|
13
41
|
* The individual SQL statements a single security rule compiles to: a
|
|
14
42
|
* `DROP POLICY IF EXISTS` / `CREATE POLICY` pair per operation, each a complete
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare const main: () => Promise<void>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "module";
|
|
2
|
+
__createRequire(import.meta.url);
|
|
3
|
+
import { a as generatePostgresVectorDdl, i as generatePostgresSearchDdl, n as generatePostgresDdl, r as generatePostgresPoliciesDdl } from "../generate-postgres-ddl-logic-CtnpIBVv.js";
|
|
4
|
+
import { n as outError, t as out } from "../cli-output-CNdMql-L.js";
|
|
5
|
+
import { loadCollectionsFromDirectory } from "@rebasepro/server";
|
|
6
|
+
import { declaredDatabaseExtensions } from "@rebasepro/types";
|
|
7
|
+
import * as fs$1 from "fs";
|
|
8
|
+
import { promises } from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { pathToFileURL } from "url";
|
|
11
|
+
//#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
|
+
/**
|
|
20
|
+
* Evaluate the project's `resources.ts`, so `declaredDatabaseExtensions()` can
|
|
21
|
+
* answer.
|
|
22
|
+
*
|
|
23
|
+
* This generator runs as its own tsx subprocess and loads only the collections
|
|
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.
|
|
38
|
+
*/
|
|
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
|
+
var runGeneration = async (collectionsFilePath, outputPath) => {
|
|
45
|
+
try {
|
|
46
|
+
if (!collectionsFilePath) throw new Error("No collections file path provided.");
|
|
47
|
+
const resolvedPath = path.resolve(collectionsFilePath);
|
|
48
|
+
await loadDeclaredResources(path.dirname(resolvedPath));
|
|
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 = await generatePostgresDdl(collections, {
|
|
53
|
+
includePolicies: false,
|
|
54
|
+
includeSearch: false,
|
|
55
|
+
includeVector: false
|
|
56
|
+
});
|
|
57
|
+
const policiesContent = generatePostgresPoliciesDdl(collections);
|
|
58
|
+
const searchContent = generatePostgresSearchDdl(collections);
|
|
59
|
+
const vectorContent = generatePostgresVectorDdl(collections, { extensions: declaredDatabaseExtensions() });
|
|
60
|
+
if (outputPath) {
|
|
61
|
+
const outputDir = path.dirname(outputPath);
|
|
62
|
+
await promises.mkdir(outputDir, { recursive: true });
|
|
63
|
+
await promises.writeFile(outputPath, ddlContent);
|
|
64
|
+
out(`✅ PostgreSQL DDL generated successfully at ${outputPath}`);
|
|
65
|
+
const policiesPath = path.join(outputDir, "policies.sql");
|
|
66
|
+
await promises.writeFile(policiesPath, policiesContent);
|
|
67
|
+
out(`✅ PostgreSQL Policies DDL generated successfully at ${policiesPath}`);
|
|
68
|
+
const searchPath = path.join(outputDir, "search.sql");
|
|
69
|
+
if (searchContent) {
|
|
70
|
+
await promises.writeFile(searchPath, searchContent);
|
|
71
|
+
out(`✅ PostgreSQL search DDL generated successfully at ${searchPath}`);
|
|
72
|
+
} else if (fs$1.existsSync(searchPath)) await promises.rm(searchPath);
|
|
73
|
+
const vectorPath = path.join(outputDir, "vector.sql");
|
|
74
|
+
if (vectorContent) {
|
|
75
|
+
await promises.writeFile(vectorPath, vectorContent);
|
|
76
|
+
out(`✅ PostgreSQL vector DDL generated successfully at ${vectorPath}`);
|
|
77
|
+
} else if (fs$1.existsSync(vectorPath)) await promises.rm(vectorPath);
|
|
78
|
+
} else {
|
|
79
|
+
out("✅ PostgreSQL DDL generated successfully.");
|
|
80
|
+
out(String(ddlContent));
|
|
81
|
+
out("\n✅ PostgreSQL Policies DDL generated successfully.");
|
|
82
|
+
out(String(policiesContent));
|
|
83
|
+
}
|
|
84
|
+
} catch (error) {
|
|
85
|
+
outError(`Error generating DDL schema: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var main = async () => {
|
|
91
|
+
const collectionsFilePathArg = process.argv.find((arg) => arg.startsWith("--collections="));
|
|
92
|
+
const collectionsFilePath = collectionsFilePathArg ? collectionsFilePathArg.split("=")[1] : process.argv[2];
|
|
93
|
+
const outputPathArg = process.argv.find((arg) => arg.startsWith("--output="));
|
|
94
|
+
const outputPath = outputPathArg ? outputPathArg.split("=")[1] : void 0;
|
|
95
|
+
const watch = process.argv.includes("--watch");
|
|
96
|
+
if (!collectionsFilePath) {
|
|
97
|
+
out("Usage: ts-node generate-postgres-ddl.ts <path-to-collections-file> [--output <path-to-output-file>] [--watch]");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const resolvedPath = path.resolve(process.cwd(), collectionsFilePath);
|
|
101
|
+
const resolvedOutputPath = outputPath ? path.resolve(process.cwd(), outputPath) : void 0;
|
|
102
|
+
if (watch) {
|
|
103
|
+
out(`Watching for changes in ${resolvedPath}...`);
|
|
104
|
+
const { default: chokidar } = await import("chokidar");
|
|
105
|
+
chokidar.watch(resolvedPath, {
|
|
106
|
+
persistent: true,
|
|
107
|
+
ignoreInitial: false
|
|
108
|
+
}).on("all", (event, filePath) => {
|
|
109
|
+
out(`[${event}] ${filePath}. Regenerating DDL schema...`);
|
|
110
|
+
runGeneration(resolvedPath, resolvedOutputPath).catch(() => void 0);
|
|
111
|
+
});
|
|
112
|
+
} else await runGeneration(resolvedPath, resolvedOutputPath);
|
|
113
|
+
};
|
|
114
|
+
if (import.meta.url.endsWith(process.argv[1])) main().catch(() => {
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
});
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/schema/bin/generate-postgres-ddl.ts
|
|
119
|
+
/**
|
|
120
|
+
* The built entry point for `generate-postgres-ddl`.
|
|
121
|
+
*
|
|
122
|
+
* `generate-postgres-ddl.ts` guards its own `main()` with
|
|
123
|
+
* `import.meta.url.endsWith(process.argv[1])`, because `src/index.ts` re-exports
|
|
124
|
+
* the module and importing the driver must not run the generator. That guard is
|
|
125
|
+
* correct when the file IS the process entry — which is how it runs from source
|
|
126
|
+
* under tsx — and silently false once the module is bundled: the executed file
|
|
127
|
+
* is a stub, the code lives in a shared chunk, and the chunk's `import.meta.url`
|
|
128
|
+
* is not the stub's path. The generator then did nothing, exited 0, and the
|
|
129
|
+
* caller reported success over an unwritten schema.
|
|
130
|
+
*
|
|
131
|
+
* So the built path gets an entry that says what it means.
|
|
132
|
+
*/
|
|
133
|
+
main().catch(() => {
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
});
|
|
136
|
+
//#endregion
|
|
137
|
+
|
|
138
|
+
//# sourceMappingURL=generate-postgres-ddl.js.map
|
|
@@ -0,0 +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 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 = await 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\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 } 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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"}
|
|
@@ -56,3 +56,40 @@ export interface MissingGeneratedName {
|
|
|
56
56
|
export declare function findMissingGeneratedNames(generatedSource: string, collections: CollectionConfig[]): MissingGeneratedName[];
|
|
57
57
|
/** One-line summary for a log or a CLI notice. */
|
|
58
58
|
export declare function describeMissingGeneratedNames(missing: MissingGeneratedName[]): string;
|
|
59
|
+
/** What `rebase schema stale` found, and what that costs. */
|
|
60
|
+
export interface StaleVerdict {
|
|
61
|
+
/** The lines to print, in order. Empty only under `--fix` with nothing to fix. */
|
|
62
|
+
lines: string[];
|
|
63
|
+
/** Non-zero only when the reader must act before the server will boot. */
|
|
64
|
+
exitCode: 0 | 1;
|
|
65
|
+
/** True when `--fix` should regenerate. */
|
|
66
|
+
regenerate: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The whole of `schema stale`'s decision, separated from doing it.
|
|
70
|
+
*
|
|
71
|
+
* Separated because of what the command used to do on the clean path: **exit 0
|
|
72
|
+
* having written zero bytes to stdout and zero to stderr.** For a command the
|
|
73
|
+
* top-level help describes as "Report generated schema files the collections
|
|
74
|
+
* have moved past", that is indistinguishable from a no-op, a crash, and a
|
|
75
|
+
* subcommand the driver does not implement. Three of its four paths were
|
|
76
|
+
* silent, and none of them could be tested where they stood: `cli.ts` uses
|
|
77
|
+
* `import.meta`, which the driver's jest suite cannot load at all.
|
|
78
|
+
*
|
|
79
|
+
* `--fix` is the exception, and stays silent when there is nothing to fix.
|
|
80
|
+
* `rebase dev` runs `schema stale --fix` before every boot with inherited
|
|
81
|
+
* stdio, and "nothing was wrong" is not news a hundred lines into a start-up
|
|
82
|
+
* transcript. The flag is the automated caller's; its silence is the point.
|
|
83
|
+
*/
|
|
84
|
+
export declare function staleVerdict(input: {
|
|
85
|
+
/** `--output`, as the reader typed it. */
|
|
86
|
+
outputPath: string;
|
|
87
|
+
/** Whether the generated schema exists at all. */
|
|
88
|
+
generatedExists: boolean;
|
|
89
|
+
/** The legacy foreign-key names found in it, if it was read. */
|
|
90
|
+
stale: LegacyForeignKeyName[];
|
|
91
|
+
/** Why the comparison could not be made, when it could not. */
|
|
92
|
+
unreadable?: string;
|
|
93
|
+
/** Whether `--fix` was given. */
|
|
94
|
+
fix: boolean;
|
|
95
|
+
}): StaleVerdict;
|
|
@@ -16,6 +16,40 @@ export interface QueryableClient {
|
|
|
16
16
|
rows: R[];
|
|
17
17
|
}>;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* A Postgres identifier, quoted so that whatever is inside it stays inside it.
|
|
21
|
+
*
|
|
22
|
+
* Introspection reads table and schema names out of `pg_class` and puts them
|
|
23
|
+
* straight back into SQL, and wrapping a name in double quotes is not the same
|
|
24
|
+
* as escaping it: a table named `x" ; DROP TABLE users; --` closes the quote
|
|
25
|
+
* and then the statement. Postgres's own escape for a quote inside a quoted
|
|
26
|
+
* identifier is to double it, which is what this does.
|
|
27
|
+
*
|
|
28
|
+
* "The catalogue is trusted" is true of your own database, and is exactly the
|
|
29
|
+
* assumption this tool cannot make. Introspection exists to be pointed at
|
|
30
|
+
* databases somebody else built, and it runs with the privileges of whoever
|
|
31
|
+
* ran it — which is usually a superuser, because that is who has a connection
|
|
32
|
+
* string to a database they are adopting.
|
|
33
|
+
*/
|
|
34
|
+
export declare function quoteIdentifier(name: string): string;
|
|
35
|
+
/** `"schema"."table"`, both halves quoted. */
|
|
36
|
+
export declare function quoteRelation(schema: string, table: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Can this table's name be the name of the file describing it?
|
|
39
|
+
*
|
|
40
|
+
* Introspection writes `<collectionsDir>/<tableName>.ts`, and the table name
|
|
41
|
+
* came from a database this tool did not create. Postgres will hold a table
|
|
42
|
+
* called `../../../etc/cron.d/x`, and `path.join` resolves it — so without this
|
|
43
|
+
* the generator writes a file wherever the name points, as whoever ran the
|
|
44
|
+
* command.
|
|
45
|
+
*
|
|
46
|
+
* Deliberately narrow, and deliberately not a blocklist of `..` and `/`: the
|
|
47
|
+
* question is "is this a plain file name?", and every answer that is not
|
|
48
|
+
* obviously yes is a table better written by hand. NUL and the Windows
|
|
49
|
+
* separator are in scope because a collections directory is checked out on
|
|
50
|
+
* whatever machine the developer has.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isUsableFileName(name: string): boolean;
|
|
19
53
|
/**
|
|
20
54
|
* Base tables, excluding partitions.
|
|
21
55
|
*
|