@rebasepro/server-postgres 0.23.0 → 0.24.1-canary.g19596ef
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/README.md +16 -0
- package/dist/{BranchService-DGPL6G_C.js → BranchService-UgKa416x.js} +47 -19
- package/dist/BranchService-UgKa416x.js.map +1 -0
- package/dist/PostgresBackendDriver.d.ts +187 -13
- package/dist/PostgresBootstrapper.d.ts +101 -2
- 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 +75 -2
- 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-yiKd1GsC.js} +168 -75
- package/dist/backup-cli-yiKd1GsC.js.map +1 -0
- package/dist/{backup-service-HQ9GC4tN.js → backup-service-AZWc2wvI.js} +104 -35
- package/dist/backup-service-AZWc2wvI.js.map +1 -0
- package/dist/boot-ddl-B9ZmBJ75.js +105 -0
- package/dist/boot-ddl-B9ZmBJ75.js.map +1 -0
- package/dist/cli-console-DGee3Zl7.js +225 -0
- package/dist/cli-console-DGee3Zl7.js.map +1 -0
- package/dist/cli-console.d.ts +39 -0
- package/dist/cli-errors-oVUuD8k0.js +2180 -0
- package/dist/cli-errors-oVUuD8k0.js.map +1 -0
- package/dist/cli-errors.d.ts +133 -9
- package/dist/{cli-flags-BglvpjHv.js → cli-flags-DnPHXjRH.js} +56 -6
- package/dist/cli-flags-DnPHXjRH.js.map +1 -0
- package/dist/cli-flags.d.ts +15 -0
- package/dist/cli-helpers.d.ts +88 -34
- package/dist/cli-output.d.ts +16 -28
- package/dist/{arg-Dni7MzLB.js → cli-prompt-egtNKkZl.js} +49 -3
- package/dist/cli-prompt-egtNKkZl.js.map +1 -0
- package/dist/cli-prompt.d.ts +19 -0
- package/dist/cli-push.d.ts +87 -0
- package/dist/cli-scratch-database.d.ts +33 -8
- package/dist/cli.js +826 -230
- package/dist/cli.js.map +1 -1
- package/dist/{column-plan-helpers-DF-8dTVa.js → column-plan-helpers-D_V4oaZh.js} +43 -27
- package/dist/column-plan-helpers-D_V4oaZh.js.map +1 -0
- package/dist/{databasePoolManager-Bj5FbeAs.js → connection-C938nR3E.js} +25 -113
- package/dist/connection-C938nR3E.js.map +1 -0
- package/dist/connection.d.ts +13 -2
- package/dist/data-transformer.d.ts +0 -8
- package/dist/databasePoolManager-DZgnBOgj.js +119 -0
- package/dist/databasePoolManager-DZgnBOgj.js.map +1 -0
- package/dist/deferred-index-build-BqmEHGaD.js +1051 -0
- package/dist/deferred-index-build-BqmEHGaD.js.map +1 -0
- package/dist/{doctor-Cb2thZ8s.js → doctor-CUAAOFZm.js} +281 -89
- package/dist/doctor-CUAAOFZm.js.map +1 -0
- package/dist/ensure-collection-policies-Bb51V2OV.js +541 -0
- package/dist/ensure-collection-policies-Bb51V2OV.js.map +1 -0
- package/dist/ensure-collection-tables-egjGBjhv.js +532 -0
- package/dist/ensure-collection-tables-egjGBjhv.js.map +1 -0
- package/dist/{ensure-tables-Dhn9KM3B.js → ensure-tables-DMPWhJ6R.js} +180 -77
- package/dist/ensure-tables-DMPWhJ6R.js.map +1 -0
- package/dist/enum-labels-kept-Msq5gMcb.js +56 -0
- package/dist/enum-labels-kept-Msq5gMcb.js.map +1 -0
- package/dist/generate-drizzle-schema-DrA-YgXv.js +57 -0
- package/dist/generate-drizzle-schema-DrA-YgXv.js.map +1 -0
- package/dist/{generate-drizzle-schema-logic-BfzK7UQd.js → generate-drizzle-schema-logic-BaT0H7Gq.js} +29 -11
- package/dist/generate-drizzle-schema-logic-BaT0H7Gq.js.map +1 -0
- package/dist/generate-files-OoRJLEdk.js +199 -0
- package/dist/generate-files-OoRJLEdk.js.map +1 -0
- package/dist/generate-postgres-ddl-logic-B4k9djAV.js +152 -0
- package/dist/generate-postgres-ddl-logic-B4k9djAV.js.map +1 -0
- package/dist/{generated-schema-staleness-Di9c4ZxN.js → generated-schema-staleness-BWN_PsMi.js} +2 -2
- package/dist/{generated-schema-staleness-Di9c4ZxN.js.map → generated-schema-staleness-BWN_PsMi.js.map} +1 -1
- package/dist/generated-sql.d.ts +34 -0
- package/dist/history/HistoryService.d.ts +64 -5
- package/dist/history/ensure-history-table.d.ts +63 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +139 -16738
- package/dist/index.es.js.map +1 -1
- package/dist/{introspect-db-logic-WMuAfxvw.js → introspect-db-logic-BCc1Jym2.js} +654 -353
- package/dist/introspect-db-logic-BCc1Jym2.js.map +1 -0
- package/dist/introspect-db-queries-BhhJg0b8.js +364 -0
- package/dist/introspect-db-queries-BhhJg0b8.js.map +1 -0
- package/dist/open-rebase-CAjWpPjs.js +20895 -0
- package/dist/open-rebase-CAjWpPjs.js.map +1 -0
- package/dist/open-rebase.d.ts +94 -0
- package/dist/pg-error-utils-Bs_Ue4wn.js +395 -0
- package/dist/pg-error-utils-Bs_Ue4wn.js.map +1 -0
- package/dist/{plan-schema-C0fxM8dY.js → plan-schema-CsNgK0qi.js} +279 -534
- package/dist/plan-schema-CsNgK0qi.js.map +1 -0
- package/dist/{policy-drift-DljYdrpW.js → policy-drift-BKC50y2p.js} +33 -9
- package/dist/policy-drift-BKC50y2p.js.map +1 -0
- package/dist/push-index-prebuild-C_qhyoJa.js +45 -0
- package/dist/push-index-prebuild-C_qhyoJa.js.map +1 -0
- package/dist/push-legacy-renames-5g9h3HOa.js +34 -0
- package/dist/push-legacy-renames-5g9h3HOa.js.map +1 -0
- package/dist/{generate-postgres-ddl-logic-Bt2d2mRH.js → render-ddl-CrfEe__B.js} +24 -157
- package/dist/render-ddl-CrfEe__B.js.map +1 -0
- package/dist/{rls-bootstrap-sql-B8EclDyM.js → rls-bootstrap-sql-Bd-hJk08.js} +125 -1101
- package/dist/rls-bootstrap-sql-Bd-hJk08.js.map +1 -0
- package/dist/{rls-enforcement-C6Xk0lA6.js → rls-enforcement-D7P4-ZYX.js} +138 -18
- package/dist/rls-enforcement-D7P4-ZYX.js.map +1 -0
- package/dist/{__vite-browser-external-BnuHet1e.js → rolldown-runtime-V7rFbMRe.js} +1 -8
- package/dist/schema/atlas-exclude.d.ts +42 -0
- package/dist/schema/auth-schema.d.ts +172 -2
- package/dist/schema/boot-ddl.d.ts +47 -0
- package/dist/schema/carved-out-migration.d.ts +5 -4
- 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 +50 -2
- 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/generated-schema-diff.d.ts +7 -0
- package/dist/schema/introspect-db-logic.d.ts +63 -0
- package/dist/schema/introspect-db-queries.d.ts +21 -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 +29 -21
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/migrate-baseline.d.ts +33 -0
- 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 +8 -1
- 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/schema/undeclarable-constraints.d.ts +59 -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 +29 -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-bus/index.d.ts +1 -1
- 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-opt-ins.d.ts +64 -0
- package/dist/source-DB6xJv3p.js +873 -0
- package/dist/source-DB6xJv3p.js.map +1 -0
- package/dist/tsx-loader.d.ts +14 -0
- package/dist/utils/connection-string.d.ts +31 -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/{vector-index-CMJKb3C9.js → vector-index-BFhyEaiz.js} +2 -2
- package/dist/{vector-index-CMJKb3C9.js.map → vector-index-BFhyEaiz.js.map} +1 -1
- package/dist/websocket-DUiiwQd2.js +1632 -0
- package/dist/websocket-DUiiwQd2.js.map +1 -0
- package/dist/websocket.d.ts +63 -18
- package/package.json +7 -7
- package/dist/BranchService-DGPL6G_C.js.map +0 -1
- package/dist/__vite-browser-external-BnuHet1e.js.map +0 -1
- package/dist/arg-Dni7MzLB.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
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implements the `BackendBootstrapper` interface for PostgreSQL.
|
|
5
5
|
*/
|
|
6
6
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
7
|
-
import { BackendBootstrapper, CollectionConfig, type RealtimeChannelsConfig } from "@rebasepro/types";
|
|
7
|
+
import { BackendBootstrapper, CollectionConfig, InitializedDriver, type DatabaseAdapterInitConfig, type RealtimeChannelsConfig } from "@rebasepro/types";
|
|
8
8
|
import { PostgresBackendDriver } from "./PostgresBackendDriver.js";
|
|
9
9
|
import { RealtimeService } from "./services/realtimeService.js";
|
|
10
10
|
import { DatabasePoolManager } from "./databasePoolManager.js";
|
|
@@ -14,7 +14,27 @@ import { type UnknownFilterFieldsMode } from "./utils/drizzle-conditions.js";
|
|
|
14
14
|
export interface PostgresDriverConfig {
|
|
15
15
|
connectionString?: string;
|
|
16
16
|
adminConnectionString?: string;
|
|
17
|
+
/**
|
|
18
|
+
* This database's read replica: reads are routed there, writes stay on
|
|
19
|
+
* the primary. Unset, the boot ({@link postgresDriverConfigFromEnv}) fills
|
|
20
|
+
* it from `DATABASE_READ_URL` for the default source only; any other
|
|
21
|
+
* source has none unless its config names one — the managed runtime
|
|
22
|
+
* resolves `DATABASE_READ_URL__<KEY>` into this for each source.
|
|
23
|
+
*/
|
|
17
24
|
readConnectionString?: string;
|
|
25
|
+
/**
|
|
26
|
+
* A direct (non-pooled) URL to this database, for the connections that
|
|
27
|
+
* hold a `LISTEN`: change capture, cross-instance realtime, the Postgres
|
|
28
|
+
* channel bus and `admin.listen`. A transaction-mode pooler such as
|
|
29
|
+
* PgBouncer does not keep a `LISTEN`, so behind one this has to name the
|
|
30
|
+
* database itself.
|
|
31
|
+
*
|
|
32
|
+
* Unset, the boot fills it from `DATABASE_DIRECT_URL` for the default
|
|
33
|
+
* source only, and any other source listens on its own
|
|
34
|
+
* `connectionString` — see {@link resolveSourceUrls}. The managed runtime
|
|
35
|
+
* resolves `DATABASE_DIRECT_URL__<KEY>` into this for each source.
|
|
36
|
+
*/
|
|
37
|
+
directConnectionString?: string;
|
|
18
38
|
connection?: unknown;
|
|
19
39
|
/**
|
|
20
40
|
* The project's generated Drizzle module, for **diagnostics only**.
|
|
@@ -37,13 +57,17 @@ export interface PostgresDriverConfig {
|
|
|
37
57
|
*/
|
|
38
58
|
introspectionSchema?: string;
|
|
39
59
|
/**
|
|
40
|
-
* Realtime options,
|
|
60
|
+
* Realtime options, among them:
|
|
41
61
|
*
|
|
42
62
|
* - `channels` — retention. Without rules no channel keeps any history and
|
|
43
63
|
* broadcast stays fire-and-forget. See {@link ChannelRetentionRule}.
|
|
44
64
|
* - `bus` — the cross-instance transport for channel broadcast and
|
|
45
65
|
* presence. Defaults to in-process only, which is correct for a single
|
|
46
66
|
* instance and wrong for two. See {@link ChannelBusConfig}.
|
|
67
|
+
* - `cdc` — how changes are captured (`auto` by default).
|
|
68
|
+
*
|
|
69
|
+
* At boot each has an environment variable that wins over it — see
|
|
70
|
+
* {@link postgresDriverConfigFromEnv}.
|
|
47
71
|
*/
|
|
48
72
|
realtime?: RealtimeChannelsConfig;
|
|
49
73
|
/**
|
|
@@ -65,6 +89,13 @@ export interface PostgresDriverInternals {
|
|
|
65
89
|
realtimeService: RealtimeService;
|
|
66
90
|
driver: PostgresBackendDriver;
|
|
67
91
|
poolManager?: DatabasePoolManager;
|
|
92
|
+
/**
|
|
93
|
+
* Whether the driver was opened with provisioning (`provision`, default
|
|
94
|
+
* true). The hooks that run after it — auth, history, the final security
|
|
95
|
+
* posture — read it, so `provision: false` means no DDL anywhere on the
|
|
96
|
+
* driver's path, not only while it opens.
|
|
97
|
+
*/
|
|
98
|
+
provisioned: boolean;
|
|
68
99
|
/**
|
|
69
100
|
* Attach CDC triggers to tables that did not exist when the driver
|
|
70
101
|
* bootstrapped. Only set when database-level capture is actually active.
|
|
@@ -118,6 +149,27 @@ export declare function describeSchemaDriftCause(provisioning: {
|
|
|
118
149
|
attempted: boolean;
|
|
119
150
|
reason?: string;
|
|
120
151
|
} | undefined): string[];
|
|
152
|
+
/**
|
|
153
|
+
* The URLs one source's driver opens besides its pool: the read replica, and
|
|
154
|
+
* the direct connection its `LISTEN`s are held on.
|
|
155
|
+
*
|
|
156
|
+
* The bare `DATABASE_READ_URL` and `DATABASE_DIRECT_URL` belong to the default
|
|
157
|
+
* source. Both used to be read here for every source, so a second database
|
|
158
|
+
* (`database("analytics")`) held its change-capture `LISTEN` on the default
|
|
159
|
+
* database — where none of its notifications are sent, so its realtime feed
|
|
160
|
+
* was silent — and read through the default's replica. A keyed source gets
|
|
161
|
+
* the URLs its config names, and otherwise its own connection and no replica.
|
|
162
|
+
*
|
|
163
|
+
* The default source still falls back to the environment: an application that
|
|
164
|
+
* builds this adapter itself passes a `connectionString` and nothing else, and
|
|
165
|
+
* has always had those two variables honoured.
|
|
166
|
+
*
|
|
167
|
+
* Exported for its own test; the caller needs a live database.
|
|
168
|
+
*/
|
|
169
|
+
export declare function resolveSourceUrls(pgConfig: Pick<PostgresDriverConfig, "connectionString" | "readConnectionString" | "directConnectionString">, sourceKey: string, env?: Record<string, string | undefined>): {
|
|
170
|
+
readUrl?: string;
|
|
171
|
+
directUrl?: string;
|
|
172
|
+
};
|
|
121
173
|
/**
|
|
122
174
|
* Is this the local database `rebase init` scaffolds — i.e. the one case where
|
|
123
175
|
* "you are connected as a superuser" is not news?
|
|
@@ -144,4 +196,51 @@ export declare function describeSchemaDriftCause(provisioning: {
|
|
|
144
196
|
* ships `NODE_ENV=development` and some deployments inherit it.
|
|
145
197
|
*/
|
|
146
198
|
export declare function isScaffoldedLocalDatabase(connectionString: string | undefined): boolean;
|
|
199
|
+
/**
|
|
200
|
+
* `config` with the environment read into it, the way the server's boot reads
|
|
201
|
+
* it. The one place this driver reads environment variables:
|
|
202
|
+
*
|
|
203
|
+
* | variable | setting |
|
|
204
|
+
* |----------------------------------------|----------------------------------------|
|
|
205
|
+
* | `DATABASE_READ_URL` | `readConnectionString`, when unset (*) |
|
|
206
|
+
* | `DATABASE_DIRECT_URL` | `directConnectionString`, when unset (*)|
|
|
207
|
+
* | `REALTIME_CDC` | `realtime.cdc` |
|
|
208
|
+
* | `REALTIME_CHANNEL_BUS` | `realtime.bus` (a named transport) |
|
|
209
|
+
* | `REALTIME_MAX_SUBSCRIPTIONS_PER_SOCKET`| `realtime.maxSubscriptionsPerSocket` |
|
|
210
|
+
* | `REALTIME_MAX_SOCKETS_PER_USER` | `realtime.maxSocketsPerUser` |
|
|
211
|
+
* | `REALTIME_REFETCH_MAX_WAIT_MS` | `realtime.refetchMaxWaitMs` |
|
|
212
|
+
* | `REALTIME_RECOUNT_INTERVAL_MS` | `realtime.recountIntervalMs` |
|
|
213
|
+
* | `REALTIME_RECOUNT_MIN_ROWS` | `realtime.recountMinRows` |
|
|
214
|
+
*
|
|
215
|
+
* A `REALTIME_*` variable that is set wins over the option, so a deployment
|
|
216
|
+
* can change it without a rebuild; the URLs fill in only what the config left
|
|
217
|
+
* empty. A mistyped number throws, naming the variable.
|
|
218
|
+
*
|
|
219
|
+
* (*) For the default source only. The bare URL variables name the default
|
|
220
|
+
* database; read for a second source (`database("analytics")`), they put its
|
|
221
|
+
* change-capture `LISTEN` on the default database, where none of its
|
|
222
|
+
* notifications arrive, and its reads on the default's replica. A keyed
|
|
223
|
+
* source's URLs come from its own config — the managed runtime passes
|
|
224
|
+
* `DATABASE_*_URL__<KEY>` there — see {@link resolveSourceUrls}.
|
|
225
|
+
*
|
|
226
|
+
* `createPostgresAdapter` applies this when the server boots, and
|
|
227
|
+
* {@link openPostgresDriver} does not — a script that opens the driver itself
|
|
228
|
+
* gets exactly the config it passed.
|
|
229
|
+
*/
|
|
230
|
+
export declare function postgresDriverConfigFromEnv(config: PostgresDriverConfig, env?: NodeJS.ProcessEnv, sourceKey?: string): PostgresDriverConfig;
|
|
231
|
+
/**
|
|
232
|
+
* Open the Postgres data driver from explicit settings.
|
|
233
|
+
*
|
|
234
|
+
* What `createPostgresAdapter` runs when the server boots, once it has read
|
|
235
|
+
* the environment into `pgConfig` (see {@link postgresDriverConfigFromEnv});
|
|
236
|
+
* this function reads no environment variable itself. `config` says what the
|
|
237
|
+
* caller wants opened: which collections, whether opening may change the
|
|
238
|
+
* database (`provision`, default true), and what part this process takes in
|
|
239
|
+
* realtime (`realtime`, or `false` for none at all).
|
|
240
|
+
*
|
|
241
|
+
* `openRebase` is this with `provision: false` and `realtime: false`.
|
|
242
|
+
*/
|
|
243
|
+
export declare function openPostgresDriver(pgConfig: PostgresDriverConfig, config: DatabaseAdapterInitConfig): Promise<InitializedDriver & {
|
|
244
|
+
internals: PostgresDriverInternals;
|
|
245
|
+
}>;
|
|
147
246
|
export declare function createPostgresBootstrapper(pgConfig: PostgresDriverConfig): BackendBootstrapper;
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
2
|
import type { CollectionConfig } from "@rebasepro/types";
|
|
3
|
+
/**
|
|
4
|
+
* One statement that drops each named index — but only one that covers a
|
|
5
|
+
* single column that a valid, unique, non-partial btree on the same table also
|
|
6
|
+
* leads with. That is the UNIQUE (token_hash) constraint's index, which serves
|
|
7
|
+
* every `token_hash = $1` lookup; a table that somehow lacks it keeps its
|
|
8
|
+
* plain index, so no lookup is ever left without one.
|
|
9
|
+
*
|
|
10
|
+
* `DROP INDEX` takes the table's ACCESS EXCLUSIVE lock, on the tables every
|
|
11
|
+
* sign-in and token refresh touches, so it runs under the boot lock budget
|
|
12
|
+
* (`lock_timeout`, transaction-local to this statement — see `boot-ddl`).
|
|
13
|
+
*/
|
|
14
|
+
export declare function dropDuplicateTokenHashIndexesSql(schema: string, names: readonly string[]): string;
|
|
3
15
|
/**
|
|
4
16
|
* Auto-create auth tables if they don't exist.
|
|
5
17
|
*
|
package/dist/auth/services.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
2
|
import { type RebasePgTable } from "../types.js";
|
|
3
|
-
import { UserRepository, TokenRepository, MfaRepository, AuthRepository, UserData, CreateUserData,
|
|
4
|
-
export type { Role };
|
|
3
|
+
import { UserRepository, TokenRepository, MfaRepository, AuthRepository, UserData, CreateUserData, RefreshTokenInfo, RefreshTokenSession, PasswordResetTokenInfo, MagicLinkTokenInfo, UserIdentityData, ListUsersOptions, PaginatedUsersResult, MfaFactor, MfaChallengeInfo, AccountAccessState, PendingEmailChange } from "@rebasepro/server";
|
|
5
4
|
export interface AuthSchemaTables {
|
|
6
5
|
users: RebasePgTable;
|
|
7
6
|
refreshTokens: RebasePgTable;
|
|
@@ -38,6 +37,8 @@ export declare class UserService implements UserRepository {
|
|
|
38
37
|
private db;
|
|
39
38
|
private usersTable;
|
|
40
39
|
private userIdentitiesTable;
|
|
40
|
+
/** Read by `getAccountAccessState`, to say whether one sign-in is still live. */
|
|
41
|
+
private refreshTokensTable;
|
|
41
42
|
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
42
43
|
private getQualifiedUsersTableName;
|
|
43
44
|
/**
|
|
@@ -83,6 +84,11 @@ export declare class UserService implements UserRepository {
|
|
|
83
84
|
updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null>;
|
|
84
85
|
deleteUser(id: string): Promise<void>;
|
|
85
86
|
listUsers(): Promise<UserData[]>;
|
|
87
|
+
/**
|
|
88
|
+
* @see UserRepository.firstUserIds — an unordered `LIMIT` read of the
|
|
89
|
+
* primary key, not `listUsersPaginated`'s count and sort of every row.
|
|
90
|
+
*/
|
|
91
|
+
firstUserIds(limit: number): Promise<string[]>;
|
|
86
92
|
listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult>;
|
|
87
93
|
/**
|
|
88
94
|
* Update user's password hash; `null` removes the password.
|
|
@@ -100,10 +106,6 @@ export declare class UserService implements UserRepository {
|
|
|
100
106
|
* Find user by email verification token
|
|
101
107
|
*/
|
|
102
108
|
getUserByVerificationToken(token: string): Promise<UserData | null>;
|
|
103
|
-
/**
|
|
104
|
-
* Get roles for a user from database (inline TEXT[] column)
|
|
105
|
-
*/
|
|
106
|
-
getUserRoles(uid: string): Promise<Role[]>;
|
|
107
109
|
/**
|
|
108
110
|
* Get role IDs for a user
|
|
109
111
|
*/
|
|
@@ -116,12 +118,53 @@ export declare class UserService implements UserRepository {
|
|
|
116
118
|
* Assign a specific role to new user (appends if not present)
|
|
117
119
|
*/
|
|
118
120
|
assignDefaultRole(uid: string, roleId: string): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* One read of what an access token is judged against: whether the account
|
|
123
|
+
* exists, its roles, and its revocation watermark. See
|
|
124
|
+
* `TokenRepository.getAccountAccessState`.
|
|
125
|
+
*
|
|
126
|
+
* The watermark is read only where the table declares it, as
|
|
127
|
+
* `RefreshTokenService.getTokensValidAfter` reads it — a table without the
|
|
128
|
+
* column has no watermark rather than an error on every request.
|
|
129
|
+
*/
|
|
130
|
+
getAccountAccessState(uid: string, sessionId?: string): Promise<AccountAccessState | null>;
|
|
131
|
+
/**
|
|
132
|
+
* Whether the users table has `disabled_at`. `ensureAuthTablesExist` adds
|
|
133
|
+
* it to every auth table at boot, but the app's own drizzle object may
|
|
134
|
+
* predate it, so the database is asked — once — rather than the object.
|
|
135
|
+
*/
|
|
136
|
+
private disabledColumn?;
|
|
137
|
+
private hasDisabledColumn;
|
|
138
|
+
/**
|
|
139
|
+
* Whether the users table has the address-change columns. Added at boot to
|
|
140
|
+
* every auth table, but the app's drizzle object never declares them, so
|
|
141
|
+
* the database is asked — once — as for `disabled_at`.
|
|
142
|
+
*/
|
|
143
|
+
private emailChangeColumns?;
|
|
144
|
+
private hasEmailChangeColumns;
|
|
145
|
+
private requireEmailChangeColumns;
|
|
146
|
+
/** @see UserRepository.setPendingEmailChange */
|
|
147
|
+
setPendingEmailChange(uid: string, change: {
|
|
148
|
+
email: string;
|
|
149
|
+
tokenHash: string;
|
|
150
|
+
} | null): Promise<void>;
|
|
151
|
+
/** @see UserRepository.getPendingEmailChange */
|
|
152
|
+
getPendingEmailChange(uid: string): Promise<PendingEmailChange | null>;
|
|
153
|
+
/** @see UserRepository.findPendingEmailChange */
|
|
154
|
+
findPendingEmailChange(tokenHash: string): Promise<{
|
|
155
|
+
user: UserData;
|
|
156
|
+
change: PendingEmailChange;
|
|
157
|
+
} | null>;
|
|
158
|
+
/** @see UserRepository.applyPendingEmailChange — one write, held to the token. */
|
|
159
|
+
applyPendingEmailChange(uid: string, tokenHash: string): Promise<UserData | null>;
|
|
160
|
+
/** @see UserRepository.setUserDisabled */
|
|
161
|
+
setUserDisabled(uid: string, disabled: boolean): Promise<void>;
|
|
119
162
|
/**
|
|
120
163
|
* Get user with their roles
|
|
121
164
|
*/
|
|
122
165
|
getUserWithRoles(uid: string): Promise<{
|
|
123
166
|
user: UserData;
|
|
124
|
-
roles:
|
|
167
|
+
roles: string[];
|
|
125
168
|
} | null>;
|
|
126
169
|
}
|
|
127
170
|
export declare class RefreshTokenService {
|
|
@@ -191,6 +234,12 @@ export declare class RefreshTokenService {
|
|
|
191
234
|
deleteAllForUser(uid: string): Promise<void>;
|
|
192
235
|
listForUser(uid: string): Promise<RefreshTokenInfo[]>;
|
|
193
236
|
deleteById(id: string, uid: string): Promise<void>;
|
|
237
|
+
/**
|
|
238
|
+
* Every token past its expiry, whoever holds it. `prune` clears a user's
|
|
239
|
+
* expired rows when that user refreshes; a session nobody comes back to
|
|
240
|
+
* is only ever cleared here.
|
|
241
|
+
*/
|
|
242
|
+
deleteExpired(): Promise<void>;
|
|
194
243
|
}
|
|
195
244
|
/**
|
|
196
245
|
* Password reset token service
|
|
@@ -235,6 +284,8 @@ export declare class MagicLinkTokenService {
|
|
|
235
284
|
private getQualifiedTableName;
|
|
236
285
|
createToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
237
286
|
findValidByHash(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
|
|
287
|
+
/** Links and email codes past their expiry, used or not. */
|
|
288
|
+
deleteExpired(): Promise<void>;
|
|
238
289
|
markAsUsed(tokenHash: string): Promise<void>;
|
|
239
290
|
}
|
|
240
291
|
/**
|
|
@@ -262,6 +313,10 @@ export declare class PostgresTokenRepository implements TokenRepository {
|
|
|
262
313
|
findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null>;
|
|
263
314
|
markPasswordResetTokenUsed(tokenHash: string): Promise<void>;
|
|
264
315
|
deleteAllPasswordResetTokensForUser(uid: string): Promise<void>;
|
|
316
|
+
/**
|
|
317
|
+
* @see TokenRepository.deleteExpiredTokens — reset links, magic links and
|
|
318
|
+
* email codes, and refresh tokens, each past its own expiry.
|
|
319
|
+
*/
|
|
265
320
|
deleteExpiredTokens(): Promise<void>;
|
|
266
321
|
createMagicLinkToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
267
322
|
findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
|
|
@@ -288,23 +343,30 @@ export declare class PostgresAuthRepository implements AuthRepository {
|
|
|
288
343
|
deleteUser(id: string): Promise<void>;
|
|
289
344
|
listUsers(): Promise<UserData[]>;
|
|
290
345
|
listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult>;
|
|
346
|
+
firstUserIds(limit: number): Promise<string[]>;
|
|
291
347
|
updatePassword(id: string, passwordHash: string | null): Promise<void>;
|
|
292
348
|
setEmailVerified(id: string, verified: boolean): Promise<void>;
|
|
293
349
|
setVerificationToken(id: string, token: string | null): Promise<void>;
|
|
294
350
|
getUserByVerificationToken(token: string): Promise<UserData | null>;
|
|
295
|
-
getUserRoles(uid: string): Promise<RoleData[]>;
|
|
296
351
|
getUserRoleIds(uid: string): Promise<string[]>;
|
|
297
352
|
setUserRoles(uid: string, roleIds: string[]): Promise<void>;
|
|
298
353
|
assignDefaultRole(uid: string, roleId: string): Promise<void>;
|
|
299
354
|
getUserWithRoles(uid: string): Promise<{
|
|
300
355
|
user: UserData;
|
|
301
|
-
roles:
|
|
356
|
+
roles: string[];
|
|
357
|
+
} | null>;
|
|
358
|
+
getAccountAccessState(uid: string, sessionId?: string): Promise<AccountAccessState | null>;
|
|
359
|
+
setUserDisabled(uid: string, disabled: boolean): Promise<void>;
|
|
360
|
+
setPendingEmailChange(uid: string, change: {
|
|
361
|
+
email: string;
|
|
362
|
+
tokenHash: string;
|
|
363
|
+
} | null): Promise<void>;
|
|
364
|
+
getPendingEmailChange(uid: string): Promise<PendingEmailChange | null>;
|
|
365
|
+
findPendingEmailChange(tokenHash: string): Promise<{
|
|
366
|
+
user: UserData;
|
|
367
|
+
change: PendingEmailChange;
|
|
302
368
|
} | null>;
|
|
303
|
-
|
|
304
|
-
listRoles(): Promise<RoleData[]>;
|
|
305
|
-
createRole(_data: CreateRoleData): Promise<RoleData>;
|
|
306
|
-
updateRole(id: string, data: Partial<Omit<RoleData, "id">>): Promise<RoleData | null>;
|
|
307
|
-
deleteRole(_id: string): Promise<void>;
|
|
369
|
+
applyPendingEmailChange(uid: string, tokenHash: string): Promise<UserData | null>;
|
|
308
370
|
createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string, session?: RefreshTokenSession): Promise<boolean>;
|
|
309
371
|
markRefreshTokenRotated(tokenHash: string): Promise<void>;
|
|
310
372
|
revokeRefreshTokenSession(sessionId: string): Promise<void>;
|
|
@@ -383,6 +445,8 @@ export declare class MfaService implements MfaRepository {
|
|
|
383
445
|
* share a single increment.
|
|
384
446
|
*/
|
|
385
447
|
recordMfaChallengeAttempt(challengeId: string): Promise<number>;
|
|
448
|
+
/** Challenges past their five minutes, answered or not. */
|
|
449
|
+
deleteExpiredChallenges(): Promise<void>;
|
|
386
450
|
verifyMfaChallenge(challengeId: string): Promise<void>;
|
|
387
451
|
createRecoveryCodes(uid: string, codeHashes: string[]): Promise<void>;
|
|
388
452
|
useRecoveryCode(uid: string, codeHash: string): Promise<boolean>;
|
|
@@ -62,6 +62,22 @@ var AUTH_USERS_COLUMNS = [
|
|
|
62
62
|
column: "tokens_valid_after",
|
|
63
63
|
type: "TIMESTAMP WITH TIME ZONE"
|
|
64
64
|
},
|
|
65
|
+
{
|
|
66
|
+
column: "disabled_at",
|
|
67
|
+
type: "TIMESTAMP WITH TIME ZONE"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
column: "pending_email",
|
|
71
|
+
type: "TEXT"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
column: "email_change_token",
|
|
75
|
+
type: "TEXT"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
column: "email_change_sent_at",
|
|
79
|
+
type: "TIMESTAMP WITH TIME ZONE"
|
|
80
|
+
},
|
|
65
81
|
{
|
|
66
82
|
column: "created_at",
|
|
67
83
|
type: "TIMESTAMP WITH TIME ZONE",
|
|
@@ -113,4 +129,4 @@ function isAuthCollection(collection) {
|
|
|
113
129
|
//#endregion
|
|
114
130
|
export { isAuthCollection as i, authUsersColumnDefinition as n, authUsersColumnSql as r, AUTH_USERS_COLUMNS as t };
|
|
115
131
|
|
|
116
|
-
//# sourceMappingURL=auth-users-columns-
|
|
132
|
+
//# sourceMappingURL=auth-users-columns-C72EMoDJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-users-columns-
|
|
1
|
+
{"version":3,"file":"auth-users-columns-C72EMoDJ.js","names":[],"sources":["../src/schema/auth-users-columns.ts"],"sourcesContent":["/**\n * The one description of what an auth user table's columns must be.\n *\n * ## Why this exists\n *\n * Three different code paths create `rebase.users`, and before this module they\n * disagreed about it:\n *\n * | column | `db push` (generator) | `ensureAuthTablesExist` | `ensureCollectionTables` |\n * |------------------|-----------------------|-------------------------|--------------------------|\n * | `email` | `TEXT UNIQUE NOT NULL`| `TEXT NOT NULL` + CHECK | `TEXT` (nullable) |\n * | `roles` | `TEXT[]` | `NOT NULL DEFAULT '{}'` | nullable, no default |\n * | `email_verified` | `BOOLEAN` | `NOT NULL DEFAULT FALSE`| nullable |\n * | `created_at` | `DEFAULT now()` | `NOT NULL DEFAULT NOW()`| no default |\n *\n * They are all `CREATE TABLE IF NOT EXISTS` / `ADD COLUMN IF NOT EXISTS`, so\n * whichever ran first decided the table and the other two silently no-op'd.\n * Boot order therefore chose the constraints: a managed deploy (collection\n * tables first) got a users table whose `email` was nullable and whose\n * `email_verified` had no default, while the same project pushed from a\n * checkout got the strict one. In the same table, `is_anonymous` came out\n * `NOT NULL DEFAULT false` — because *that* column existed in only one of the\n * three lists, so its owner's definition won by default.\n *\n * A second consequence was drift in the other direction. The scaffold's\n * `users.ts` describes 12 columns; auth needs 14. `db push` is declarative and\n * builds its desired state from the collection alone, so the two columns only\n * auth knows about (`is_anonymous`, `tokens_valid_after`) read as unmanaged\n * drift, and a push run after the server had booted once planned to DROP them.\n *\n * Both problems are the same problem: no single place said what this table is.\n * This is that place. Every creator asks here first and only then falls back to\n * the collection's own property definitions, so a column auth owns has one\n * definition regardless of who gets there first, and a column the developer\n * added to their users collection still behaves like any other field.\n *\n * ## What belongs here\n *\n * Only columns the auth services read or write. A developer's own additions to\n * their users collection (`bio`, `stripe_customer_id`) are ordinary columns and\n * must NOT be listed — they are generated from the collection like every other\n * field, and listing them here would freeze a user's schema.\n *\n * `id` is deliberately absent: its type comes from the collection's id property\n * (uuid / increment / text) and each creator already derives it.\n *\n * Keep in step with {@link AUTH_SCHEMA_VERSION} when a change here makes an\n * older runtime unable to work against a migrated table.\n */\n\n/**\n * A column auth owns.\n *\n * Structured rather than one SQL string because the same facts are needed in\n * three grammars — a `CREATE TABLE` column list, an `ADD COLUMN IF NOT EXISTS`,\n * and a pair of `ALTER COLUMN … SET DEFAULT` / `SET NOT NULL` reconciles for a\n * table that already exists with the wrong shape. Parsing a string back apart\n * for the third of those is how the copies drifted in the first place.\n */\nexport interface AuthUsersColumn {\n /** Physical column name. */\n column: string;\n /** Postgres type. */\n type: string;\n /** Default expression, verbatim, or absent for no default. */\n default?: string;\n /** Whether the column is NOT NULL. */\n notNull?: boolean;\n}\n\n/**\n * `email` is NOT NULL on purpose, and the anonymous sign-in route depends on it\n * — it synthesizes `anon_<32 hex>@anonymous.local` rather than inserting NULL.\n * The 320-char bound (RFC 5321) is a CHECK rather than a `VARCHAR(n)`, added\n * separately by `ensureAuthTablesExist` so it can be `NOT VALID` on an adopted\n * table that already holds a longer row.\n */\nexport const AUTH_USERS_COLUMNS: readonly AuthUsersColumn[] = [\n { column: \"email\", type: \"TEXT\", notNull: true },\n { column: \"display_name\", type: \"TEXT\" },\n { column: \"photo_url\", type: \"TEXT\" },\n { column: \"roles\", type: \"TEXT[]\", default: \"'{}'\", notNull: true },\n { column: \"password_hash\", type: \"TEXT\" },\n { column: \"email_verified\", type: \"BOOLEAN\", default: \"FALSE\", notNull: true },\n { column: \"email_verification_token\", type: \"TEXT\" },\n { column: \"email_verification_sent_at\", type: \"TIMESTAMP WITH TIME ZONE\" },\n { column: \"is_anonymous\", type: \"BOOLEAN\", default: \"FALSE\", notNull: true },\n { column: \"metadata\", type: \"JSONB\", default: \"'{}'\", notNull: true },\n { column: \"tokens_valid_after\", type: \"TIMESTAMP WITH TIME ZONE\" },\n // When an administrator switched the account off; NULL while it is on.\n { column: \"disabled_at\", type: \"TIMESTAMP WITH TIME ZONE\" },\n // A self-service address change waiting for its link: the address, the\n // hash of the token mailed to it, and when (it lives 24 hours).\n { column: \"pending_email\", type: \"TEXT\" },\n { column: \"email_change_token\", type: \"TEXT\" },\n { column: \"email_change_sent_at\", type: \"TIMESTAMP WITH TIME ZONE\" },\n { column: \"created_at\", type: \"TIMESTAMP WITH TIME ZONE\", default: \"NOW()\", notNull: true },\n { column: \"updated_at\", type: \"TIMESTAMP WITH TIME ZONE\", default: \"NOW()\", notNull: true }\n];\n\nconst BY_COLUMN = new Map(AUTH_USERS_COLUMNS.map(c => [c.column, c]));\n\n/** Type + inline constraints, as they appear after the column name. */\nexport function authUsersColumnSql(spec: AuthUsersColumn): string {\n return [\n spec.type,\n spec.default !== undefined ? `DEFAULT ${spec.default}` : \"\",\n spec.notNull ? \"NOT NULL\" : \"\"\n ].filter(Boolean).join(\" \");\n}\n\n/**\n * The auth-owned definition for a physical column name, or `undefined` when\n * auth does not own it.\n *\n * Callers pass the RESOLVED column name (after `columnName` mapping), because\n * that is the only name the three creators agree on: the scaffold's users\n * collection spells the property `displayName` and the column `display_name`.\n */\nexport function authUsersColumnDefinition(column: string): string | undefined {\n const spec = BY_COLUMN.get(column);\n return spec ? authUsersColumnSql(spec) : undefined;\n}\n\n/**\n * Whether a collection is an auth collection, i.e. whether the definitions in\n * this module apply to its table at all.\n *\n * Duplicated in shape from `@rebasepro/common`'s policy defaults on purpose:\n * that one takes a `CollectionConfig`, this one is called from DDL code paths\n * that hold looser objects, and both spellings must accept `auth: true` as well\n * as `auth: { enabled: true }`.\n */\nexport function isAuthCollection(collection: unknown): boolean {\n const auth = (collection as { auth?: unknown } | undefined)?.auth;\n if (auth === true) return true;\n return typeof auth === \"object\" && auth !== null && (auth as { enabled?: unknown }).enabled === true;\n}\n"],"mappings":";;;;;;;;;;AA6EA,IAAa,qBAAiD;CAC1D;EAAE,QAAQ;EAAS,MAAM;EAAQ,SAAS;CAAK;CAC/C;EAAE,QAAQ;EAAgB,MAAM;CAAO;CACvC;EAAE,QAAQ;EAAa,MAAM;CAAO;CACpC;EAAE,QAAQ;EAAS,MAAM;EAAU,SAAS;EAAQ,SAAS;CAAK;CAClE;EAAE,QAAQ;EAAiB,MAAM;CAAO;CACxC;EAAE,QAAQ;EAAkB,MAAM;EAAW,SAAS;EAAS,SAAS;CAAK;CAC7E;EAAE,QAAQ;EAA4B,MAAM;CAAO;CACnD;EAAE,QAAQ;EAA8B,MAAM;CAA2B;CACzE;EAAE,QAAQ;EAAgB,MAAM;EAAW,SAAS;EAAS,SAAS;CAAK;CAC3E;EAAE,QAAQ;EAAY,MAAM;EAAS,SAAS;EAAQ,SAAS;CAAK;CACpE;EAAE,QAAQ;EAAsB,MAAM;CAA2B;CAEjE;EAAE,QAAQ;EAAe,MAAM;CAA2B;CAG1D;EAAE,QAAQ;EAAiB,MAAM;CAAO;CACxC;EAAE,QAAQ;EAAsB,MAAM;CAAO;CAC7C;EAAE,QAAQ;EAAwB,MAAM;CAA2B;CACnE;EAAE,QAAQ;EAAc,MAAM;EAA4B,SAAS;EAAS,SAAS;CAAK;CAC1F;EAAE,QAAQ;EAAc,MAAM;EAA4B,SAAS;EAAS,SAAS;CAAK;AAC9F;AAEA,IAAM,YAAY,IAAI,IAAI,mBAAmB,KAAI,MAAK,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;;AAGpE,SAAgB,mBAAmB,MAA+B;CAC9D,OAAO;EACH,KAAK;EACL,KAAK,YAAY,KAAA,IAAY,WAAW,KAAK,YAAY;EACzD,KAAK,UAAU,aAAa;CAChC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AAC9B;;;;;;;;;AAUA,SAAgB,0BAA0B,QAAoC;CAC1E,MAAM,OAAO,UAAU,IAAI,MAAM;CACjC,OAAO,OAAO,mBAAmB,IAAI,IAAI,KAAA;AAC7C;;;;;;;;;;AAWA,SAAgB,iBAAiB,YAA8B;CAC3D,MAAM,OAAQ,YAA+C;CAC7D,IAAI,SAAS,MAAM,OAAO;CAC1B,OAAO,OAAO,SAAS,YAAY,SAAS,QAAS,KAA+B,YAAY;AACpG"}
|
|
@@ -3,9 +3,82 @@
|
|
|
3
3
|
* `rebase db backups list`. Kept out of the main `cli.ts` dispatcher so the
|
|
4
4
|
* backup surface stays self-contained.
|
|
5
5
|
*/
|
|
6
|
-
import arg from "arg";
|
|
6
|
+
import type arg from "arg";
|
|
7
|
+
import type { StorageController } from "@rebasepro/server";
|
|
8
|
+
import { BackupDestination } from "./pg-tools.js";
|
|
9
|
+
/**
|
|
10
|
+
* The controller for an object-storage destination, or `null` for a local one.
|
|
11
|
+
*
|
|
12
|
+
* Built by `backupStorageFromEnv` from `@rebasepro/server` — the function the
|
|
13
|
+
* Backups panel's routes use — so the CLI and the panel read a bucket the same
|
|
14
|
+
* way. This file had its own copy, and the two had already drifted once
|
|
15
|
+
* (`S3_FORCE_PATH_STYLE` read as `=== "true"`, so a MinIO backup addressed the
|
|
16
|
+
* bucket as a host).
|
|
17
|
+
*
|
|
18
|
+
* Exported for its test.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveStorageForDestination(dest: BackupDestination, env: Record<string, string | undefined>): Promise<StorageController | null>;
|
|
21
|
+
/**
|
|
22
|
+
* The directory `rebase` was run from, which `rebase` puts here for the driver.
|
|
23
|
+
*
|
|
24
|
+
* The driver runs in the project's `backend/`, so a relative path resolved
|
|
25
|
+
* against its own working directory named a different place from the one the
|
|
26
|
+
* user typed it in: `rebase db backup --out ./backups` wrote to `<root>/backups`
|
|
27
|
+
* (the CLI resolves explicit paths itself) while `rebase db backups list`
|
|
28
|
+
* looked in `<root>/backend/backups`. Spelled the same in
|
|
29
|
+
* `packages/cli/src/commands/db.ts`.
|
|
30
|
+
*/
|
|
31
|
+
export declare const INVOCATION_CWD_ENV = "REBASE_INVOCATION_CWD";
|
|
32
|
+
/** Where a relative path the user typed is relative to: where they ran `rebase`. */
|
|
33
|
+
export declare function invocationDir(env?: Readonly<Record<string, string | undefined>>): string;
|
|
34
|
+
/**
|
|
35
|
+
* Where `db backup` writes and `db backups list` reads, by one rule.
|
|
36
|
+
*
|
|
37
|
+
* - `--out` is relative to where `rebase` was run, like any path typed on a
|
|
38
|
+
* command line.
|
|
39
|
+
* - `BACKUP_DESTINATION` is relative to the backend directory, because the
|
|
40
|
+
* server reads the same variable there: the Backups panel and the scheduled
|
|
41
|
+
* backup have to find what the CLI wrote.
|
|
42
|
+
* - With neither, `./backups` where `rebase` was run, as the help says.
|
|
43
|
+
*
|
|
44
|
+
* An `s3://` or `gs://` destination is returned as given.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveBackupLocation(opts: {
|
|
47
|
+
flag?: string;
|
|
48
|
+
configured?: string;
|
|
49
|
+
invocationDir: string;
|
|
50
|
+
backendDir: string;
|
|
51
|
+
}): string;
|
|
52
|
+
/**
|
|
53
|
+
* Parse a `rebase db backup …` line. An undeclared flag is refused (see
|
|
54
|
+
* `parseCommandFlags`), and so is a positional: `rebase db backup ./backups`
|
|
55
|
+
* means `--out ./backups`, and taken as nothing it wrote to the default
|
|
56
|
+
* directory.
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseBackupLine(rawArgs: string[]): arg.Result<{
|
|
59
|
+
"--database-url": StringConstructor;
|
|
60
|
+
"--docker": BooleanConstructor;
|
|
61
|
+
"--debug": BooleanConstructor;
|
|
62
|
+
"--help": BooleanConstructor;
|
|
63
|
+
"-h": string;
|
|
64
|
+
} & {
|
|
65
|
+
"--out": StringConstructor;
|
|
66
|
+
"--output": string;
|
|
67
|
+
"--exclude-schema": [StringConstructor];
|
|
68
|
+
"--no-owner": BooleanConstructor;
|
|
69
|
+
"--enable-row-security": BooleanConstructor;
|
|
70
|
+
"--row-security-role": StringConstructor;
|
|
71
|
+
"-o": string;
|
|
72
|
+
}>;
|
|
7
73
|
export declare function backupCommand(rawArgs: string[]): Promise<void>;
|
|
8
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Parse a `rebase db restore …` line; the one positional is the backup.
|
|
76
|
+
*
|
|
77
|
+
* Strict, because this is the command where a typo costs most: parsed
|
|
78
|
+
* permissively, `--targt-db x` became a second positional, the target fell
|
|
79
|
+
* back to DATABASE_URL's own database, and the restore ran against the live
|
|
80
|
+
* one.
|
|
81
|
+
*/
|
|
9
82
|
export declare function parseRestoreLine(rawArgs: string[]): arg.Result<{
|
|
10
83
|
"--database-url": StringConstructor;
|
|
11
84
|
"--docker": BooleanConstructor;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StorageController } from "@rebasepro/server";
|
|
2
|
+
import { resolvePgBinary } from "./pg-binary.js";
|
|
2
3
|
import { BackupDestination, type RowSecurityIdentity, VersionCompatibility } from "./pg-tools.js";
|
|
3
4
|
import { BackupObject, RetentionOptions } from "./retention.js";
|
|
4
5
|
/**
|
|
@@ -11,8 +12,7 @@ export declare class BackupToolError extends Error {
|
|
|
11
12
|
readonly hint?: string | undefined;
|
|
12
13
|
constructor(message: string, hint?: string | undefined);
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
export declare function resolvePgBinary(tool: "pg_dump" | "pg_restore" | "pg_dumpall", env?: Record<string, string | undefined>): string | null;
|
|
15
|
+
export { resolvePgBinary };
|
|
16
16
|
/** Run `<bin> --version` and extract the major version. */
|
|
17
17
|
export declare function detectToolMajor(bin: string): Promise<number | null>;
|
|
18
18
|
/** Query the server for its major version via `server_version_num`. */
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type PgTool = "pg_dump" | "pg_restore" | "pg_dumpall";
|
|
2
|
+
/** The variable that points at `tool` when it is not on the PATH. */
|
|
3
|
+
export declare function pgToolVariable(tool: PgTool): string;
|
|
4
|
+
/** Locate `pg_dump` / `pg_restore` / `pg_dumpall`, honouring an env override. */
|
|
5
|
+
export declare function resolvePgBinary(tool: PgTool, env?: Record<string, string | undefined>): string | null;
|