vibeorm 1.1.1 → 1.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeorm",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "CLI for VibeORM — generate clients, run migrations, introspect databases",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -41,10 +41,10 @@
41
41
  "bun": ">=1.1.0"
42
42
  },
43
43
  "dependencies": {
44
- "@vibeorm/parser": "1.1.1",
45
- "@vibeorm/generator": "1.1.1",
46
- "@vibeorm/migrate": "1.1.1",
47
- "@vibeorm/runtime": "1.1.1",
48
- "@vibeorm/adapter-bun": "1.1.1"
44
+ "@vibeorm/parser": "1.1.2",
45
+ "@vibeorm/generator": "1.1.2",
46
+ "@vibeorm/migrate": "1.1.2",
47
+ "@vibeorm/runtime": "1.1.2",
48
+ "@vibeorm/adapter-bun": "1.1.2"
49
49
  }
50
50
  }
@@ -99,19 +99,40 @@ export async function runMigrateGenerate(params: {
99
99
  return;
100
100
  }
101
101
 
102
- // 5. Create migration files
102
+ // 5. Generate down SQL (reverse operations in reverse order)
103
+ const downSql = operations
104
+ .filter((op: DiffOperation) => op.isReversible)
105
+ .reverse()
106
+ .map((op: DiffOperation) => op.downSql)
107
+ .join("\n\n");
108
+
109
+ const nonReversibleOps = operations.filter((op: DiffOperation) => !op.isReversible);
110
+ let downMigrationSql = "";
111
+ if (nonReversibleOps.length > 0) {
112
+ const warnings = nonReversibleOps
113
+ .map((op: DiffOperation) => `-- WARNING: "${op.description}" is not cleanly reversible.\n${op.downSql}`)
114
+ .reverse()
115
+ .join("\n\n");
116
+ downMigrationSql = `-- Down migration\n-- WARNING: Some operations in this migration are not cleanly reversible.\n\n${warnings}${downSql ? "\n\n" + downSql : ""}`;
117
+ } else {
118
+ downMigrationSql = `-- Down migration\n\n${downSql}`;
119
+ }
120
+
121
+ // 6. Create migration files
103
122
  const timestamp = generateTimestamp();
104
123
  const dirName = `${timestamp}_${migrationName}`;
105
124
  const migrationDir = join(config.migrations, dirName);
106
125
  const sqlPath = join(migrationDir, "migration.sql");
126
+ const downSqlPath = join(migrationDir, "down.sql");
107
127
 
108
128
  mkdirSync(migrationDir, { recursive: true });
109
129
  writeFileSync(sqlPath, migrationSql, "utf-8");
130
+ writeFileSync(downSqlPath, downMigrationSql, "utf-8");
110
131
 
111
- // 6. Save snapshot
132
+ // 7. Save snapshot
112
133
  saveSnapshot({ schema: currentSchema, migrationsDir: config.migrations, timestamp });
113
134
 
114
- // 7. Update journal
135
+ // 8. Update journal
115
136
  const checksum = computeChecksum({ content: migrationSql });
116
137
  const journal = loadJournal({ migrationsDir: config.migrations });
117
138
 
@@ -127,7 +148,8 @@ export async function runMigrateGenerate(params: {
127
148
 
128
149
  console.log("");
129
150
  console.log(`Created migration: ${dirName}`);
130
- console.log(` SQL: ${sqlPath}`);
151
+ console.log(` Up: ${sqlPath}`);
152
+ console.log(` Down: ${downSqlPath}`);
131
153
  console.log("");
132
154
  console.log("Generated SQL:");
133
155
  console.log(migrationSql);
@@ -0,0 +1,115 @@
1
+ /**
2
+ * vibeorm migrate rollback
3
+ *
4
+ * Roll back the last N applied migrations using their down.sql files.
5
+ */
6
+
7
+ import {
8
+ ensureMigrationTable,
9
+ getAppliedMigrations,
10
+ rollbackMigration,
11
+ loadJournal,
12
+ } from "@vibeorm/migrate";
13
+ import type { AppliedMigration, JournalEntry } from "@vibeorm/migrate";
14
+ import { existsSync, readFileSync } from "fs";
15
+ import { join } from "path";
16
+ import type { VibeOrmConfig } from "../config.ts";
17
+ import { createConnection } from "../connection.ts";
18
+
19
+ export async function runMigrateRollback(params: {
20
+ config: VibeOrmConfig;
21
+ flags: Record<string, string>;
22
+ }): Promise<void> {
23
+ const { config, flags } = params;
24
+ const steps = parseInt(flags.steps ?? "1", 10);
25
+ const dryRun = flags["dry-run"] === "true";
26
+
27
+ if (isNaN(steps) || steps < 1) {
28
+ console.error("Error: --steps must be a positive integer.");
29
+ process.exit(1);
30
+ }
31
+
32
+ console.log("VibeORM Migrate Rollback");
33
+ console.log(` Migrations: ${config.migrations}`);
34
+ console.log(` Steps: ${steps}`);
35
+ console.log("");
36
+
37
+ // 1. Load journal
38
+ const journal = loadJournal({ migrationsDir: config.migrations });
39
+ if (journal.entries.length === 0) {
40
+ console.log("No migrations found in journal.");
41
+ return;
42
+ }
43
+
44
+ // 2. Connect to database
45
+ const conn = createConnection({ url: flags.url });
46
+
47
+ try {
48
+ // 3. Ensure migration table
49
+ await ensureMigrationTable({ executor: conn.executor });
50
+
51
+ // 4. Get applied migrations (ordered by id)
52
+ const applied = await getAppliedMigrations({ executor: conn.executor });
53
+
54
+ if (applied.length === 0) {
55
+ console.log("No migrations have been applied. Nothing to roll back.");
56
+ return;
57
+ }
58
+
59
+ // 5. Take the last N applied migrations (in reverse order for rollback)
60
+ const toRollback = applied.slice(-steps).reverse();
61
+
62
+ console.log(`Rolling back ${toRollback.length} migration(s):`);
63
+ for (const migration of toRollback) {
64
+ console.log(` - ${migration.migrationName}`);
65
+ }
66
+
67
+ if (dryRun) {
68
+ console.log("");
69
+ console.log("DRY RUN — No changes applied.");
70
+
71
+ for (const migration of toRollback) {
72
+ const downSqlPath = join(config.migrations, migration.migrationName, "down.sql");
73
+ if (existsSync(downSqlPath)) {
74
+ const sql = readFileSync(downSqlPath, "utf-8");
75
+ console.log("");
76
+ console.log(`=== ${migration.migrationName} (down.sql) ===`);
77
+ console.log(sql);
78
+ } else {
79
+ console.log("");
80
+ console.log(`=== ${migration.migrationName} ===`);
81
+ console.log("WARNING: No down.sql found for this migration.");
82
+ }
83
+ }
84
+ return;
85
+ }
86
+
87
+ // 6. Roll back each migration in reverse order
88
+ console.log("");
89
+ for (const migration of toRollback) {
90
+ const downSqlPath = join(config.migrations, migration.migrationName, "down.sql");
91
+
92
+ if (!existsSync(downSqlPath)) {
93
+ console.error(`Error: No down.sql found for migration: ${migration.migrationName}`);
94
+ console.error(` Expected at: ${downSqlPath}`);
95
+ console.error(" Cannot roll back without a down migration file.");
96
+ process.exit(1);
97
+ }
98
+
99
+ const sql = readFileSync(downSqlPath, "utf-8");
100
+
101
+ console.log(`Rolling back ${migration.migrationName}...`);
102
+ const result = await rollbackMigration({
103
+ executor: conn.executor,
104
+ migrationName: migration.migrationName,
105
+ sql,
106
+ });
107
+ console.log(` Done in ${result.executionTime}ms`);
108
+ }
109
+
110
+ console.log("");
111
+ console.log(`Rolled back ${toRollback.length} migration(s) successfully.`);
112
+ } finally {
113
+ await conn.close();
114
+ }
115
+ }
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@
13
13
  * vibeorm migrate generate [--name <n>] [--dry-run] Generate migration from schema diff
14
14
  * vibeorm migrate apply [--dry-run] Apply pending migrations
15
15
  * vibeorm migrate status Show migration state
16
+ * vibeorm migrate rollback [--steps <n>] [--dry-run] Roll back last N applied migrations
16
17
  * vibeorm migrate resolve --applied <name> | --rolled-back <name> Repair migration state
17
18
  */
18
19
 
@@ -29,6 +30,7 @@ import { runMigrateGenerate } from "./commands/migrate-generate.ts";
29
30
  import { runMigrateApply } from "./commands/migrate-apply.ts";
30
31
  import { runMigrateStatus } from "./commands/migrate-status.ts";
31
32
  import { runMigrateResolve } from "./commands/migrate-resolve.ts";
33
+ import { runMigrateRollback } from "./commands/migrate-rollback.ts";
32
34
 
33
35
  // ─── Command Router ───────────────────────────────────────────────
34
36
 
@@ -43,6 +45,7 @@ const COMMANDS: Record<string, (params: { config: VibeOrmConfig; flags: Record<s
43
45
  "migrate apply": runMigrateApply,
44
46
  "migrate status": runMigrateStatus,
45
47
  "migrate resolve": runMigrateResolve,
48
+ "migrate rollback": runMigrateRollback,
46
49
  };
47
50
 
48
51
  function printHelp(): void {
@@ -60,6 +63,7 @@ function printHelp(): void {
60
63
  console.log(" migrate generate [--name <n>] [--dry-run] Generate a new migration from schema changes");
61
64
  console.log(" migrate apply [--dry-run] Apply all pending migrations");
62
65
  console.log(" migrate status Show which migrations are applied/pending");
66
+ console.log(" migrate rollback [--steps <n>] [--dry-run] Roll back the last N applied migrations");
63
67
  console.log(" migrate resolve --applied <name> | --rolled-back <name> Repair migration state");
64
68
  console.log("");
65
69
  console.log("Options:");