@rebasepro/server-postgresql 0.5.0 → 0.6.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 (165) hide show
  1. package/dist/{server-postgresql/src/PostgresAdapter.d.ts → PostgresAdapter.d.ts} +1 -1
  2. package/dist/{server-postgresql/src/PostgresBackendDriver.d.ts → PostgresBackendDriver.d.ts} +2 -2
  3. package/dist/{server-postgresql/src/PostgresBootstrapper.d.ts → PostgresBootstrapper.d.ts} +11 -1
  4. package/dist/{server-postgresql/src/collections → collections}/PostgresCollectionRegistry.d.ts +4 -0
  5. package/dist/index.es.js +10168 -11145
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +10735 -11429
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/{server-postgresql/src/services → services}/EntityPersistService.d.ts +0 -14
  10. package/dist/utils/pg-error-utils.d.ts +55 -0
  11. package/package.json +24 -21
  12. package/src/PostgresAdapter.ts +9 -10
  13. package/src/PostgresBackendDriver.ts +134 -121
  14. package/src/PostgresBootstrapper.ts +86 -13
  15. package/src/auth/ensure-tables.ts +28 -5
  16. package/src/auth/services.ts +28 -18
  17. package/src/cli.ts +99 -96
  18. package/src/collections/PostgresCollectionRegistry.ts +7 -0
  19. package/src/connection.ts +11 -6
  20. package/src/data-transformer.ts +16 -14
  21. package/src/databasePoolManager.ts +3 -2
  22. package/src/history/HistoryService.ts +3 -2
  23. package/src/history/ensure-history-table.ts +5 -4
  24. package/src/schema/auth-schema.ts +1 -2
  25. package/src/schema/doctor-cli.ts +2 -1
  26. package/src/schema/doctor.ts +40 -37
  27. package/src/schema/generate-drizzle-schema-logic.ts +56 -18
  28. package/src/schema/generate-drizzle-schema.ts +11 -11
  29. package/src/schema/introspect-db-inference.ts +25 -25
  30. package/src/schema/introspect-db-logic.ts +38 -38
  31. package/src/schema/introspect-db.ts +28 -27
  32. package/src/services/BranchService.ts +14 -0
  33. package/src/services/EntityFetchService.ts +28 -25
  34. package/src/services/EntityPersistService.ts +11 -141
  35. package/src/services/RelationService.ts +57 -37
  36. package/src/services/entity-helpers.ts +6 -2
  37. package/src/services/realtimeService.ts +45 -32
  38. package/src/utils/drizzle-conditions.ts +31 -15
  39. package/src/utils/pg-error-utils.ts +211 -0
  40. package/src/websocket.ts +15 -12
  41. package/test/auth-services.test.ts +36 -19
  42. package/test/batch-many-to-many-regression.test.ts +119 -39
  43. package/test/data-transformer-hardening.test.ts +67 -33
  44. package/test/data-transformer.test.ts +4 -2
  45. package/test/doctor.test.ts +10 -5
  46. package/test/drizzle-conditions.test.ts +59 -6
  47. package/test/generate-drizzle-schema.test.ts +65 -40
  48. package/test/introspect-db-generation.test.ts +179 -81
  49. package/test/introspect-db-utils.test.ts +92 -37
  50. package/test/mocks/chalk.cjs +7 -0
  51. package/test/pg-error-utils.test.ts +221 -0
  52. package/test/postgresDataDriver.test.ts +14 -5
  53. package/test/property-ordering.test.ts +126 -79
  54. package/test/realtimeService.test.ts +6 -2
  55. package/test/relation-pipeline-gaps.test.ts +84 -36
  56. package/test/relations.test.ts +247 -0
  57. package/test/unmapped-tables-safety.test.ts +14 -6
  58. package/test/websocket.test.ts +1 -1
  59. package/tsconfig.json +5 -0
  60. package/tsconfig.prod.json +3 -0
  61. package/vite.config.ts +5 -5
  62. package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
  63. package/dist/common/src/collections/default-collections.d.ts +0 -9
  64. package/dist/common/src/collections/index.d.ts +0 -2
  65. package/dist/common/src/data/buildRebaseData.d.ts +0 -14
  66. package/dist/common/src/data/query_builder.d.ts +0 -55
  67. package/dist/common/src/index.d.ts +0 -4
  68. package/dist/common/src/util/builders.d.ts +0 -57
  69. package/dist/common/src/util/callbacks.d.ts +0 -6
  70. package/dist/common/src/util/collections.d.ts +0 -11
  71. package/dist/common/src/util/common.d.ts +0 -2
  72. package/dist/common/src/util/conditions.d.ts +0 -26
  73. package/dist/common/src/util/entities.d.ts +0 -58
  74. package/dist/common/src/util/enums.d.ts +0 -3
  75. package/dist/common/src/util/index.d.ts +0 -16
  76. package/dist/common/src/util/navigation_from_path.d.ts +0 -34
  77. package/dist/common/src/util/navigation_utils.d.ts +0 -20
  78. package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
  79. package/dist/common/src/util/paths.d.ts +0 -14
  80. package/dist/common/src/util/permissions.d.ts +0 -14
  81. package/dist/common/src/util/references.d.ts +0 -2
  82. package/dist/common/src/util/relations.d.ts +0 -22
  83. package/dist/common/src/util/resolutions.d.ts +0 -72
  84. package/dist/common/src/util/storage.d.ts +0 -24
  85. package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
  86. package/dist/types/src/controllers/auth.d.ts +0 -104
  87. package/dist/types/src/controllers/client.d.ts +0 -168
  88. package/dist/types/src/controllers/collection_registry.d.ts +0 -46
  89. package/dist/types/src/controllers/customization_controller.d.ts +0 -60
  90. package/dist/types/src/controllers/data.d.ts +0 -207
  91. package/dist/types/src/controllers/data_driver.d.ts +0 -218
  92. package/dist/types/src/controllers/database_admin.d.ts +0 -11
  93. package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
  94. package/dist/types/src/controllers/effective_role.d.ts +0 -4
  95. package/dist/types/src/controllers/email.d.ts +0 -36
  96. package/dist/types/src/controllers/index.d.ts +0 -18
  97. package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
  98. package/dist/types/src/controllers/navigation.d.ts +0 -225
  99. package/dist/types/src/controllers/registry.d.ts +0 -63
  100. package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
  101. package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
  102. package/dist/types/src/controllers/snackbar.d.ts +0 -24
  103. package/dist/types/src/controllers/storage.d.ts +0 -171
  104. package/dist/types/src/index.d.ts +0 -4
  105. package/dist/types/src/rebase_context.d.ts +0 -122
  106. package/dist/types/src/types/auth_adapter.d.ts +0 -301
  107. package/dist/types/src/types/backend.d.ts +0 -571
  108. package/dist/types/src/types/backend_hooks.d.ts +0 -172
  109. package/dist/types/src/types/builders.d.ts +0 -15
  110. package/dist/types/src/types/chips.d.ts +0 -5
  111. package/dist/types/src/types/collections.d.ts +0 -961
  112. package/dist/types/src/types/component_ref.d.ts +0 -47
  113. package/dist/types/src/types/cron.d.ts +0 -102
  114. package/dist/types/src/types/data_source.d.ts +0 -64
  115. package/dist/types/src/types/database_adapter.d.ts +0 -94
  116. package/dist/types/src/types/entities.d.ts +0 -145
  117. package/dist/types/src/types/entity_actions.d.ts +0 -104
  118. package/dist/types/src/types/entity_callbacks.d.ts +0 -173
  119. package/dist/types/src/types/entity_link_builder.d.ts +0 -7
  120. package/dist/types/src/types/entity_overrides.d.ts +0 -10
  121. package/dist/types/src/types/entity_views.d.ts +0 -87
  122. package/dist/types/src/types/export_import.d.ts +0 -21
  123. package/dist/types/src/types/formex.d.ts +0 -40
  124. package/dist/types/src/types/index.d.ts +0 -28
  125. package/dist/types/src/types/locales.d.ts +0 -4
  126. package/dist/types/src/types/modify_collections.d.ts +0 -5
  127. package/dist/types/src/types/plugins.d.ts +0 -282
  128. package/dist/types/src/types/properties.d.ts +0 -1173
  129. package/dist/types/src/types/property_config.d.ts +0 -74
  130. package/dist/types/src/types/relations.d.ts +0 -336
  131. package/dist/types/src/types/slots.d.ts +0 -262
  132. package/dist/types/src/types/translations.d.ts +0 -900
  133. package/dist/types/src/types/user_management_delegate.d.ts +0 -86
  134. package/dist/types/src/types/websockets.d.ts +0 -78
  135. package/dist/types/src/users/index.d.ts +0 -1
  136. package/dist/types/src/users/user.d.ts +0 -50
  137. /package/dist/{server-postgresql/src/auth → auth}/ensure-tables.d.ts +0 -0
  138. /package/dist/{server-postgresql/src/auth → auth}/services.d.ts +0 -0
  139. /package/dist/{server-postgresql/src/cli.d.ts → cli.d.ts} +0 -0
  140. /package/dist/{server-postgresql/src/connection.d.ts → connection.d.ts} +0 -0
  141. /package/dist/{server-postgresql/src/data-transformer.d.ts → data-transformer.d.ts} +0 -0
  142. /package/dist/{server-postgresql/src/databasePoolManager.d.ts → databasePoolManager.d.ts} +0 -0
  143. /package/dist/{server-postgresql/src/history → history}/HistoryService.d.ts +0 -0
  144. /package/dist/{server-postgresql/src/history → history}/ensure-history-table.d.ts +0 -0
  145. /package/dist/{server-postgresql/src/index.d.ts → index.d.ts} +0 -0
  146. /package/dist/{server-postgresql/src/interfaces.d.ts → interfaces.d.ts} +0 -0
  147. /package/dist/{server-postgresql/src/schema → schema}/auth-schema.d.ts +0 -0
  148. /package/dist/{server-postgresql/src/schema → schema}/doctor-cli.d.ts +0 -0
  149. /package/dist/{server-postgresql/src/schema → schema}/doctor.d.ts +0 -0
  150. /package/dist/{server-postgresql/src/schema → schema}/generate-drizzle-schema-logic.d.ts +0 -0
  151. /package/dist/{server-postgresql/src/schema → schema}/generate-drizzle-schema.d.ts +0 -0
  152. /package/dist/{server-postgresql/src/schema → schema}/introspect-db-inference.d.ts +0 -0
  153. /package/dist/{server-postgresql/src/schema → schema}/introspect-db-logic.d.ts +0 -0
  154. /package/dist/{server-postgresql/src/schema → schema}/introspect-db.d.ts +0 -0
  155. /package/dist/{server-postgresql/src/schema → schema}/test-schema.d.ts +0 -0
  156. /package/dist/{server-postgresql/src/services → services}/BranchService.d.ts +0 -0
  157. /package/dist/{server-postgresql/src/services → services}/EntityFetchService.d.ts +0 -0
  158. /package/dist/{server-postgresql/src/services → services}/RelationService.d.ts +0 -0
  159. /package/dist/{server-postgresql/src/services → services}/entity-helpers.d.ts +0 -0
  160. /package/dist/{server-postgresql/src/services → services}/entityService.d.ts +0 -0
  161. /package/dist/{server-postgresql/src/services → services}/index.d.ts +0 -0
  162. /package/dist/{server-postgresql/src/services → services}/realtimeService.d.ts +0 -0
  163. /package/dist/{server-postgresql/src/types.d.ts → types.d.ts} +0 -0
  164. /package/dist/{server-postgresql/src/utils → utils}/drizzle-conditions.d.ts +0 -0
  165. /package/dist/{server-postgresql/src/websocket.d.ts → websocket.d.ts} +0 -0
@@ -4,7 +4,6 @@ import { logger } from "@rebasepro/server-core";
4
4
  import type { EntityCollection } from "@rebasepro/types";
5
5
 
6
6
 
7
-
8
7
  /**
9
8
  * Auto-create auth tables if they don't exist.
10
9
  *
@@ -20,16 +19,17 @@ export async function ensureAuthTablesExist(db: NodePgDatabase, collection?: Ent
20
19
  let usersTableName = '"rebase"."users"';
21
20
  let userIdType = "TEXT";
22
21
  let usersSchema = "rebase";
22
+ let resolvedTable = "users";
23
23
  if (collection) {
24
- const rawTable = ("table" in collection && typeof collection.table === "string")
24
+ resolvedTable = ("table" in collection && typeof collection.table === "string")
25
25
  ? collection.table
26
26
  : collection.slug;
27
27
  usersSchema = ("schema" in collection && typeof collection.schema === "string")
28
28
  ? collection.schema
29
29
  : "public";
30
30
  usersTableName = usersSchema === "public"
31
- ? `"${rawTable}"`
32
- : `"${usersSchema}"."${rawTable}"`;
31
+ ? `"${resolvedTable}"`
32
+ : `"${usersSchema}"."${resolvedTable}"`;
33
33
 
34
34
  // Derive ID column type from collection properties
35
35
  const idProp = collection.properties?.id;
@@ -44,6 +44,30 @@ export async function ensureAuthTablesExist(db: NodePgDatabase, collection?: Ent
44
44
  }
45
45
  }
46
46
 
47
+ // Introspect the database to find the actual type of usersTableName's ID column if the table exists
48
+ try {
49
+ const result = await db.execute(sql`
50
+ SELECT data_type
51
+ FROM information_schema.columns
52
+ WHERE table_schema = ${usersSchema}
53
+ AND table_name = ${resolvedTable}
54
+ AND column_name = 'id'
55
+ `);
56
+ if (result && result.rows && result.rows.length > 0) {
57
+ const dbType = String((result.rows[0] as { data_type: string }).data_type).toUpperCase();
58
+ if (dbType === "UUID") {
59
+ userIdType = "UUID";
60
+ } else if (dbType === "INTEGER" || dbType === "SMALLINT" || dbType === "BIGINT") {
61
+ userIdType = "INTEGER";
62
+ } else {
63
+ userIdType = "TEXT";
64
+ }
65
+ logger.info(`✨ Detected ${usersTableName}.id type from database: ${dbType}. Using user_id type: ${userIdType}`);
66
+ }
67
+ } catch (err) {
68
+ // Ignore introspection errors, fallback to derived/default type
69
+ logger.warn(`⚠️ Failed to introspect ${usersTableName}.id type from database, falling back to config type: ${userIdType}`, { error: err });
70
+ }
47
71
 
48
72
 
49
73
  // ── Create schemas (idempotent) ──────────────────────────────────
@@ -81,7 +105,6 @@ export async function ensureAuthTablesExist(db: NodePgDatabase, collection?: Ent
81
105
  `);
82
106
 
83
107
 
84
-
85
108
  // Create refresh tokens table (includes user_agent, ip_address, and unique constraint)
86
109
  await db.execute(sql`
87
110
  CREATE TABLE IF NOT EXISTS ${sql.raw(refreshTokensTableName)} (
@@ -21,7 +21,6 @@ import {
21
21
  MfaFactor,
22
22
  MfaChallengeInfo,
23
23
  RoleData as Role
24
- // @ts-ignore
25
24
  } from "@rebasepro/server-core";
26
25
  import { toSnakeCase, camelCase } from "@rebasepro/utils";
27
26
 
@@ -172,18 +171,18 @@ export class UserService implements UserRepository {
172
171
 
173
172
  for (const [key, val] of Object.entries(metadata)) {
174
173
  const tableColKey = getColumnKey(this.usersTable, key);
175
- if (tableColKey &&
176
- tableColKey !== idKey &&
177
- tableColKey !== emailKey &&
178
- tableColKey !== passwordHashKey &&
179
- tableColKey !== displayNameKey &&
180
- tableColKey !== photoUrlKey &&
181
- tableColKey !== emailVerifiedKey &&
182
- tableColKey !== emailVerificationTokenKey &&
183
- tableColKey !== emailVerificationSentAtKey &&
174
+ if (tableColKey &&
175
+ tableColKey !== idKey &&
176
+ tableColKey !== emailKey &&
177
+ tableColKey !== passwordHashKey &&
178
+ tableColKey !== displayNameKey &&
179
+ tableColKey !== photoUrlKey &&
180
+ tableColKey !== emailVerifiedKey &&
181
+ tableColKey !== emailVerificationTokenKey &&
182
+ tableColKey !== emailVerificationSentAtKey &&
184
183
  tableColKey !== isAnonymousKey &&
185
- tableColKey !== createdAtKey &&
186
- tableColKey !== updatedAtKey &&
184
+ tableColKey !== createdAtKey &&
185
+ tableColKey !== updatedAtKey &&
187
186
  tableColKey !== metadataKey) {
188
187
  payload[tableColKey] = val;
189
188
  } else {
@@ -221,7 +220,7 @@ export class UserService implements UserRepository {
221
220
  async getUserByIdentity(provider: string, providerId: string): Promise<UserData | null> {
222
221
  const userIdCol = getColumn(this.usersTable, "id");
223
222
  if (!userIdCol) return null;
224
-
223
+
225
224
  const result = await this.db
226
225
  .select({ user: this.usersTable })
227
226
  .from(this.usersTable)
@@ -496,7 +495,6 @@ export class UserService implements UserRepository {
496
495
  }
497
496
 
498
497
 
499
-
500
498
  export class RefreshTokenService {
501
499
  private refreshTokensTable: RebasePgTable;
502
500
 
@@ -869,9 +867,21 @@ export class PostgresAuthRepository implements AuthRepository {
869
867
 
870
868
  async listRoles(): Promise<RoleData[]> {
871
869
  return [
872
- { id: "admin", name: "Admin", isAdmin: true, defaultPermissions: null, collectionPermissions: null },
873
- { id: "editor", name: "Editor", isAdmin: false, defaultPermissions: null, collectionPermissions: null },
874
- { id: "viewer", name: "Viewer", isAdmin: false, defaultPermissions: null, collectionPermissions: null }
870
+ { id: "admin",
871
+ name: "Admin",
872
+ isAdmin: true,
873
+ defaultPermissions: null,
874
+ collectionPermissions: null },
875
+ { id: "editor",
876
+ name: "Editor",
877
+ isAdmin: false,
878
+ defaultPermissions: null,
879
+ collectionPermissions: null },
880
+ { id: "viewer",
881
+ name: "Viewer",
882
+ isAdmin: false,
883
+ defaultPermissions: null,
884
+ collectionPermissions: null }
875
885
  ];
876
886
  }
877
887
 
@@ -1017,7 +1027,7 @@ export class PostgresAuthRepository implements AuthRepository {
1017
1027
  * Handles all MFA-related database operations.
1018
1028
  */
1019
1029
  export class MfaService implements MfaRepository {
1020
- constructor(private db: NodePgDatabase, private schemaName: string = "rebase") {}
1030
+ constructor(private db: NodePgDatabase, private schemaName = "rebase") {}
1021
1031
 
1022
1032
  private qualify(tableName: string): string {
1023
1033
  return `"${this.schemaName}"."${tableName}"`;
package/src/cli.ts CHANGED
@@ -5,6 +5,7 @@ import path from "path";
5
5
  import fs from "fs";
6
6
  import { execSync } from "child_process";
7
7
  import { fileURLToPath } from "url";
8
+ import { logger } from "@rebasepro/server-core";
8
9
 
9
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
11
 
@@ -40,7 +41,7 @@ export async function runPluginCommand(args: string[]) {
40
41
  } else if (domain === "doctor") {
41
42
  await doctorPluginCommand(args);
42
43
  } else {
43
- console.error(chalk.red(`Unknown domain command: ${domain}`));
44
+ logger.error(chalk.red(`Unknown domain command: ${domain}`));
44
45
  process.exit(1);
45
46
  }
46
47
  }
@@ -48,7 +49,7 @@ export async function runPluginCommand(args: string[]) {
48
49
  async function dbCommand(subcommand: string, rawArgs: string[]): Promise<void> {
49
50
  const VALID_ACTIONS = ["push", "generate", "migrate", "studio", "branch"];
50
51
  if (!subcommand || !VALID_ACTIONS.includes(subcommand)) {
51
- console.error(chalk.red(`Unknown db command. Valid: ${VALID_ACTIONS.join(", ")}`));
52
+ logger.error(chalk.red(`Unknown db command. Valid: ${VALID_ACTIONS.join(", ")}`));
52
53
  process.exit(1);
53
54
  }
54
55
 
@@ -58,44 +59,44 @@ async function dbCommand(subcommand: string, rawArgs: string[]): Promise<void> {
58
59
  }
59
60
 
60
61
  if (subcommand === "generate") {
61
- console.log("");
62
- console.log(chalk.bold(" 📦 Rebase DB Generate"));
63
- console.log(chalk.gray(" Step 1/2: Generating Drizzle schema from collections..."));
64
- console.log("");
62
+ logger.info("");
63
+ logger.info(chalk.bold(" 📦 Rebase DB Generate"));
64
+ logger.info(chalk.gray(" Step 1/2: Generating Drizzle schema from collections..."));
65
+ logger.info("");
65
66
  await schemaCommand("generate", rawArgs);
66
- console.log("");
67
- console.log(chalk.gray(" Step 2/2: Generating SQL migration files..."));
68
- console.log("");
67
+ logger.info("");
68
+ logger.info(chalk.gray(" Step 2/2: Generating SQL migration files..."));
69
+ logger.info("");
69
70
  await runDrizzleKit("generate", rawArgs);
70
71
  await fixMigrationStatementOrder();
71
- console.log("");
72
- console.log(` You can now run ${chalk.bold.green("rebase db migrate")} to apply the migrations to your database.`);
73
- console.log("");
72
+ logger.info("");
73
+ logger.info(` You can now run ${chalk.bold.green("rebase db migrate")} to apply the migrations to your database.`);
74
+ logger.info("");
74
75
  } else if (subcommand === "pull") {
75
- console.log("");
76
- console.log(chalk.yellow(" ⚠ \"rebase db pull\" has been removed."));
77
- console.log(chalk.gray(" Use \"rebase schema introspect\" instead."));
78
- console.log("");
76
+ logger.info("");
77
+ logger.info(chalk.yellow(" ⚠ \"rebase db pull\" has been removed."));
78
+ logger.info(chalk.gray(" Use \"rebase schema introspect\" instead."));
79
+ logger.info("");
79
80
  process.exit(1);
80
81
  } else {
81
- console.log("");
82
- console.log(chalk.bold(` 🗄️ Rebase DB ${subcommand.charAt(0).toUpperCase() + subcommand.slice(1)}`));
83
- console.log("");
82
+ logger.info("");
83
+ logger.info(chalk.bold(` 🗄️ Rebase DB ${subcommand.charAt(0).toUpperCase() + subcommand.slice(1)}`));
84
+ logger.info("");
84
85
 
85
86
  if (subcommand === "push") {
86
- console.log(chalk.gray(" Step 1/2: Generating Drizzle schema from collections..."));
87
- console.log("");
87
+ logger.info(chalk.gray(" Step 1/2: Generating Drizzle schema from collections..."));
88
+ logger.info("");
88
89
  await schemaCommand("generate", rawArgs);
89
- console.log("");
90
- console.log(chalk.gray(" Step 2/2: Pushing schema to database..."));
91
- console.log("");
90
+ logger.info("");
91
+ logger.info(chalk.gray(" Step 2/2: Pushing schema to database..."));
92
+ logger.info("");
92
93
  await runDrizzleKit("push", rawArgs);
93
94
  } else if (subcommand === "migrate") {
94
95
  await runDrizzleKit("migrate", rawArgs);
95
96
  } else if (subcommand === "studio") {
96
97
  const schemaPath = path.join(process.cwd(), "src", "schema.generated.ts");
97
98
  if (!fs.existsSync(schemaPath)) {
98
- console.log(chalk.yellow(" ⚠ schema.generated.ts not found. Generating schema first..."));
99
+ logger.info(chalk.yellow(" ⚠ schema.generated.ts not found. Generating schema first..."));
99
100
  await schemaCommand("generate", rawArgs);
100
101
  }
101
102
  await runDrizzleKit("studio", rawArgs);
@@ -103,9 +104,9 @@ async function dbCommand(subcommand: string, rawArgs: string[]): Promise<void> {
103
104
  await runDrizzleKit(subcommand, rawArgs);
104
105
  }
105
106
 
106
- console.log("");
107
- console.log(chalk.green(` ✓ rebase db ${subcommand} completed successfully.`));
108
- console.log("");
107
+ logger.info("");
108
+ logger.info(chalk.green(` ✓ rebase db ${subcommand} completed successfully.`));
109
+ logger.info("");
109
110
  }
110
111
  }
111
112
 
@@ -132,7 +133,7 @@ async function branchCommand(rawArgs: string[]): Promise<void> {
132
133
 
133
134
  const databaseUrl = process.env.DATABASE_URL || process.env.ADMIN_CONNECTION_STRING;
134
135
  if (!databaseUrl) {
135
- console.error(chalk.red("✗ DATABASE_URL is not set. Make sure your .env file is configured."));
136
+ logger.error(chalk.red("✗ DATABASE_URL is not set. Make sure your .env file is configured."));
136
137
  process.exit(1);
137
138
  }
138
139
 
@@ -156,8 +157,8 @@ max: 3 });
156
157
  case "create": {
157
158
  const name = rawArgs[3];
158
159
  if (!name) {
159
- console.error(chalk.red("✗ Branch name is required."));
160
- console.log(chalk.gray(" Usage: rebase db branch create <name> [--from <source>]"));
160
+ logger.error(chalk.red("✗ Branch name is required."));
161
+ logger.info(chalk.gray(" Usage: rebase db branch create <name> [--from <source>]"));
161
162
  process.exit(1);
162
163
  }
163
164
  let source: string | undefined;
@@ -165,81 +166,81 @@ max: 3 });
165
166
  if (fromIdx !== -1 && rawArgs[fromIdx + 1]) {
166
167
  source = rawArgs[fromIdx + 1];
167
168
  }
168
- console.log("");
169
- console.log(chalk.bold(" 🌿 Creating database branch..."));
170
- console.log(chalk.gray(` Name: ${name}`));
171
- if (source) console.log(chalk.gray(` Source: ${source}`));
172
- console.log("");
169
+ logger.info("");
170
+ logger.info(chalk.bold(" 🌿 Creating database branch..."));
171
+ logger.info(chalk.gray(` Name: ${name}`));
172
+ if (source) logger.info(chalk.gray(` Source: ${source}`));
173
+ logger.info("");
173
174
  const branch = await branchService.createBranch(name, source ? { source } : undefined);
174
- console.log(chalk.green(` ✓ Branch "${branch.name}" created successfully.`));
175
- console.log(chalk.gray(` Database: rb_${branch.name}`));
176
- console.log(chalk.gray(` Parent: ${branch.parentDatabase}`));
177
- console.log("");
175
+ logger.info(chalk.green(` ✓ Branch "${branch.name}" created successfully.`));
176
+ logger.info(chalk.gray(` Database: rb_${branch.name}`));
177
+ logger.info(chalk.gray(` Parent: ${branch.parentDatabase}`));
178
+ logger.info("");
178
179
  break;
179
180
  }
180
181
 
181
182
  case "list": {
182
183
  const branches = await branchService.listBranches();
183
- console.log("");
184
+ logger.info("");
184
185
  if (branches.length === 0) {
185
- console.log(chalk.gray(" No branches found. Create one with: rebase db branch create <name>"));
186
+ logger.info(chalk.gray(" No branches found. Create one with: rebase db branch create <name>"));
186
187
  } else {
187
- console.log(chalk.bold(` 🌿 ${branches.length} branch(es):`));
188
- console.log("");
188
+ logger.info(chalk.bold(` 🌿 ${branches.length} branch(es):`));
189
+ logger.info("");
189
190
  for (const b of branches) {
190
191
  const size = b.sizeBytes != null
191
192
  ? chalk.gray(` (${formatBytes(b.sizeBytes)})`)
192
193
  : "";
193
194
  const age = chalk.gray(` — created ${timeAgo(b.createdAt)}`);
194
- console.log(` ${chalk.green("●")} ${chalk.bold(b.name)}${size}${age}`);
195
- console.log(chalk.gray(` from ${b.parentDatabase}`));
195
+ logger.info(` ${chalk.green("●")} ${chalk.bold(b.name)}${size}${age}`);
196
+ logger.info(chalk.gray(` from ${b.parentDatabase}`));
196
197
  }
197
198
  }
198
- console.log("");
199
+ logger.info("");
199
200
  break;
200
201
  }
201
202
 
202
203
  case "delete": {
203
204
  const name = rawArgs[3];
204
205
  if (!name) {
205
- console.error(chalk.red("✗ Branch name is required."));
206
- console.log(chalk.gray(" Usage: rebase db branch delete <name>"));
206
+ logger.error(chalk.red("✗ Branch name is required."));
207
+ logger.info(chalk.gray(" Usage: rebase db branch delete <name>"));
207
208
  process.exit(1);
208
209
  }
209
- console.log("");
210
- console.log(chalk.bold(` 🗑️ Deleting branch "${name}"...`));
210
+ logger.info("");
211
+ logger.info(chalk.bold(` 🗑️ Deleting branch "${name}"...`));
211
212
  await branchService.deleteBranch(name);
212
- console.log(chalk.green(` ✓ Branch "${name}" deleted.`));
213
- console.log("");
213
+ logger.info(chalk.green(` ✓ Branch "${name}" deleted.`));
214
+ logger.info("");
214
215
  break;
215
216
  }
216
217
 
217
218
  case "info": {
218
219
  const name = rawArgs[3];
219
220
  if (!name) {
220
- console.error(chalk.red("✗ Branch name is required."));
221
- console.log(chalk.gray(" Usage: rebase db branch info <name>"));
221
+ logger.error(chalk.red("✗ Branch name is required."));
222
+ logger.info(chalk.gray(" Usage: rebase db branch info <name>"));
222
223
  process.exit(1);
223
224
  }
224
225
  const info = await branchService.getBranchInfo(name);
225
- console.log("");
226
+ logger.info("");
226
227
  if (!info) {
227
- console.error(chalk.red(` ✗ Branch "${name}" not found.`));
228
+ logger.error(chalk.red(` ✗ Branch "${name}" not found.`));
228
229
  } else {
229
- console.log(chalk.bold(` 🌿 Branch: ${info.name}`));
230
- console.log(chalk.gray(` Database: rb_${info.name}`));
231
- console.log(chalk.gray(` Parent: ${info.parentDatabase}`));
232
- console.log(chalk.gray(` Created: ${info.createdAt.toISOString()}`));
230
+ logger.info(chalk.bold(` 🌿 Branch: ${info.name}`));
231
+ logger.info(chalk.gray(` Database: rb_${info.name}`));
232
+ logger.info(chalk.gray(` Parent: ${info.parentDatabase}`));
233
+ logger.info(chalk.gray(` Created: ${info.createdAt.toISOString()}`));
233
234
  if (info.sizeBytes != null) {
234
- console.log(chalk.gray(` Size: ${formatBytes(info.sizeBytes)}`));
235
+ logger.info(chalk.gray(` Size: ${formatBytes(info.sizeBytes)}`));
235
236
  }
236
237
  }
237
- console.log("");
238
+ logger.info("");
238
239
  break;
239
240
  }
240
241
 
241
242
  default:
242
- console.error(chalk.red(`Unknown branch action: "${branchAction}".`));
243
+ logger.error(chalk.red(`Unknown branch action: "${branchAction}".`));
243
244
  printBranchHelp();
244
245
  process.exit(1);
245
246
  }
@@ -250,7 +251,7 @@ max: 3 });
250
251
  }
251
252
 
252
253
  function printBranchHelp() {
253
- console.log(`
254
+ logger.info(`
254
255
  ${chalk.bold("rebase db branch")} — Database branching commands
255
256
 
256
257
  ${chalk.green.bold("Usage")}
@@ -398,14 +399,16 @@ idx }));
398
399
  const reordered = stmtEntries.map(e => e.stmt).join(DELIMITER);
399
400
  fs.writeFileSync(latestFile, reordered, "utf-8");
400
401
 
401
- console.log(chalk.yellow(` \u26A0 Reordered migration statements in ${sqlFiles[0].name} (DROP POLICY before ALTER COLUMN)`));
402
+ logger.info(chalk.yellow(` \u26A0 Reordered migration statements in ${sqlFiles[0].name} (DROP POLICY before ALTER COLUMN)`));
402
403
  }
403
404
 
404
405
  async function runDrizzleKit(action: string, _rawArgs: string[]): Promise<void> {
405
406
  const drizzleKitBin = resolveLocalBin("drizzle-kit");
406
407
  if (!drizzleKitBin) {
407
- console.error(chalk.red("✗ Could not find drizzle-kit binary."));
408
- console.error(chalk.gray(" Install it with: pnpm add -D drizzle-kit"));
408
+ logger.error(chalk.red("✗ Could not find drizzle-kit binary."));
409
+ const isNpm = (process.env.npm_config_user_agent ?? "").startsWith("npm/") || fs.existsSync(path.join(process.cwd(), "package-lock.json"));
410
+ const installCmd = isNpm ? "npm install -D drizzle-kit" : "pnpm add -D drizzle-kit";
411
+ logger.error(chalk.gray(` Install it with: ${installCmd}`));
409
412
  process.exit(1);
410
413
  }
411
414
 
@@ -486,14 +489,14 @@ async function runDrizzleKit(action: string, _rawArgs: string[]): Promise<void>
486
489
  const stdout = stripAnsi(result.stdout || "").trim();
487
490
  const stderr = stripAnsi(result.stderr || "").trim();
488
491
 
489
- const hasTtyError = stdout.includes("Interactive prompts require a TTY terminal") ||
492
+ const hasTtyError = stdout.includes("Interactive prompts require a TTY terminal") ||
490
493
  stderr.includes("Interactive prompts require a TTY terminal");
491
494
 
492
495
  if (result.exitCode !== 0 || hasTtyError) {
493
- console.error(chalk.red(`\n✗ drizzle-kit ${action} failed.\n`));
496
+ logger.error(chalk.red(`\n✗ drizzle-kit ${action} failed.\n`));
494
497
  if (hasTtyError) {
495
- console.error(chalk.red(" Error: Interactive prompts require a TTY terminal."));
496
- console.error(chalk.gray(" Please run with --force to skip interactive prompts or run in an interactive terminal."));
498
+ logger.error(chalk.red(" Error: Interactive prompts require a TTY terminal."));
499
+ logger.error(chalk.gray(" Please run with --force to skip interactive prompts or run in an interactive terminal."));
497
500
  } else {
498
501
  const errorOutput = stderr || stdout;
499
502
  if (errorOutput) {
@@ -501,16 +504,16 @@ async function runDrizzleKit(action: string, _rawArgs: string[]): Promise<void>
501
504
  let printedCount = 0;
502
505
  for (const line of lines) {
503
506
  if (line.toLowerCase().includes("error") || line.includes("cannot") || line.includes("already exists") || line.includes("does not exist") || line.includes("violates") || line.includes("permission denied")) {
504
- console.error(chalk.red(` ${line.trim()}`));
507
+ logger.error(chalk.red(` ${line.trim()}`));
505
508
  printedCount++;
506
509
  }
507
510
  }
508
511
  if (printedCount === 0) {
509
- lines.slice(0, 10).forEach(line => console.error(chalk.red(` ${line.trim()}`)));
512
+ lines.slice(0, 10).forEach(line => logger.error(chalk.red(` ${line.trim()}`)));
510
513
  }
511
514
  }
512
515
  }
513
- console.error("");
516
+ logger.error("");
514
517
  process.exit(1);
515
518
  }
516
519
  }
@@ -520,22 +523,22 @@ async function runDrizzleKit(action: string, _rawArgs: string[]): Promise<void>
520
523
  const stripAnsi = (s: string) => s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\[?[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣷⣯⣟⡿⢿⣻⣽]+\]\s*/g, "");
521
524
  const cleaned = stripAnsi(msg).trim();
522
525
  const hasTtyError = cleaned.includes("Interactive prompts require a TTY terminal");
523
- console.error(chalk.red(`\n✗ drizzle-kit ${action} failed.\n`));
526
+ logger.error(chalk.red(`\n✗ drizzle-kit ${action} failed.\n`));
524
527
  if (hasTtyError) {
525
- console.error(chalk.red(" Error: Interactive prompts require a TTY terminal."));
526
- console.error(chalk.gray(" Please run with --force to skip interactive prompts or run in an interactive terminal."));
528
+ logger.error(chalk.red(" Error: Interactive prompts require a TTY terminal."));
529
+ logger.error(chalk.gray(" Please run with --force to skip interactive prompts or run in an interactive terminal."));
527
530
  } else {
528
531
  const lines = cleaned.split("\n").filter((l: string) => l.trim());
529
532
  for (const line of lines) {
530
533
  if (line.toLowerCase().includes("error") || line.includes("cannot") || line.includes("already exists") || line.includes("does not exist") || line.includes("violates")) {
531
- console.error(chalk.red(` ${line.trim()}`));
534
+ logger.error(chalk.red(` ${line.trim()}`));
532
535
  }
533
536
  }
534
537
  if (lines.length === 0) {
535
- console.error(chalk.gray(` ${cleaned}`));
538
+ logger.error(chalk.gray(` ${cleaned}`));
536
539
  }
537
540
  }
538
- console.error("");
541
+ logger.error("");
539
542
  process.exit(1);
540
543
  }
541
544
  }
@@ -561,13 +564,13 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
561
564
  // If installed in node_modules, __dirname is node_modules/@rebasepro/server-postgresql/dist or src.
562
565
  const generatorScript = path.join(__dirname, "schema", "generate-drizzle-schema.ts");
563
566
  if (!fs.existsSync(generatorScript)) {
564
- console.error(chalk.red(`✗ Could not find generate-drizzle-schema.ts at ${generatorScript}`));
567
+ logger.error(chalk.red(`✗ Could not find generate-drizzle-schema.ts at ${generatorScript}`));
565
568
  process.exit(1);
566
569
  }
567
570
 
568
571
  const tsxBin = resolveLocalBin("tsx");
569
572
  if (!tsxBin) {
570
- console.error(chalk.red("✗ Could not find tsx binary."));
573
+ logger.error(chalk.red("✗ Could not find tsx binary."));
571
574
  process.exit(1);
572
575
  }
573
576
 
@@ -575,9 +578,9 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
575
578
  const outputPath = argsList["--output"] || path.join("src", "schema.generated.ts");
576
579
  const watch = argsList["--watch"] || false;
577
580
 
578
- console.log("");
579
- console.log(chalk.bold(" 🔧 Rebase Schema Generator"));
580
- console.log("");
581
+ logger.info("");
582
+ logger.info(chalk.bold(" 🔧 Rebase Schema Generator"));
583
+ logger.info("");
581
584
 
582
585
  const cmdParts = [
583
586
  tsxBin,
@@ -596,7 +599,7 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
596
599
  env: { ...process.env as Record<string, string> }
597
600
  });
598
601
  } catch (err: unknown) {
599
- console.error(chalk.red(`✗ Failed to run schema generator: ${err instanceof Error ? err.message : String(err)}`));
602
+ logger.error(chalk.red(`✗ Failed to run schema generator: ${err instanceof Error ? err.message : String(err)}`));
600
603
  process.exit(1);
601
604
  }
602
605
  } else if (subcommand === "introspect") {
@@ -618,21 +621,21 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
618
621
 
619
622
  const introspectScript = path.join(__dirname, "schema", "introspect-db.ts");
620
623
  if (!fs.existsSync(introspectScript)) {
621
- console.error(chalk.red(`✗ Could not find introspect-db.ts at ${introspectScript}`));
624
+ logger.error(chalk.red(`✗ Could not find introspect-db.ts at ${introspectScript}`));
622
625
  process.exit(1);
623
626
  }
624
627
 
625
628
  const tsxBin = resolveLocalBin("tsx");
626
629
  if (!tsxBin) {
627
- console.error(chalk.red("✗ Could not find tsx binary."));
630
+ logger.error(chalk.red("✗ Could not find tsx binary."));
628
631
  process.exit(1);
629
632
  }
630
633
 
631
634
  const outputPath = argsList["--output"] || argsList["--collections"] || path.join("..", "config", "collections");
632
635
 
633
- console.log("");
634
- console.log(chalk.bold(" 🔍 Rebase Schema Introspector"));
635
- console.log("");
636
+ logger.info("");
637
+ logger.info(chalk.bold(" 🔍 Rebase Schema Introspector"));
638
+ logger.info("");
636
639
 
637
640
  const cmdParts = [
638
641
  tsxBin,
@@ -649,11 +652,11 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
649
652
  env: { ...process.env as Record<string, string> }
650
653
  });
651
654
  } catch (err: unknown) {
652
- console.error(chalk.red(`✗ Failed to run schema introspector: ${err instanceof Error ? err.message : String(err)}`));
655
+ logger.error(chalk.red(`✗ Failed to run schema introspector: ${err instanceof Error ? err.message : String(err)}`));
653
656
  process.exit(1);
654
657
  }
655
658
  } else {
656
- console.error(chalk.red("Unknown schema command."));
659
+ logger.error(chalk.red("Unknown schema command."));
657
660
  process.exit(1);
658
661
  }
659
662
  }
@@ -676,13 +679,13 @@ async function doctorPluginCommand(rawArgs: string[]): Promise<void> {
676
679
 
677
680
  const doctorScript = path.join(__dirname, "schema", "doctor-cli.ts");
678
681
  if (!fs.existsSync(doctorScript)) {
679
- console.error(chalk.red(`✗ Could not find doctor.ts at ${doctorScript}`));
682
+ logger.error(chalk.red(`✗ Could not find doctor.ts at ${doctorScript}`));
680
683
  process.exit(1);
681
684
  }
682
685
 
683
686
  const tsxBin = resolveLocalBin("tsx");
684
687
  if (!tsxBin) {
685
- console.error(chalk.red("✗ Could not find tsx binary."));
688
+ logger.error(chalk.red("✗ Could not find tsx binary."));
686
689
  process.exit(1);
687
690
  }
688
691
 
@@ -32,6 +32,13 @@ export class PostgresCollectionRegistry extends CollectionRegistry implements Co
32
32
  return this.tables.has(tableName);
33
33
  }
34
34
 
35
+ /**
36
+ * Returns all registered table names.
37
+ */
38
+ getTableNames(): string[] {
39
+ return Array.from(this.tables.keys());
40
+ }
41
+
35
42
  /**
36
43
  * Finds collections assigned to a specific driver that do not have a registered table.
37
44
  */
package/src/connection.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Pool, PoolConfig } from "pg";
2
2
  import { drizzle } from "drizzle-orm/node-postgres";
3
+ import { logger } from "@rebasepro/server-core";
3
4
 
4
5
  /**
5
6
  * Configuration for the Postgres connection pool.
@@ -69,9 +70,9 @@ export function createPostgresDatabaseConnection(
69
70
  // (a separate package). The caller can replace these with the structured
70
71
  // logger if desired via pool.on() after creation.
71
72
  pool.on("error", (err) => {
72
- console.error("[pg-pool] Unexpected pool error:", err.message);
73
+ logger.error("[pg-pool] Unexpected pool error", { detail: err.message });
73
74
  if (err.message.includes("ETIMEDOUT")) {
74
- console.warn("[pg-pool] Connection timeout detected — pool will auto-retry");
75
+ logger.warn("[pg-pool] Connection timeout detected — pool will auto-retry");
75
76
  }
76
77
  });
77
78
 
@@ -115,12 +116,14 @@ export function createDirectDatabaseConnection(
115
116
  const pool = new Pool(pgPoolConfig);
116
117
 
117
118
  pool.on("error", (err) => {
118
- console.error("[pg-direct-pool] Unexpected pool error:", err.message);
119
+ logger.error("[pg-direct-pool] Unexpected pool error", { detail: err.message });
119
120
  });
120
121
 
121
122
  const db = schema ? drizzle(pool, { schema }) : drizzle(pool);
122
123
 
123
- return { db, pool, connectionString };
124
+ return { db,
125
+ pool,
126
+ connectionString };
124
127
  }
125
128
 
126
129
  /**
@@ -152,10 +155,12 @@ export function createReadReplicaConnection(
152
155
  const pool = new Pool(pgPoolConfig);
153
156
 
154
157
  pool.on("error", (err) => {
155
- console.error("[pg-replica-pool] Unexpected pool error:", err.message);
158
+ logger.error("[pg-replica-pool] Unexpected pool error", { detail: err.message });
156
159
  });
157
160
 
158
161
  const db = schema ? drizzle(pool, { schema }) : drizzle(pool);
159
162
 
160
- return { db, pool, connectionString };
163
+ return { db,
164
+ pool,
165
+ connectionString };
161
166
  }