@rebasepro/server-postgres 0.17.3 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +31 -0
  3. package/dist/backup-argv.d.ts +19 -0
  4. package/dist/branch-argv.d.ts +28 -0
  5. package/dist/branch-prune.d.ts +77 -0
  6. package/dist/cli-collections-path.d.ts +31 -0
  7. package/dist/cli-errors.d.ts +123 -0
  8. package/dist/cli-flags.d.ts +101 -0
  9. package/dist/cli-helpers.d.ts +94 -10
  10. package/dist/collection-index-C7LopMyX.js +247 -0
  11. package/dist/collection-index-C7LopMyX.js.map +1 -0
  12. package/dist/databasePoolManager.d.ts +27 -0
  13. package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-CagOzEPS.js} +6 -6
  14. package/dist/{ensure-collection-policies-UCqgv_8c.js.map → ensure-collection-policies-CagOzEPS.js.map} +1 -1
  15. package/dist/{ensure-collection-tables-DgVixhX3.js → ensure-collection-tables-DbAhrjnG.js} +614 -52
  16. package/dist/ensure-collection-tables-DbAhrjnG.js.map +1 -0
  17. package/dist/index.d.ts +12 -0
  18. package/dist/index.es.js +19307 -3059
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-ClJoTboT.js} +53 -189
  21. package/dist/rls-enforcement-ClJoTboT.js.map +1 -0
  22. package/dist/schema/column-type-drift.d.ts +79 -0
  23. package/dist/schema/doctor.d.ts +42 -0
  24. package/dist/schema/ensure-collection-tables.d.ts +90 -0
  25. package/dist/schema/generate-next-step.d.ts +30 -0
  26. package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -0
  27. package/dist/schema/generated-schema-staleness.d.ts +37 -0
  28. package/dist/schema/introspect-db-queries.d.ts +34 -0
  29. package/dist/security/rls-enforcement.d.ts +63 -2
  30. package/dist/services/BranchService.d.ts +86 -1
  31. package/dist/services/collection-helpers.d.ts +1 -1
  32. package/dist/services/realtimeService.d.ts +30 -2
  33. package/dist/services/row-pipeline.d.ts +12 -1
  34. package/dist/utils/pg-error-utils.d.ts +3 -1
  35. package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
  36. package/package.json +32 -27
  37. package/dist/backup-service-DCk7KhhL.js +0 -9044
  38. package/dist/backup-service-DCk7KhhL.js.map +0 -1
  39. package/dist/collection-index-BRUg10H5.js +0 -5690
  40. package/dist/collection-index-BRUg10H5.js.map +0 -1
  41. package/dist/connection-GOKU3Hu5.js +0 -277
  42. package/dist/connection-GOKU3Hu5.js.map +0 -1
  43. package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
  44. package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
  45. package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
  46. package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
  47. package/dist/src-DiB5RP2Z.js +0 -541
  48. package/dist/src-DiB5RP2Z.js.map +0 -1
  49. package/dist/websocket-BZ4H5wUz.js +0 -8198
  50. package/dist/websocket-BZ4H5wUz.js.map +0 -1
  51. package/src/PostgresAdapter.ts +0 -91
  52. package/src/PostgresBackendDriver.ts +0 -1857
  53. package/src/PostgresBootstrapper.ts +0 -1157
  54. package/src/auth/ensure-tables.ts +0 -896
  55. package/src/auth/schema-version.ts +0 -260
  56. package/src/auth/services.ts +0 -1610
  57. package/src/backup/backup-cli.ts +0 -520
  58. package/src/backup/backup-cron.ts +0 -212
  59. package/src/backup/backup-logic.ts +0 -93
  60. package/src/backup/backup-service.ts +0 -485
  61. package/src/backup/index.ts +0 -12
  62. package/src/backup/pg-tools.ts +0 -403
  63. package/src/backup/retention.ts +0 -86
  64. package/src/cli-errors.ts +0 -265
  65. package/src/cli-helpers.ts +0 -569
  66. package/src/cli-output.ts +0 -43
  67. package/src/cli.ts +0 -1339
  68. package/src/collections/PostgresCollectionRegistry.ts +0 -108
  69. package/src/collections/buildRegistry.ts +0 -68
  70. package/src/collections/validate-relations.ts +0 -402
  71. package/src/connection.ts +0 -333
  72. package/src/data-transformer.ts +0 -845
  73. package/src/databasePoolManager.ts +0 -95
  74. package/src/history/HistoryService.ts +0 -254
  75. package/src/history/ensure-history-table.ts +0 -53
  76. package/src/index.ts +0 -19
  77. package/src/interfaces.ts +0 -60
  78. package/src/module-dir.ts +0 -7
  79. package/src/schema/atlas-argv.ts +0 -94
  80. package/src/schema/auth-schema.ts +0 -298
  81. package/src/schema/auth-users-columns.ts +0 -131
  82. package/src/schema/carved-out-migration.ts +0 -404
  83. package/src/schema/classify-change.ts +0 -436
  84. package/src/schema/collection-index.ts +0 -427
  85. package/src/schema/collections-schema-version.ts +0 -103
  86. package/src/schema/destructive-sql.ts +0 -94
  87. package/src/schema/doctor-cli.ts +0 -66
  88. package/src/schema/doctor-policy-checks.ts +0 -105
  89. package/src/schema/doctor.ts +0 -837
  90. package/src/schema/drizzle-ddl.ts +0 -33
  91. package/src/schema/dynamic-tables.test.ts +0 -302
  92. package/src/schema/dynamic-tables.ts +0 -293
  93. package/src/schema/ensure-collection-policies.ts +0 -198
  94. package/src/schema/ensure-collection-tables.test.ts +0 -518
  95. package/src/schema/ensure-collection-tables.ts +0 -1267
  96. package/src/schema/generate-drizzle-schema-logic.ts +0 -957
  97. package/src/schema/generate-drizzle-schema.ts +0 -143
  98. package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
  99. package/src/schema/generate-postgres-ddl.ts +0 -180
  100. package/src/schema/generate-schema-commit.ts +0 -267
  101. package/src/schema/generated-schema-staleness.ts +0 -284
  102. package/src/schema/introspect-db-constraints.ts +0 -385
  103. package/src/schema/introspect-db-inference.ts +0 -255
  104. package/src/schema/introspect-db-logic.ts +0 -1387
  105. package/src/schema/introspect-db-naming.ts +0 -15
  106. package/src/schema/introspect-db-project.ts +0 -78
  107. package/src/schema/introspect-db-queries.ts +0 -326
  108. package/src/schema/introspect-db-structure.ts +0 -670
  109. package/src/schema/introspect-db-types.ts +0 -56
  110. package/src/schema/introspect-db.ts +0 -257
  111. package/src/schema/introspect-runtime.test.ts +0 -260
  112. package/src/schema/introspect-runtime.ts +0 -327
  113. package/src/schema/non-sql-collections.test.ts +0 -131
  114. package/src/schema/rls-bootstrap-sql.ts +0 -288
  115. package/src/schema/search-column.ts +0 -643
  116. package/src/schema/test-schema.ts +0 -11
  117. package/src/schema/vector-index.ts +0 -491
  118. package/src/security/anonymous-grants.test.ts +0 -73
  119. package/src/security/policy-drift.test.ts +0 -435
  120. package/src/security/policy-drift.ts +0 -473
  121. package/src/security/rls-enforcement.ts +0 -505
  122. package/src/services/BranchService.ts +0 -326
  123. package/src/services/FetchService.ts +0 -2115
  124. package/src/services/PersistService.ts +0 -557
  125. package/src/services/RelationService.ts +0 -978
  126. package/src/services/RelationWriteService.ts +0 -653
  127. package/src/services/cdc/CdcListener.ts +0 -103
  128. package/src/services/cdc/junction-tables.ts +0 -91
  129. package/src/services/cdc/trigger-cdc.ts +0 -173
  130. package/src/services/channel-bus/ChannelBus.ts +0 -44
  131. package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
  132. package/src/services/channel-bus/index.ts +0 -114
  133. package/src/services/channel-history.ts +0 -424
  134. package/src/services/channel-presence.ts +0 -185
  135. package/src/services/collection-helpers.ts +0 -420
  136. package/src/services/dataService.ts +0 -256
  137. package/src/services/index.ts +0 -14
  138. package/src/services/junction-writes.ts +0 -295
  139. package/src/services/nested-path.ts +0 -145
  140. package/src/services/pg-notify-listener.ts +0 -151
  141. package/src/services/realtimeService.ts +0 -2545
  142. package/src/services/row-pipeline.ts +0 -240
  143. package/src/services/write-denial.ts +0 -55
  144. package/src/types.ts +0 -4
  145. package/src/utils/connection-string.ts +0 -58
  146. package/src/utils/drizzle-conditions.ts +0 -2743
  147. package/src/utils/pg-array-null-patch.ts +0 -42
  148. package/src/utils/pg-error-utils.ts +0 -391
  149. package/src/websocket.ts +0 -855
@@ -1,95 +0,0 @@
1
- import { Pool } from "pg";
2
- import { drizzle } from "drizzle-orm/node-postgres";
3
- import { NodePgDatabase } from "drizzle-orm/node-postgres";
4
- import { logger } from "@rebasepro/server";
5
- import { guardPoolAgainstDirtyRelease, pinSearchPath, cappedPoolMax } from "./connection";
6
-
7
- export class DatabasePoolManager {
8
- private pools: Map<string, Pool> = new Map();
9
- private drizzleInstances: Map<string, NodePgDatabase> = new Map();
10
- public readonly defaultDatabaseName: string;
11
- private readonly rootConnectionString: string;
12
-
13
- constructor(adminConnectionString: string) {
14
- this.rootConnectionString = adminConnectionString;
15
- try {
16
- const url = new URL(adminConnectionString);
17
- this.defaultDatabaseName = url.pathname.slice(1);
18
- } catch (e) {
19
- throw new Error(`Invalid adminConnectionString provided: ${e}`);
20
- }
21
- }
22
-
23
- public getDrizzle(databaseName: string): NodePgDatabase<Record<string, never>> {
24
- const existing = this.drizzleInstances.get(databaseName);
25
- if (existing) {
26
- return existing;
27
- }
28
-
29
- const pool = this.getPool(databaseName);
30
- const db = drizzle(pool);
31
- this.drizzleInstances.set(databaseName, db);
32
- return db;
33
- }
34
-
35
- public getPool(databaseName: string): Pool {
36
- if (this.pools.has(databaseName)) {
37
- return this.pools.get(databaseName)!;
38
- }
39
-
40
- const url = new URL(this.rootConnectionString);
41
- url.pathname = `/${databaseName}`;
42
-
43
- const pool = new Pool({
44
- // Same pin as the primary pool: these are branch/multi-database
45
- // connections to the *same* server, so they inherit the same
46
- // `"$user"` hazard. See `pinSearchPath`.
47
- connectionString: pinSearchPath(url.toString()),
48
- // Capped by REBASE_DB_POOL_MAX, which the managed development
49
- // database sets to 1: PGlite multiplexes onto a single session and
50
- // overlapping transactions deadlock there.
51
- max: cappedPoolMax(10),
52
- idleTimeoutMillis: 10000, // Reduced from 30000 for aggressive cleanup
53
- allowExitOnIdle: true // Prevent idle clients from hanging the Node.js process
54
- });
55
-
56
- // Prevent idle client errors from crashing the Node.js process
57
- pool.on("error", (err) => {
58
- logger.error(`[DatabasePoolManager] Unexpected error on idle client for db ${databaseName}`, { error: err });
59
- });
60
- guardPoolAgainstDirtyRelease(pool, `pg-pool:${databaseName}`);
61
-
62
- this.pools.set(databaseName, pool);
63
- return pool;
64
- }
65
-
66
- /**
67
- * Disconnect and remove the pool for a specific database.
68
- * Required before `CREATE DATABASE ... TEMPLATE` or `DROP DATABASE`,
69
- * which need exclusive access to the target database.
70
- */
71
- public async disconnectDatabase(databaseName: string): Promise<void> {
72
- const pool = this.pools.get(databaseName);
73
- if (pool) {
74
- await pool.end();
75
- this.pools.delete(databaseName);
76
- this.drizzleInstances.delete(databaseName);
77
- }
78
- }
79
-
80
- /** Check if a pool exists for a given database name. */
81
- public hasPool(databaseName: string): boolean {
82
- return this.pools.has(databaseName);
83
- }
84
-
85
- public async shutdown(): Promise<void> {
86
- const promises = [];
87
- for (const [dbName, pool] of this.pools.entries()) {
88
- logger.info(`[DatabasePoolManager] Shutting down pool for ${dbName}`);
89
- promises.push(pool.end());
90
- }
91
- await Promise.all(promises);
92
- this.pools.clear();
93
- this.drizzleInstances.clear();
94
- }
95
- }
@@ -1,254 +0,0 @@
1
- import { sql } from "drizzle-orm";
2
- import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
- import { logger } from "@rebasepro/server";
4
- import type { EntityHistoryEntry } from "@rebasepro/types";
5
-
6
- export type {
7
- RecordHistoryParams,
8
- FetchHistoryOptions,
9
- HistoryRetentionConfig
10
- } from "@rebasepro/types";
11
- import type { RecordHistoryParams, FetchHistoryOptions, HistoryRetentionConfig } from "@rebasepro/types";
12
-
13
- /**
14
- * A Postgres history row is already the wire shape — `updated_at` comes back
15
- * from the driver as a string. Kept as an alias because the name is used
16
- * throughout this package and in `PostgresBackendDriver`.
17
- */
18
- export type HistoryEntry = EntityHistoryEntry;
19
-
20
- const DEFAULT_RETENTION: HistoryRetentionConfig = {
21
- maxEntries: 200,
22
- ttlDays: 90
23
- };
24
-
25
- /**
26
- * Service for recording and querying row change history.
27
- * Stores history entries in the `rebase.entity_history` table.
28
- */
29
- export class HistoryService {
30
- public retention: HistoryRetentionConfig;
31
-
32
- constructor(
33
- private db: NodePgDatabase,
34
- retention?: Partial<HistoryRetentionConfig>
35
- ) {
36
- this.retention = { ...DEFAULT_RETENTION,
37
- ...retention };
38
- }
39
-
40
- /**
41
- * Record a history entry for an row change.
42
- * This is intentionally fire-and-forget safe — errors are logged but never
43
- * bubble up to block the main save/delete operation.
44
- *
45
- * After inserting, kicks off a non-blocking pruning pass for this row.
46
- */
47
- async recordHistory(params: RecordHistoryParams): Promise<void> {
48
- const {
49
- tableName,
50
- id,
51
- action,
52
- values,
53
- previousValues,
54
- updatedBy
55
- } = params;
56
-
57
- const changedFields = previousValues && values
58
- ? findChangedFields(previousValues, values)
59
- : null;
60
-
61
-
62
- // Skip recording if this is an update with zero actual changes
63
-
64
- if (action === "update" && (!changedFields || changedFields.length === 0)) {
65
- return;
66
- }
67
-
68
- try {
69
- await this.db.execute(sql`
70
- INSERT INTO rebase.entity_history
71
- (table_name, entity_id, action, changed_fields, "values", previous_values, updated_by)
72
- VALUES (
73
- ${tableName},
74
- ${String(id)},
75
- ${action},
76
- ${changedFields ? sql`ARRAY[${sql.join(changedFields.map(f => sql`${f}`), sql`, `)}]::text[]` : sql`NULL`},
77
- ${values ? sql`${JSON.stringify(values)}::jsonb` : sql`NULL`},
78
- ${previousValues ? sql`${JSON.stringify(previousValues)}::jsonb` : sql`NULL`},
79
- ${updatedBy ?? null}
80
- )
81
- `);
82
-
83
- // Non-blocking prune for this specific row
84
- this.pruneEntity(tableName, id).catch(err =>
85
- logger.error("History prune failed", { error: err })
86
- );
87
- } catch (error) {
88
- logger.error("Failed to record row history", { error: error });
89
- }
90
- }
91
-
92
- /**
93
- * Fetch history entries for an row, ordered by most recent first.
94
- */
95
- async fetchHistory(
96
- tableName: string,
97
- id: string,
98
- options: FetchHistoryOptions = {}
99
- ): Promise<{ data: HistoryEntry[]; total: number }> {
100
- const limit = options.limit ?? 20;
101
- const offset = options.offset ?? 0;
102
-
103
- const [countResult, dataResult] = await Promise.all([
104
- this.db.execute(sql`
105
- SELECT COUNT(*) as count
106
- FROM rebase.entity_history
107
- WHERE table_name = ${tableName}
108
- AND entity_id = ${String(id)}
109
- `),
110
- this.db.execute(sql`
111
- SELECT id, table_name, entity_id, action, changed_fields,
112
- "values", previous_values, updated_by, updated_at
113
- FROM rebase.entity_history
114
- WHERE table_name = ${tableName}
115
- AND entity_id = ${String(id)}
116
- ORDER BY updated_at DESC
117
- LIMIT ${limit}
118
- OFFSET ${offset}
119
- `)
120
- ]);
121
-
122
- const total = parseInt(
123
- (countResult.rows[0] as Record<string, string>)?.count ?? "0",
124
- 10
125
- );
126
-
127
- return {
128
- data: dataResult.rows as unknown as HistoryEntry[],
129
- total
130
- };
131
- }
132
-
133
- /**
134
- * Fetch a single history entry by ID.
135
- */
136
- async fetchHistoryEntry(historyId: string): Promise<HistoryEntry | null> {
137
- const result = await this.db.execute(sql`
138
- SELECT id, table_name, entity_id, action, changed_fields,
139
- "values", previous_values, updated_by, updated_at
140
- FROM rebase.entity_history
141
- WHERE id = ${historyId}
142
- `);
143
-
144
- if (result.rows.length === 0) return null;
145
- return result.rows[0] as unknown as HistoryEntry;
146
- }
147
-
148
- // ───────── Retention / Pruning ─────────
149
-
150
- /**
151
- * Prune history for a single row: enforce maxEntries and TTL.
152
- */
153
- async pruneEntity(tableName: string, id: string): Promise<number> {
154
- let deleted = 0;
155
-
156
- // 1. TTL — delete entries older than ttlDays
157
- const ttlResult = await this.db.execute(sql`
158
- DELETE FROM rebase.entity_history
159
- WHERE table_name = ${tableName}
160
- AND entity_id = ${String(id)}
161
- AND updated_at < NOW() - MAKE_INTERVAL(days => ${this.retention.ttlDays})
162
- `);
163
- deleted += ttlResult.rowCount ?? 0;
164
-
165
- // 2. Max entries — keep the newest maxEntries, delete the rest
166
- const maxResult = await this.db.execute(sql`
167
- DELETE FROM rebase.entity_history
168
- WHERE id IN (
169
- SELECT id FROM rebase.entity_history
170
- WHERE table_name = ${tableName}
171
- AND entity_id = ${String(id)}
172
- ORDER BY updated_at DESC
173
- OFFSET ${this.retention.maxEntries}
174
- )
175
- `);
176
- deleted += maxResult.rowCount ?? 0;
177
-
178
- return deleted;
179
- }
180
-
181
- /**
182
- * Global prune: enforce TTL across ALL rows in a single sweep.
183
- * Intended to be called periodically (e.g. once per hour or daily).
184
- */
185
- async pruneExpired(): Promise<number> {
186
- const result = await this.db.execute(sql`
187
- DELETE FROM rebase.entity_history
188
- WHERE updated_at < NOW() - MAKE_INTERVAL(days => ${this.retention.ttlDays})
189
- `);
190
- return result.rowCount ?? 0;
191
- }
192
- }
193
-
194
-
195
- /**
196
- * Deep equality without JSON.stringify.
197
- * Handles primitives, arrays, Dates, and plain objects recursively.
198
- */
199
- function deepEqual(a: unknown, b: unknown): boolean {
200
- if (a === b) return true;
201
- if (a == null || b == null) return false;
202
- if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
203
- if (Array.isArray(a) && Array.isArray(b)) {
204
- if (a.length !== b.length) return false;
205
- return a.every((v, i) => deepEqual(v, b[i]));
206
- }
207
- if (typeof a === "object" && typeof b === "object") {
208
- const aObj = a as Record<string, unknown>;
209
- const bObj = b as Record<string, unknown>;
210
- const aKeys = Object.keys(aObj);
211
- const bKeys = Object.keys(bObj);
212
- if (aKeys.length !== bKeys.length) return false;
213
- return aKeys.every(k => deepEqual(aObj[k], bObj[k]));
214
- }
215
- return false;
216
- }
217
-
218
- /**
219
- * Shallow comparison to find top-level keys that changed between two objects.
220
- */
221
- export function findChangedFields(
222
- oldValues: Record<string, unknown>,
223
- newValues: Record<string, unknown>
224
- ): string[] | null {
225
- const changed: string[] = [];
226
- const allKeys = new Set([
227
- ...Object.keys(oldValues),
228
- ...Object.keys(newValues)
229
- ]);
230
-
231
- for (const key of allKeys) {
232
- const oldVal = oldValues[key];
233
- const newVal = newValues[key];
234
-
235
- // Skip internal metadata
236
- if (key.startsWith("__")) continue;
237
-
238
- if (oldVal !== newVal) {
239
- // For objects/arrays, use structural comparison
240
- if (
241
- typeof oldVal === "object" && oldVal !== null &&
242
- typeof newVal === "object" && newVal !== null
243
- ) {
244
- if (!deepEqual(oldVal, newVal)) {
245
- changed.push(key);
246
- }
247
- } else {
248
- changed.push(key);
249
- }
250
- }
251
- }
252
-
253
- return changed.length > 0 ? changed : null;
254
- }
@@ -1,53 +0,0 @@
1
- import { sql } from "drizzle-orm";
2
- import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
- import { logger } from "@rebasepro/server";
4
- import { revokeInternalTableSql } from "@rebasepro/common";
5
-
6
- /**
7
- * Auto-create the row history table if it doesn't exist.
8
- * This runs on startup when history is enabled, following the same
9
- * pattern as `ensureAuthTablesExist`.
10
- */
11
- export async function ensureHistoryTableExists(db: NodePgDatabase): Promise<void> {
12
- logger.debug("🔍 Checking row history table...");
13
-
14
- try {
15
- // Create the rebase schema (idempotent — may already exist from auth init)
16
- await db.execute(sql`CREATE SCHEMA IF NOT EXISTS rebase`);
17
-
18
- await db.execute(sql`
19
- CREATE TABLE IF NOT EXISTS rebase.entity_history (
20
- id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
21
- table_name TEXT NOT NULL,
22
- entity_id TEXT NOT NULL,
23
- action TEXT NOT NULL,
24
- changed_fields TEXT[],
25
- "values" JSONB,
26
- previous_values JSONB,
27
- updated_by TEXT,
28
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
29
- )
30
- `);
31
-
32
- await db.execute(sql`
33
- CREATE INDEX IF NOT EXISTS idx_history_entity
34
- ON rebase.entity_history(table_name, entity_id)
35
- `);
36
-
37
- await db.execute(sql`
38
- CREATE INDEX IF NOT EXISTS idx_history_time
39
- ON rebase.entity_history(table_name, entity_id, updated_at DESC)
40
- `);
41
-
42
- // Every previous value of every audited row, in one table with no RLS
43
- // and no tenant scoping — so a readable copy defeats the row policies on
44
- // the tables it shadows. The driver's schema-wide grant reaches it
45
- // (created here, after that grant ran), so take it back.
46
- await db.execute(sql.raw(revokeInternalTableSql("rebase", "entity_history")));
47
-
48
- logger.debug("✅ Entity history table ready");
49
- } catch (error) {
50
- logger.error("❌ Failed to create row history table", { error: error });
51
- logger.warn("⚠️ Continuing without creating history table.");
52
- }
53
- }
package/src/index.ts DELETED
@@ -1,19 +0,0 @@
1
- export * from "./connection";
2
- export * from "./interfaces";
3
- export * from "./PostgresBackendDriver";
4
- export * from "./databasePoolManager";
5
- export * from "./schema/auth-schema";
6
- export * from "./schema/generate-drizzle-schema-logic";
7
- export * from "./schema/generate-drizzle-schema";
8
- export * from "./utils/drizzle-conditions";
9
- export * from "./services/realtimeService";
10
- // The channel bus is a public extension point: a transport published as its own
11
- // package implements `ChannelBus` (declared in @rebasepro/types and re-exported
12
- // here) and is passed to `realtime.bus`. Without this export it would be one.
13
- export * from "./services/channel-bus";
14
- export * from "./websocket";
15
- export * from "./collections/PostgresCollectionRegistry";
16
- export * from "./services/BranchService";
17
- export * from "./backup";
18
- export * from "./PostgresBootstrapper";
19
- export * from "./PostgresAdapter";
package/src/interfaces.ts DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * Database Abstraction Interfaces
3
- *
4
- * These interfaces define the contracts that any database backend must implement
5
- * to be used with Rebase. This allows for pluggable database backends like
6
- * PostgreSQL, MongoDB, MySQL, etc.
7
- */
8
-
9
- import {
10
- Entity,
11
- CollectionConfig,
12
- FilterValues,
13
- WhereFilterOp,
14
- DatabaseConnection,
15
- QueryFilter,
16
- FetchCollectionOptions,
17
- SearchOptions,
18
- CountOptions,
19
- ConditionBuilder,
20
- ConditionBuilderStatic,
21
- DataRepository,
22
- CollectionSubscriptionConfig,
23
- SingleSubscriptionConfig,
24
- RealtimeProvider,
25
- CollectionRegistryInterface,
26
- DataTransformer,
27
- BackendConfig,
28
- BackendInstance,
29
- BackendFactory
30
- } from "@rebasepro/types";
31
- import { NodePgDatabase } from "drizzle-orm/node-postgres";
32
- import { PgTransaction } from "drizzle-orm/pg-core";
33
-
34
- /**
35
- * Type representing either a direct database connection or a transaction.
36
- * Used to allow services to operate within a transaction context.
37
- * Note: `any` is intentional here — it represents a Drizzle client with
38
- * a dynamic schema, enabling `db.query[tableName]` access without casts.
39
- */
40
-
41
- export type DrizzleClient = NodePgDatabase<any> | PgTransaction<any, any, any>;
42
-
43
- export type {
44
- DatabaseConnection,
45
- QueryFilter,
46
- FetchCollectionOptions,
47
- SearchOptions,
48
- CountOptions,
49
- ConditionBuilder,
50
- ConditionBuilderStatic,
51
- DataRepository,
52
- CollectionSubscriptionConfig,
53
- SingleSubscriptionConfig,
54
- RealtimeProvider,
55
- CollectionRegistryInterface,
56
- DataTransformer,
57
- BackendConfig,
58
- BackendInstance,
59
- BackendFactory
60
- };
package/src/module-dir.ts DELETED
@@ -1,7 +0,0 @@
1
- import path from "path";
2
- import { fileURLToPath } from "url";
3
-
4
- // Isolated in its own module so the jest CommonJS transform can swap it for a
5
- // __dirname shim via moduleNameMapper — `import.meta` is a syntax error once
6
- // ts-jest transpiles to CJS.
7
- export const moduleDir = path.dirname(fileURLToPath(import.meta.url));
@@ -1,94 +0,0 @@
1
- /**
2
- * The argv `runAtlas` hands the Atlas binary, as a pure function.
3
- *
4
- * Split out of `cli.ts` because the flags an Atlas subcommand accepts are not
5
- * uniform and getting one wrong is fatal rather than degraded: Atlas rejects an
6
- * unknown flag before doing any work, so a flag on the wrong subcommand takes
7
- * the whole command down.
8
- *
9
- * `--exclude` is the one that bit. Of the six invocations here, exactly one
10
- * accepts it. The guard this replaces was `args.includes("apply") ||
11
- * args.includes("diff")`, and reading it as a *subcommand* test is the trap:
12
- * `migrate apply` passes `args.includes("apply")` just as `schema apply` does.
13
- * So the flag went onto three invocations, two of which reject it, and both
14
- * `rebase db generate` and `rebase db migrate` exited 1 with `unknown flag:
15
- * --exclude` for every project declaring a `search` block.
16
- *
17
- * The domain is half the identity of an Atlas subcommand, and a guard that
18
- * looks only at `args` cannot tell `schema apply` from `migrate apply`.
19
- *
20
- * Nothing here touches a database or the filesystem, so the whole matrix is
21
- * unit-tested in `atlas-argv.test.ts`.
22
- */
23
-
24
- export interface AtlasInvocation {
25
- /** `schema` or `migrate`. */
26
- domain: string;
27
- /** The subcommand and its own flags, e.g. `["diff", "--dir", "file://…"]`. */
28
- args: string[];
29
- /** The target database, already rewritten for libpq. */
30
- url: string;
31
- /** The database Atlas plans against, already rewritten for libpq. */
32
- devUrl: string;
33
- /**
34
- * Everything the caller resolved for `--exclude`, in the order it should
35
- * appear. Dropped entirely when the subcommand does not accept the flag —
36
- * see {@link acceptsExcludeFlag}.
37
- */
38
- excludes?: string[];
39
- }
40
-
41
- /**
42
- * Does this Atlas invocation accept `--exclude`?
43
- *
44
- * Only `atlas schema apply` does, on the pinned 1.2.3. Measured against the
45
- * binary: `migrate diff` and `migrate apply` both answer `unknown flag:
46
- * --exclude`, and neither lists it in `--help` (`migrate apply` offers
47
- * `--url/--dir/--format/--revisions-schema/--dry-run/--lock-name/--lock-timeout/`
48
- * `--skip-lock/--baseline/--to-version/--tx-mode/--exec-order/--allow-dirty`).
49
- * Hence `domain === "schema"` and not just the subcommand.
50
- *
51
- * The `exclude` attribute of an `atlas.hcl` `env` block is not a way round it
52
- * either: measured, it is accepted and then ignored on the diff path, which is
53
- * the silent-no-match shape that costs the most time to notice.
54
- *
55
- * So the diff keeps the carved-out objects out of the migration afterwards
56
- * instead, with `stripCarvedOutStatements`.
57
- */
58
- export function acceptsExcludeFlag(domain: string, args: string[]): boolean {
59
- return domain === "schema" && args.includes("apply");
60
- }
61
-
62
- /** Assemble the full argv, connection flags and all. */
63
- export function buildAtlasArgs(invocation: AtlasInvocation): string[] {
64
- const { domain, args, url, devUrl, excludes = [] } = invocation;
65
- const argv = [domain, ...args];
66
-
67
- if (domain === "schema") {
68
- if (args.includes("apply")) {
69
- argv.push("--url", url, "--dev-url", devUrl);
70
- } else if (args.includes("clean") || args.includes("inspect")) {
71
- argv.push("--url", url);
72
- }
73
- } else if (domain === "migrate") {
74
- if (args.includes("diff")) {
75
- argv.push("--dev-url", devUrl);
76
- } else if (args.includes("apply") || args.includes("status")) {
77
- argv.push("--url", url, "--revisions-schema", "rebase");
78
- if (args.includes("apply")) {
79
- argv.push("--allow-dirty");
80
- }
81
- }
82
- }
83
-
84
- // Second line of defence, deliberately not a caller's responsibility: a
85
- // future caller that resolves excludes for the diff path gets them dropped
86
- // here rather than a command that will not run.
87
- if (acceptsExcludeFlag(domain, args)) {
88
- for (const exclude of excludes) {
89
- argv.push("--exclude", exclude);
90
- }
91
- }
92
-
93
- return argv;
94
- }