@rebasepro/server-postgresql 0.0.1-canary.4d4fb3e → 0.0.1-canary.5584634
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/dist/common/src/collections/CollectionRegistry.d.ts +8 -0
- package/dist/common/src/util/entities.d.ts +22 -0
- package/dist/common/src/util/relations.d.ts +14 -4
- package/dist/common/src/util/resolutions.d.ts +1 -1
- package/dist/index.es.js +1254 -591
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1254 -591
- package/dist/index.umd.js.map +1 -1
- package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +17 -29
- package/dist/server-postgresql/src/auth/services.d.ts +7 -3
- package/dist/server-postgresql/src/collections/PostgresCollectionRegistry.d.ts +1 -1
- package/dist/server-postgresql/src/connection.d.ts +34 -1
- package/dist/server-postgresql/src/data-transformer.d.ts +26 -4
- package/dist/server-postgresql/src/databasePoolManager.d.ts +2 -2
- package/dist/server-postgresql/src/schema/auth-schema.d.ts +139 -38
- package/dist/server-postgresql/src/schema/doctor-cli.d.ts +2 -0
- package/dist/server-postgresql/src/schema/doctor.d.ts +43 -0
- package/dist/server-postgresql/src/schema/generate-drizzle-schema-logic.d.ts +1 -1
- package/dist/server-postgresql/src/schema/introspect-db-logic.d.ts +82 -0
- package/dist/server-postgresql/src/schema/introspect-db.d.ts +1 -0
- package/dist/server-postgresql/src/schema/test-schema.d.ts +24 -0
- package/dist/server-postgresql/src/services/EntityFetchService.d.ts +22 -8
- package/dist/server-postgresql/src/services/EntityPersistService.d.ts +1 -1
- package/dist/server-postgresql/src/services/RelationService.d.ts +11 -5
- package/dist/server-postgresql/src/services/entity-helpers.d.ts +16 -2
- package/dist/server-postgresql/src/services/entityService.d.ts +8 -6
- package/dist/server-postgresql/src/services/realtimeService.d.ts +2 -0
- package/dist/server-postgresql/src/utils/drizzle-conditions.d.ts +2 -2
- package/dist/types/src/controllers/auth.d.ts +2 -0
- package/dist/types/src/controllers/client.d.ts +119 -7
- package/dist/types/src/controllers/collection_registry.d.ts +4 -3
- package/dist/types/src/controllers/customization_controller.d.ts +7 -1
- package/dist/types/src/controllers/data.d.ts +34 -7
- package/dist/types/src/controllers/data_driver.d.ts +20 -28
- package/dist/types/src/controllers/database_admin.d.ts +2 -2
- package/dist/types/src/controllers/email.d.ts +34 -0
- package/dist/types/src/controllers/index.d.ts +1 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +4 -4
- package/dist/types/src/controllers/navigation.d.ts +5 -5
- package/dist/types/src/controllers/registry.d.ts +6 -3
- package/dist/types/src/controllers/side_entity_controller.d.ts +7 -6
- package/dist/types/src/controllers/storage.d.ts +24 -26
- package/dist/types/src/rebase_context.d.ts +8 -4
- package/dist/types/src/types/backend.d.ts +4 -1
- package/dist/types/src/types/builders.d.ts +5 -4
- package/dist/types/src/types/chips.d.ts +1 -1
- package/dist/types/src/types/collections.d.ts +169 -125
- package/dist/types/src/types/cron.d.ts +102 -0
- package/dist/types/src/types/data_source.d.ts +1 -1
- package/dist/types/src/types/entity_actions.d.ts +8 -8
- package/dist/types/src/types/entity_callbacks.d.ts +15 -15
- package/dist/types/src/types/entity_link_builder.d.ts +1 -1
- package/dist/types/src/types/entity_overrides.d.ts +2 -1
- package/dist/types/src/types/entity_views.d.ts +8 -8
- package/dist/types/src/types/export_import.d.ts +3 -3
- package/dist/types/src/types/index.d.ts +1 -0
- package/dist/types/src/types/plugins.d.ts +72 -18
- package/dist/types/src/types/properties.d.ts +118 -33
- package/dist/types/src/types/relations.d.ts +1 -1
- package/dist/types/src/types/slots.d.ts +30 -6
- package/dist/types/src/types/translations.d.ts +44 -0
- package/dist/types/src/types/user_management_delegate.d.ts +1 -0
- package/drizzle-test/0000_woozy_junta.sql +6 -0
- package/drizzle-test/0001_youthful_arachne.sql +1 -0
- package/drizzle-test/0002_lively_dragon_lord.sql +2 -0
- package/drizzle-test/0003_mean_king_cobra.sql +2 -0
- package/drizzle-test/meta/0000_snapshot.json +47 -0
- package/drizzle-test/meta/0001_snapshot.json +48 -0
- package/drizzle-test/meta/0002_snapshot.json +38 -0
- package/drizzle-test/meta/0003_snapshot.json +48 -0
- package/drizzle-test/meta/_journal.json +34 -0
- package/drizzle-test-out/0000_tan_trauma.sql +6 -0
- package/drizzle-test-out/0001_rapid_drax.sql +1 -0
- package/drizzle-test-out/meta/0000_snapshot.json +44 -0
- package/drizzle-test-out/meta/0001_snapshot.json +54 -0
- package/drizzle-test-out/meta/_journal.json +20 -0
- package/drizzle.test.config.ts +10 -0
- package/package.json +89 -89
- package/scratch.ts +41 -0
- package/src/PostgresBackendDriver.ts +63 -79
- package/src/PostgresBootstrapper.ts +7 -8
- package/src/auth/ensure-tables.ts +158 -86
- package/src/auth/services.ts +109 -50
- package/src/cli.ts +317 -16
- package/src/collections/PostgresCollectionRegistry.ts +6 -6
- package/src/connection.ts +70 -48
- package/src/data-transformer.ts +155 -116
- package/src/databasePoolManager.ts +6 -5
- package/src/history/HistoryService.ts +3 -12
- package/src/interfaces.ts +3 -3
- package/src/schema/auth-schema.ts +26 -3
- package/src/schema/doctor-cli.ts +47 -0
- package/src/schema/doctor.ts +595 -0
- package/src/schema/generate-drizzle-schema-logic.ts +204 -57
- package/src/schema/generate-drizzle-schema.ts +6 -6
- package/src/schema/introspect-db-logic.ts +592 -0
- package/src/schema/introspect-db.ts +211 -0
- package/src/schema/test-schema.ts +11 -0
- package/src/services/BranchService.ts +5 -5
- package/src/services/EntityFetchService.ts +317 -188
- package/src/services/EntityPersistService.ts +15 -17
- package/src/services/RelationService.ts +299 -37
- package/src/services/entity-helpers.ts +39 -13
- package/src/services/entityService.ts +11 -9
- package/src/services/realtimeService.ts +58 -29
- package/src/utils/drizzle-conditions.ts +25 -24
- package/src/websocket.ts +52 -21
- package/test/auth-services.test.ts +131 -39
- package/test/batch-many-to-many-regression.test.ts +573 -0
- package/test/branchService.test.ts +22 -12
- package/test/data-transformer-hardening.test.ts +417 -0
- package/test/data-transformer.test.ts +175 -0
- package/test/doctor.test.ts +182 -0
- package/test/entityService.errors.test.ts +31 -16
- package/test/entityService.relations.test.ts +155 -59
- package/test/entityService.subcollection-search.test.ts +107 -57
- package/test/entityService.test.ts +105 -47
- package/test/generate-drizzle-schema.test.ts +262 -69
- package/test/historyService.test.ts +31 -16
- package/test/introspect-db-generation.test.ts +436 -0
- package/test/introspect-db-utils.test.ts +389 -0
- package/test/n-plus-one-regression.test.ts +314 -0
- package/test/postgresDataDriver.test.ts +260 -168
- package/test/realtimeService.test.ts +70 -39
- package/test/relation-pipeline-gaps.test.ts +637 -0
- package/test/relations.test.ts +492 -39
- package/test/unmapped-tables-safety.test.ts +345 -0
- package/test-drizzle-bug.ts +18 -0
- package/test-drizzle-out/0000_cultured_freak.sql +7 -0
- package/test-drizzle-out/0001_tiresome_professor_monster.sql +1 -0
- package/test-drizzle-out/meta/0000_snapshot.json +55 -0
- package/test-drizzle-out/meta/0001_snapshot.json +63 -0
- package/test-drizzle-out/meta/_journal.json +20 -0
- package/test-drizzle-prompt.sh +2 -0
- package/test-policy-prompt.sh +3 -0
- package/test-programmatic.ts +30 -0
- package/test-programmatic2.ts +59 -0
- package/test-schema-no-policies.ts +12 -0
- package/test_drizzle_mock.js +2 -2
- package/test_find_changed.mjs +3 -1
- package/test_hash.js +14 -0
- package/tsconfig.json +1 -1
- package/vite.config.ts +5 -5
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import pg from "pg";
|
|
5
|
+
import arg from "arg";
|
|
6
|
+
import * as dotenv from "dotenv";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
TableRow,
|
|
10
|
+
TableColumn,
|
|
11
|
+
EnumValue,
|
|
12
|
+
PrimaryKeyRow,
|
|
13
|
+
ForeignKeyRow,
|
|
14
|
+
buildTablesMap,
|
|
15
|
+
buildEnumMap,
|
|
16
|
+
identifyJoinTables,
|
|
17
|
+
generateCollectionFile,
|
|
18
|
+
generateIndexContent,
|
|
19
|
+
mergeIndexContent,
|
|
20
|
+
safeHostFromUrl,
|
|
21
|
+
} from "./introspect-db-logic";
|
|
22
|
+
|
|
23
|
+
async function main() {
|
|
24
|
+
const args = arg(
|
|
25
|
+
{
|
|
26
|
+
"--output": String,
|
|
27
|
+
"--force": Boolean,
|
|
28
|
+
"--schema": String,
|
|
29
|
+
"-o": "--output",
|
|
30
|
+
"-f": "--force",
|
|
31
|
+
},
|
|
32
|
+
{ permissive: true }
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const outDir = args["--output"] || path.resolve(process.cwd(), "config", "collections");
|
|
36
|
+
const force = args["--force"] || false;
|
|
37
|
+
const pgSchema = args["--schema"] || "public";
|
|
38
|
+
|
|
39
|
+
if (!fs.existsSync(outDir)) {
|
|
40
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Load env
|
|
44
|
+
const envPaths = [
|
|
45
|
+
process.env.DOTENV_CONFIG_PATH,
|
|
46
|
+
path.resolve(process.cwd(), ".env"),
|
|
47
|
+
path.resolve(process.cwd(), "../.env"),
|
|
48
|
+
path.resolve(process.cwd(), "../../.env")
|
|
49
|
+
].filter(Boolean) as string[];
|
|
50
|
+
|
|
51
|
+
for (const p of envPaths) {
|
|
52
|
+
if (fs.existsSync(p)) {
|
|
53
|
+
dotenv.config({ path: p });
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const databaseUrl = process.env.DATABASE_URL || process.env.ADMIN_CONNECTION_STRING;
|
|
59
|
+
if (!databaseUrl) {
|
|
60
|
+
console.error(chalk.red("✗ DATABASE_URL is not set. Make sure your .env file is configured."));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const client = new pg.Client({ connectionString: databaseUrl });
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await client.connect();
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error(chalk.red(`✗ Failed to connect to database: ${err instanceof Error ? err.message : String(err)}`));
|
|
70
|
+
console.error(chalk.gray(" Check your DATABASE_URL and ensure the database is reachable."));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Log the host portion safely — handle URLs without "@"
|
|
75
|
+
const hostPart = safeHostFromUrl(databaseUrl);
|
|
76
|
+
console.log(chalk.gray(`Connected to database: ${hostPart}`));
|
|
77
|
+
console.log(chalk.gray(`Introspecting schema '${pgSchema}'...`));
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// 1. Get Tables
|
|
81
|
+
const { rows: tables } = await client.query<TableRow>(`
|
|
82
|
+
SELECT table_name
|
|
83
|
+
FROM information_schema.tables
|
|
84
|
+
WHERE table_schema = $1 AND table_type = 'BASE TABLE'
|
|
85
|
+
AND table_name NOT LIKE 'drizzle_%'
|
|
86
|
+
AND table_name NOT LIKE 'rebase_%'
|
|
87
|
+
ORDER BY table_name
|
|
88
|
+
`, [pgSchema]);
|
|
89
|
+
|
|
90
|
+
// 2. Get Columns
|
|
91
|
+
const { rows: columns } = await client.query<TableColumn>(`
|
|
92
|
+
SELECT table_name, column_name, data_type, udt_name, is_nullable, column_default
|
|
93
|
+
FROM information_schema.columns
|
|
94
|
+
WHERE table_schema = $1
|
|
95
|
+
`, [pgSchema]);
|
|
96
|
+
|
|
97
|
+
// 2b. Get Enum Types and their values
|
|
98
|
+
const { rows: enumValues } = await client.query<EnumValue>(`
|
|
99
|
+
SELECT t.typname AS enum_name,
|
|
100
|
+
e.enumlabel AS enum_value,
|
|
101
|
+
e.enumsortorder AS sort_order
|
|
102
|
+
FROM pg_type t
|
|
103
|
+
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
104
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
|
105
|
+
WHERE n.nspname = $1
|
|
106
|
+
ORDER BY t.typname, e.enumsortorder
|
|
107
|
+
`, [pgSchema]);
|
|
108
|
+
|
|
109
|
+
// Build a map: enum_name -> ordered list of values
|
|
110
|
+
const enumMap = buildEnumMap(enumValues);
|
|
111
|
+
|
|
112
|
+
// 3. Get Primary Keys
|
|
113
|
+
const { rows: pks } = await client.query<PrimaryKeyRow>(`
|
|
114
|
+
SELECT t.relname as table_name, a.attname as column_name
|
|
115
|
+
FROM pg_index i
|
|
116
|
+
JOIN pg_attribute a ON a.attrelid = i.indrelid
|
|
117
|
+
AND a.attnum = ANY(i.indkey)
|
|
118
|
+
JOIN pg_class t ON t.oid = i.indrelid
|
|
119
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
120
|
+
WHERE i.indisprimary AND n.nspname = $1
|
|
121
|
+
`, [pgSchema]);
|
|
122
|
+
|
|
123
|
+
// 4. Get Foreign Keys
|
|
124
|
+
const { rows: fks } = await client.query<ForeignKeyRow>(`
|
|
125
|
+
SELECT
|
|
126
|
+
tc.table_name,
|
|
127
|
+
kcu.column_name,
|
|
128
|
+
ccu.table_name AS foreign_table_name,
|
|
129
|
+
ccu.column_name AS foreign_column_name
|
|
130
|
+
FROM
|
|
131
|
+
information_schema.table_constraints AS tc
|
|
132
|
+
JOIN information_schema.key_column_usage AS kcu
|
|
133
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
134
|
+
AND tc.table_schema = kcu.table_schema
|
|
135
|
+
JOIN information_schema.constraint_column_usage AS ccu
|
|
136
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
137
|
+
AND ccu.table_schema = tc.table_schema
|
|
138
|
+
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = $1
|
|
139
|
+
`, [pgSchema]);
|
|
140
|
+
|
|
141
|
+
const tablesMap = buildTablesMap(tables, columns, pks, fks);
|
|
142
|
+
const joinTables = identifyJoinTables(tablesMap);
|
|
143
|
+
|
|
144
|
+
console.log(chalk.blue(`Found ${tablesMap.size} tables (including ${joinTables.size} detected join tables).`));
|
|
145
|
+
|
|
146
|
+
// Generate Collections
|
|
147
|
+
const generatedFiles: string[] = [];
|
|
148
|
+
const skippedFiles: string[] = [];
|
|
149
|
+
|
|
150
|
+
for (const [tableName, meta] of tablesMap.entries()) {
|
|
151
|
+
if (joinTables.has(tableName)) continue; // We don't generate base collections for pure join tables
|
|
152
|
+
|
|
153
|
+
// ── File overwrite protection ──────────────────────────────
|
|
154
|
+
const filePath = path.join(outDir, `${tableName}.ts`);
|
|
155
|
+
if (fs.existsSync(filePath) && !force) {
|
|
156
|
+
skippedFiles.push(tableName);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const fileContent = generateCollectionFile(
|
|
161
|
+
tableName,
|
|
162
|
+
meta,
|
|
163
|
+
fks,
|
|
164
|
+
joinTables,
|
|
165
|
+
tablesMap,
|
|
166
|
+
enumMap,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
fs.writeFileSync(filePath, fileContent, "utf-8");
|
|
170
|
+
generatedFiles.push(tableName);
|
|
171
|
+
console.log(chalk.green(` ✓ ${filePath}`));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Generate index.ts (sorted alphabetically for deterministic output)
|
|
175
|
+
if (generatedFiles.length > 0) {
|
|
176
|
+
const indexPath = path.join(outDir, "index.ts");
|
|
177
|
+
|
|
178
|
+
if (fs.existsSync(indexPath) && !force) {
|
|
179
|
+
// Merge: read existing index, add new exports that don't already exist
|
|
180
|
+
const existing = fs.readFileSync(indexPath, "utf-8");
|
|
181
|
+
const merged = mergeIndexContent(existing, generatedFiles);
|
|
182
|
+
fs.writeFileSync(indexPath, merged, "utf-8");
|
|
183
|
+
} else {
|
|
184
|
+
const indexContent = generateIndexContent(generatedFiles);
|
|
185
|
+
fs.writeFileSync(indexPath, indexContent, "utf-8");
|
|
186
|
+
}
|
|
187
|
+
console.log(chalk.green(` ✓ ${indexPath}`));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log("");
|
|
191
|
+
if (skippedFiles.length > 0) {
|
|
192
|
+
console.log(chalk.yellow(`⚠ Skipped ${skippedFiles.length} existing file(s): ${skippedFiles.join(", ")}`));
|
|
193
|
+
console.log(chalk.gray(` Use --force to overwrite existing files.`));
|
|
194
|
+
console.log("");
|
|
195
|
+
}
|
|
196
|
+
console.log(chalk.bold.green(`✓ Introspected ${tablesMap.size} tables — generated ${generatedFiles.length} collection(s).`));
|
|
197
|
+
console.log(chalk.gray(` Review the generated files in ${outDir} and customize properties as needed.`));
|
|
198
|
+
console.log("");
|
|
199
|
+
|
|
200
|
+
} catch (e) {
|
|
201
|
+
console.error(chalk.red(`✗ Error introspecting database: ${e instanceof Error ? e.message : String(e)}`));
|
|
202
|
+
process.exit(1);
|
|
203
|
+
} finally {
|
|
204
|
+
await client.end();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
main().catch((err) => {
|
|
209
|
+
console.error(err);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { pgTable, text, pgPolicy } from "drizzle-orm/pg-core";
|
|
2
|
+
import { sql } from "drizzle-orm";
|
|
3
|
+
|
|
4
|
+
export const testTable = pgTable("test", {
|
|
5
|
+
id: text("id").primaryKey()
|
|
6
|
+
}, (t) => [
|
|
7
|
+
pgPolicy("renamed_policy", { as: "permissive",
|
|
8
|
+
to: "public",
|
|
9
|
+
for: "select",
|
|
10
|
+
using: sql`true` })
|
|
11
|
+
]);
|
|
@@ -103,7 +103,7 @@ export class BranchService {
|
|
|
103
103
|
if (msg.includes("being accessed by other users")) {
|
|
104
104
|
throw new Error(
|
|
105
105
|
`Cannot create branch: the source database "${sourceDb}" has active connections. ` +
|
|
106
|
-
|
|
106
|
+
"Close other clients or connections and try again."
|
|
107
107
|
);
|
|
108
108
|
}
|
|
109
109
|
throw err;
|
|
@@ -119,7 +119,7 @@ export class BranchService {
|
|
|
119
119
|
return {
|
|
120
120
|
name: sanitizedName,
|
|
121
121
|
parentDatabase: sourceDb,
|
|
122
|
-
createdAt: now
|
|
122
|
+
createdAt: now
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -156,7 +156,7 @@ export class BranchService {
|
|
|
156
156
|
if (msg.includes("being accessed by other users")) {
|
|
157
157
|
throw new Error(
|
|
158
158
|
`Cannot delete branch "${sanitizedName}": the database has active connections. ` +
|
|
159
|
-
|
|
159
|
+
"Close other clients and try again."
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
162
|
throw err;
|
|
@@ -188,7 +188,7 @@ export class BranchService {
|
|
|
188
188
|
name: row.name as string,
|
|
189
189
|
parentDatabase: row.parent_db as string,
|
|
190
190
|
createdAt: new Date(row.created_at as string),
|
|
191
|
-
sizeBytes: row.size_bytes != null ? Number(row.size_bytes) : undefined
|
|
191
|
+
sizeBytes: row.size_bytes != null ? Number(row.size_bytes) : undefined
|
|
192
192
|
}));
|
|
193
193
|
}
|
|
194
194
|
|
|
@@ -231,7 +231,7 @@ export class BranchService {
|
|
|
231
231
|
name: row.name as string,
|
|
232
232
|
parentDatabase: row.parent_db as string,
|
|
233
233
|
createdAt: new Date(row.created_at as string),
|
|
234
|
-
sizeBytes
|
|
234
|
+
sizeBytes
|
|
235
235
|
};
|
|
236
236
|
}
|
|
237
237
|
}
|