@bungres/kit 0.3.0 → 0.4.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/src/differ.ts DELETED
@@ -1,236 +0,0 @@
1
- import type { ColumnConfig, TableConfig } from "@bungres/orm";
2
- import { generateCreateTable, generateAddColumn, generateDropColumn, generateAddConstraint, generateDropConstraint } from "@bungres/orm";
3
-
4
- // ---------------------------------------------------------------------------
5
- // Schema differ — compares two schema snapshots and emits SQL statements
6
- // that migrate from `prev` to `next`
7
- // ---------------------------------------------------------------------------
8
-
9
- /** Snapshot stored on disk after each generate */
10
- export type SchemaSnapshot = Record<string, TableConfig>;
11
-
12
- export interface DiffResult {
13
- statements: string[];
14
- summary: string[];
15
- warnings: string[];
16
- }
17
-
18
- export function diffSchemas(
19
- prev: SchemaSnapshot,
20
- next: SchemaSnapshot
21
- ): DiffResult {
22
- const statements: string[] = [];
23
- const summary: string[] = [];
24
- const warnings: string[] = [];
25
-
26
- const prevTables = new Set(Object.keys(prev));
27
- const nextTables = new Set(Object.keys(next));
28
-
29
- // ── New tables — topo-sorted so FK deps come first ────────────────────────
30
- const newTableConfigs: TableConfig[] = [];
31
- for (const tableName of nextTables) {
32
- if (!prevTables.has(tableName)) {
33
- newTableConfigs.push(next[tableName]!);
34
- }
35
- }
36
-
37
- for (const config of topoSortConfigs(newTableConfigs)) {
38
- statements.push(generateCreateTable(config, true));
39
- summary.push(`CREATE TABLE ${config.name}`);
40
- }
41
-
42
- // ── Dropped tables ────────────────────────────────────────────────────────
43
- for (const tableName of prevTables) {
44
- if (!nextTables.has(tableName)) {
45
- const config = prev[tableName]!;
46
- const tbl = config.schema
47
- ? `"${config.schema}"."${tableName}"`
48
- : `"${tableName}"`;
49
- statements.push(`DROP TABLE IF EXISTS ${tbl};`);
50
- summary.push(`DROP TABLE ${tableName}`);
51
- warnings.push(`Data loss warning: Table '${tableName}' will be permanently deleted.`);
52
- }
53
- }
54
-
55
- // ── Modified tables — column-level diff ───────────────────────────────────
56
- for (const tableName of nextTables) {
57
- if (!prevTables.has(tableName)) continue; // already handled above
58
-
59
- const prevConfig = prev[tableName]!;
60
- const nextConfig = next[tableName]!;
61
- const prevCols = prevConfig.columns;
62
- const nextCols = nextConfig.columns;
63
- const prevColNames = new Set(Object.keys(prevCols));
64
- const nextColNames = new Set(Object.keys(nextCols));
65
-
66
- // Added columns
67
- for (const key of nextColNames) {
68
- if (!prevColNames.has(key)) {
69
- const col = nextCols[key]!;
70
- statements.push(generateAddColumn(tableName, nextConfig.schema, key, col));
71
- summary.push(`ALTER TABLE ${tableName} ADD COLUMN ${col.name}`);
72
- }
73
- }
74
-
75
- // Dropped columns
76
- for (const key of prevColNames) {
77
- if (!nextColNames.has(key)) {
78
- const col = prevCols[key]!;
79
- statements.push(generateDropColumn(tableName, prevConfig.schema, col.name));
80
- summary.push(`ALTER TABLE ${tableName} DROP COLUMN ${col.name}`);
81
- warnings.push(`Data loss warning: Column '${col.name}' in table '${tableName}' will be permanently deleted.`);
82
- }
83
- }
84
-
85
- // Changed columns
86
- for (const key of nextColNames) {
87
- if (!prevColNames.has(key)) continue;
88
- const changes = diffColumn(prevCols[key]!, nextCols[key]!);
89
- if (changes.length > 0) {
90
- const tbl = nextConfig.schema
91
- ? `"${nextConfig.schema}"."${tableName}"`
92
- : `"${tableName}"`;
93
- for (const change of changes) {
94
- statements.push(`ALTER TABLE ${tbl} ${change};`);
95
- summary.push(`ALTER TABLE ${tableName} ALTER COLUMN ${nextCols[key]!.name} (${change})`);
96
- }
97
- }
98
-
99
- // Foreign Key diffing
100
- const prevRef = prevCols[key]!.references;
101
- const nextRef = nextCols[key]!.references;
102
- if (JSON.stringify(prevRef) !== JSON.stringify(nextRef)) {
103
- const constraintName = `${tableName}_${nextCols[key]!.name}_fkey`;
104
- if (prevRef) {
105
- statements.push(generateDropConstraint(tableName, nextConfig.schema, constraintName));
106
- summary.push(`ALTER TABLE ${tableName} DROP CONSTRAINT ${constraintName}`);
107
- }
108
- if (nextRef) {
109
- let fkDef = `FOREIGN KEY ("${nextCols[key]!.name}") REFERENCES "${nextRef.table}"("${nextRef.column}")`;
110
- if (nextRef.onDelete) fkDef += ` ON DELETE ${nextRef.onDelete.toUpperCase()}`;
111
- if (nextRef.onUpdate) fkDef += ` ON UPDATE ${nextRef.onUpdate.toUpperCase()}`;
112
- statements.push(generateAddConstraint(tableName, nextConfig.schema, constraintName, fkDef));
113
- summary.push(`ALTER TABLE ${tableName} ADD CONSTRAINT ${constraintName}`);
114
- }
115
- }
116
- }
117
-
118
- // New indexes
119
- const prevIdxNames = new Set(
120
- (prevConfig.indexes ?? []).map(
121
- (i) => i.name ?? `idx_${tableName}_${i.columns.join("_")}`
122
- )
123
- );
124
- for (const idx of nextConfig.indexes ?? []) {
125
- const idxName = idx.name ?? `idx_${tableName}_${idx.columns.join("_")}`;
126
- if (!prevIdxNames.has(idxName)) {
127
- const tbl = nextConfig.schema
128
- ? `"${nextConfig.schema}"."${tableName}"`
129
- : `"${tableName}"`;
130
- const unique = idx.unique ? "UNIQUE " : "";
131
- const using = idx.using ? ` USING ${idx.using.toUpperCase()}` : "";
132
- const cols = idx.columns.map((c) => `"${c}"`).join(", ");
133
- const where = idx.where ? ` WHERE ${idx.where}` : "";
134
- statements.push(
135
- `CREATE ${unique}INDEX IF NOT EXISTS "${idxName}" ON ${tbl}${using} (${cols})${where};`
136
- );
137
- summary.push(`CREATE INDEX ${idxName} ON ${tableName}`);
138
- }
139
- }
140
-
141
- // Dropped indexes
142
- const nextIdxNames = new Set(
143
- (nextConfig.indexes ?? []).map(
144
- (i) => i.name ?? `idx_${tableName}_${i.columns.join("_")}`
145
- )
146
- );
147
- for (const idx of prevConfig.indexes ?? []) {
148
- const idxName = idx.name ?? `idx_${tableName}_${idx.columns.join("_")}`;
149
- if (!nextIdxNames.has(idxName)) {
150
- statements.push(`DROP INDEX IF EXISTS "${idxName}";`);
151
- summary.push(`DROP INDEX ${idxName}`);
152
- }
153
- }
154
- }
155
-
156
- return { statements, summary, warnings };
157
- }
158
-
159
- // ---------------------------------------------------------------------------
160
- // Topological sort for TableConfig objects
161
- // ---------------------------------------------------------------------------
162
-
163
- export function topoSortConfigs(tables: TableConfig[]): TableConfig[] {
164
- const byName = new Map<string, TableConfig>(tables.map((t) => [t.name, t]));
165
- const visited = new Set<string>();
166
- const result: TableConfig[] = [];
167
-
168
- function deps(config: TableConfig): string[] {
169
- return Object.values(config.columns)
170
- .map((col) => col.references?.table)
171
- .filter((t): t is string => t !== undefined && byName.has(t) && t !== config.name);
172
- }
173
-
174
- function visit(name: string): void {
175
- if (visited.has(name)) return;
176
- visited.add(name);
177
- const config = byName.get(name);
178
- if (!config) return;
179
- for (const dep of deps(config)) visit(dep);
180
- result.push(config);
181
- }
182
-
183
- for (const table of tables) visit(table.name);
184
- return result;
185
- }
186
-
187
- // ---------------------------------------------------------------------------
188
- // Column-level diff
189
- // ---------------------------------------------------------------------------
190
-
191
- function diffColumn(prev: ColumnConfig, next: ColumnConfig): string[] {
192
- const changes: string[] = [];
193
- const col = `"${next.name}"`;
194
-
195
- if (prev.dataType !== next.dataType) {
196
- changes.push(
197
- `ALTER COLUMN ${col} TYPE ${next.dataType.toUpperCase()} USING ${col}::${next.dataType}`
198
- );
199
- }
200
-
201
- if (!prev.notNull && next.notNull) {
202
- changes.push(`ALTER COLUMN ${col} SET NOT NULL`);
203
- }
204
- if (prev.notNull && !next.notNull) {
205
- changes.push(`ALTER COLUMN ${col} DROP NOT NULL`);
206
- }
207
-
208
- const prevDef =
209
- prev.defaultFn ??
210
- (prev.defaultValue !== undefined ? String(prev.defaultValue) : undefined);
211
- const nextDef =
212
- next.defaultFn ??
213
- (next.defaultValue !== undefined ? String(next.defaultValue) : undefined);
214
-
215
- if (prevDef !== nextDef) {
216
- if (nextDef !== undefined) {
217
- const val = next.defaultFn ?? formatDefault(next.defaultValue, next.dataType);
218
- changes.push(`ALTER COLUMN ${col} SET DEFAULT ${val}`);
219
- } else {
220
- changes.push(`ALTER COLUMN ${col} DROP DEFAULT`);
221
- }
222
- }
223
-
224
- return changes;
225
- }
226
-
227
- function formatDefault(value: unknown, dataType: ColumnConfig["dataType"]): string {
228
- if (value === null || value === undefined) return "NULL";
229
- if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
230
- if (typeof value === "number") return String(value);
231
- if (typeof value === "string") {
232
- if (dataType === "boolean") return value;
233
- return `'${value.replace(/'/g, "''")}'`;
234
- }
235
- return `'${JSON.stringify(value)}'`;
236
- }
package/src/ensure-db.ts DELETED
@@ -1,32 +0,0 @@
1
- export async function ensureDatabase(dbUrl: string): Promise<void> {
2
- let sql;
3
- try {
4
- sql = new Bun.SQL(dbUrl);
5
- // Ping to check connection
6
- await sql.unsafe('SELECT 1');
7
- return;
8
- } catch (err: any) {
9
- if (err.message && err.message.includes('does not exist')) {
10
- try {
11
- const parsed = new URL(dbUrl);
12
- const dbName = parsed.pathname.slice(1);
13
- parsed.pathname = '/postgres';
14
- const fallbackUrl = parsed.toString();
15
-
16
- const fallback = new Bun.SQL(fallbackUrl);
17
- try {
18
- await fallback.unsafe(`CREATE DATABASE "${dbName}"`);
19
- console.log(`Created database "${dbName}" automatically.`);
20
- } finally {
21
- await fallback.end();
22
- }
23
- } catch (e: any) {
24
- // Ignore fallback errors, it will just fail downstream
25
- }
26
- }
27
- } finally {
28
- if (sql) {
29
- await sql.end();
30
- }
31
- }
32
- }
package/src/index.ts DELETED
@@ -1,21 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // @bungres/kit — programmatic API (for use without the CLI)
3
- // ---------------------------------------------------------------------------
4
-
5
- export { loadConfig } from "./config.js";
6
- export type { BungresKitConfig, ResolvedConfig } from "./config.js";
7
-
8
- /** Identity helper for type-safe config — mirrors drizzle-kit's defineConfig */
9
- export function defineConfig(config: import("./config.js").BungresKitConfig) {
10
- return config;
11
- }
12
-
13
- export { loadSchemas } from "./schema-loader.js";
14
- export type { SchemaEntry } from "./schema-loader.js";
15
-
16
- export { runPush } from "./commands/push.js";
17
- export { runGenerate } from "./commands/generate.js";
18
- export { runMigrate } from "./commands/migrate.js";
19
- export { runPull } from "./commands/pull.js";
20
- export { runStatus } from "./commands/status.js";
21
- export { runDrop } from "./commands/drop.js";
@@ -1,50 +0,0 @@
1
- import { resolve, join } from "node:path";
2
- import { TableConfigSymbol, type TableConfig } from "@bungres/orm";
3
-
4
- // ---------------------------------------------------------------------------
5
- // Schema loader — imports user schema files and extracts Table definitions
6
- // ---------------------------------------------------------------------------
7
-
8
- export interface SchemaEntry {
9
- exportName: string;
10
- config: TableConfig;
11
- table: any;
12
- filePath: string;
13
- }
14
-
15
- export async function loadSchemas(
16
- patterns: string | string[],
17
- cwd = process.cwd()
18
- ): Promise<SchemaEntry[]> {
19
- const globs = Array.isArray(patterns) ? patterns : [patterns];
20
- const entries: SchemaEntry[] = [];
21
-
22
- for (const pattern of globs) {
23
- const glob = new Bun.Glob(pattern);
24
- for await (const file of glob.scan({ cwd, absolute: false })) {
25
- const absPath = resolve(join(cwd, file));
26
- const mod = await import(absPath);
27
-
28
- for (const [exportName, value] of Object.entries(mod)) {
29
- if (isTable(value)) {
30
- entries.push({
31
- exportName,
32
- config: (value as any)[TableConfigSymbol],
33
- table: value,
34
- filePath: absPath,
35
- });
36
- }
37
- }
38
- }
39
- }
40
-
41
- return entries;
42
- }
43
-
44
- function isTable(value: unknown): boolean {
45
- return (
46
- typeof value === "object" &&
47
- value !== null &&
48
- TableConfigSymbol in value
49
- );
50
- }
@@ -1,4 +0,0 @@
1
- export function colorize(text: string, color: string): string {
2
- const code = Bun.color(color, "ansi");
3
- return code ? `${code}${text}\x1b[0m` : text;
4
- }