tempest-db-js 0.2.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.
package/dist/bin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { runMigrationCli, diffSchema, replaySchema, reflectSchema, detectRenames } from './chunk-QMW4NKMH.js';
3
- import './chunk-AGDD7K3F.js';
2
+ import { runMigrationCli, diffSchema, replaySchema, reflectSchema, detectRenames } from './chunk-OP7FRDI5.js';
3
+ import './chunk-Q32CBI2A.js';
4
4
  import { existsSync } from 'fs';
5
5
  import { resolve } from 'path';
6
6
  import { createInterface } from 'readline/promises';
@@ -1,4 +1,4 @@
1
- import { columnsOf } from './chunk-AGDD7K3F.js';
1
+ import { columnsOf } from './chunk-Q32CBI2A.js';
2
2
 
3
3
  // src/migrations/operations.ts
4
4
  var IrreversibleMigration = class extends Error {
@@ -90,8 +90,8 @@ function makeRevisionId(label, parents) {
90
90
  }
91
91
 
92
92
  // src/migrations/ddl.ts
93
- function quoteId(name) {
94
- return `"${name.replace(/"/g, '""')}"`;
93
+ function quoteId(name, dialect) {
94
+ return dialect === "mysql" ? `\`${name.replace(/`/g, "``")}\`` : `"${name.replace(/"/g, '""')}"`;
95
95
  }
96
96
  function quoteLiteral(value) {
97
97
  return `'${value.replace(/'/g, "''")}'`;
@@ -116,6 +116,45 @@ function renderColumnType(type, dialect) {
116
116
  return "TEXT";
117
117
  }
118
118
  }
119
+ if (dialect === "mysql") {
120
+ switch (kind) {
121
+ case "smallint":
122
+ return "SMALLINT";
123
+ case "integer":
124
+ return "INT";
125
+ case "bigint":
126
+ return "BIGINT";
127
+ case "numeric":
128
+ return meta.precision !== void 0 ? `DECIMAL(${meta.precision}${meta.scale !== void 0 ? `, ${meta.scale}` : ""})` : "DECIMAL";
129
+ case "real":
130
+ return "FLOAT";
131
+ case "double":
132
+ return "DOUBLE";
133
+ case "varchar":
134
+ return `VARCHAR(${meta.length ?? 255})`;
135
+ case "char":
136
+ return `CHAR(${meta.length ?? 255})`;
137
+ case "text":
138
+ return "TEXT";
139
+ case "boolean":
140
+ return "TINYINT(1)";
141
+ case "date":
142
+ return "DATE";
143
+ case "time":
144
+ return "TIME";
145
+ case "datetime":
146
+ case "timestamp":
147
+ return "DATETIME";
148
+ case "blob":
149
+ return "BLOB";
150
+ case "json":
151
+ return "JSON";
152
+ case "uuid":
153
+ return "CHAR(36)";
154
+ case "enum":
155
+ return `ENUM(${(meta.values ?? []).map(quoteLiteral).join(", ")})`;
156
+ }
157
+ }
119
158
  switch (kind) {
120
159
  case "smallint":
121
160
  return "SMALLINT";
@@ -160,19 +199,21 @@ function renderDefault(def, dialect) {
160
199
  if (typeof expr === "object") return expr.raw;
161
200
  switch (expr) {
162
201
  case "now":
163
- return dialect === "sqlite" ? "CURRENT_TIMESTAMP" : "now()";
202
+ return dialect === "postgresql" ? "now()" : "CURRENT_TIMESTAMP";
164
203
  case "current_date":
165
204
  return "CURRENT_DATE";
166
205
  case "current_time":
167
206
  return "CURRENT_TIME";
168
207
  case "uuidv4":
169
- return dialect === "sqlite" ? "(lower(hex(randomblob(16))))" : "gen_random_uuid()";
208
+ if (dialect === "postgresql") return "gen_random_uuid()";
209
+ if (dialect === "mysql") return "(UUID())";
210
+ return "(lower(hex(randomblob(16))))";
170
211
  }
171
212
  }
172
213
  const value = def.value;
173
214
  if (value === null) return "NULL";
174
215
  if (typeof value === "boolean") {
175
- return dialect === "sqlite" ? value ? "1" : "0" : value ? "TRUE" : "FALSE";
216
+ return dialect === "postgresql" ? value ? "TRUE" : "FALSE" : value ? "1" : "0";
176
217
  }
177
218
  if (typeof value === "number" || typeof value === "bigint") return String(value);
178
219
  if (value instanceof Date) return quoteLiteral(value.toISOString());
@@ -180,7 +221,7 @@ function renderDefault(def, dialect) {
180
221
  return quoteLiteral(String(value));
181
222
  }
182
223
  function renderColumnDef(col, dialect) {
183
- let sql = `${quoteId(col.name)} ${renderColumnType(col.type, dialect)}`;
224
+ let sql = `${quoteId(col.name, dialect)} ${renderColumnType(col.type, dialect)}`;
184
225
  if (col.notNull) sql += " NOT NULL";
185
226
  if (col.default !== null) sql += ` DEFAULT ${renderDefault(col.default, dialect)}`;
186
227
  return sql;
@@ -202,23 +243,28 @@ function renderCreateTable(table, dialect) {
202
243
  if (dialect === "postgresql" && c.type.kind === "enum") {
203
244
  const typeName = enumTypeName(table.name, c.name);
204
245
  const values = (c.type.meta.values ?? []).map(quoteLiteral).join(", ");
205
- typeStmts.push(`CREATE TYPE ${quoteId(typeName)} AS ENUM (${values})`);
206
- let def = `${quoteId(c.name)} ${quoteId(typeName)}`;
246
+ typeStmts.push(`CREATE TYPE ${quoteId(typeName, dialect)} AS ENUM (${values})`);
247
+ let def = `${quoteId(c.name, dialect)} ${quoteId(typeName, dialect)}`;
207
248
  if (c.notNull) def += " NOT NULL";
208
249
  if (c.default !== null) def += ` DEFAULT ${renderDefault(c.default, dialect)}`;
209
250
  return def;
210
251
  }
211
252
  if (dialect === "postgresql" && isAutoIncrementPk(table, c)) {
212
- return `${quoteId(c.name)} ${postgresSerialType(c.type.kind)}`;
253
+ return `${quoteId(c.name, dialect)} ${postgresSerialType(c.type.kind)}`;
254
+ }
255
+ if (dialect === "mysql" && isAutoIncrementPk(table, c)) {
256
+ return `${quoteId(c.name, dialect)} ${renderColumnType(c.type, dialect)} NOT NULL AUTO_INCREMENT`;
213
257
  }
214
258
  return renderColumnDef(c, dialect);
215
259
  });
216
260
  if (table.primaryKey.length > 0) {
217
- cols.push(`PRIMARY KEY (${table.primaryKey.map(quoteId).join(", ")})`);
261
+ cols.push(
262
+ `PRIMARY KEY (${table.primaryKey.map((c) => quoteId(c, dialect)).join(", ")})`
263
+ );
218
264
  }
219
265
  return [
220
266
  ...typeStmts,
221
- `CREATE TABLE ${quoteId(table.name)} (
267
+ `CREATE TABLE ${quoteId(table.name, dialect)} (
222
268
  ${cols.join(",\n ")}
223
269
  )`
224
270
  ];
@@ -228,23 +274,27 @@ function renderOperation(op, dialect) {
228
274
  case "create_table":
229
275
  return renderCreateTable(op.table, dialect);
230
276
  case "drop_table":
231
- return [`DROP TABLE ${quoteId(op.table.name)}`];
277
+ return [`DROP TABLE ${quoteId(op.table.name, dialect)}`];
232
278
  case "rename_table":
233
- return [`ALTER TABLE ${quoteId(op.from)} RENAME TO ${quoteId(op.to)}`];
279
+ return dialect === "mysql" ? [`RENAME TABLE ${quoteId(op.from, dialect)} TO ${quoteId(op.to, dialect)}`] : [
280
+ `ALTER TABLE ${quoteId(op.from, dialect)} RENAME TO ${quoteId(op.to, dialect)}`
281
+ ];
234
282
  case "add_column":
235
283
  return [
236
- `ALTER TABLE ${quoteId(op.table)} ADD COLUMN ${renderColumnDef(op.column, dialect)}`
284
+ `ALTER TABLE ${quoteId(op.table, dialect)} ADD COLUMN ${renderColumnDef(op.column, dialect)}`
237
285
  ];
238
286
  case "drop_column":
239
- return [`ALTER TABLE ${quoteId(op.table)} DROP COLUMN ${quoteId(op.column.name)}`];
287
+ return [
288
+ `ALTER TABLE ${quoteId(op.table, dialect)} DROP COLUMN ${quoteId(op.column.name, dialect)}`
289
+ ];
240
290
  case "rename_column":
241
291
  return [
242
- `ALTER TABLE ${quoteId(op.table)} RENAME COLUMN ${quoteId(op.from)} TO ${quoteId(op.to)}`
292
+ `ALTER TABLE ${quoteId(op.table, dialect)} RENAME COLUMN ${quoteId(op.from, dialect)} TO ${quoteId(op.to, dialect)}`
243
293
  ];
244
294
  case "alter_column":
245
295
  return renderAlterColumn(op.table, op.to, dialect);
246
296
  case "recreate_table":
247
- return dialect === "sqlite" ? renderSqliteRebuild(op.from, op.to) : renderPostgresTableDiff(op.from, op.to);
297
+ return dialect === "sqlite" ? renderSqliteRebuild(op.from, op.to) : renderTableDiff(op.from, op.to, dialect);
248
298
  case "execute":
249
299
  return [op.up];
250
300
  }
@@ -254,34 +304,38 @@ function renderSqliteRebuild(from, to) {
254
304
  const common = Object.keys(to.columns).filter((c) => c in from.columns);
255
305
  const cols = Object.values(to.columns).map((c) => renderColumnDef(c, "sqlite"));
256
306
  if (to.primaryKey.length > 0) {
257
- cols.push(`PRIMARY KEY (${to.primaryKey.map(quoteId).join(", ")})`);
307
+ cols.push(
308
+ `PRIMARY KEY (${to.primaryKey.map((c) => quoteId(c, "sqlite")).join(", ")})`
309
+ );
258
310
  }
259
- const commonSql = common.map(quoteId).join(", ");
311
+ const commonSql = common.map((c) => quoteId(c, "sqlite")).join(", ");
260
312
  return [
261
313
  "PRAGMA foreign_keys=off",
262
- `CREATE TABLE ${quoteId(tmp)} (
314
+ `CREATE TABLE ${quoteId(tmp, "sqlite")} (
263
315
  ${cols.join(",\n ")}
264
316
  )`,
265
- common.length > 0 ? `INSERT INTO ${quoteId(tmp)} (${commonSql}) SELECT ${commonSql} FROM ${quoteId(from.name)}` : `-- no common columns to copy from ${from.name}`,
266
- `DROP TABLE ${quoteId(from.name)}`,
267
- `ALTER TABLE ${quoteId(tmp)} RENAME TO ${quoteId(to.name)}`,
317
+ common.length > 0 ? `INSERT INTO ${quoteId(tmp, "sqlite")} (${commonSql}) SELECT ${commonSql} FROM ${quoteId(from.name, "sqlite")}` : `-- no common columns to copy from ${from.name}`,
318
+ `DROP TABLE ${quoteId(from.name, "sqlite")}`,
319
+ `ALTER TABLE ${quoteId(tmp, "sqlite")} RENAME TO ${quoteId(to.name, "sqlite")}`,
268
320
  "PRAGMA foreign_keys=on"
269
321
  ];
270
322
  }
271
- function renderPostgresTableDiff(from, to) {
323
+ function renderTableDiff(from, to, dialect) {
272
324
  const stmts = [];
273
325
  for (const [name, col] of Object.entries(to.columns)) {
274
326
  if (!(name in from.columns)) {
275
327
  stmts.push(
276
- `ALTER TABLE ${quoteId(to.name)} ADD COLUMN ${renderColumnDef(col, "postgresql")}`
328
+ `ALTER TABLE ${quoteId(to.name, dialect)} ADD COLUMN ${renderColumnDef(col, dialect)}`
277
329
  );
278
330
  } else {
279
- stmts.push(...renderAlterColumn(to.name, col, "postgresql"));
331
+ stmts.push(...renderAlterColumn(to.name, col, dialect));
280
332
  }
281
333
  }
282
334
  for (const name of Object.keys(from.columns)) {
283
335
  if (!(name in to.columns)) {
284
- stmts.push(`ALTER TABLE ${quoteId(to.name)} DROP COLUMN ${quoteId(name)}`);
336
+ stmts.push(
337
+ `ALTER TABLE ${quoteId(to.name, dialect)} DROP COLUMN ${quoteId(name, dialect)}`
338
+ );
285
339
  }
286
340
  }
287
341
  return stmts;
@@ -289,11 +343,16 @@ function renderPostgresTableDiff(from, to) {
289
343
  function renderAlterColumn(table, to, dialect) {
290
344
  if (dialect === "sqlite") {
291
345
  throw new Error(
292
- `alter_column on SQLite needs batch/table-rebuild (Phase 6e); column ${table}.${to.name}`
346
+ `alter_column on SQLite needs a table-rebuild (recreate_table); column ${table}.${to.name}`
293
347
  );
294
348
  }
295
- const t = quoteId(table);
296
- const c = quoteId(to.name);
349
+ if (dialect === "mysql") {
350
+ return [
351
+ `ALTER TABLE ${quoteId(table, dialect)} MODIFY COLUMN ${renderColumnDef(to, dialect)}`
352
+ ];
353
+ }
354
+ const t = quoteId(table, dialect);
355
+ const c = quoteId(to.name, dialect);
297
356
  const stmts = [
298
357
  `ALTER TABLE ${t} ALTER COLUMN ${c} TYPE ${renderColumnType(to.type, dialect)}`
299
358
  ];
@@ -856,6 +915,112 @@ var MigrationRunner = class {
856
915
  return reverted;
857
916
  }
858
917
  };
918
+ function quoteIdent(name, dialect) {
919
+ return dialect === "mysql" ? `\`${name.replace(/`/g, "``")}\`` : `"${name.replace(/"/g, '""')}"`;
920
+ }
921
+ function placeholder(index, dialect) {
922
+ return dialect === "postgresql" ? `$${index}` : "?";
923
+ }
924
+ var AsyncMigrationRunner = class {
925
+ constructor(driver, dialect) {
926
+ this.driver = driver;
927
+ this.dialect = dialect;
928
+ this.vt = quoteIdent(VERSION_TABLE, dialect);
929
+ }
930
+ driver;
931
+ dialect;
932
+ vt;
933
+ /** Create the version-tracking table if it does not exist. */
934
+ async ensureVersionTable() {
935
+ await this.driver.execute(
936
+ `CREATE TABLE IF NOT EXISTS ${this.vt} (revision ${this.textType()} PRIMARY KEY, applied_at ${this.textType()} NOT NULL, down_revision ${this.textType()} NOT NULL)`,
937
+ []
938
+ );
939
+ }
940
+ /** A portable "text" column type for the version table. */
941
+ textType() {
942
+ return this.dialect === "mysql" ? "VARCHAR(255)" : "TEXT";
943
+ }
944
+ /** The set of applied revision ids. */
945
+ async applied() {
946
+ await this.ensureVersionTable();
947
+ const { rows } = await this.driver.execute(`SELECT revision FROM ${this.vt}`, []);
948
+ return new Set(rows.map((r) => String(r.revision)));
949
+ }
950
+ async runOps(ops) {
951
+ for (const op of ops) {
952
+ for (const stmt of renderOperation(op, this.dialect)) {
953
+ const trimmed = stmt.trim();
954
+ if (trimmed.length === 0 || trimmed.startsWith("--")) continue;
955
+ await this.driver.execute(stmt, []);
956
+ }
957
+ }
958
+ }
959
+ async record(migration, appliedAt) {
960
+ const p = (i) => placeholder(i, this.dialect);
961
+ await this.driver.execute(
962
+ `INSERT INTO ${this.vt} (revision, applied_at, down_revision) VALUES (${p(1)}, ${p(2)}, ${p(3)})`,
963
+ [migration.revision, appliedAt, migration.downRevision.join(",")]
964
+ );
965
+ }
966
+ async forget(revision) {
967
+ await this.driver.execute(
968
+ `DELETE FROM ${this.vt} WHERE revision = ${placeholder(1, this.dialect)}`,
969
+ [revision]
970
+ );
971
+ }
972
+ /**
973
+ * Apply all pending migrations up to the head(s), in DAG order.
974
+ *
975
+ * @param migrations All known migrations.
976
+ * @param appliedAt Timestamp string to stamp on each applied revision.
977
+ * @returns The revision ids that were applied this run.
978
+ */
979
+ async upgrade(migrations, appliedAt) {
980
+ const done = await this.applied();
981
+ const ordered = topoOrder(migrations);
982
+ const ran = [];
983
+ for (const migration of ordered) {
984
+ if (done.has(migration.revision)) continue;
985
+ const op = new Op();
986
+ migration.up(op);
987
+ await this.runOps(op.operations);
988
+ await this.record(migration, appliedAt);
989
+ ran.push(migration.revision);
990
+ }
991
+ return ran;
992
+ }
993
+ /**
994
+ * Revert the last `steps` applied migrations (default 1), newest first.
995
+ *
996
+ * @param migrations All known migrations.
997
+ * @param steps How many applied revisions to roll back.
998
+ * @returns The revision ids that were reverted.
999
+ */
1000
+ async downgrade(migrations, steps = 1) {
1001
+ const done = await this.applied();
1002
+ const ordered = topoOrder(migrations).filter((m) => done.has(m.revision));
1003
+ const toRevert = ordered.slice(-steps).reverse();
1004
+ const reverted = [];
1005
+ for (const migration of toRevert) {
1006
+ const op = new Op();
1007
+ try {
1008
+ migration.down(op);
1009
+ } catch {
1010
+ op.operations.length = 0;
1011
+ }
1012
+ if (op.operations.length === 0) {
1013
+ const upOp = new Op();
1014
+ migration.up(upOp);
1015
+ op.operations.push(...invertAll(upOp.operations));
1016
+ }
1017
+ await this.runOps(op.operations);
1018
+ await this.forget(migration.revision);
1019
+ reverted.push(migration.revision);
1020
+ }
1021
+ return reverted;
1022
+ }
1023
+ };
859
1024
 
860
1025
  // src/migrations/replay.ts
861
1026
  function applyOperation(schema, op) {
@@ -1055,6 +1220,6 @@ function runMigrationCli(argv, config) {
1055
1220
  }
1056
1221
  }
1057
1222
 
1058
- export { CyclicMigrationGraph, IrreversibleMigration, MigrationRunner, Op, UnknownRevision, applyOperation, applyRenames, checkDrift, checkDriftPostgres, defineMigrationConfig, detectRenames, diffSchema, emptySchema, generateMigration, heads, introspectPostgres, introspectSqlite, invert, invertAll, makeRevisionId, reflectSchema, reflectTable, renderColumnDef, renderColumnType, renderDefault, renderOperation, replaySchema, runMigrationCli, sqliteAffinity, topoOrder };
1059
- //# sourceMappingURL=chunk-QMW4NKMH.js.map
1060
- //# sourceMappingURL=chunk-QMW4NKMH.js.map
1223
+ export { AsyncMigrationRunner, CyclicMigrationGraph, IrreversibleMigration, MigrationRunner, Op, UnknownRevision, applyOperation, applyRenames, checkDrift, checkDriftPostgres, defineMigrationConfig, detectRenames, diffSchema, emptySchema, generateMigration, heads, introspectPostgres, introspectSqlite, invert, invertAll, makeRevisionId, reflectSchema, reflectTable, renderColumnDef, renderColumnType, renderDefault, renderOperation, replaySchema, runMigrationCli, sqliteAffinity, topoOrder };
1224
+ //# sourceMappingURL=chunk-OP7FRDI5.js.map
1225
+ //# sourceMappingURL=chunk-OP7FRDI5.js.map