tempest-db-js 0.1.0 → 0.3.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.
@@ -1,28 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var module$1 = require('module');
4
-
5
- var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
6
- // src/query.ts
7
- var OPERATORS = [
8
- "eq",
9
- "ne",
10
- "gt",
11
- "gte",
12
- "lt",
13
- "lte",
14
- "like",
15
- "ilike",
16
- "in",
17
- "notIn",
18
- "between",
19
- "isNull"
20
- ];
21
-
22
- // src/dialect.ts
23
- new Set(OPERATORS);
24
- module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
25
-
26
3
  // src/index.ts
27
4
  function isDefaultValue(value) {
28
5
  return typeof value === "object" && value !== null && "kind" in value && (value.kind === "literal" || value.kind === "expression");
@@ -76,7 +53,10 @@ var Column = class _Column {
76
53
  return new _Column(this.type, this.flags, this.defaultValue, resolved);
77
54
  }
78
55
  };
56
+ var columnsCache = /* @__PURE__ */ new WeakMap();
79
57
  function columnsOf(model) {
58
+ const cached = columnsCache.get(model);
59
+ if (cached) return cached;
80
60
  const instance = new model();
81
61
  const out = {};
82
62
  for (const [key, value] of Object.entries(instance)) {
@@ -84,6 +64,7 @@ function columnsOf(model) {
84
64
  out[key] = value;
85
65
  }
86
66
  }
67
+ columnsCache.set(model, out);
87
68
  return out;
88
69
  }
89
70
 
@@ -159,8 +140,8 @@ function invertAll(ops) {
159
140
  }
160
141
 
161
142
  // src/migrations/ddl.ts
162
- function quoteId(name) {
163
- return `"${name.replace(/"/g, '""')}"`;
143
+ function quoteId(name, dialect) {
144
+ return dialect === "mysql" ? `\`${name.replace(/`/g, "``")}\`` : `"${name.replace(/"/g, '""')}"`;
164
145
  }
165
146
  function quoteLiteral(value) {
166
147
  return `'${value.replace(/'/g, "''")}'`;
@@ -185,6 +166,45 @@ function renderColumnType(type, dialect) {
185
166
  return "TEXT";
186
167
  }
187
168
  }
169
+ if (dialect === "mysql") {
170
+ switch (kind) {
171
+ case "smallint":
172
+ return "SMALLINT";
173
+ case "integer":
174
+ return "INT";
175
+ case "bigint":
176
+ return "BIGINT";
177
+ case "numeric":
178
+ return meta.precision !== void 0 ? `DECIMAL(${meta.precision}${meta.scale !== void 0 ? `, ${meta.scale}` : ""})` : "DECIMAL";
179
+ case "real":
180
+ return "FLOAT";
181
+ case "double":
182
+ return "DOUBLE";
183
+ case "varchar":
184
+ return `VARCHAR(${meta.length ?? 255})`;
185
+ case "char":
186
+ return `CHAR(${meta.length ?? 255})`;
187
+ case "text":
188
+ return "TEXT";
189
+ case "boolean":
190
+ return "TINYINT(1)";
191
+ case "date":
192
+ return "DATE";
193
+ case "time":
194
+ return "TIME";
195
+ case "datetime":
196
+ case "timestamp":
197
+ return "DATETIME";
198
+ case "blob":
199
+ return "BLOB";
200
+ case "json":
201
+ return "JSON";
202
+ case "uuid":
203
+ return "CHAR(36)";
204
+ case "enum":
205
+ return `ENUM(${(meta.values ?? []).map(quoteLiteral).join(", ")})`;
206
+ }
207
+ }
188
208
  switch (kind) {
189
209
  case "smallint":
190
210
  return "SMALLINT";
@@ -229,19 +249,21 @@ function renderDefault(def, dialect) {
229
249
  if (typeof expr === "object") return expr.raw;
230
250
  switch (expr) {
231
251
  case "now":
232
- return dialect === "sqlite" ? "CURRENT_TIMESTAMP" : "now()";
252
+ return dialect === "postgresql" ? "now()" : "CURRENT_TIMESTAMP";
233
253
  case "current_date":
234
254
  return "CURRENT_DATE";
235
255
  case "current_time":
236
256
  return "CURRENT_TIME";
237
257
  case "uuidv4":
238
- return dialect === "sqlite" ? "(lower(hex(randomblob(16))))" : "gen_random_uuid()";
258
+ if (dialect === "postgresql") return "gen_random_uuid()";
259
+ if (dialect === "mysql") return "(UUID())";
260
+ return "(lower(hex(randomblob(16))))";
239
261
  }
240
262
  }
241
263
  const value = def.value;
242
264
  if (value === null) return "NULL";
243
265
  if (typeof value === "boolean") {
244
- return dialect === "sqlite" ? value ? "1" : "0" : value ? "TRUE" : "FALSE";
266
+ return dialect === "postgresql" ? value ? "TRUE" : "FALSE" : value ? "1" : "0";
245
267
  }
246
268
  if (typeof value === "number" || typeof value === "bigint") return String(value);
247
269
  if (value instanceof Date) return quoteLiteral(value.toISOString());
@@ -249,7 +271,7 @@ function renderDefault(def, dialect) {
249
271
  return quoteLiteral(String(value));
250
272
  }
251
273
  function renderColumnDef(col, dialect) {
252
- let sql = `${quoteId(col.name)} ${renderColumnType(col.type, dialect)}`;
274
+ let sql = `${quoteId(col.name, dialect)} ${renderColumnType(col.type, dialect)}`;
253
275
  if (col.notNull) sql += " NOT NULL";
254
276
  if (col.default !== null) sql += ` DEFAULT ${renderDefault(col.default, dialect)}`;
255
277
  return sql;
@@ -257,26 +279,42 @@ function renderColumnDef(col, dialect) {
257
279
  function enumTypeName(table, column) {
258
280
  return `${table}_${column}`;
259
281
  }
282
+ function isAutoIncrementPk(table, col) {
283
+ return table.primaryKey.length === 1 && table.primaryKey[0] === col.name && col.default === null && (col.type.kind === "smallint" || col.type.kind === "integer" || col.type.kind === "bigint");
284
+ }
285
+ function postgresSerialType(kind) {
286
+ if (kind === "bigint") return "BIGSERIAL";
287
+ if (kind === "smallint") return "SMALLSERIAL";
288
+ return "SERIAL";
289
+ }
260
290
  function renderCreateTable(table, dialect) {
261
291
  const typeStmts = [];
262
292
  const cols = Object.values(table.columns).map((c) => {
263
293
  if (dialect === "postgresql" && c.type.kind === "enum") {
264
294
  const typeName = enumTypeName(table.name, c.name);
265
295
  const values = (c.type.meta.values ?? []).map(quoteLiteral).join(", ");
266
- typeStmts.push(`CREATE TYPE ${quoteId(typeName)} AS ENUM (${values})`);
267
- let def = `${quoteId(c.name)} ${quoteId(typeName)}`;
296
+ typeStmts.push(`CREATE TYPE ${quoteId(typeName, dialect)} AS ENUM (${values})`);
297
+ let def = `${quoteId(c.name, dialect)} ${quoteId(typeName, dialect)}`;
268
298
  if (c.notNull) def += " NOT NULL";
269
299
  if (c.default !== null) def += ` DEFAULT ${renderDefault(c.default, dialect)}`;
270
300
  return def;
271
301
  }
302
+ if (dialect === "postgresql" && isAutoIncrementPk(table, c)) {
303
+ return `${quoteId(c.name, dialect)} ${postgresSerialType(c.type.kind)}`;
304
+ }
305
+ if (dialect === "mysql" && isAutoIncrementPk(table, c)) {
306
+ return `${quoteId(c.name, dialect)} ${renderColumnType(c.type, dialect)} NOT NULL AUTO_INCREMENT`;
307
+ }
272
308
  return renderColumnDef(c, dialect);
273
309
  });
274
310
  if (table.primaryKey.length > 0) {
275
- cols.push(`PRIMARY KEY (${table.primaryKey.map(quoteId).join(", ")})`);
311
+ cols.push(
312
+ `PRIMARY KEY (${table.primaryKey.map((c) => quoteId(c, dialect)).join(", ")})`
313
+ );
276
314
  }
277
315
  return [
278
316
  ...typeStmts,
279
- `CREATE TABLE ${quoteId(table.name)} (
317
+ `CREATE TABLE ${quoteId(table.name, dialect)} (
280
318
  ${cols.join(",\n ")}
281
319
  )`
282
320
  ];
@@ -286,23 +324,27 @@ function renderOperation(op, dialect) {
286
324
  case "create_table":
287
325
  return renderCreateTable(op.table, dialect);
288
326
  case "drop_table":
289
- return [`DROP TABLE ${quoteId(op.table.name)}`];
327
+ return [`DROP TABLE ${quoteId(op.table.name, dialect)}`];
290
328
  case "rename_table":
291
- return [`ALTER TABLE ${quoteId(op.from)} RENAME TO ${quoteId(op.to)}`];
329
+ return dialect === "mysql" ? [`RENAME TABLE ${quoteId(op.from, dialect)} TO ${quoteId(op.to, dialect)}`] : [
330
+ `ALTER TABLE ${quoteId(op.from, dialect)} RENAME TO ${quoteId(op.to, dialect)}`
331
+ ];
292
332
  case "add_column":
293
333
  return [
294
- `ALTER TABLE ${quoteId(op.table)} ADD COLUMN ${renderColumnDef(op.column, dialect)}`
334
+ `ALTER TABLE ${quoteId(op.table, dialect)} ADD COLUMN ${renderColumnDef(op.column, dialect)}`
295
335
  ];
296
336
  case "drop_column":
297
- return [`ALTER TABLE ${quoteId(op.table)} DROP COLUMN ${quoteId(op.column.name)}`];
337
+ return [
338
+ `ALTER TABLE ${quoteId(op.table, dialect)} DROP COLUMN ${quoteId(op.column.name, dialect)}`
339
+ ];
298
340
  case "rename_column":
299
341
  return [
300
- `ALTER TABLE ${quoteId(op.table)} RENAME COLUMN ${quoteId(op.from)} TO ${quoteId(op.to)}`
342
+ `ALTER TABLE ${quoteId(op.table, dialect)} RENAME COLUMN ${quoteId(op.from, dialect)} TO ${quoteId(op.to, dialect)}`
301
343
  ];
302
344
  case "alter_column":
303
345
  return renderAlterColumn(op.table, op.to, dialect);
304
346
  case "recreate_table":
305
- return dialect === "sqlite" ? renderSqliteRebuild(op.from, op.to) : renderPostgresTableDiff(op.from, op.to);
347
+ return dialect === "sqlite" ? renderSqliteRebuild(op.from, op.to) : renderTableDiff(op.from, op.to, dialect);
306
348
  case "execute":
307
349
  return [op.up];
308
350
  }
@@ -312,34 +354,38 @@ function renderSqliteRebuild(from, to) {
312
354
  const common = Object.keys(to.columns).filter((c) => c in from.columns);
313
355
  const cols = Object.values(to.columns).map((c) => renderColumnDef(c, "sqlite"));
314
356
  if (to.primaryKey.length > 0) {
315
- cols.push(`PRIMARY KEY (${to.primaryKey.map(quoteId).join(", ")})`);
357
+ cols.push(
358
+ `PRIMARY KEY (${to.primaryKey.map((c) => quoteId(c, "sqlite")).join(", ")})`
359
+ );
316
360
  }
317
- const commonSql = common.map(quoteId).join(", ");
361
+ const commonSql = common.map((c) => quoteId(c, "sqlite")).join(", ");
318
362
  return [
319
363
  "PRAGMA foreign_keys=off",
320
- `CREATE TABLE ${quoteId(tmp)} (
364
+ `CREATE TABLE ${quoteId(tmp, "sqlite")} (
321
365
  ${cols.join(",\n ")}
322
366
  )`,
323
- common.length > 0 ? `INSERT INTO ${quoteId(tmp)} (${commonSql}) SELECT ${commonSql} FROM ${quoteId(from.name)}` : `-- no common columns to copy from ${from.name}`,
324
- `DROP TABLE ${quoteId(from.name)}`,
325
- `ALTER TABLE ${quoteId(tmp)} RENAME TO ${quoteId(to.name)}`,
367
+ common.length > 0 ? `INSERT INTO ${quoteId(tmp, "sqlite")} (${commonSql}) SELECT ${commonSql} FROM ${quoteId(from.name, "sqlite")}` : `-- no common columns to copy from ${from.name}`,
368
+ `DROP TABLE ${quoteId(from.name, "sqlite")}`,
369
+ `ALTER TABLE ${quoteId(tmp, "sqlite")} RENAME TO ${quoteId(to.name, "sqlite")}`,
326
370
  "PRAGMA foreign_keys=on"
327
371
  ];
328
372
  }
329
- function renderPostgresTableDiff(from, to) {
373
+ function renderTableDiff(from, to, dialect) {
330
374
  const stmts = [];
331
375
  for (const [name, col] of Object.entries(to.columns)) {
332
376
  if (!(name in from.columns)) {
333
377
  stmts.push(
334
- `ALTER TABLE ${quoteId(to.name)} ADD COLUMN ${renderColumnDef(col, "postgresql")}`
378
+ `ALTER TABLE ${quoteId(to.name, dialect)} ADD COLUMN ${renderColumnDef(col, dialect)}`
335
379
  );
336
380
  } else {
337
- stmts.push(...renderAlterColumn(to.name, col, "postgresql"));
381
+ stmts.push(...renderAlterColumn(to.name, col, dialect));
338
382
  }
339
383
  }
340
384
  for (const name of Object.keys(from.columns)) {
341
385
  if (!(name in to.columns)) {
342
- stmts.push(`ALTER TABLE ${quoteId(to.name)} DROP COLUMN ${quoteId(name)}`);
386
+ stmts.push(
387
+ `ALTER TABLE ${quoteId(to.name, dialect)} DROP COLUMN ${quoteId(name, dialect)}`
388
+ );
343
389
  }
344
390
  }
345
391
  return stmts;
@@ -347,11 +393,16 @@ function renderPostgresTableDiff(from, to) {
347
393
  function renderAlterColumn(table, to, dialect) {
348
394
  if (dialect === "sqlite") {
349
395
  throw new Error(
350
- `alter_column on SQLite needs batch/table-rebuild (Phase 6e); column ${table}.${to.name}`
396
+ `alter_column on SQLite needs a table-rebuild (recreate_table); column ${table}.${to.name}`
351
397
  );
352
398
  }
353
- const t = quoteId(table);
354
- const c = quoteId(to.name);
399
+ if (dialect === "mysql") {
400
+ return [
401
+ `ALTER TABLE ${quoteId(table, dialect)} MODIFY COLUMN ${renderColumnDef(to, dialect)}`
402
+ ];
403
+ }
404
+ const t = quoteId(table, dialect);
405
+ const c = quoteId(to.name, dialect);
355
406
  const stmts = [
356
407
  `ALTER TABLE ${t} ALTER COLUMN ${c} TYPE ${renderColumnType(to.type, dialect)}`
357
408
  ];
@@ -642,6 +693,112 @@ var MigrationRunner = class {
642
693
  return reverted;
643
694
  }
644
695
  };
696
+ function quoteIdent(name, dialect) {
697
+ return dialect === "mysql" ? `\`${name.replace(/`/g, "``")}\`` : `"${name.replace(/"/g, '""')}"`;
698
+ }
699
+ function placeholder(index, dialect) {
700
+ return dialect === "postgresql" ? `$${index}` : "?";
701
+ }
702
+ var AsyncMigrationRunner = class {
703
+ constructor(driver, dialect) {
704
+ this.driver = driver;
705
+ this.dialect = dialect;
706
+ this.vt = quoteIdent(VERSION_TABLE, dialect);
707
+ }
708
+ driver;
709
+ dialect;
710
+ vt;
711
+ /** Create the version-tracking table if it does not exist. */
712
+ async ensureVersionTable() {
713
+ await this.driver.execute(
714
+ `CREATE TABLE IF NOT EXISTS ${this.vt} (revision ${this.textType()} PRIMARY KEY, applied_at ${this.textType()} NOT NULL, down_revision ${this.textType()} NOT NULL)`,
715
+ []
716
+ );
717
+ }
718
+ /** A portable "text" column type for the version table. */
719
+ textType() {
720
+ return this.dialect === "mysql" ? "VARCHAR(255)" : "TEXT";
721
+ }
722
+ /** The set of applied revision ids. */
723
+ async applied() {
724
+ await this.ensureVersionTable();
725
+ const { rows } = await this.driver.execute(`SELECT revision FROM ${this.vt}`, []);
726
+ return new Set(rows.map((r) => String(r.revision)));
727
+ }
728
+ async runOps(ops) {
729
+ for (const op of ops) {
730
+ for (const stmt of renderOperation(op, this.dialect)) {
731
+ const trimmed = stmt.trim();
732
+ if (trimmed.length === 0 || trimmed.startsWith("--")) continue;
733
+ await this.driver.execute(stmt, []);
734
+ }
735
+ }
736
+ }
737
+ async record(migration, appliedAt) {
738
+ const p = (i) => placeholder(i, this.dialect);
739
+ await this.driver.execute(
740
+ `INSERT INTO ${this.vt} (revision, applied_at, down_revision) VALUES (${p(1)}, ${p(2)}, ${p(3)})`,
741
+ [migration.revision, appliedAt, migration.downRevision.join(",")]
742
+ );
743
+ }
744
+ async forget(revision) {
745
+ await this.driver.execute(
746
+ `DELETE FROM ${this.vt} WHERE revision = ${placeholder(1, this.dialect)}`,
747
+ [revision]
748
+ );
749
+ }
750
+ /**
751
+ * Apply all pending migrations up to the head(s), in DAG order.
752
+ *
753
+ * @param migrations All known migrations.
754
+ * @param appliedAt Timestamp string to stamp on each applied revision.
755
+ * @returns The revision ids that were applied this run.
756
+ */
757
+ async upgrade(migrations, appliedAt) {
758
+ const done = await this.applied();
759
+ const ordered = topoOrder(migrations);
760
+ const ran = [];
761
+ for (const migration of ordered) {
762
+ if (done.has(migration.revision)) continue;
763
+ const op = new Op();
764
+ migration.up(op);
765
+ await this.runOps(op.operations);
766
+ await this.record(migration, appliedAt);
767
+ ran.push(migration.revision);
768
+ }
769
+ return ran;
770
+ }
771
+ /**
772
+ * Revert the last `steps` applied migrations (default 1), newest first.
773
+ *
774
+ * @param migrations All known migrations.
775
+ * @param steps How many applied revisions to roll back.
776
+ * @returns The revision ids that were reverted.
777
+ */
778
+ async downgrade(migrations, steps = 1) {
779
+ const done = await this.applied();
780
+ const ordered = topoOrder(migrations).filter((m) => done.has(m.revision));
781
+ const toRevert = ordered.slice(-steps).reverse();
782
+ const reverted = [];
783
+ for (const migration of toRevert) {
784
+ const op = new Op();
785
+ try {
786
+ migration.down(op);
787
+ } catch {
788
+ op.operations.length = 0;
789
+ }
790
+ if (op.operations.length === 0) {
791
+ const upOp = new Op();
792
+ migration.up(upOp);
793
+ op.operations.push(...invertAll(upOp.operations));
794
+ }
795
+ await this.runOps(op.operations);
796
+ await this.forget(migration.revision);
797
+ reverted.push(migration.revision);
798
+ }
799
+ return reverted;
800
+ }
801
+ };
645
802
 
646
803
  // src/migrations/introspect.ts
647
804
  function sqliteAffinity(declared) {
@@ -917,13 +1074,133 @@ function replaySchema(migrations) {
917
1074
  return schema;
918
1075
  }
919
1076
 
1077
+ // src/migrations/renames.ts
1078
+ function columnShape(col) {
1079
+ return JSON.stringify({
1080
+ type: col.type,
1081
+ notNull: col.notNull,
1082
+ primaryKey: col.primaryKey,
1083
+ default: col.default
1084
+ });
1085
+ }
1086
+ function tableShape(columns) {
1087
+ return Object.entries(columns).map(([name, col]) => `${name}:${columnShape(col)}`).sort().join("|");
1088
+ }
1089
+ function detectRenames(ops) {
1090
+ const candidates = [];
1091
+ const creates = ops.filter((o) => o.kind === "create_table");
1092
+ const tableDrops = ops.filter((o) => o.kind === "drop_table");
1093
+ const takenCreate = /* @__PURE__ */ new Set();
1094
+ const takenDrop = /* @__PURE__ */ new Set();
1095
+ for (const create of creates) {
1096
+ const createShape = tableShape(create.table.columns);
1097
+ const matches = tableDrops.filter(
1098
+ (d) => !takenDrop.has(d.table.name) && tableShape(d.table.columns) === createShape
1099
+ );
1100
+ const uniqueCreate = creates.filter(
1101
+ (c) => !takenCreate.has(c.table.name) && tableShape(c.table.columns) === createShape
1102
+ ).length === 1;
1103
+ if (matches.length === 1 && uniqueCreate) {
1104
+ const drop = matches[0];
1105
+ if (drop.table.name !== create.table.name) {
1106
+ candidates.push({ kind: "table", from: drop.table.name, to: create.table.name });
1107
+ takenCreate.add(create.table.name);
1108
+ takenDrop.add(drop.table.name);
1109
+ }
1110
+ }
1111
+ }
1112
+ const adds = ops.filter((o) => o.kind === "add_column");
1113
+ const colDrops = ops.filter((o) => o.kind === "drop_column");
1114
+ const tables = /* @__PURE__ */ new Set([...adds.map((o) => o.table), ...colDrops.map((o) => o.table)]);
1115
+ for (const table of tables) {
1116
+ const tableAdds = adds.filter((o) => o.table === table);
1117
+ const tableColDrops = colDrops.filter((o) => o.table === table);
1118
+ const takenAdd = /* @__PURE__ */ new Set();
1119
+ const takenColDrop = /* @__PURE__ */ new Set();
1120
+ for (const add of tableAdds) {
1121
+ const shape = columnShape(add.column);
1122
+ const dropMatches = tableColDrops.filter(
1123
+ (d) => !takenColDrop.has(d.column.name) && columnShape(d.column) === shape
1124
+ );
1125
+ const addMatches = tableAdds.filter(
1126
+ (a) => !takenAdd.has(a.column.name) && columnShape(a.column) === shape
1127
+ );
1128
+ if (dropMatches.length === 1 && addMatches.length === 1) {
1129
+ const drop = dropMatches[0];
1130
+ candidates.push({
1131
+ kind: "column",
1132
+ table,
1133
+ from: drop.column.name,
1134
+ to: add.column.name
1135
+ });
1136
+ takenAdd.add(add.column.name);
1137
+ takenColDrop.add(drop.column.name);
1138
+ }
1139
+ }
1140
+ }
1141
+ return candidates;
1142
+ }
1143
+ function isTableRename(op, r) {
1144
+ return op.kind === "create_table" && op.table.name === r.to || op.kind === "drop_table" && op.table.name === r.from;
1145
+ }
1146
+ function isColumnRename(op, r) {
1147
+ return op.kind === "add_column" && op.table === r.table && op.column.name === r.to || op.kind === "drop_column" && op.table === r.table && op.column.name === r.from;
1148
+ }
1149
+ function applyRenames(ops, confirmed) {
1150
+ const out = [];
1151
+ const emitted = /* @__PURE__ */ new Set();
1152
+ for (const op of ops) {
1153
+ const match = confirmed.find(
1154
+ (r) => r.kind === "table" ? isTableRename(op, r) : isColumnRename(op, r)
1155
+ );
1156
+ if (!match) {
1157
+ out.push(op);
1158
+ continue;
1159
+ }
1160
+ if (!emitted.has(match)) {
1161
+ emitted.add(match);
1162
+ out.push(
1163
+ match.kind === "table" ? { kind: "rename_table", from: match.from, to: match.to } : { kind: "rename_column", table: match.table, from: match.from, to: match.to }
1164
+ );
1165
+ }
1166
+ }
1167
+ return out;
1168
+ }
1169
+
920
1170
  // src/migrations/cli.ts
1171
+ function defineMigrationConfig(config) {
1172
+ return config;
1173
+ }
921
1174
  function ok(lines) {
922
1175
  return { code: 0, lines };
923
1176
  }
924
1177
  function fail(lines) {
925
1178
  return { code: 1, lines };
926
1179
  }
1180
+ function parseRenameFlags(rest) {
1181
+ const out = [];
1182
+ for (let i = 0; i < rest.length; i += 1) {
1183
+ const arg = rest[i];
1184
+ if (arg === "--rename-table") {
1185
+ const [from, to] = (rest[i + 1] ?? "").split(":");
1186
+ i += 1;
1187
+ if (from && to) out.push({ kind: "table", from, to });
1188
+ } else if (arg === "--rename-column") {
1189
+ const [left, to] = (rest[i + 1] ?? "").split(":");
1190
+ i += 1;
1191
+ const dot = left?.lastIndexOf(".") ?? -1;
1192
+ if (left && to && dot > 0) {
1193
+ out.push({
1194
+ kind: "column",
1195
+ table: left.slice(0, dot),
1196
+ from: left.slice(dot + 1),
1197
+ to
1198
+ });
1199
+ }
1200
+ }
1201
+ }
1202
+ return out;
1203
+ }
927
1204
  function pending(config, runner) {
928
1205
  const done = runner.applied();
929
1206
  return topoOrder(config.migrations).filter((m) => !done.has(m.revision));
@@ -990,7 +1267,11 @@ function runMigrationCli(argv, config) {
990
1267
  const msgIndex = rest.indexOf("-m");
991
1268
  const label = msgIndex >= 0 ? rest[msgIndex + 1] ?? "revision" : "revision";
992
1269
  const parents = heads(config.migrations);
993
- const ops = rest.includes("--autogenerate") ? diffSchema(replaySchema(config.migrations), reflectSchema(config.models)) : [];
1270
+ let ops = rest.includes("--autogenerate") ? diffSchema(replaySchema(config.migrations), reflectSchema(config.models)) : [];
1271
+ if (rest.includes("--autogenerate")) {
1272
+ const confirmed = rest.includes("--autorename") ? detectRenames(ops) : parseRenameFlags(rest);
1273
+ if (confirmed.length > 0) ops = applyRenames(ops, confirmed);
1274
+ }
994
1275
  const source = generateMigration({
995
1276
  revision: makeRevisionId(label, parents),
996
1277
  downRevision: parents,
@@ -1007,14 +1288,18 @@ function runMigrationCli(argv, config) {
1007
1288
  }
1008
1289
  }
1009
1290
 
1291
+ exports.AsyncMigrationRunner = AsyncMigrationRunner;
1010
1292
  exports.CyclicMigrationGraph = CyclicMigrationGraph;
1011
1293
  exports.IrreversibleMigration = IrreversibleMigration;
1012
1294
  exports.MigrationRunner = MigrationRunner;
1013
1295
  exports.Op = Op;
1014
1296
  exports.UnknownRevision = UnknownRevision;
1015
1297
  exports.applyOperation = applyOperation;
1298
+ exports.applyRenames = applyRenames;
1016
1299
  exports.checkDrift = checkDrift;
1017
1300
  exports.checkDriftPostgres = checkDriftPostgres;
1301
+ exports.defineMigrationConfig = defineMigrationConfig;
1302
+ exports.detectRenames = detectRenames;
1018
1303
  exports.diffSchema = diffSchema;
1019
1304
  exports.emptySchema = emptySchema;
1020
1305
  exports.generateMigration = generateMigration;