@bungres/kit 0.3.0 → 0.5.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 +22 -13
- package/dist/cli.js +1513 -638
- package/dist/commands/drop.d.ts.map +1 -1
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/studio.d.ts.map +1 -1
- package/dist/config.d.ts +32 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/index.js +609 -191
- package/package.json +20 -4
- package/src/cli.ts +0 -162
- package/src/commands/drop.ts +0 -92
- package/src/commands/fresh.ts +0 -17
- package/src/commands/generate.ts +0 -151
- package/src/commands/migrate.ts +0 -84
- package/src/commands/pull.ts +0 -339
- package/src/commands/push.ts +0 -105
- package/src/commands/refresh.ts +0 -37
- package/src/commands/seed.ts +0 -41
- package/src/commands/status.ts +0 -64
- package/src/commands/studio.ts +0 -471
- package/src/commands/tusky.ts +0 -83
- package/src/config.ts +0 -102
- package/src/differ.ts +0 -236
- package/src/ensure-db.ts +0 -32
- package/src/index.ts +0 -21
- package/src/schema-loader.ts +0 -50
- package/src/utils/colors.ts +0 -4
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";
|
package/src/schema-loader.ts
DELETED
|
@@ -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
|
-
}
|
package/src/utils/colors.ts
DELETED