@rebasepro/server-postgres 0.0.1-canary.4829d6e

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.
Files changed (121) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +86 -0
  3. package/dist/PostgresAdapter.d.ts +6 -0
  4. package/dist/PostgresBackendDriver.d.ts +150 -0
  5. package/dist/PostgresBootstrapper.d.ts +51 -0
  6. package/dist/auth/ensure-tables.d.ts +10 -0
  7. package/dist/auth/services.d.ts +250 -0
  8. package/dist/backup/backup-cli.d.ts +3 -0
  9. package/dist/backup/backup-cron.d.ts +53 -0
  10. package/dist/backup/backup-service.d.ts +85 -0
  11. package/dist/backup/index.d.ts +12 -0
  12. package/dist/backup/pg-tools.d.ts +110 -0
  13. package/dist/backup/retention.d.ts +35 -0
  14. package/dist/chunk-DSJWtz9O.js +40 -0
  15. package/dist/cli-errors.d.ts +42 -0
  16. package/dist/cli-helpers.d.ts +7 -0
  17. package/dist/cli.d.ts +1 -0
  18. package/dist/collections/PostgresCollectionRegistry.d.ts +47 -0
  19. package/dist/connection.d.ts +65 -0
  20. package/dist/data-transformer.d.ts +55 -0
  21. package/dist/databasePoolManager.d.ts +20 -0
  22. package/dist/history/HistoryService.d.ts +71 -0
  23. package/dist/history/ensure-history-table.d.ts +7 -0
  24. package/dist/index.d.ts +15 -0
  25. package/dist/index.es.js +23535 -0
  26. package/dist/index.es.js.map +1 -0
  27. package/dist/interfaces.d.ts +18 -0
  28. package/dist/schema/auth-bootstrap-sql.d.ts +24 -0
  29. package/dist/schema/auth-default-policies.d.ts +10 -0
  30. package/dist/schema/auth-schema.d.ts +2376 -0
  31. package/dist/schema/doctor-cli.d.ts +2 -0
  32. package/dist/schema/doctor.d.ts +58 -0
  33. package/dist/schema/dynamic-tables.d.ts +31 -0
  34. package/dist/schema/generate-drizzle-schema-logic.d.ts +2 -0
  35. package/dist/schema/generate-drizzle-schema.d.ts +1 -0
  36. package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -0
  37. package/dist/schema/generate-postgres-ddl.d.ts +1 -0
  38. package/dist/schema/introspect-db-inference.d.ts +5 -0
  39. package/dist/schema/introspect-db-logic.d.ts +118 -0
  40. package/dist/schema/introspect-db.d.ts +1 -0
  41. package/dist/schema/introspect-runtime.d.ts +57 -0
  42. package/dist/schema/test-schema.d.ts +24 -0
  43. package/dist/security/policy-drift.d.ts +57 -0
  44. package/dist/security/rls-enforcement.d.ts +122 -0
  45. package/dist/services/BranchService.d.ts +47 -0
  46. package/dist/services/FetchService.d.ts +214 -0
  47. package/dist/services/PersistService.d.ts +39 -0
  48. package/dist/services/RelationService.d.ts +109 -0
  49. package/dist/services/cdc/CdcListener.d.ts +54 -0
  50. package/dist/services/cdc/trigger-cdc.d.ts +64 -0
  51. package/dist/services/collection-helpers.d.ts +38 -0
  52. package/dist/services/dataService.d.ts +110 -0
  53. package/dist/services/index.d.ts +4 -0
  54. package/dist/services/realtimeService.d.ts +298 -0
  55. package/dist/src-Eh-CZosp.js +595 -0
  56. package/dist/src-Eh-CZosp.js.map +1 -0
  57. package/dist/types.d.ts +3 -0
  58. package/dist/utils/drizzle-conditions.d.ts +138 -0
  59. package/dist/utils/pg-array-null-patch.d.ts +16 -0
  60. package/dist/utils/pg-error-utils.d.ts +65 -0
  61. package/dist/utils/table-classification.d.ts +8 -0
  62. package/dist/websocket.d.ts +18 -0
  63. package/package.json +113 -0
  64. package/src/PostgresAdapter.ts +58 -0
  65. package/src/PostgresBackendDriver.ts +1387 -0
  66. package/src/PostgresBootstrapper.ts +581 -0
  67. package/src/auth/ensure-tables.ts +367 -0
  68. package/src/auth/services.ts +1321 -0
  69. package/src/backup/backup-cli.ts +383 -0
  70. package/src/backup/backup-cron.ts +189 -0
  71. package/src/backup/backup-service.ts +299 -0
  72. package/src/backup/index.ts +12 -0
  73. package/src/backup/pg-tools.ts +231 -0
  74. package/src/backup/retention.ts +75 -0
  75. package/src/cli-errors.ts +265 -0
  76. package/src/cli-helpers.ts +196 -0
  77. package/src/cli.ts +786 -0
  78. package/src/collections/PostgresCollectionRegistry.ts +103 -0
  79. package/src/connection.ts +166 -0
  80. package/src/data-transformer.ts +733 -0
  81. package/src/databasePoolManager.ts +87 -0
  82. package/src/history/HistoryService.ts +272 -0
  83. package/src/history/ensure-history-table.ts +46 -0
  84. package/src/index.ts +15 -0
  85. package/src/interfaces.ts +60 -0
  86. package/src/schema/auth-bootstrap-sql.ts +41 -0
  87. package/src/schema/auth-default-policies.ts +125 -0
  88. package/src/schema/auth-schema.ts +233 -0
  89. package/src/schema/doctor-cli.ts +113 -0
  90. package/src/schema/doctor.ts +733 -0
  91. package/src/schema/dynamic-tables.test.ts +302 -0
  92. package/src/schema/dynamic-tables.ts +293 -0
  93. package/src/schema/generate-drizzle-schema-logic.ts +850 -0
  94. package/src/schema/generate-drizzle-schema.ts +131 -0
  95. package/src/schema/generate-postgres-ddl-logic.ts +490 -0
  96. package/src/schema/generate-postgres-ddl.ts +92 -0
  97. package/src/schema/introspect-db-inference.ts +238 -0
  98. package/src/schema/introspect-db-logic.ts +910 -0
  99. package/src/schema/introspect-db.ts +266 -0
  100. package/src/schema/introspect-runtime.test.ts +212 -0
  101. package/src/schema/introspect-runtime.ts +293 -0
  102. package/src/schema/test-schema.ts +11 -0
  103. package/src/security/policy-drift.test.ts +122 -0
  104. package/src/security/policy-drift.ts +159 -0
  105. package/src/security/rls-enforcement.ts +295 -0
  106. package/src/services/BranchService.ts +251 -0
  107. package/src/services/FetchService.ts +1661 -0
  108. package/src/services/PersistService.ts +329 -0
  109. package/src/services/RelationService.ts +1306 -0
  110. package/src/services/cdc/CdcListener.ts +167 -0
  111. package/src/services/cdc/trigger-cdc.ts +169 -0
  112. package/src/services/collection-helpers.ts +151 -0
  113. package/src/services/dataService.ts +246 -0
  114. package/src/services/index.ts +13 -0
  115. package/src/services/realtimeService.ts +1502 -0
  116. package/src/types.ts +4 -0
  117. package/src/utils/drizzle-conditions.ts +1162 -0
  118. package/src/utils/pg-array-null-patch.ts +42 -0
  119. package/src/utils/pg-error-utils.ts +227 -0
  120. package/src/utils/table-classification.ts +16 -0
  121. package/src/websocket.ts +640 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rebase
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @rebasepro/server-postgres
2
+
3
+ PostgreSQL database driver for Rebase, built on Drizzle ORM.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @rebasepro/server-postgres
9
+ ```
10
+
11
+ ## What This Package Does
12
+
13
+ Implements the Rebase `DatabaseAdapter` / `BackendBootstrapper` interfaces for PostgreSQL. It provides connection pooling, a Drizzle-based data driver, Postgres LISTEN/NOTIFY realtime, auth table management, snapshot history, schema generation, branching, read replicas, and WebSocket support. Plug it into `@rebasepro/server` via `createPostgresAdapter()` or `createPostgresBootstrapper()`.
14
+
15
+ ## Key Exports
16
+
17
+ | Export | Description |
18
+ |--------|-------------|
19
+ | `createPostgresAdapter(config)` | Creates a `DatabaseAdapter` for use with `initializeRebaseBackend({ database: ... })`. Recommended API. |
20
+ | `createPostgresBootstrapper(config)` | Lower-level `BackendBootstrapper` factory. Used internally by the adapter. |
21
+ | `createPostgresDatabaseConnection(url, schema?, poolConfig?)` | Creates a production-grade pooled Drizzle connection. Returns `{ db, pool, connectionString }`. |
22
+ | `createDirectDatabaseConnection(url, schema?, poolConfig?)` | Non-pooled connection for LISTEN/NOTIFY and advisory locks (bypasses PgBouncer). |
23
+ | `createReadReplicaConnection(url, schema?, poolConfig?)` | Read-only connection for routing reads to replicas. |
24
+ | `PostgresBackendDriver` | The `DataDriver` implementation — CRUD, filtering, RLS, subcollections, admin SQL. |
25
+ | `RealtimeService` | Postgres LISTEN/NOTIFY-based `RealtimeProvider`. |
26
+ | `DatabasePoolManager` | Per-branch/per-tenant dynamic pool management (used with `ADMIN_CONNECTION_STRING`). |
27
+ | `PostgresCollectionRegistry` | Collection → Drizzle table registry with enum and relation tracking. |
28
+ | `BranchService` | Database branching (schema-level isolation). |
29
+ | `generateDrizzleSchema(collections)` | Generates Drizzle schema code from collection definitions. |
30
+ | `createAuthSchema(schemaName?)` | Generates Drizzle tables for the auth system (`users`, `roles`, `user_roles`). |
31
+
32
+ ## Quick Start
33
+
34
+ ```typescript
35
+ import { createPostgresDatabaseConnection } from "@rebasepro/server-postgres";
36
+ import { createPostgresAdapter } from "@rebasepro/server-postgres";
37
+ import { initializeRebaseBackend } from "@rebasepro/server";
38
+ import * as schema from "./generated/schema";
39
+
40
+ // Create connection
41
+ const { db, pool } = createPostgresDatabaseConnection(
42
+ process.env.DATABASE_URL,
43
+ schema
44
+ );
45
+
46
+ // Create adapter and pass to server
47
+ const database = createPostgresAdapter({
48
+ connection: db,
49
+ connectionString: process.env.DATABASE_URL,
50
+ schema: { tables: schema },
51
+ });
52
+
53
+ const backend = await initializeRebaseBackend({
54
+ app,
55
+ server,
56
+ database,
57
+ collections,
58
+ auth: { /* ... */ },
59
+ });
60
+
61
+ // Graceful shutdown
62
+ process.on("SIGTERM", async () => {
63
+ await backend.shutdown();
64
+ await pool.end();
65
+ });
66
+ ```
67
+
68
+ ## Connection Pool Defaults
69
+
70
+ | Option | Default |
71
+ |--------|---------|
72
+ | `max` | 20 |
73
+ | `idleTimeoutMillis` | 30,000 |
74
+ | `connectionTimeoutMillis` | 10,000 |
75
+ | `queryTimeout` | 30,000 |
76
+ | `statementTimeout` | 30,000 |
77
+ | `keepAlive` | true |
78
+
79
+ ## Related Packages
80
+
81
+ | Package | Role |
82
+ |---------|------|
83
+ | `@rebasepro/server` | Backend orchestrator that consumes this adapter |
84
+ | `@rebasepro/types` | Shared interfaces (`DatabaseAdapter`, `BackendBootstrapper`, `DataDriver`) |
85
+ | `@rebasepro/codegen` | Generates typed SDKs from collections |
86
+ | `@rebasepro/common` | Default collections and shared utilities |
@@ -0,0 +1,6 @@
1
+ import { DatabaseAdapter } from "@rebasepro/types";
2
+ import type { PostgresDriverConfig } from "./PostgresBootstrapper";
3
+ /**
4
+ * Creates a Postgres database adapter for Rebase.
5
+ */
6
+ export declare function createPostgresAdapter(pgConfig: PostgresDriverConfig): DatabaseAdapter;
@@ -0,0 +1,150 @@
1
+ import { DataService } from "./services/dataService";
2
+ import { BranchService } from "./services/BranchService";
3
+ import { RealtimeService } from "./services/realtimeService";
4
+ import { DatabasePoolManager } from "./databasePoolManager";
5
+ import { DrizzleClient } from "./interfaces";
6
+ import { DatabaseAdmin, DataDriver, DeleteProps, CollectionConfig, FetchCollectionProps, FetchOneProps, ListenCollectionProps, ListenOneProps, RebaseClient, RebaseSdkData, RestFetchService, SaveProps, TableMetadata, User } from "@rebasepro/types";
7
+ import { PostgresCollectionRegistry } from "./collections/PostgresCollectionRegistry";
8
+ import { HistoryService } from "./history/HistoryService";
9
+ export declare class PostgresBackendDriver implements DataDriver {
10
+ db: DrizzleClient;
11
+ readonly registry: PostgresCollectionRegistry;
12
+ poolManager?: DatabasePoolManager | undefined;
13
+ key: string;
14
+ initialised: boolean;
15
+ dataService: DataService;
16
+ realtimeService: RealtimeService;
17
+ historyService?: HistoryService;
18
+ branchService?: BranchService;
19
+ user?: User;
20
+ data: RebaseSdkData;
21
+ client?: RebaseClient;
22
+ /**
23
+ * Auto-set to `true` when a SET LOCAL ROLE fails with insufficient
24
+ * privileges, so subsequent queries skip the doomed attempt.
25
+ * Mirrors the static `DISABLE_DB_ROLE_SWITCHING` env var but is
26
+ * learned at runtime.
27
+ */
28
+ private _roleSwitchingDisabled;
29
+ /**
30
+ * Restricted role that authenticated (user-context) requests run as (via
31
+ * `SET LOCAL ROLE`) so RLS binds every statement — reads *and* writes. Set
32
+ * by the bootstrapper after posture detection: defined when the connection
33
+ * would otherwise bypass RLS (superuser / BYPASSRLS / table owner),
34
+ * undefined when RLS already applies natively. The base (server-context)
35
+ * driver never switches — it is the trusted owner plane (auth flows,
36
+ * migrations, `dataAsAdmin`).
37
+ */
38
+ rlsUserRole?: string;
39
+ /**
40
+ * When true, realtime notifications are deferred until after the
41
+ * wrapping transaction commits. Set by `withAuth` → `withTransaction`.
42
+ */
43
+ _deferNotifications: boolean;
44
+ _pendingNotifications: Array<{
45
+ path: string;
46
+ id: string;
47
+ row: Record<string, unknown> | null;
48
+ databaseId?: string;
49
+ }>;
50
+ constructor(db: DrizzleClient, realtimeService: RealtimeService, registry: PostgresCollectionRegistry, user?: User, poolManager?: DatabasePoolManager | undefined, historyService?: HistoryService);
51
+ /**
52
+ * Typed admin capabilities (SQLAdmin + SchemaAdmin + BranchAdmin).
53
+ * Implemented as a getter so method references are resolved at call-time,
54
+ * allowing test spies applied after construction to take effect.
55
+ */
56
+ get admin(): DatabaseAdmin;
57
+ /**
58
+ * REST-optimised fetch service (include-aware eager-loading).
59
+ * Delegates to the underlying FetchService (include-aware eager loading),
60
+ * then runs the afterRead pipeline on the results. The raw FetchService does
61
+ * NOT run callbacks, so masking must be applied here — otherwise every
62
+ * REST/SDK read leaks unmasked data (see {@link applyAfterReadForRest}).
63
+ */
64
+ get restFetchService(): RestFetchService;
65
+ private buildCallContext;
66
+ private resolveCollectionCallbacks;
67
+ /**
68
+ * Run the three-tier afterRead pipeline (global → collection → property) on a
69
+ * single row for a collection whose callbacks have already been resolved.
70
+ */
71
+ private applyAfterReadToRow;
72
+ private static hasAfterRead;
73
+ /**
74
+ * Apply afterRead to REST/SDK read results.
75
+ *
76
+ * The REST / `include` path fetches rows through the raw fetch service, which
77
+ * does NOT run callbacks — so without this, `afterRead` transforms (e.g. PII
78
+ * masking) are silently skipped on every SDK/REST read, leaking raw data.
79
+ * This choke point guarantees afterRead runs there too, matching the driver's
80
+ * fetchCollection/fetchOne paths.
81
+ *
82
+ * It also masks embedded relation data one level deep by running the TARGET
83
+ * collection's afterRead (so `post.author.email` is masked by the authors
84
+ * collection, not left raw).
85
+ */
86
+ applyAfterReadForRest(rows: Record<string, unknown>[], path: string): Promise<Record<string, unknown>[]>;
87
+ fetchCollection<M extends Record<string, unknown>>({ path, collection, filter, limit, offset, startAfter, orderBy, searchString, order, vectorSearch }: FetchCollectionProps<M>): Promise<Record<string, unknown>[]>;
88
+ listenCollection<M extends Record<string, unknown>>({ path, collection, filter, limit, offset, startAfter, orderBy, searchString, order, onUpdate, onError }: ListenCollectionProps<M>): () => void;
89
+ fetchOne<M extends Record<string, unknown>>({ path, id, databaseId, collection }: FetchOneProps<M>): Promise<Record<string, unknown> | undefined>;
90
+ listenOne<M extends Record<string, unknown>>({ path, id, collection, onUpdate, onError }: ListenOneProps<M>): () => void;
91
+ save<M extends Record<string, unknown>>({ path, id, values, collection, status }: SaveProps<M>): Promise<Record<string, unknown>>;
92
+ delete<M extends Record<string, unknown>>({ row, collection }: DeleteProps<M>): Promise<void>;
93
+ deleteAll(path: string): Promise<void>;
94
+ checkUniqueField(path: string, name: string, value: unknown, id?: string, collection?: CollectionConfig): Promise<boolean>;
95
+ count<M extends Record<string, unknown>>({ path, collection, filter, searchString }: FetchCollectionProps<M>): Promise<number>;
96
+ private getTargetDb;
97
+ executeSql(sqlText: string, options?: {
98
+ database?: string;
99
+ role?: string;
100
+ params?: unknown[];
101
+ }): Promise<Record<string, unknown>[]>;
102
+ fetchAvailableDatabases(): Promise<string[]>;
103
+ fetchAvailableRoles(): Promise<string[]>;
104
+ fetchCurrentDatabase(): Promise<string | undefined>;
105
+ /**
106
+ * Fetch public tables that are not yet mapped to a collection.
107
+ * Excludes internal tables (_rebase_*, _auth_*, auth tables, etc.)
108
+ * and junction/connection tables used for many-to-many relations.
109
+ */
110
+ fetchUnmappedTables(mappedPaths?: string[]): Promise<string[]>;
111
+ /**
112
+ * Fetch metadata for a given table from information_schema (columns, policies, constraints).
113
+ */
114
+ fetchTableMetadata(tableName: string): Promise<TableMetadata>;
115
+ private generateSubscriptionId;
116
+ /**
117
+ * Create a new delegate instance with authenticated context.
118
+ * Starts a transaction and sets the current_user_id and current_user_roles
119
+ * configuration parameters for PostgreSQL Row Level Security.
120
+ */
121
+ withAuth(user: User): Promise<DataDriver>;
122
+ }
123
+ export declare class AuthenticatedPostgresBackendDriver implements DataDriver {
124
+ delegate: PostgresBackendDriver;
125
+ key: string;
126
+ initialised: boolean;
127
+ user: User;
128
+ data: RebaseSdkData;
129
+ constructor(delegate: PostgresBackendDriver, user: User);
130
+ /**
131
+ * Typed admin capabilities — delegates to the base driver.
132
+ */
133
+ admin: DatabaseAdmin;
134
+ get restFetchService(): RestFetchService;
135
+ private withTransaction;
136
+ fetchCollection<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]>;
137
+ /**
138
+ * Injects the authenticated user's context into the most recently
139
+ * registered realtime subscription so RLS-aware polling can apply.
140
+ */
141
+ private injectAuthContext;
142
+ listenCollection<M extends Record<string, unknown>>(props: ListenCollectionProps<M>): () => void;
143
+ fetchOne<M extends Record<string, unknown>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined>;
144
+ listenOne<M extends Record<string, unknown>>(props: ListenOneProps<M>): () => void;
145
+ save<M extends Record<string, unknown>>(props: SaveProps<M>): Promise<Record<string, unknown>>;
146
+ delete<M extends Record<string, unknown>>(props: DeleteProps<M>): Promise<void>;
147
+ deleteAll(path: string): Promise<void>;
148
+ checkUniqueField(path: string, name: string, value: unknown, id?: string, collection?: CollectionConfig): Promise<boolean>;
149
+ count<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number>;
150
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * PostgresBootstrapper
3
+ *
4
+ * Implements the `BackendBootstrapper` interface for PostgreSQL.
5
+ */
6
+ import { NodePgDatabase } from "drizzle-orm/node-postgres";
7
+ import { BackendBootstrapper } from "@rebasepro/types";
8
+ import { PostgresBackendDriver } from "./PostgresBackendDriver";
9
+ import { RealtimeService } from "./services/realtimeService";
10
+ import { DatabasePoolManager } from "./databasePoolManager";
11
+ import { PostgresCollectionRegistry } from "./collections/PostgresCollectionRegistry";
12
+ export interface PostgresDriverConfig {
13
+ connectionString?: string;
14
+ adminConnectionString?: string;
15
+ readConnectionString?: string;
16
+ connection?: unknown;
17
+ schema?: {
18
+ tables?: Record<string, unknown>;
19
+ enums?: Record<string, unknown>;
20
+ relations?: Record<string, unknown>;
21
+ };
22
+ /**
23
+ * PostgreSQL schema to read when deriving collections from the database
24
+ * (BaaS mode). Defaults to `public`.
25
+ */
26
+ introspectionSchema?: string;
27
+ }
28
+ /**
29
+ * Opaque internals bag that PostgresBootstrapper stores during `initializeDriver()`
30
+ * and re-uses in subsequent lifecycle hooks.
31
+ */
32
+ export interface PostgresDriverInternals {
33
+ db: NodePgDatabase<any>;
34
+ readDb?: NodePgDatabase<any>;
35
+ registry: PostgresCollectionRegistry;
36
+ realtimeService: RealtimeService;
37
+ driver: PostgresBackendDriver;
38
+ poolManager?: DatabasePoolManager;
39
+ }
40
+ /**
41
+ * Default PostgreSQL bootstrapper.
42
+ *
43
+ * Use it to register Postgres with `initializeRebaseBackend()`:
44
+ * ```typescript
45
+ * initializeRebaseBackend({
46
+ * ...config,
47
+ * bootstrappers: [postgresBootstrapper()]
48
+ * });
49
+ * ```
50
+ */
51
+ export declare function createPostgresBootstrapper(pgConfig: PostgresDriverConfig): BackendBootstrapper;
@@ -0,0 +1,10 @@
1
+ import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
+ import type { CollectionConfig } from "@rebasepro/types";
3
+ /**
4
+ * Auto-create auth tables if they don't exist.
5
+ *
6
+ * @param db — Drizzle database instance
7
+ * @param collection — The collection that represents auth users.
8
+ * When omitted, a default `rebase.users` table is created.
9
+ */
10
+ export declare function ensureAuthTablesExist(db: NodePgDatabase, collection?: CollectionConfig): Promise<void>;
@@ -0,0 +1,250 @@
1
+ import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
+ import type { RebasePgTable } from "../types";
3
+ import { UserRepository, TokenRepository, MfaRepository, AuthRepository, UserData, CreateUserData, RoleData, CreateRoleData, RefreshTokenInfo, PasswordResetTokenInfo, MagicLinkTokenInfo, UserIdentityData, ListUsersOptions, PaginatedUsersResult, MfaFactor, MfaChallengeInfo, RoleData as Role } from "@rebasepro/server";
4
+ export type { Role };
5
+ export interface AuthSchemaTables {
6
+ users: RebasePgTable;
7
+ refreshTokens: RebasePgTable;
8
+ passwordResetTokens: RebasePgTable;
9
+ appConfig: RebasePgTable;
10
+ userIdentities: RebasePgTable;
11
+ }
12
+ /**
13
+ * PostgreSQL implementation of UserRepository.
14
+ * Handles all user-related database operations using Drizzle ORM.
15
+ */
16
+ export declare class UserService implements UserRepository {
17
+ private db;
18
+ private usersTable;
19
+ private userIdentitiesTable;
20
+ constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
21
+ private getQualifiedUsersTableName;
22
+ private mapRowToUser;
23
+ private mapPayload;
24
+ createUser(data: CreateUserData): Promise<UserData>;
25
+ getUserById(id: string): Promise<UserData | null>;
26
+ getUserByEmail(email: string): Promise<UserData | null>;
27
+ getUserByIdentity(provider: string, providerId: string): Promise<UserData | null>;
28
+ getUserIdentities(userId: string): Promise<UserIdentityData[]>;
29
+ linkUserIdentity(userId: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void>;
30
+ updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null>;
31
+ deleteUser(id: string): Promise<void>;
32
+ listUsers(): Promise<UserData[]>;
33
+ listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult>;
34
+ /**
35
+ * Update user's password hash
36
+ */
37
+ updatePassword(id: string, passwordHash: string): Promise<void>;
38
+ /**
39
+ * Set email verification status
40
+ */
41
+ setEmailVerified(id: string, verified: boolean): Promise<void>;
42
+ /**
43
+ * Set email verification token
44
+ */
45
+ setVerificationToken(id: string, token: string | null): Promise<void>;
46
+ /**
47
+ * Find user by email verification token
48
+ */
49
+ getUserByVerificationToken(token: string): Promise<UserData | null>;
50
+ /**
51
+ * Get roles for a user from database (inline TEXT[] column)
52
+ */
53
+ getUserRoles(userId: string): Promise<Role[]>;
54
+ /**
55
+ * Get role IDs for a user
56
+ */
57
+ getUserRoleIds(userId: string): Promise<string[]>;
58
+ /**
59
+ * Set roles for a user (replaces existing roles)
60
+ */
61
+ setUserRoles(userId: string, roleIds: string[]): Promise<void>;
62
+ /**
63
+ * Assign a specific role to new user (appends if not present)
64
+ */
65
+ assignDefaultRole(userId: string, roleId: string): Promise<void>;
66
+ /**
67
+ * Get user with their roles
68
+ */
69
+ getUserWithRoles(userId: string): Promise<{
70
+ user: UserData;
71
+ roles: Role[];
72
+ } | null>;
73
+ }
74
+ export declare class RefreshTokenService {
75
+ private db;
76
+ private refreshTokensTable;
77
+ constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
78
+ createToken(userId: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void>;
79
+ findByHash(tokenHash: string): Promise<RefreshTokenInfo | null>;
80
+ deleteByHash(tokenHash: string): Promise<void>;
81
+ deleteAllForUser(userId: string): Promise<void>;
82
+ listForUser(userId: string): Promise<RefreshTokenInfo[]>;
83
+ deleteById(id: string, userId: string): Promise<void>;
84
+ }
85
+ /**
86
+ * Password reset token service
87
+ */
88
+ export declare class PasswordResetTokenService {
89
+ private db;
90
+ private passwordResetTokensTable;
91
+ constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
92
+ private getQualifiedPasswordResetTokensTableName;
93
+ /**
94
+ * Create a password reset token
95
+ */
96
+ createToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
97
+ /**
98
+ * Find a valid (not expired, not used) token by hash
99
+ */
100
+ findValidByHash(tokenHash: string): Promise<{
101
+ userId: string;
102
+ expiresAt: Date;
103
+ } | null>;
104
+ /**
105
+ * Mark token as used
106
+ */
107
+ markAsUsed(tokenHash: string): Promise<void>;
108
+ /**
109
+ * Delete all tokens for a user
110
+ */
111
+ deleteAllForUser(userId: string): Promise<void>;
112
+ /**
113
+ * Clean up expired tokens
114
+ */
115
+ deleteExpired(): Promise<void>;
116
+ }
117
+ /**
118
+ * Magic link token service.
119
+ * Handles magic link token storage for passwordless email login.
120
+ */
121
+ export declare class MagicLinkTokenService {
122
+ private db;
123
+ private magicLinkTokensTable;
124
+ constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
125
+ private getQualifiedTableName;
126
+ createToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
127
+ findValidByHash(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
128
+ markAsUsed(tokenHash: string): Promise<void>;
129
+ }
130
+ /**
131
+ * PostgreSQL implementation of TokenRepository.
132
+ * Combines refresh token and password reset token operations.
133
+ */
134
+ export declare class PostgresTokenRepository implements TokenRepository {
135
+ private db;
136
+ private refreshTokenService;
137
+ private passwordResetTokenService;
138
+ private magicLinkTokenService;
139
+ constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
140
+ createRefreshToken(userId: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void>;
141
+ findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null>;
142
+ deleteRefreshToken(tokenHash: string): Promise<void>;
143
+ deleteAllRefreshTokensForUser(userId: string): Promise<void>;
144
+ listRefreshTokensForUser(userId: string): Promise<RefreshTokenInfo[]>;
145
+ deleteRefreshTokenById(id: string, userId: string): Promise<void>;
146
+ createPasswordResetToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
147
+ findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null>;
148
+ markPasswordResetTokenUsed(tokenHash: string): Promise<void>;
149
+ deleteAllPasswordResetTokensForUser(userId: string): Promise<void>;
150
+ deleteExpiredTokens(): Promise<void>;
151
+ createMagicLinkToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
152
+ findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
153
+ markMagicLinkTokenUsed(tokenHash: string): Promise<void>;
154
+ }
155
+ /**
156
+ * PostgreSQL implementation of AuthRepository.
157
+ * Combines user, role, and token repository operations.
158
+ * This provides a convenient single-class interface for all auth operations.
159
+ */
160
+ export declare class PostgresAuthRepository implements AuthRepository {
161
+ private db;
162
+ private userService;
163
+ private tokenRepository;
164
+ constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
165
+ createUser(data: CreateUserData): Promise<UserData>;
166
+ getUserById(id: string): Promise<UserData | null>;
167
+ getUserByEmail(email: string): Promise<UserData | null>;
168
+ getUserByIdentity(provider: string, providerId: string): Promise<UserData | null>;
169
+ getUserIdentities(userId: string): Promise<UserIdentityData[]>;
170
+ linkUserIdentity(userId: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void>;
171
+ updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null>;
172
+ deleteUser(id: string): Promise<void>;
173
+ listUsers(): Promise<UserData[]>;
174
+ listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult>;
175
+ updatePassword(id: string, passwordHash: string): Promise<void>;
176
+ setEmailVerified(id: string, verified: boolean): Promise<void>;
177
+ setVerificationToken(id: string, token: string | null): Promise<void>;
178
+ getUserByVerificationToken(token: string): Promise<UserData | null>;
179
+ getUserRoles(userId: string): Promise<RoleData[]>;
180
+ getUserRoleIds(userId: string): Promise<string[]>;
181
+ setUserRoles(userId: string, roleIds: string[]): Promise<void>;
182
+ assignDefaultRole(userId: string, roleId: string): Promise<void>;
183
+ getUserWithRoles(userId: string): Promise<{
184
+ user: UserData;
185
+ roles: RoleData[];
186
+ } | null>;
187
+ getRoleById(id: string): Promise<RoleData | null>;
188
+ listRoles(): Promise<RoleData[]>;
189
+ createRole(_data: CreateRoleData): Promise<RoleData>;
190
+ updateRole(id: string, data: Partial<Omit<RoleData, "id">>): Promise<RoleData | null>;
191
+ deleteRole(_id: string): Promise<void>;
192
+ createRefreshToken(userId: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void>;
193
+ findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null>;
194
+ deleteRefreshToken(tokenHash: string): Promise<void>;
195
+ deleteAllRefreshTokensForUser(userId: string): Promise<void>;
196
+ listRefreshTokensForUser(userId: string): Promise<RefreshTokenInfo[]>;
197
+ deleteRefreshTokenById(id: string, userId: string): Promise<void>;
198
+ createPasswordResetToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
199
+ findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null>;
200
+ markPasswordResetTokenUsed(tokenHash: string): Promise<void>;
201
+ deleteAllPasswordResetTokensForUser(userId: string): Promise<void>;
202
+ deleteExpiredTokens(): Promise<void>;
203
+ createMagicLinkToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
204
+ findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
205
+ markMagicLinkTokenUsed(tokenHash: string): Promise<void>;
206
+ private _mfaService;
207
+ private getMfaService;
208
+ createMfaFactor(userId: string, factorType: "totp", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor>;
209
+ getMfaFactors(userId: string): Promise<MfaFactor[]>;
210
+ getMfaFactorById(factorId: string): Promise<(MfaFactor & {
211
+ secretEncrypted: string;
212
+ }) | null>;
213
+ verifyMfaFactor(factorId: string): Promise<void>;
214
+ deleteMfaFactor(factorId: string, userId: string): Promise<void>;
215
+ createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo>;
216
+ getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null>;
217
+ verifyMfaChallenge(challengeId: string): Promise<void>;
218
+ createRecoveryCodes(userId: string, codeHashes: string[]): Promise<void>;
219
+ useRecoveryCode(userId: string, codeHash: string): Promise<boolean>;
220
+ getUnusedRecoveryCodeCount(userId: string): Promise<number>;
221
+ deleteAllRecoveryCodes(userId: string): Promise<void>;
222
+ hasVerifiedMfaFactors(userId: string): Promise<boolean>;
223
+ }
224
+ /**
225
+ * PostgreSQL implementation of MfaRepository.
226
+ * Handles all MFA-related database operations.
227
+ */
228
+ export declare class MfaService implements MfaRepository {
229
+ private db;
230
+ private schemaName;
231
+ constructor(db: NodePgDatabase, schemaName?: string);
232
+ private qualify;
233
+ createMfaFactor(userId: string, factorType: "totp", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor>;
234
+ getMfaFactors(userId: string): Promise<MfaFactor[]>;
235
+ getMfaFactorById(factorId: string): Promise<(MfaFactor & {
236
+ secretEncrypted: string;
237
+ }) | null>;
238
+ verifyMfaFactor(factorId: string): Promise<void>;
239
+ deleteMfaFactor(factorId: string, userId: string): Promise<void>;
240
+ createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo>;
241
+ getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null>;
242
+ verifyMfaChallenge(challengeId: string): Promise<void>;
243
+ createRecoveryCodes(userId: string, codeHashes: string[]): Promise<void>;
244
+ useRecoveryCode(userId: string, codeHash: string): Promise<boolean>;
245
+ getUnusedRecoveryCodeCount(userId: string): Promise<number>;
246
+ deleteAllRecoveryCodes(userId: string): Promise<void>;
247
+ hasVerifiedMfaFactors(userId: string): Promise<boolean>;
248
+ }
249
+ /** PostgreSQL user repository implementation */
250
+ export type PostgresUserRepository = UserService;
@@ -0,0 +1,3 @@
1
+ export declare function backupCommand(rawArgs: string[]): Promise<void>;
2
+ export declare function restoreCommand(rawArgs: string[]): Promise<void>;
3
+ export declare function backupsCommand(rawArgs: string[]): Promise<void>;
@@ -0,0 +1,53 @@
1
+ import type { CronJobDefinition } from "@rebasepro/types";
2
+ import type { StorageController } from "@rebasepro/server";
3
+ import { BackupDestination } from "./pg-tools";
4
+ export interface BackupCronConfig {
5
+ /** Cron expression, e.g. `"0 3 * * *"` for 03:00 daily. */
6
+ schedule: string;
7
+ /** Postgres connection string. Defaults to `DATABASE_URL`. */
8
+ connectionString: string;
9
+ /** Where backups are written: a local path or an `s3://` / `gs://` URL. */
10
+ destination: BackupDestination;
11
+ /**
12
+ * Storage controller used for `s3`/`gcs` destinations. Reuse the one the
13
+ * backend already configured (S3/GCS/Local StorageController). Not needed
14
+ * for local destinations.
15
+ */
16
+ storage?: StorageController;
17
+ /** Delete backups older than this many days. `0` disables pruning. */
18
+ retentionDays?: number;
19
+ /** Always keep at least this many recent backups regardless of age. */
20
+ keepMinimum?: number;
21
+ /** Schemas to exclude from the dump (defaults to Atlas revision schema). */
22
+ excludeSchemas?: string[];
23
+ /** Cron job display name. */
24
+ name?: string;
25
+ /** Whether the job is enabled. */
26
+ enabled?: boolean;
27
+ }
28
+ export interface EnvResolution {
29
+ /** Resolved config, present when a schedule is configured. */
30
+ config?: Omit<BackupCronConfig, "storage">;
31
+ /** True when `BACKUP_SCHEDULE` is unset — the cron should be a no-op. */
32
+ disabled?: boolean;
33
+ /** Human-readable reason the config could not be built. */
34
+ error?: string;
35
+ }
36
+ /**
37
+ * Build backup-cron config purely from environment variables.
38
+ * Recognised keys:
39
+ * - `BACKUP_SCHEDULE` cron expression (required to enable)
40
+ * - `BACKUP_DESTINATION` local path or `s3://` / `gs://` URL (required)
41
+ * - `BACKUP_RETENTION_DAYS` integer days (optional)
42
+ * - `BACKUP_KEEP_MINIMUM` integer count (optional)
43
+ * - `DATABASE_URL` connection string
44
+ *
45
+ * Pure and side-effect free so it can be unit-tested without a database.
46
+ */
47
+ export declare function backupCronConfigFromEnv(env: Record<string, string | undefined>): EnvResolution;
48
+ /**
49
+ * Create a {@link CronJobDefinition} that dumps the database, uploads the
50
+ * result to the configured destination, and prunes old backups. Object
51
+ * destinations require {@link BackupCronConfig.storage}.
52
+ */
53
+ export declare function createBackupCron(config: BackupCronConfig): CronJobDefinition;