@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/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bungres/kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "CLI toolkit for @bungres/orm — migrate, push, generate, pull",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=24.0.0",
|
|
8
|
+
"bun": ">=1.3.0"
|
|
9
|
+
},
|
|
5
10
|
"type": "module",
|
|
6
11
|
"bin": {
|
|
7
12
|
"bungres": "./dist/cli.js"
|
|
@@ -17,7 +22,6 @@
|
|
|
17
22
|
},
|
|
18
23
|
"files": [
|
|
19
24
|
"dist",
|
|
20
|
-
"src",
|
|
21
25
|
"README.md",
|
|
22
26
|
"LICENSE"
|
|
23
27
|
],
|
|
@@ -25,8 +29,20 @@
|
|
|
25
29
|
"repository": {
|
|
26
30
|
"type": "git",
|
|
27
31
|
"url": "https://github.com/bungres/bungres.git",
|
|
28
|
-
"directory": "packages
|
|
32
|
+
"directory": "packages/bungres-kit"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/bungres/bungres/issues"
|
|
29
36
|
},
|
|
37
|
+
"homepage": "https://github.com/bungres/bungres#readme",
|
|
38
|
+
"keywords": [
|
|
39
|
+
"bun",
|
|
40
|
+
"postgres",
|
|
41
|
+
"orm",
|
|
42
|
+
"cli",
|
|
43
|
+
"migration",
|
|
44
|
+
"generator"
|
|
45
|
+
],
|
|
30
46
|
"scripts": {
|
|
31
47
|
"build:types": "tsc --emitDeclarationOnly",
|
|
32
48
|
"build": "bun build ./src/cli.ts ./src/index.ts --outdir ./dist --target bun --format esm && npm run build:types",
|
|
@@ -34,7 +50,7 @@
|
|
|
34
50
|
"test": "bun test"
|
|
35
51
|
},
|
|
36
52
|
"dependencies": {
|
|
37
|
-
"@bungres/orm": "
|
|
53
|
+
"@bungres/orm": "workspace:*"
|
|
38
54
|
},
|
|
39
55
|
"devDependencies": {
|
|
40
56
|
"bun-types": "latest",
|
package/src/cli.ts
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
// ---------------------------------------------------------------------------
|
|
3
|
-
// @bungres/kit CLI — entry point
|
|
4
|
-
// Usage: bungres <command> [options]
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
|
|
7
|
-
import { loadConfig } from "./config.js";
|
|
8
|
-
import { runPush } from "./commands/push.js";
|
|
9
|
-
import { runGenerate } from "./commands/generate.js";
|
|
10
|
-
import { runMigrate } from "./commands/migrate.js";
|
|
11
|
-
import { runPull } from "./commands/pull.js";
|
|
12
|
-
import { runStatus } from "./commands/status.js";
|
|
13
|
-
import { runDrop } from "./commands/drop.js";
|
|
14
|
-
import { runFresh } from "./commands/fresh.js";
|
|
15
|
-
import { runRefresh } from "./commands/refresh.js";
|
|
16
|
-
import { runSeed } from "./commands/seed.js";
|
|
17
|
-
import { runStudio } from "./commands/studio.js";
|
|
18
|
-
import { runTusky } from "./commands/tusky.js";
|
|
19
|
-
import { ensureDatabase } from "./ensure-db.js";
|
|
20
|
-
import { colorize } from "./utils/colors.js";
|
|
21
|
-
|
|
22
|
-
const VERSION = "0.0.1";
|
|
23
|
-
|
|
24
|
-
const HELP = `
|
|
25
|
-
${colorize("🐘 Bungres ORM CLI", "cyan")} v${VERSION}
|
|
26
|
-
|
|
27
|
-
${colorize("Usage:", "yellow")}
|
|
28
|
-
bungres <command> [options]
|
|
29
|
-
|
|
30
|
-
${colorize("Commands:", "yellow")}
|
|
31
|
-
generate Generate SQL migration files from your schema definitions
|
|
32
|
-
migrate Run pending migration files against the database
|
|
33
|
-
push Apply schema directly to the DB (no migration files, dev mode)
|
|
34
|
-
pull Introspect the database and generate TypeScript schema
|
|
35
|
-
status Show applied vs. pending migrations
|
|
36
|
-
fresh Drop all tables and re-run all migrations from scratch
|
|
37
|
-
refresh Truncate all tables to quickly reset data without dropping schema
|
|
38
|
-
seed Execute the seed script to populate the database
|
|
39
|
-
studio Start a local web interface to browse database data
|
|
40
|
-
tusky Boot up a Node REPL connected to the database with schema loaded
|
|
41
|
-
drop Drop all tables defined in the schema (dev only)
|
|
42
|
-
|
|
43
|
-
${colorize("Options:", "yellow")}
|
|
44
|
-
--config Path to config file (default: bungres.config.ts)
|
|
45
|
-
--verbose Enable verbose SQL logging
|
|
46
|
-
--force Skip confirmation prompts (use with drop)
|
|
47
|
-
--version Show version
|
|
48
|
-
--help Show this help
|
|
49
|
-
|
|
50
|
-
${colorize("Examples:", "yellow")}
|
|
51
|
-
bungres generate
|
|
52
|
-
bungres migrate
|
|
53
|
-
bungres push
|
|
54
|
-
bungres pull
|
|
55
|
-
bungres status
|
|
56
|
-
bungres fresh
|
|
57
|
-
bungres refresh
|
|
58
|
-
bungres seed
|
|
59
|
-
bungres studio
|
|
60
|
-
bungres tusky
|
|
61
|
-
bungres drop --force
|
|
62
|
-
`.trim();
|
|
63
|
-
|
|
64
|
-
async function main() {
|
|
65
|
-
const args = process.argv.slice(2);
|
|
66
|
-
|
|
67
|
-
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
68
|
-
console.log(HELP);
|
|
69
|
-
process.exit(0);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (args.includes("--version") || args.includes("-v")) {
|
|
73
|
-
console.log(`@bungres/kit v${VERSION}`);
|
|
74
|
-
process.exit(0);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const command = args[0];
|
|
78
|
-
const flags = parseFlags(args.slice(1));
|
|
79
|
-
|
|
80
|
-
const config = await loadConfig(process.cwd());
|
|
81
|
-
|
|
82
|
-
// Override verbose from flag
|
|
83
|
-
if (flags.verbose) config.verbose = true;
|
|
84
|
-
|
|
85
|
-
if (command && ["migrate", "push", "drop", "status", "fresh", "refresh"].includes(command)) {
|
|
86
|
-
await ensureDatabase(config.dbUrl);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
switch (command) {
|
|
90
|
-
case "generate":
|
|
91
|
-
await runGenerate(config, flags.name as string | undefined);
|
|
92
|
-
break;
|
|
93
|
-
|
|
94
|
-
case "migrate":
|
|
95
|
-
await runMigrate(config);
|
|
96
|
-
break;
|
|
97
|
-
|
|
98
|
-
case "push":
|
|
99
|
-
await runPush(config, { force: !!flags.force });
|
|
100
|
-
break;
|
|
101
|
-
|
|
102
|
-
case "pull":
|
|
103
|
-
await runPull(config);
|
|
104
|
-
break;
|
|
105
|
-
|
|
106
|
-
case "status":
|
|
107
|
-
await runStatus(config);
|
|
108
|
-
break;
|
|
109
|
-
|
|
110
|
-
case "drop":
|
|
111
|
-
await runDrop(config, { force: !!flags.force });
|
|
112
|
-
break;
|
|
113
|
-
|
|
114
|
-
case "fresh":
|
|
115
|
-
await runFresh(config);
|
|
116
|
-
break;
|
|
117
|
-
|
|
118
|
-
case "refresh":
|
|
119
|
-
await runRefresh(config);
|
|
120
|
-
break;
|
|
121
|
-
|
|
122
|
-
case "seed":
|
|
123
|
-
await runSeed(config);
|
|
124
|
-
break;
|
|
125
|
-
|
|
126
|
-
case "studio":
|
|
127
|
-
await runStudio(config);
|
|
128
|
-
break;
|
|
129
|
-
|
|
130
|
-
case "tusky":
|
|
131
|
-
await runTusky(config);
|
|
132
|
-
break;
|
|
133
|
-
|
|
134
|
-
default:
|
|
135
|
-
console.error(`Unknown command: ${command}\n`);
|
|
136
|
-
console.log(HELP);
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function parseFlags(args: string[]): Record<string, boolean | string> {
|
|
142
|
-
const flags: Record<string, boolean | string> = {};
|
|
143
|
-
for (let i = 0; i < args.length; i++) {
|
|
144
|
-
const arg = args[i];
|
|
145
|
-
if (arg?.startsWith("--")) {
|
|
146
|
-
const key = arg.slice(2);
|
|
147
|
-
const next = args[i + 1];
|
|
148
|
-
if (next && !next.startsWith("--")) {
|
|
149
|
-
flags[key] = next;
|
|
150
|
-
i++;
|
|
151
|
-
} else {
|
|
152
|
-
flags[key] = true;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return flags;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
main().catch((err) => {
|
|
160
|
-
console.error("@bungres/kit error:", err instanceof Error ? err.message : err);
|
|
161
|
-
process.exit(1);
|
|
162
|
-
});
|
package/src/commands/drop.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { generateDropTable } from "@bungres/orm";
|
|
2
|
-
import type { ResolvedConfig } from "../config.js";
|
|
3
|
-
import { loadSchemas } from "../schema-loader.js";
|
|
4
|
-
import { colorize } from "../utils/colors.js";
|
|
5
|
-
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
// drop — drop all tables defined in the schema (dev utility)
|
|
8
|
-
// Always prompts for confirmation unless --force is passed
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
export async function runDrop(
|
|
12
|
-
config: ResolvedConfig,
|
|
13
|
-
opts: { force?: boolean } = {}
|
|
14
|
-
): Promise<void> {
|
|
15
|
-
const schemas = await loadSchemas(config.schema);
|
|
16
|
-
|
|
17
|
-
if (schemas.length === 0) {
|
|
18
|
-
console.warn("No table definitions found in schema files.");
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const sql = new Bun.SQL(config.dbUrl);
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
// Check which tables actually exist in the database
|
|
26
|
-
const existingTablesResult = await sql`
|
|
27
|
-
SELECT table_name
|
|
28
|
-
FROM information_schema.tables
|
|
29
|
-
WHERE table_schema = 'public' OR table_schema = current_schema()
|
|
30
|
-
`;
|
|
31
|
-
const existingTableNames = new Set(
|
|
32
|
-
existingTablesResult.map((row: any) => row.table_name)
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
// Also track the migration and push tables since they are dropped too
|
|
36
|
-
const migrationTableExists = existingTableNames.has("__bungres_migrations");
|
|
37
|
-
const pushTableExists = existingTableNames.has("__bungres_push");
|
|
38
|
-
|
|
39
|
-
// Filter our schema list to only those that exist in the DB
|
|
40
|
-
const tablesToDrop = schemas.filter((s) => existingTableNames.has(s.config.name));
|
|
41
|
-
|
|
42
|
-
if (tablesToDrop.length === 0 && !migrationTableExists && !pushTableExists) {
|
|
43
|
-
console.log("No tables to drop (they either don't exist or were already dropped).");
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const tableNamesToPrint = tablesToDrop.map(
|
|
48
|
-
(s) => (s.config.schema ? s.config.schema + "." : "") + s.config.name
|
|
49
|
-
);
|
|
50
|
-
if (migrationTableExists) {
|
|
51
|
-
tableNamesToPrint.push("__bungres_migrations");
|
|
52
|
-
}
|
|
53
|
-
if (pushTableExists) {
|
|
54
|
-
tableNamesToPrint.push("__bungres_push");
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!opts.force) {
|
|
58
|
-
console.warn(colorize("\n⚠️ WARNING: This will drop the following tables and ALL their data:\n", "red"));
|
|
59
|
-
for (const name of tableNamesToPrint) console.log(colorize(` - ${name}`, "yellow"));
|
|
60
|
-
process.stdout.write(colorize("\nAre you sure? Type YES to continue: ", "cyan"));
|
|
61
|
-
|
|
62
|
-
// Read confirmation from stdin using Bun's native console iterator
|
|
63
|
-
for await (const line of console) {
|
|
64
|
-
if (line.trim().toLowerCase() !== "yes") {
|
|
65
|
-
console.log("Aborted.");
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
break; // break loop after reading the first line
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
for (const entry of tablesToDrop) {
|
|
73
|
-
const ddl = `DROP TABLE IF EXISTS "${entry.config.name}" CASCADE;`;
|
|
74
|
-
await sql.unsafe(ddl);
|
|
75
|
-
console.log(colorize(` ✓ dropped ${entry.config.name}`, "green"));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (migrationTableExists) {
|
|
79
|
-
await sql.unsafe("DROP TABLE IF EXISTS public.__bungres_migrations CASCADE");
|
|
80
|
-
console.log(colorize(` ✓ dropped __bungres_migrations`, "green"));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (pushTableExists) {
|
|
84
|
-
await sql.unsafe("DROP TABLE IF EXISTS public.__bungres_push CASCADE");
|
|
85
|
-
console.log(colorize(` ✓ dropped __bungres_push`, "green"));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
console.log(colorize("\nDrop complete.", "green"));
|
|
89
|
-
} finally {
|
|
90
|
-
await sql.end();
|
|
91
|
-
}
|
|
92
|
-
}
|
package/src/commands/fresh.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { ResolvedConfig } from "../config.js";
|
|
2
|
-
import { runDrop } from "./drop.js";
|
|
3
|
-
import { runMigrate } from "./migrate.js";
|
|
4
|
-
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// fresh — drop all tables and re-run all migrations from scratch
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
export async function runFresh(config: ResolvedConfig): Promise<void> {
|
|
10
|
-
console.log("Dropping all tables...");
|
|
11
|
-
await runDrop(config, { force: true });
|
|
12
|
-
|
|
13
|
-
console.log("\nRe-running migrations...");
|
|
14
|
-
await runMigrate(config);
|
|
15
|
-
|
|
16
|
-
console.log("\nFresh complete.");
|
|
17
|
-
}
|
package/src/commands/generate.ts
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { join, resolve } from "node:path";
|
|
2
|
-
import { generateCreateTable } from "@bungres/orm";
|
|
3
|
-
import type { TableConfig } from "@bungres/orm";
|
|
4
|
-
import type { ResolvedConfig } from "../config.js";
|
|
5
|
-
import { loadSchemas, type SchemaEntry } from "../schema-loader.js";
|
|
6
|
-
import { diffSchemas, type SchemaSnapshot } from "../differ.js";
|
|
7
|
-
import { colorize } from "../utils/colors.js";
|
|
8
|
-
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
// generate — diff-based SQL migration generator (drizzle-style numbering)
|
|
11
|
-
//
|
|
12
|
-
// First run: full CREATE TABLE for every table → 0001_init.sql
|
|
13
|
-
// Later runs: only the changes (ADD COLUMN, CREATE TABLE, etc.) → 0002_*.sql
|
|
14
|
-
//
|
|
15
|
-
// Schema state is tracked in <migrationsDir>/.snapshot.json
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
|
|
18
|
-
const SNAPSHOT_FILE = ".snapshot.json";
|
|
19
|
-
|
|
20
|
-
export async function runGenerate(
|
|
21
|
-
config: ResolvedConfig,
|
|
22
|
-
name?: string
|
|
23
|
-
): Promise<void> {
|
|
24
|
-
console.log("@bungres/kit generate: loading schemas...");
|
|
25
|
-
|
|
26
|
-
const schemas = await loadSchemas(config.schema);
|
|
27
|
-
|
|
28
|
-
if (schemas.length === 0) {
|
|
29
|
-
console.warn(colorize("No table definitions found. Check your schema glob pattern.", "yellow"));
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const migrationsDir = resolve(config.migrationsDir);
|
|
34
|
-
await Bun.$`mkdir -p ${migrationsDir}`.quiet();
|
|
35
|
-
|
|
36
|
-
// ── Build current snapshot from loaded schemas ────────────────────────────
|
|
37
|
-
const currentSnapshot: SchemaSnapshot = Object.fromEntries(
|
|
38
|
-
schemas.map((s) => [s.config.name, s.config])
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
// ── Load previous snapshot (if any) ──────────────────────────────────────
|
|
42
|
-
const snapshotPath = join(migrationsDir, SNAPSHOT_FILE);
|
|
43
|
-
const snapshotFile = Bun.file(snapshotPath);
|
|
44
|
-
const isFirstMigration = !(await snapshotFile.exists());
|
|
45
|
-
const prevSnapshot: SchemaSnapshot = isFirstMigration
|
|
46
|
-
? {}
|
|
47
|
-
: (JSON.parse(await snapshotFile.text()) as SchemaSnapshot);
|
|
48
|
-
|
|
49
|
-
// ── Determine next sequence number ───────────────────────────────────────
|
|
50
|
-
const now = new Date();
|
|
51
|
-
const yyyy = now.getUTCFullYear();
|
|
52
|
-
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
53
|
-
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
54
|
-
const HH = String(now.getUTCHours()).padStart(2, "0");
|
|
55
|
-
const MM = String(now.getUTCMinutes()).padStart(2, "0");
|
|
56
|
-
const SS = String(now.getUTCSeconds()).padStart(2, "0");
|
|
57
|
-
|
|
58
|
-
const prefix = `${yyyy}_${mm}_${dd}_${HH}${MM}${SS}`;
|
|
59
|
-
const filename = name ? `${prefix}_${name}.sql` : `${prefix}.sql`;
|
|
60
|
-
const outPath = join(migrationsDir, filename);
|
|
61
|
-
|
|
62
|
-
// ── Diff ──────────────────────────────────────────────────────────────────
|
|
63
|
-
let statements: string[];
|
|
64
|
-
let summary: string[];
|
|
65
|
-
let warnings: string[] = [];
|
|
66
|
-
|
|
67
|
-
if (isFirstMigration) {
|
|
68
|
-
// First migration — full schema sorted by FK dependency order
|
|
69
|
-
const sorted = topoSort(schemas);
|
|
70
|
-
statements = [
|
|
71
|
-
`CREATE EXTENSION IF NOT EXISTS "pgcrypto";`,
|
|
72
|
-
``,
|
|
73
|
-
...sorted.flatMap((entry) => [
|
|
74
|
-
`-- ${entry.exportName}`,
|
|
75
|
-
generateCreateTable(entry.config, true),
|
|
76
|
-
``,
|
|
77
|
-
]),
|
|
78
|
-
];
|
|
79
|
-
summary = sorted.map((s) => `CREATE TABLE ${s.config.name}`);
|
|
80
|
-
} else {
|
|
81
|
-
// Subsequent migrations — diff only
|
|
82
|
-
const diff = diffSchemas(prevSnapshot, currentSnapshot);
|
|
83
|
-
|
|
84
|
-
if (diff.statements.length === 0) {
|
|
85
|
-
console.log(colorize("\nNo schema changes detected. Nothing to generate.", "yellow"));
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
statements = diff.statements;
|
|
90
|
-
summary = diff.summary;
|
|
91
|
-
warnings = diff.warnings;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ── Write migration file ──────────────────────────────────────────────────
|
|
95
|
-
const lines: string[] = [
|
|
96
|
-
`-- Migration: ${filename}`,
|
|
97
|
-
`-- Generated by @bungres/kit at ${new Date().toISOString()}`,
|
|
98
|
-
`-- Changes: ${summary.join(", ")}`,
|
|
99
|
-
``,
|
|
100
|
-
...statements,
|
|
101
|
-
];
|
|
102
|
-
|
|
103
|
-
await Bun.write(outPath, lines.join("\n"));
|
|
104
|
-
|
|
105
|
-
// ── Save updated snapshot ─────────────────────────────────────────────────
|
|
106
|
-
await Bun.write(snapshotPath, JSON.stringify(currentSnapshot, null, 2));
|
|
107
|
-
|
|
108
|
-
console.log(colorize(`\nGenerated: ${outPath}`, "green"));
|
|
109
|
-
console.log(` Changes:`);
|
|
110
|
-
for (const s of summary) console.log(colorize(` + ${s}`, "cyan"));
|
|
111
|
-
|
|
112
|
-
if (warnings.length > 0) {
|
|
113
|
-
console.warn(colorize(`\n ⚠️ WARNING: Data Loss Detected!`, "red"));
|
|
114
|
-
for (const w of warnings) console.warn(colorize(` ! ${w}`, "red"));
|
|
115
|
-
console.warn(colorize(` Please review the generated migration carefully before applying it.\n`, "yellow"));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
console.log(colorize(`\nRun \`bungres migrate\` to apply it.`, "cyan"));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
// Topological sort — referenced tables come before dependents
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
|
|
125
|
-
function topoSort(schemas: SchemaEntry[]): SchemaEntry[] {
|
|
126
|
-
const byName = new Map<string, SchemaEntry>(
|
|
127
|
-
schemas.map((s) => [s.config.name, s])
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
function deps(config: TableConfig): string[] {
|
|
131
|
-
return Object.values(config.columns)
|
|
132
|
-
.map((col) => col.references?.table)
|
|
133
|
-
.filter((t): t is string => t !== undefined && byName.has(t) && t !== config.name);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const visited = new Set<string>();
|
|
137
|
-
const result: SchemaEntry[] = [];
|
|
138
|
-
|
|
139
|
-
function visit(name: string): void {
|
|
140
|
-
if (visited.has(name)) return;
|
|
141
|
-
visited.add(name);
|
|
142
|
-
const entry = byName.get(name);
|
|
143
|
-
if (!entry) return;
|
|
144
|
-
for (const dep of deps(entry.config)) visit(dep);
|
|
145
|
-
result.push(entry);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
for (const schema of schemas) visit(schema.config.name);
|
|
149
|
-
|
|
150
|
-
return result;
|
|
151
|
-
}
|
package/src/commands/migrate.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { join, resolve } from "node:path";
|
|
2
|
-
import type { ResolvedConfig } from "../config.js";
|
|
3
|
-
import { colorize } from "../utils/colors.js";
|
|
4
|
-
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// migrate — run pending .sql files, track applied in __bungres_migrations
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
const MIGRATIONS_TABLE = "__bungres_migrations";
|
|
10
|
-
|
|
11
|
-
const CREATE_MIGRATIONS_TABLE = `
|
|
12
|
-
CREATE TABLE IF NOT EXISTS "${MIGRATIONS_TABLE}" (
|
|
13
|
-
id SERIAL PRIMARY KEY,
|
|
14
|
-
name TEXT NOT NULL UNIQUE,
|
|
15
|
-
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
16
|
-
);
|
|
17
|
-
`.trim();
|
|
18
|
-
|
|
19
|
-
export async function runMigrate(config: ResolvedConfig): Promise<void> {
|
|
20
|
-
const migrationsDir = resolve(config.migrationsDir);
|
|
21
|
-
const sql = new Bun.SQL(config.dbUrl);
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
// Ensure tracking table exists
|
|
25
|
-
await sql.unsafe(CREATE_MIGRATIONS_TABLE);
|
|
26
|
-
|
|
27
|
-
// Discover migration files in order
|
|
28
|
-
const glob = new Bun.Glob("*.sql");
|
|
29
|
-
const files: string[] = [];
|
|
30
|
-
for await (const file of glob.scan({ cwd: migrationsDir, absolute: false })) {
|
|
31
|
-
files.push(file);
|
|
32
|
-
}
|
|
33
|
-
files.sort(); // 0001_ < 0002_ etc.
|
|
34
|
-
|
|
35
|
-
if (files.length === 0) {
|
|
36
|
-
console.log(colorize("No migration files found in " + migrationsDir, "yellow"));
|
|
37
|
-
console.log(colorize("Run `bungres generate` first.", "yellow"));
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Fetch already applied
|
|
42
|
-
const applied = await sql.unsafe(`SELECT name FROM "${MIGRATIONS_TABLE}"`);
|
|
43
|
-
const appliedSet = new Set((applied as { name: string }[]).map((r) => r.name));
|
|
44
|
-
|
|
45
|
-
const pending = files.filter((f) => !appliedSet.has(f));
|
|
46
|
-
|
|
47
|
-
if (pending.length === 0) {
|
|
48
|
-
console.log(colorize("Everything is up to date.", "green"));
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
console.log(colorize(`\nRunning ${pending.length} pending migration(s)...\n`, "cyan"));
|
|
53
|
-
|
|
54
|
-
for (const file of pending) {
|
|
55
|
-
const content = await Bun.file(join(migrationsDir, file)).text();
|
|
56
|
-
|
|
57
|
-
if (config.verbose) {
|
|
58
|
-
console.log(`-- ${file} --\n${content}\n`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
await sql.transaction(async (txSql: InstanceType<typeof Bun.SQL>) => {
|
|
62
|
-
const statements = content
|
|
63
|
-
.split(";")
|
|
64
|
-
.map((s) => s.trim())
|
|
65
|
-
.filter(Boolean);
|
|
66
|
-
|
|
67
|
-
for (const stmt of statements) {
|
|
68
|
-
await txSql.unsafe(stmt + ";");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
await txSql.unsafe(
|
|
72
|
-
`INSERT INTO "${MIGRATIONS_TABLE}" (name) VALUES ($1)`,
|
|
73
|
-
[file]
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
console.log(colorize(` ✓ ${file}`, "green"));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
console.log(colorize("\nDone.", "green"));
|
|
81
|
-
} finally {
|
|
82
|
-
await sql.end();
|
|
83
|
-
}
|
|
84
|
-
}
|