@rebasepro/server-postgresql 0.4.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.
- package/README.md +69 -89
- package/dist/{server-postgresql/src/PostgresAdapter.d.ts → PostgresAdapter.d.ts} +1 -1
- package/dist/{server-postgresql/src/PostgresBackendDriver.d.ts → PostgresBackendDriver.d.ts} +2 -2
- package/dist/{server-postgresql/src/PostgresBootstrapper.d.ts → PostgresBootstrapper.d.ts} +11 -1
- package/dist/{server-postgresql/src/auth → auth}/services.d.ts +11 -11
- package/dist/{server-postgresql/src/collections → collections}/PostgresCollectionRegistry.d.ts +4 -0
- package/dist/{server-postgresql/src/data-transformer.d.ts → data-transformer.d.ts} +0 -3
- package/dist/{server-postgresql/src/databasePoolManager.d.ts → databasePoolManager.d.ts} +1 -1
- package/dist/index.es.js +10174 -11184
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +10735 -11462
- package/dist/index.umd.js.map +1 -1
- package/dist/{server-postgresql/src/services → services}/EntityPersistService.d.ts +0 -14
- package/dist/types.d.ts +3 -0
- package/dist/utils/pg-error-utils.d.ts +55 -0
- package/dist/{server-postgresql/src/websocket.d.ts → websocket.d.ts} +8 -3
- package/package.json +24 -21
- package/src/PostgresAdapter.ts +9 -10
- package/src/PostgresBackendDriver.ts +135 -122
- package/src/PostgresBootstrapper.ts +90 -16
- package/src/auth/ensure-tables.ts +28 -5
- package/src/auth/services.ts +56 -45
- package/src/cli.ts +140 -110
- package/src/collections/PostgresCollectionRegistry.ts +7 -0
- package/src/connection.ts +11 -6
- package/src/data-transformer.ts +73 -109
- package/src/databasePoolManager.ts +5 -3
- package/src/history/HistoryService.ts +3 -2
- package/src/history/ensure-history-table.ts +5 -4
- package/src/schema/auth-schema.ts +1 -2
- package/src/schema/doctor-cli.ts +2 -1
- package/src/schema/doctor.ts +40 -37
- package/src/schema/generate-drizzle-schema-logic.ts +56 -18
- package/src/schema/generate-drizzle-schema.ts +11 -11
- package/src/schema/introspect-db-inference.ts +25 -25
- package/src/schema/introspect-db-logic.ts +38 -38
- package/src/schema/introspect-db.ts +28 -27
- package/src/services/BranchService.ts +14 -0
- package/src/services/EntityFetchService.ts +28 -25
- package/src/services/EntityPersistService.ts +11 -124
- package/src/services/RelationService.ts +57 -37
- package/src/services/entity-helpers.ts +6 -2
- package/src/services/realtimeService.ts +45 -32
- package/src/types.ts +4 -0
- package/src/utils/drizzle-conditions.ts +31 -15
- package/src/utils/pg-error-utils.ts +211 -0
- package/src/websocket.ts +51 -33
- package/test/auth-services.test.ts +36 -19
- package/test/batch-many-to-many-regression.test.ts +119 -39
- package/test/data-transformer-hardening.test.ts +67 -33
- package/test/data-transformer.test.ts +4 -2
- package/test/doctor.test.ts +10 -5
- package/test/drizzle-conditions.test.ts +59 -6
- package/test/generate-drizzle-schema.test.ts +65 -40
- package/test/introspect-db-generation.test.ts +179 -81
- package/test/introspect-db-utils.test.ts +92 -37
- package/test/mocks/chalk.cjs +7 -0
- package/test/pg-error-utils.test.ts +221 -0
- package/test/postgresDataDriver.test.ts +14 -5
- package/test/property-ordering.test.ts +126 -79
- package/test/realtimeService.test.ts +6 -2
- package/test/relation-pipeline-gaps.test.ts +84 -36
- package/test/relations.test.ts +247 -0
- package/test/unmapped-tables-safety.test.ts +14 -6
- package/test/websocket.test.ts +1 -1
- package/tsconfig.json +5 -0
- package/tsconfig.prod.json +3 -0
- package/vite.config.ts +5 -5
- package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
- package/dist/common/src/collections/default-collections.d.ts +0 -9
- package/dist/common/src/collections/index.d.ts +0 -2
- package/dist/common/src/data/buildRebaseData.d.ts +0 -14
- package/dist/common/src/data/query_builder.d.ts +0 -55
- package/dist/common/src/index.d.ts +0 -4
- package/dist/common/src/util/builders.d.ts +0 -57
- package/dist/common/src/util/callbacks.d.ts +0 -6
- package/dist/common/src/util/collections.d.ts +0 -11
- package/dist/common/src/util/common.d.ts +0 -2
- package/dist/common/src/util/conditions.d.ts +0 -26
- package/dist/common/src/util/entities.d.ts +0 -58
- package/dist/common/src/util/enums.d.ts +0 -3
- package/dist/common/src/util/index.d.ts +0 -16
- package/dist/common/src/util/navigation_from_path.d.ts +0 -34
- package/dist/common/src/util/navigation_utils.d.ts +0 -20
- package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
- package/dist/common/src/util/paths.d.ts +0 -14
- package/dist/common/src/util/permissions.d.ts +0 -6
- package/dist/common/src/util/references.d.ts +0 -2
- package/dist/common/src/util/relations.d.ts +0 -22
- package/dist/common/src/util/resolutions.d.ts +0 -72
- package/dist/common/src/util/storage.d.ts +0 -24
- package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
- package/dist/types/src/controllers/auth.d.ts +0 -104
- package/dist/types/src/controllers/client.d.ts +0 -168
- package/dist/types/src/controllers/collection_registry.d.ts +0 -46
- package/dist/types/src/controllers/customization_controller.d.ts +0 -60
- package/dist/types/src/controllers/data.d.ts +0 -207
- package/dist/types/src/controllers/data_driver.d.ts +0 -218
- package/dist/types/src/controllers/database_admin.d.ts +0 -11
- package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
- package/dist/types/src/controllers/effective_role.d.ts +0 -4
- package/dist/types/src/controllers/email.d.ts +0 -36
- package/dist/types/src/controllers/index.d.ts +0 -18
- package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
- package/dist/types/src/controllers/navigation.d.ts +0 -225
- package/dist/types/src/controllers/registry.d.ts +0 -63
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
- package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
- package/dist/types/src/controllers/snackbar.d.ts +0 -24
- package/dist/types/src/controllers/storage.d.ts +0 -171
- package/dist/types/src/index.d.ts +0 -4
- package/dist/types/src/rebase_context.d.ts +0 -122
- package/dist/types/src/types/auth_adapter.d.ts +0 -301
- package/dist/types/src/types/backend.d.ts +0 -536
- package/dist/types/src/types/backend_hooks.d.ts +0 -172
- package/dist/types/src/types/builders.d.ts +0 -15
- package/dist/types/src/types/chips.d.ts +0 -5
- package/dist/types/src/types/collections.d.ts +0 -941
- package/dist/types/src/types/component_ref.d.ts +0 -47
- package/dist/types/src/types/cron.d.ts +0 -102
- package/dist/types/src/types/data_source.d.ts +0 -64
- package/dist/types/src/types/database_adapter.d.ts +0 -94
- package/dist/types/src/types/entities.d.ts +0 -145
- package/dist/types/src/types/entity_actions.d.ts +0 -104
- package/dist/types/src/types/entity_callbacks.d.ts +0 -173
- package/dist/types/src/types/entity_link_builder.d.ts +0 -7
- package/dist/types/src/types/entity_overrides.d.ts +0 -10
- package/dist/types/src/types/entity_views.d.ts +0 -87
- package/dist/types/src/types/export_import.d.ts +0 -21
- package/dist/types/src/types/formex.d.ts +0 -40
- package/dist/types/src/types/index.d.ts +0 -28
- package/dist/types/src/types/locales.d.ts +0 -4
- package/dist/types/src/types/modify_collections.d.ts +0 -5
- package/dist/types/src/types/plugins.d.ts +0 -282
- package/dist/types/src/types/properties.d.ts +0 -1181
- package/dist/types/src/types/property_config.d.ts +0 -74
- package/dist/types/src/types/relations.d.ts +0 -336
- package/dist/types/src/types/slots.d.ts +0 -262
- package/dist/types/src/types/translations.d.ts +0 -900
- package/dist/types/src/types/user_management_delegate.d.ts +0 -86
- package/dist/types/src/types/websockets.d.ts +0 -78
- package/dist/types/src/users/index.d.ts +0 -1
- package/dist/types/src/users/user.d.ts +0 -50
- package/drizzle.test.config.ts +0 -10
- /package/dist/{server-postgresql/src/auth → auth}/ensure-tables.d.ts +0 -0
- /package/dist/{server-postgresql/src/cli.d.ts → cli.d.ts} +0 -0
- /package/dist/{server-postgresql/src/connection.d.ts → connection.d.ts} +0 -0
- /package/dist/{server-postgresql/src/history → history}/HistoryService.d.ts +0 -0
- /package/dist/{server-postgresql/src/history → history}/ensure-history-table.d.ts +0 -0
- /package/dist/{server-postgresql/src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{server-postgresql/src/interfaces.d.ts → interfaces.d.ts} +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/auth-schema.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/doctor-cli.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/doctor.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/generate-drizzle-schema-logic.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/generate-drizzle-schema.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/introspect-db-inference.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/introspect-db-logic.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/introspect-db.d.ts +0 -0
- /package/dist/{server-postgresql/src/schema → schema}/test-schema.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/BranchService.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/EntityFetchService.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/RelationService.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/entity-helpers.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/entityService.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/index.d.ts +0 -0
- /package/dist/{server-postgresql/src/services → services}/realtimeService.d.ts +0 -0
- /package/dist/{server-postgresql/src/utils → utils}/drizzle-conditions.d.ts +0 -0
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
87
|
-
|
|
87
|
+
logger.info(chalk.gray(" Step 1/2: Generating Drizzle schema from collections..."));
|
|
88
|
+
logger.info("");
|
|
88
89
|
await schemaCommand("generate", rawArgs);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (source)
|
|
172
|
-
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
184
|
+
logger.info("");
|
|
184
185
|
if (branches.length === 0) {
|
|
185
|
-
|
|
186
|
+
logger.info(chalk.gray(" No branches found. Create one with: rebase db branch create <name>"));
|
|
186
187
|
} else {
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
195
|
-
|
|
195
|
+
logger.info(` ${chalk.green("●")} ${chalk.bold(b.name)}${size}${age}`);
|
|
196
|
+
logger.info(chalk.gray(` from ${b.parentDatabase}`));
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
|
-
|
|
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
|
-
|
|
206
|
-
|
|
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
|
-
|
|
210
|
-
|
|
210
|
+
logger.info("");
|
|
211
|
+
logger.info(chalk.bold(` 🗑️ Deleting branch "${name}"...`));
|
|
211
212
|
await branchService.deleteBranch(name);
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
221
|
-
|
|
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
|
-
|
|
226
|
+
logger.info("");
|
|
226
227
|
if (!info) {
|
|
227
|
-
|
|
228
|
+
logger.error(chalk.red(` ✗ Branch "${name}" not found.`));
|
|
228
229
|
} else {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
235
|
+
logger.info(chalk.gray(` Size: ${formatBytes(info.sizeBytes)}`));
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
|
-
|
|
238
|
+
logger.info("");
|
|
238
239
|
break;
|
|
239
240
|
}
|
|
240
241
|
|
|
241
242
|
default:
|
|
242
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
408
|
-
|
|
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
|
|
|
@@ -436,15 +439,28 @@ async function runDrizzleKit(action: string, _rawArgs: string[]): Promise<void>
|
|
|
436
439
|
// dotenv may not be available — fall through
|
|
437
440
|
}
|
|
438
441
|
|
|
439
|
-
const interactive = ["generate", "push"].includes(action);
|
|
442
|
+
const interactive = ["generate", "push"].includes(action) && Boolean(process.stdout.isTTY);
|
|
440
443
|
|
|
441
444
|
// For push: always use --strict (prompts before destructive ops) and --verbose
|
|
442
445
|
// (shows all SQL). This ensures unmapped tables are never silently dropped.
|
|
443
446
|
const drizzleKitArgs = [action];
|
|
444
447
|
if (action === "push") {
|
|
445
448
|
drizzleKitArgs.push("--strict", "--verbose");
|
|
446
|
-
|
|
447
|
-
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Forward any additional arguments, excluding schema-generator-specific options
|
|
452
|
+
const excludedFlags = ["--collections", "-c", "--output", "-o", "--watch", "-w"];
|
|
453
|
+
for (let i = 2; i < _rawArgs.length; i++) {
|
|
454
|
+
const arg = _rawArgs[i];
|
|
455
|
+
if (excludedFlags.includes(arg)) {
|
|
456
|
+
// Skip this flag and its value if it takes a parameter
|
|
457
|
+
if (["--collections", "-c", "--output", "-o"].includes(arg)) {
|
|
458
|
+
i++; // Skip the next arg (the value)
|
|
459
|
+
}
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (!drizzleKitArgs.includes(arg)) {
|
|
463
|
+
drizzleKitArgs.push(arg);
|
|
448
464
|
}
|
|
449
465
|
}
|
|
450
466
|
|
|
@@ -456,7 +472,7 @@ async function runDrizzleKit(action: string, _rawArgs: string[]): Promise<void>
|
|
|
456
472
|
env
|
|
457
473
|
});
|
|
458
474
|
} else {
|
|
459
|
-
const child = execa(drizzleKitBin,
|
|
475
|
+
const child = execa(drizzleKitBin, drizzleKitArgs, {
|
|
460
476
|
cwd: process.cwd(),
|
|
461
477
|
env,
|
|
462
478
|
reject: false
|
|
@@ -473,23 +489,31 @@ async function runDrizzleKit(action: string, _rawArgs: string[]): Promise<void>
|
|
|
473
489
|
const stdout = stripAnsi(result.stdout || "").trim();
|
|
474
490
|
const stderr = stripAnsi(result.stderr || "").trim();
|
|
475
491
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
492
|
+
const hasTtyError = stdout.includes("Interactive prompts require a TTY terminal") ||
|
|
493
|
+
stderr.includes("Interactive prompts require a TTY terminal");
|
|
494
|
+
|
|
495
|
+
if (result.exitCode !== 0 || hasTtyError) {
|
|
496
|
+
logger.error(chalk.red(`\n✗ drizzle-kit ${action} failed.\n`));
|
|
497
|
+
if (hasTtyError) {
|
|
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."));
|
|
500
|
+
} else {
|
|
501
|
+
const errorOutput = stderr || stdout;
|
|
502
|
+
if (errorOutput) {
|
|
503
|
+
const lines = errorOutput.split("\n").filter((l: string) => l.trim());
|
|
504
|
+
let printedCount = 0;
|
|
505
|
+
for (const line of lines) {
|
|
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")) {
|
|
507
|
+
logger.error(chalk.red(` ${line.trim()}`));
|
|
508
|
+
printedCount++;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (printedCount === 0) {
|
|
512
|
+
lines.slice(0, 10).forEach(line => logger.error(chalk.red(` ${line.trim()}`)));
|
|
486
513
|
}
|
|
487
|
-
}
|
|
488
|
-
if (printedCount === 0) {
|
|
489
|
-
lines.slice(0, 10).forEach(line => console.error(chalk.red(` ${line.trim()}`)));
|
|
490
514
|
}
|
|
491
515
|
}
|
|
492
|
-
|
|
516
|
+
logger.error("");
|
|
493
517
|
process.exit(1);
|
|
494
518
|
}
|
|
495
519
|
}
|
|
@@ -498,17 +522,23 @@ async function runDrizzleKit(action: string, _rawArgs: string[]): Promise<void>
|
|
|
498
522
|
// eslint-disable-next-line no-control-regex
|
|
499
523
|
const stripAnsi = (s: string) => s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\[?[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣷⣯⣟⡿⢿⣻⣽]+\]\s*/g, "");
|
|
500
524
|
const cleaned = stripAnsi(msg).trim();
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
525
|
+
const hasTtyError = cleaned.includes("Interactive prompts require a TTY terminal");
|
|
526
|
+
logger.error(chalk.red(`\n✗ drizzle-kit ${action} failed.\n`));
|
|
527
|
+
if (hasTtyError) {
|
|
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."));
|
|
530
|
+
} else {
|
|
531
|
+
const lines = cleaned.split("\n").filter((l: string) => l.trim());
|
|
532
|
+
for (const line of lines) {
|
|
533
|
+
if (line.toLowerCase().includes("error") || line.includes("cannot") || line.includes("already exists") || line.includes("does not exist") || line.includes("violates")) {
|
|
534
|
+
logger.error(chalk.red(` ${line.trim()}`));
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (lines.length === 0) {
|
|
538
|
+
logger.error(chalk.gray(` ${cleaned}`));
|
|
506
539
|
}
|
|
507
540
|
}
|
|
508
|
-
|
|
509
|
-
console.error(chalk.gray(` ${cleaned}`));
|
|
510
|
-
}
|
|
511
|
-
console.error("");
|
|
541
|
+
logger.error("");
|
|
512
542
|
process.exit(1);
|
|
513
543
|
}
|
|
514
544
|
}
|
|
@@ -534,13 +564,13 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
|
|
|
534
564
|
// If installed in node_modules, __dirname is node_modules/@rebasepro/server-postgresql/dist or src.
|
|
535
565
|
const generatorScript = path.join(__dirname, "schema", "generate-drizzle-schema.ts");
|
|
536
566
|
if (!fs.existsSync(generatorScript)) {
|
|
537
|
-
|
|
567
|
+
logger.error(chalk.red(`✗ Could not find generate-drizzle-schema.ts at ${generatorScript}`));
|
|
538
568
|
process.exit(1);
|
|
539
569
|
}
|
|
540
570
|
|
|
541
571
|
const tsxBin = resolveLocalBin("tsx");
|
|
542
572
|
if (!tsxBin) {
|
|
543
|
-
|
|
573
|
+
logger.error(chalk.red("✗ Could not find tsx binary."));
|
|
544
574
|
process.exit(1);
|
|
545
575
|
}
|
|
546
576
|
|
|
@@ -548,9 +578,9 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
|
|
|
548
578
|
const outputPath = argsList["--output"] || path.join("src", "schema.generated.ts");
|
|
549
579
|
const watch = argsList["--watch"] || false;
|
|
550
580
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
581
|
+
logger.info("");
|
|
582
|
+
logger.info(chalk.bold(" 🔧 Rebase Schema Generator"));
|
|
583
|
+
logger.info("");
|
|
554
584
|
|
|
555
585
|
const cmdParts = [
|
|
556
586
|
tsxBin,
|
|
@@ -569,7 +599,7 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
|
|
|
569
599
|
env: { ...process.env as Record<string, string> }
|
|
570
600
|
});
|
|
571
601
|
} catch (err: unknown) {
|
|
572
|
-
|
|
602
|
+
logger.error(chalk.red(`✗ Failed to run schema generator: ${err instanceof Error ? err.message : String(err)}`));
|
|
573
603
|
process.exit(1);
|
|
574
604
|
}
|
|
575
605
|
} else if (subcommand === "introspect") {
|
|
@@ -591,21 +621,21 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
|
|
|
591
621
|
|
|
592
622
|
const introspectScript = path.join(__dirname, "schema", "introspect-db.ts");
|
|
593
623
|
if (!fs.existsSync(introspectScript)) {
|
|
594
|
-
|
|
624
|
+
logger.error(chalk.red(`✗ Could not find introspect-db.ts at ${introspectScript}`));
|
|
595
625
|
process.exit(1);
|
|
596
626
|
}
|
|
597
627
|
|
|
598
628
|
const tsxBin = resolveLocalBin("tsx");
|
|
599
629
|
if (!tsxBin) {
|
|
600
|
-
|
|
630
|
+
logger.error(chalk.red("✗ Could not find tsx binary."));
|
|
601
631
|
process.exit(1);
|
|
602
632
|
}
|
|
603
633
|
|
|
604
634
|
const outputPath = argsList["--output"] || argsList["--collections"] || path.join("..", "config", "collections");
|
|
605
635
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
636
|
+
logger.info("");
|
|
637
|
+
logger.info(chalk.bold(" 🔍 Rebase Schema Introspector"));
|
|
638
|
+
logger.info("");
|
|
609
639
|
|
|
610
640
|
const cmdParts = [
|
|
611
641
|
tsxBin,
|
|
@@ -622,11 +652,11 @@ async function schemaCommand(subcommand: string, rawArgs: string[]): Promise<voi
|
|
|
622
652
|
env: { ...process.env as Record<string, string> }
|
|
623
653
|
});
|
|
624
654
|
} catch (err: unknown) {
|
|
625
|
-
|
|
655
|
+
logger.error(chalk.red(`✗ Failed to run schema introspector: ${err instanceof Error ? err.message : String(err)}`));
|
|
626
656
|
process.exit(1);
|
|
627
657
|
}
|
|
628
658
|
} else {
|
|
629
|
-
|
|
659
|
+
logger.error(chalk.red("Unknown schema command."));
|
|
630
660
|
process.exit(1);
|
|
631
661
|
}
|
|
632
662
|
}
|
|
@@ -649,13 +679,13 @@ async function doctorPluginCommand(rawArgs: string[]): Promise<void> {
|
|
|
649
679
|
|
|
650
680
|
const doctorScript = path.join(__dirname, "schema", "doctor-cli.ts");
|
|
651
681
|
if (!fs.existsSync(doctorScript)) {
|
|
652
|
-
|
|
682
|
+
logger.error(chalk.red(`✗ Could not find doctor.ts at ${doctorScript}`));
|
|
653
683
|
process.exit(1);
|
|
654
684
|
}
|
|
655
685
|
|
|
656
686
|
const tsxBin = resolveLocalBin("tsx");
|
|
657
687
|
if (!tsxBin) {
|
|
658
|
-
|
|
688
|
+
logger.error(chalk.red("✗ Could not find tsx binary."));
|
|
659
689
|
process.exit(1);
|
|
660
690
|
}
|
|
661
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
|
-
|
|
73
|
+
logger.error("[pg-pool] Unexpected pool error", { detail: err.message });
|
|
73
74
|
if (err.message.includes("ETIMEDOUT")) {
|
|
74
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
163
|
+
return { db,
|
|
164
|
+
pool,
|
|
165
|
+
connectionString };
|
|
161
166
|
}
|