rake-db 2.30.8 → 2.31.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/index.mjs CHANGED
@@ -1,247 +1,8 @@
1
- import { colors, singleQuote, isRawSQL, escapeForMigration, ArrayColumn, toSnakeCase, toCamelCase, DomainColumn, toArray, EnumColumn, defaultSchemaConfig, snakeCaseKey, getColumnTypes, parseTableData, emptyObject, escapeString, tableDataMethods, setCurrentColumnName, consumeColumnName, Column, parseTableDataInput, UnknownColumn, setDefaultLanguage, deepCompare, raw, logParamToLogObject, createDbWithAdapter, getImportPath, pathToLog, emptyArray, exhaustive, codeToString, addCode, quoteObjectKey, pushTableDataCode, rawSqlToCode, primaryKeyInnerToCode, indexInnerToCode, excludeInnerToCode, constraintInnerToCode, referencesArgsToCode, backtickQuote, TimestampTZColumn, TimestampColumn, makeColumnsByType, RawSql, CustomTypeColumn, assignDbDataToColumn, PostgisGeographyPointColumn, makeColumnTypes, getStackTrace } from 'pqb';
1
+ import { singleQuote, makeColumnTypes, defaultSchemaConfig, isRawSQL, escapeForMigration, ArrayColumn, toSnakeCase, toCamelCase, DomainColumn, toArray, EnumColumn, snakeCaseKey, getColumnTypes, parseTableData, emptyObject, escapeString, tableDataMethods, setCurrentColumnName, consumeColumnName, Column, parseTableDataInput, UnknownColumn, setDefaultLanguage, deepCompare, raw, logParamToLogObject, createDbWithAdapter, getImportPath, pathToLog, emptyArray, colors, exhaustive, codeToString, addCode, quoteObjectKey, pushTableDataCode, rawSqlToCode, primaryKeyInnerToCode, indexInnerToCode, excludeInnerToCode, constraintInnerToCode, referencesArgsToCode, backtickQuote, TimestampTZColumn, TimestampColumn, makeColumnsByType, RawSql, CustomTypeColumn, assignDbDataToColumn, PostgisGeographyPointColumn, getStackTrace } from 'pqb/internal';
2
2
  import path, { join } from 'path';
3
3
  import { pathToFileURL, fileURLToPath } from 'node:url';
4
4
  import fs, { mkdir, writeFile, readdir, stat, readFile } from 'fs/promises';
5
-
6
- const ESC = "\x1B";
7
- const CSI = `${ESC}[`;
8
- const cursorShow = `${CSI}?25h`;
9
- const cursorHide = `${CSI}?25l`;
10
- const { stdin, stdout } = process;
11
- const visibleChars = (s) => s.replace(
12
- // eslint-disable-next-line no-control-regex
13
- /[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PRZcf-ntqry=><~]))/g,
14
- ""
15
- ).length;
16
- const clear = (text) => {
17
- const rows = text.split(/\r?\n/).reduce(
18
- (rows2, line) => rows2 + 1 + Math.floor(Math.max(visibleChars(line) - 1, 0) / stdout.columns),
19
- 0
20
- );
21
- let clear2 = "";
22
- for (let i = 0; i < rows; i++) {
23
- clear2 += `${CSI}2K`;
24
- if (i < rows - 1) {
25
- clear2 += `${CSI}${i < rows - 1 ? "1A" : "G"}`;
26
- }
27
- }
28
- return clear2;
29
- };
30
- const prompt = async ({
31
- render,
32
- onKeyPress,
33
- validate,
34
- value,
35
- cursor: showCursor
36
- }) => {
37
- stdin.resume();
38
- if (stdin.isTTY) stdin.setRawMode(true);
39
- stdin.setEncoding("utf-8");
40
- if (!showCursor) stdout.write(cursorHide);
41
- return new Promise((res) => {
42
- let prevText;
43
- const ctx = {
44
- value,
45
- submitted: false,
46
- render() {
47
- let text = (ctx.submitted ? colors.greenBold("\u2714") : colors.yellowBold("?")) + " " + render(ctx);
48
- if (ctx.submitted) text += "\n";
49
- stdout.write(prevText ? clear(prevText) + "\r" + text : text);
50
- prevText = text;
51
- },
52
- submit(value2) {
53
- if (value2 !== void 0) ctx.value = value2;
54
- if (ctx.value === void 0 || validate && !validate?.(ctx)) return;
55
- ctx.submitted = true;
56
- ctx.render();
57
- close();
58
- res(ctx.value);
59
- }
60
- };
61
- const close = () => {
62
- if (!showCursor) stdout.write(cursorShow);
63
- if (stdin.isTTY) stdin.setRawMode(false);
64
- stdin.off("data", keypress);
65
- stdin.pause();
66
- };
67
- const keypress = (s) => {
68
- if (s === "" || s === "") {
69
- close?.();
70
- process.exit(0);
71
- }
72
- if (s === "\r" || s === "\n" || s === "\r\n") {
73
- ctx.submit();
74
- } else {
75
- onKeyPress(ctx, s);
76
- }
77
- };
78
- stdin.on("data", keypress);
79
- ctx.render();
80
- });
81
- };
82
- const defaultActive = (s) => `${colors.blueBold("\u276F")} ${s}`;
83
- const defaultInactive = (s) => ` ${s}`;
84
- const promptSelect = ({
85
- message,
86
- options,
87
- active = defaultActive,
88
- inactive = defaultInactive
89
- }) => prompt({
90
- value: 0,
91
- render(ctx) {
92
- let text = `${message} ${colors.pale(
93
- "Use arrows or jk. Press enter to submit."
94
- )}
95
- `;
96
- for (let i = 0; i < options.length; i++) {
97
- text += (ctx.value === i ? active : inactive)(options[i]) + "\n";
98
- }
99
- return text;
100
- },
101
- onKeyPress(ctx, s) {
102
- ctx.value = s === "\x1B[H" ? 0 : s === "\x1B[F" ? options.length - 1 : s === "\x1B[A" || s === "k" ? ctx.value === 0 ? options.length - 1 : ctx.value - 1 : s === "\x1B[B" || s === "j" || s === " " ? ctx.value === options.length - 1 ? 0 : ctx.value + 1 : ctx.value;
103
- ctx.render();
104
- }
105
- });
106
- const promptConfirm = ({
107
- message
108
- }) => prompt({
109
- value: true,
110
- render(ctx) {
111
- return `${colors.bright(message)}
112
- ${ctx.submitted ? `> ${ctx.value ? colors.greenBold("Yes") : colors.yellowBold("No")}` : colors.pale(`> (Y/n)`)}
113
- `;
114
- },
115
- onKeyPress(ctx, s) {
116
- let ok;
117
- if (s === "y" || s === "Y") ok = true;
118
- else if (s === "n" || s === "N") ok = false;
119
- if (ok !== void 0) {
120
- ctx.submit(ok);
121
- }
122
- }
123
- });
124
- const promptText = ({
125
- message,
126
- default: def = "",
127
- password,
128
- min
129
- }) => {
130
- let showDefault = true;
131
- let x = 0;
132
- const renderValue = (ctx) => password ? "*".repeat(ctx.value.length) : ctx.value;
133
- return prompt({
134
- value: def,
135
- cursor: true,
136
- validate: (ctx) => !min || ctx.value.length >= min,
137
- render(ctx) {
138
- let text = `${colors.bright(message)}
139
- > ${ctx.submitted ? renderValue(ctx) : showDefault ? colors.pale(def) + "\b".repeat(def.length) : ctx.value}`;
140
- if (ctx.submitted) text += "\n";
141
- return text;
142
- },
143
- onKeyPress(ctx, s) {
144
- let value = showDefault ? "" : ctx.value;
145
- if (s === "\x1B[D" && x > 0) {
146
- x--;
147
- stdout.write("\b");
148
- } else if (s === "\x1B[C" && x < value.length) {
149
- stdout.write(value[x]);
150
- x++;
151
- }
152
- if (s !== "\x7F" && s !== "\x1B[3~" && !visibleChars(s)) return;
153
- if (showDefault) {
154
- showDefault = false;
155
- stdout.write(" ".repeat(def.length) + "\b".repeat(def.length));
156
- }
157
- const prev = value;
158
- const prevX = x;
159
- if (s === "\x7F") {
160
- if (x > 0) {
161
- value = value.slice(0, x - 1) + value.slice(x);
162
- x--;
163
- }
164
- } else if (s === "\x1B[3~") {
165
- if (x < value.length) {
166
- value = value.slice(0, x) + value.slice(x + 1);
167
- }
168
- } else {
169
- value = value.slice(0, x) + s + value.slice(x);
170
- x++;
171
- }
172
- ctx.value = value;
173
- const spaces = prev.length - value.length;
174
- stdout.write(
175
- "\b".repeat(prevX) + renderValue(ctx) + (spaces > 0 ? " ".repeat(spaces) + "\b".repeat(spaces) : "") + "\b".repeat(value.length - x)
176
- );
177
- }
178
- });
179
- };
180
-
181
- const getMaybeTransactionAdapter = (db) => "$getAdapter" in db ? db.$getAdapter() : db;
182
- const runSqlInSavePoint = async (db, sql, code) => {
183
- const adapter = getMaybeTransactionAdapter(db);
184
- try {
185
- await adapter.query(
186
- adapter.isInTransaction() ? `SAVEPOINT s; ${sql}; RELEASE SAVEPOINT s` : sql
187
- );
188
- return "done";
189
- } catch (err) {
190
- if (err.code === code) {
191
- if (adapter.isInTransaction()) {
192
- await adapter.query(`ROLLBACK TO SAVEPOINT s`);
193
- }
194
- return "already";
195
- }
196
- throw err;
197
- }
198
- };
199
-
200
- class CreateOrDropError extends Error {
201
- constructor(message, status, cause) {
202
- super(message);
203
- this.status = status;
204
- this.cause = cause;
205
- }
206
- }
207
- const createDatabase = async (db, {
208
- database,
209
- owner
210
- }) => {
211
- return createOrDrop(
212
- db,
213
- `CREATE DATABASE "${database}"${owner ? ` OWNER "${owner}"` : ""}`
214
- );
215
- };
216
- const dropDatabase = async (db, { database }) => {
217
- return createOrDrop(db, `DROP DATABASE "${database}"`);
218
- };
219
- const createOrDrop = async (db, sql) => {
220
- try {
221
- const adapter = getMaybeTransactionAdapter(db);
222
- await adapter.query(sql);
223
- return "done";
224
- } catch (error) {
225
- const err = error;
226
- if (typeof err.message === "string" && err.message.includes("sslmode=require")) {
227
- throw new CreateOrDropError("SSL required", "ssl-required", err);
228
- }
229
- if (err.code === "42P04" || err.code === "3D000") {
230
- return "already";
231
- }
232
- if (err.code === "42501") {
233
- throw new CreateOrDropError("Insufficient privilege", "forbidden", err);
234
- }
235
- if (typeof err.message === "string" && err.message.includes("password authentication failed")) {
236
- throw new CreateOrDropError("Authentication failed", "auth-failed", err);
237
- }
238
- throw err;
239
- }
240
- };
241
- const createSchema$1 = async (db, sql) => runSqlInSavePoint(db, `CREATE SCHEMA ${sql}`, "42P06");
242
- const dropSchema = async (db, sql) => runSqlInSavePoint(db, `DROP SCHEMA ${sql}`, "3F000");
243
- const createTable$1 = async (db, sql) => runSqlInSavePoint(db, `CREATE TABLE ${sql}`, "42P07");
244
- const dropTable = async (db, sql) => runSqlInSavePoint(db, `DROP TABLE ${sql}`, "42P01");
5
+ import path$1 from 'node:path';
245
6
 
246
7
  const RAKE_DB_LOCK_KEY = "8582141715823621641";
247
8
  const getFirstWordAndRest = (input) => {
@@ -330,10 +91,13 @@ const getCliParam = (args, name) => {
330
91
  return;
331
92
  };
332
93
 
333
- const makeChange = (config) => (fn) => {
334
- const change = { fn, config };
335
- pushChange(change);
336
- return change;
94
+ const createMigrationChangeFn = (config) => {
95
+ const conf = config.columnTypes ? config : { columnTypes: makeColumnTypes(defaultSchemaConfig) };
96
+ return (fn) => {
97
+ const change = { fn, config: conf };
98
+ pushChange(change);
99
+ return change;
100
+ };
337
101
  };
338
102
  let currentChanges = [];
339
103
  const clearChanges = () => {
@@ -750,7 +514,7 @@ class RakeDbError extends Error {
750
514
  class NoPrimaryKey extends RakeDbError {
751
515
  }
752
516
 
753
- const createTable = async (migration, up, tableName, first, second, third) => {
517
+ const createTable$1 = async (migration, up, tableName, first, second, third) => {
754
518
  let options;
755
519
  let fn;
756
520
  let dataFn;
@@ -1979,27 +1743,37 @@ const createMigrationInterface = (tx, up, config) => {
1979
1743
  );
1980
1744
  };
1981
1745
  Object.assign(adapter, { silentQuery: query, silentArrays: arrays });
1982
- const db = createDbWithAdapter({
1983
- adapter,
1984
- columnTypes: config.columnTypes
1985
- });
1986
- const { prototype: proto } = Migration;
1987
- for (const key of Object.getOwnPropertyNames(proto)) {
1988
- db[key] = proto[key];
1989
- }
1990
- return Object.assign(db, {
1746
+ const dbPerColumnTypes = /* @__PURE__ */ new Map();
1747
+ return {
1991
1748
  adapter,
1992
- log,
1993
- up,
1994
- options: config
1995
- });
1749
+ getDb(columnTypes) {
1750
+ let db = dbPerColumnTypes.get(columnTypes);
1751
+ if (!db) {
1752
+ db = createDbWithAdapter({
1753
+ adapter,
1754
+ columnTypes
1755
+ });
1756
+ const { prototype: proto } = Migration;
1757
+ for (const key of Object.getOwnPropertyNames(proto)) {
1758
+ db[key] = proto[key];
1759
+ }
1760
+ return Object.assign(db, {
1761
+ adapter,
1762
+ log,
1763
+ up,
1764
+ options: config
1765
+ });
1766
+ }
1767
+ return db;
1768
+ }
1769
+ };
1996
1770
  };
1997
1771
  class Migration {
1998
1772
  createTable(tableName, first, second, third) {
1999
- return createTable(this, this.up, tableName, first, second, third);
1773
+ return createTable$1(this, this.up, tableName, first, second, third);
2000
1774
  }
2001
1775
  dropTable(tableName, first, second, third) {
2002
- return createTable(this, !this.up, tableName, first, second, third);
1776
+ return createTable$1(this, !this.up, tableName, first, second, third);
2003
1777
  }
2004
1778
  changeTable(tableName, cbOrOptions, cb) {
2005
1779
  const [fn, options] = typeof cbOrOptions === "function" ? [cbOrOptions, {}] : [cb, cbOrOptions];
@@ -2332,7 +2106,7 @@ class Migration {
2332
2106
  * @param schemaName - name of the schema
2333
2107
  */
2334
2108
  createSchema(schemaName) {
2335
- return createSchema(this, this.up, schemaName);
2109
+ return createSchema$1(this, this.up, schemaName);
2336
2110
  }
2337
2111
  /**
2338
2112
  * Renames a database schema, renames it backwards on roll back.
@@ -2359,7 +2133,7 @@ class Migration {
2359
2133
  * @param schemaName - name of the schema
2360
2134
  */
2361
2135
  dropSchema(schemaName) {
2362
- return createSchema(this, !this.up, schemaName);
2136
+ return createSchema$1(this, !this.up, schemaName);
2363
2137
  }
2364
2138
  /**
2365
2139
  * `createExtension` creates a database extension, and removes it on rollback.
@@ -2920,7 +2694,7 @@ const addCheck = (migration, up, tableName, check) => {
2920
2694
  ...t.add(t.check(check))
2921
2695
  }));
2922
2696
  };
2923
- const createSchema = async (migration, up, name) => {
2697
+ const createSchema$1 = async (migration, up, name) => {
2924
2698
  const ast = {
2925
2699
  type: "schema",
2926
2700
  action: up ? "create" : "drop",
@@ -3406,7 +3180,7 @@ const getMigrations = async (ctx, config, up, allowDuplicates, getVersion = getM
3406
3180
  function getMigrationsFromConfig(config, allowDuplicates, getVersion = getMigrationVersionOrThrow) {
3407
3181
  const result = [];
3408
3182
  const versions = {};
3409
- const { migrations, basePath } = config;
3183
+ const { migrations } = config;
3410
3184
  for (const key in migrations) {
3411
3185
  const version = getVersion(config, path.basename(key));
3412
3186
  if (versions[version] && !allowDuplicates) {
@@ -3416,7 +3190,7 @@ function getMigrationsFromConfig(config, allowDuplicates, getVersion = getMigrat
3416
3190
  }
3417
3191
  versions[version] = key;
3418
3192
  result.push({
3419
- path: path.resolve(basePath, key),
3193
+ path: key,
3420
3194
  version,
3421
3195
  load: migrations[key]
3422
3196
  });
@@ -3466,7 +3240,7 @@ async function getMigrationsFromFiles(config, allowDuplicates, getVersion = getM
3466
3240
  }
3467
3241
  data.renameTo = {
3468
3242
  to: config.migrationId,
3469
- map: () => renameMigrationsMap(config, file.name)
3243
+ map: () => renameMigrationsMap(migrationsPath, file.name)
3470
3244
  };
3471
3245
  return data;
3472
3246
  } else {
@@ -3505,8 +3279,8 @@ Run \`**db command** rebase\` to reorganize files with duplicated versions.`
3505
3279
  result.migrations.sort(sortMigrationsAsc);
3506
3280
  return result;
3507
3281
  }
3508
- const renameMigrationsMap = async (config, fileName) => {
3509
- const filePath = path.join(config.migrationsPath, fileName);
3282
+ const renameMigrationsMap = async (migrationsPath, fileName) => {
3283
+ const filePath = path.join(migrationsPath, fileName);
3510
3284
  const json = await fs.readFile(filePath, "utf-8");
3511
3285
  let data;
3512
3286
  try {
@@ -3557,10 +3331,75 @@ function getDigitsPrefix(name) {
3557
3331
  return value;
3558
3332
  }
3559
3333
 
3560
- const saveMigratedVersion = async (db, version, name, config) => {
3561
- await db.silentArrays(
3562
- `INSERT INTO ${migrationsSchemaTableSql(
3563
- db,
3334
+ const getMaybeTransactionAdapter = (db) => "$getAdapter" in db ? db.$getAdapter() : db;
3335
+ const runSqlInSavePoint = async (db, sql, code) => {
3336
+ const adapter = getMaybeTransactionAdapter(db);
3337
+ try {
3338
+ await adapter.query(
3339
+ adapter.isInTransaction() ? `SAVEPOINT s; ${sql}; RELEASE SAVEPOINT s` : sql
3340
+ );
3341
+ return "done";
3342
+ } catch (err) {
3343
+ if (err.code === code) {
3344
+ if (adapter.isInTransaction()) {
3345
+ await adapter.query(`ROLLBACK TO SAVEPOINT s`);
3346
+ }
3347
+ return "already";
3348
+ }
3349
+ throw err;
3350
+ }
3351
+ };
3352
+
3353
+ class CreateOrDropError extends Error {
3354
+ constructor(message, status, cause) {
3355
+ super(message);
3356
+ this.status = status;
3357
+ this.cause = cause;
3358
+ }
3359
+ }
3360
+ const createDatabase = async (db, {
3361
+ database,
3362
+ owner
3363
+ }) => {
3364
+ return createOrDrop(
3365
+ db,
3366
+ `CREATE DATABASE "${database}"${owner ? ` OWNER "${owner}"` : ""}`
3367
+ );
3368
+ };
3369
+ const dropDatabase = async (db, { database }) => {
3370
+ return createOrDrop(db, `DROP DATABASE "${database}"`);
3371
+ };
3372
+ const createOrDrop = async (db, sql) => {
3373
+ try {
3374
+ const adapter = getMaybeTransactionAdapter(db);
3375
+ await adapter.query(sql);
3376
+ return "done";
3377
+ } catch (error) {
3378
+ const err = error;
3379
+ if (typeof err.message === "string" && err.message.includes("sslmode=require")) {
3380
+ throw new CreateOrDropError("SSL required", "ssl-required", err);
3381
+ }
3382
+ if (err.code === "42P04" || err.code === "3D000") {
3383
+ return "already";
3384
+ }
3385
+ if (err.code === "42501") {
3386
+ throw new CreateOrDropError("Insufficient privilege", "forbidden", err);
3387
+ }
3388
+ if (typeof err.message === "string" && err.message.includes("password authentication failed")) {
3389
+ throw new CreateOrDropError("Authentication failed", "auth-failed", err);
3390
+ }
3391
+ throw err;
3392
+ }
3393
+ };
3394
+ const createSchema = async (db, sql) => runSqlInSavePoint(db, `CREATE SCHEMA ${sql}`, "42P06");
3395
+ const dropSchema = async (db, sql) => runSqlInSavePoint(db, `DROP SCHEMA ${sql}`, "3F000");
3396
+ const createTable = async (db, sql) => runSqlInSavePoint(db, `CREATE TABLE ${sql}`, "42P07");
3397
+ const dropTable = async (db, sql) => runSqlInSavePoint(db, `DROP TABLE ${sql}`, "42P01");
3398
+
3399
+ const saveMigratedVersion = async (db, version, name, config) => {
3400
+ await db.silentArrays(
3401
+ `INSERT INTO ${migrationsSchemaTableSql(
3402
+ db,
3564
3403
  config
3565
3404
  )}(version, name) VALUES ($1, $2)`,
3566
3405
  [version, name]
@@ -3570,12 +3409,12 @@ const createMigrationsSchemaAndTable = async (db, config) => {
3570
3409
  const adapter = getMaybeTransactionAdapter(db);
3571
3410
  const { schema, table } = getMigrationsSchemaAndTable(adapter, config);
3572
3411
  if (schema) {
3573
- const res2 = await createSchema$1(db, schema);
3412
+ const res2 = await createSchema(db, schema);
3574
3413
  if (res2 === "done") {
3575
3414
  config.logger?.log(`Created schema "${schema}"`);
3576
3415
  }
3577
3416
  }
3578
- const res = await createTable$1(
3417
+ const res = await createTable(
3579
3418
  db,
3580
3419
  `${schema ? `"${schema}"."${table}"` : `"${table}"`} (version TEXT NOT NULL, name TEXT NOT NULL)`
3581
3420
  );
@@ -3600,53 +3439,62 @@ const deleteMigratedVersion = async (adapter, version, name, config) => {
3600
3439
  class NoMigrationsTableError extends Error {
3601
3440
  }
3602
3441
  const getMigratedVersionsMap = async (ctx, adapter, config, renameTo) => {
3442
+ const table = migrationsSchemaTableSql(adapter, config);
3443
+ const inTransaction = "isInTransaction" in adapter && adapter.isInTransaction();
3444
+ let result;
3603
3445
  try {
3604
- const table = migrationsSchemaTableSql(adapter, config);
3605
- const result = await adapter.arrays(
3446
+ if (inTransaction) {
3447
+ await adapter.query(`SAVEPOINT check_migrations_table`);
3448
+ }
3449
+ result = await adapter.arrays(
3606
3450
  `SELECT * FROM ${table} ORDER BY version`
3607
3451
  );
3608
- if (!result.fields[1]) {
3609
- const { migrations } = await getMigrations(ctx, config, true);
3610
- const map = {};
3611
- for (const item of migrations) {
3612
- const name = path.basename(item.path);
3613
- map[item.version] = name.slice(getDigitsPrefix(name).length + 1);
3614
- }
3615
- for (const row of result.rows) {
3616
- const [version] = row;
3617
- const name = map[version];
3618
- if (!name) {
3619
- throw new Error(
3620
- `Migration for version ${version} is stored in db but is not found among available migrations`
3621
- );
3622
- }
3623
- row[1] = name;
3624
- }
3625
- await adapter.arrays(`ALTER TABLE ${table} ADD COLUMN name TEXT`);
3626
- await Promise.all(
3627
- result.rows.map(
3628
- ([version, name]) => adapter.arrays(`UPDATE ${table} SET name = $2 WHERE version = $1`, [
3629
- version,
3630
- name
3631
- ])
3632
- )
3633
- );
3634
- await adapter.arrays(
3635
- `ALTER TABLE ${table} ALTER COLUMN name SET NOT NULL`
3636
- );
3452
+ if (inTransaction) {
3453
+ await adapter.query(`RELEASE SAVEPOINT check_migrations_table`);
3637
3454
  }
3638
- let versions = Object.fromEntries(result.rows);
3639
- if (renameTo) {
3640
- versions = await renameMigrations(config, adapter, versions, renameTo);
3641
- }
3642
- return { map: versions, sequence: result.rows.map((row) => +row[0]) };
3643
3455
  } catch (err) {
3644
3456
  if (err.code === "42P01") {
3457
+ if (inTransaction) {
3458
+ await adapter.query(`ROLLBACK TO SAVEPOINT check_migrations_table`);
3459
+ }
3645
3460
  throw new NoMigrationsTableError();
3646
3461
  } else {
3647
3462
  throw err;
3648
3463
  }
3649
3464
  }
3465
+ if (!result.fields[1]) {
3466
+ const { migrations } = await getMigrations(ctx, config, true);
3467
+ const map = {};
3468
+ for (const item of migrations) {
3469
+ const name = path.basename(item.path);
3470
+ map[item.version] = name.slice(getDigitsPrefix(name).length + 1);
3471
+ }
3472
+ for (const row of result.rows) {
3473
+ const [version] = row;
3474
+ const name = map[version];
3475
+ if (!name) {
3476
+ throw new Error(
3477
+ `Migration for version ${version} is stored in db but is not found among available migrations`
3478
+ );
3479
+ }
3480
+ row[1] = name;
3481
+ }
3482
+ await adapter.arrays(`ALTER TABLE ${table} ADD COLUMN name TEXT`);
3483
+ await Promise.all(
3484
+ result.rows.map(
3485
+ ([version, name]) => adapter.arrays(`UPDATE ${table} SET name = $2 WHERE version = $1`, [
3486
+ version,
3487
+ name
3488
+ ])
3489
+ )
3490
+ );
3491
+ await adapter.arrays(`ALTER TABLE ${table} ALTER COLUMN name SET NOT NULL`);
3492
+ }
3493
+ let versions = Object.fromEntries(result.rows);
3494
+ if (renameTo) {
3495
+ versions = await renameMigrations(config, adapter, versions, renameTo);
3496
+ }
3497
+ return { map: versions, sequence: result.rows.map((row) => +row[0]) };
3650
3498
  };
3651
3499
  async function renameMigrations(config, trx, versions, renameTo) {
3652
3500
  let first;
@@ -3677,11 +3525,35 @@ async function renameMigrations(config, trx, versions, renameTo) {
3677
3525
  return updatedVersions;
3678
3526
  }
3679
3527
 
3528
+ const migrateConfigDefaults = {
3529
+ migrationId: { serial: 4 },
3530
+ migrationsTable: "schemaMigrations",
3531
+ transaction: "single"
3532
+ };
3533
+ const handleConfigLogger = (config) => {
3534
+ return config.log === true ? config.logger || console : config.log === false ? void 0 : config.logger;
3535
+ };
3536
+ const processMigrateConfig = (config) => {
3537
+ let migrationsPath;
3538
+ if (!("migrations" in config)) {
3539
+ migrationsPath = config.migrationsPath ? config.migrationsPath : path$1.join("src", "db", "migrations");
3540
+ if (config.basePath && !path$1.isAbsolute(migrationsPath)) {
3541
+ migrationsPath = path$1.resolve(config.basePath, migrationsPath);
3542
+ }
3543
+ }
3544
+ return {
3545
+ ...migrateConfigDefaults,
3546
+ ...config,
3547
+ migrationsPath,
3548
+ logger: handleConfigLogger(config)
3549
+ };
3550
+ };
3680
3551
  const transactionIfSingle = (adapter, config, fn) => {
3681
3552
  return config.transaction === "single" ? transaction(adapter, config, fn) : fn(adapter);
3682
3553
  };
3683
3554
  function makeMigrateFn(up, defaultCount, fn) {
3684
- return async (db, config, params) => {
3555
+ return async (db, publicConfig, params) => {
3556
+ const config = processMigrateConfig(publicConfig);
3685
3557
  const ctx = params?.ctx || {};
3686
3558
  const set = await getMigrations(ctx, config, up);
3687
3559
  const count = params?.count ?? defaultCount;
@@ -3724,7 +3596,16 @@ function makeMigrateFn(up, defaultCount, fn) {
3724
3596
  const migrate = makeMigrateFn(
3725
3597
  true,
3726
3598
  Infinity,
3727
- (trx, config, set, versions, count, force) => migrateOrRollback(trx, config, set, versions, count, true, false, force)
3599
+ (trx, config, set, versions, count, force) => migrateOrRollback(
3600
+ trx,
3601
+ config,
3602
+ set,
3603
+ versions,
3604
+ count,
3605
+ true,
3606
+ false,
3607
+ force
3608
+ )
3728
3609
  );
3729
3610
  const migrateAndClose = async (db, config, params) => {
3730
3611
  const adapter = getMaybeTransactionAdapter(db);
@@ -3732,26 +3613,46 @@ const migrateAndClose = async (db, config, params) => {
3732
3613
  await adapter.close();
3733
3614
  };
3734
3615
  async function runMigration(db, ...args) {
3735
- const [config, migration] = args.length === 1 ? [{}, args[0]] : [args[0], args[1]];
3616
+ const [rawConfig, migration] = args.length === 1 ? [{}, args[0]] : [args[0], args[1]];
3617
+ const config = {
3618
+ ...rawConfig,
3619
+ logger: handleConfigLogger(rawConfig)
3620
+ };
3736
3621
  const adapter = getMaybeTransactionAdapter(db);
3737
3622
  await transaction(adapter, config, async (trx) => {
3738
3623
  clearChanges();
3739
3624
  const changes = await getChanges({ load: migration });
3740
- const config2 = changes[0]?.config;
3741
- await applyMigration(trx, true, changes, config2);
3625
+ await applyMigration(trx, true, changes, config);
3742
3626
  });
3743
3627
  }
3744
3628
  const rollback = makeMigrateFn(
3745
3629
  false,
3746
3630
  1,
3747
- (trx, config, set, versions, count, force) => migrateOrRollback(trx, config, set, versions, count, false, false, force)
3631
+ (trx, config, set, versions, count, force) => migrateOrRollback(
3632
+ trx,
3633
+ config,
3634
+ set,
3635
+ versions,
3636
+ count,
3637
+ false,
3638
+ false,
3639
+ force
3640
+ )
3748
3641
  );
3749
3642
  const redo = makeMigrateFn(
3750
3643
  true,
3751
3644
  1,
3752
3645
  async (trx, config, set, versions, count, force) => {
3753
3646
  set.migrations.reverse();
3754
- await migrateOrRollback(trx, config, set, versions, count, false, true);
3647
+ await migrateOrRollback(
3648
+ trx,
3649
+ config,
3650
+ set,
3651
+ versions,
3652
+ count,
3653
+ false,
3654
+ true
3655
+ );
3755
3656
  set.migrations.reverse();
3756
3657
  return migrateOrRollback(
3757
3658
  trx,
@@ -3822,7 +3723,7 @@ const migrateOrRollback = async (trx, config, set, versions, count, up, redo2, f
3822
3723
  await changeMigratedVersion(adapter, up, file, config);
3823
3724
  (migrations ?? (migrations = [])).push(file);
3824
3725
  if (up) {
3825
- const name = path.basename(file.path);
3726
+ const name = path$1.basename(file.path);
3826
3727
  versionsMap[file.version] = name;
3827
3728
  sequence.push(+file.version);
3828
3729
  } else {
@@ -3869,7 +3770,7 @@ const checkMigrationOrder = (config, set, { sequence, map }, force) => {
3869
3770
  if (version > last || map[file.version]) continue;
3870
3771
  if (!force) {
3871
3772
  throw new Error(
3872
- `Cannot migrate ${path.basename(
3773
+ `Cannot migrate ${path$1.basename(
3873
3774
  file.path
3874
3775
  )} because the higher position ${map[versionToString(config, last)]} was already migrated.
3875
3776
  Run \`**db command** up force\` to rollback the above migrations and migrate all`
@@ -3905,151 +3806,342 @@ const runMigrationInOwnTransaction = (adapter, up, changes, config) => {
3905
3806
  );
3906
3807
  };
3907
3808
  const applyMigration = async (trx, up, changes, config) => {
3908
- const db = createMigrationInterface(trx, up, config);
3809
+ const { adapter, getDb: getDb2 } = createMigrationInterface(trx, up, config);
3909
3810
  if (changes.length) {
3910
3811
  const from = up ? 0 : changes.length - 1;
3911
3812
  const to = up ? changes.length : -1;
3912
3813
  const step = up ? 1 : -1;
3913
3814
  for (let i = from; i !== to; i += step) {
3914
- await changes[i].fn(db, up);
3815
+ const change = changes[i];
3816
+ const db = getDb2(change.config.columnTypes);
3817
+ await change.fn(db, up);
3915
3818
  }
3916
3819
  }
3917
- return db.adapter;
3820
+ return adapter;
3918
3821
  };
3919
3822
  const changeMigratedVersion = async (adapter, up, file, config) => {
3920
3823
  await (up ? saveMigratedVersion : deleteMigratedVersion)(
3921
3824
  adapter,
3922
3825
  file.version,
3923
- path.basename(file.path).slice(file.version.length + 1),
3826
+ path$1.basename(file.path).slice(file.version.length + 1),
3924
3827
  config
3925
3828
  );
3926
3829
  };
3927
3830
 
3928
- const runRecurrentMigrations = async (adapters, config) => {
3929
- let dbs;
3930
- let files = 0;
3931
- await readdirRecursive(config.recurrentPath, async (path) => {
3932
- files++;
3933
- dbs ?? (dbs = adapters.map((adapter) => createDbWithAdapter({ adapter })));
3934
- const sql = await readFile(path, "utf-8");
3935
- await Promise.all(
3936
- dbs.map(async (db) => {
3937
- await db.adapter.arrays(sql);
3938
- })
3939
- );
3940
- });
3941
- if (files > 0) {
3942
- config.logger?.log(
3943
- `Applied ${files} recurrent migration file${files > 1 ? "s" : ""}`
3831
+ const rakeDbConfigDefaults = {
3832
+ ...migrateConfigDefaults,
3833
+ schemaConfig: defaultSchemaConfig,
3834
+ snakeCase: false,
3835
+ commands: {},
3836
+ log: true,
3837
+ logger: console,
3838
+ import() {
3839
+ throw new Error(
3840
+ "Add `import: (path) => import(path),` setting to `rakeDb` config"
3944
3841
  );
3945
3842
  }
3946
3843
  };
3947
- const readdirRecursive = async (dirPath, cb) => {
3948
- const list = await readdir(dirPath).catch((err) => {
3949
- if (err.code !== "ENOENT") throw err;
3950
- return;
3951
- });
3952
- if (!list) return;
3953
- await Promise.all(
3954
- list.map(async (item) => {
3955
- const path = join(dirPath, item);
3956
- const info = await stat(path);
3957
- if (info.isDirectory()) {
3958
- await readdirRecursive(path, cb);
3959
- } else if (info.isFile() && path.endsWith(".sql")) {
3960
- await cb(path);
3961
- }
3962
- })
3963
- );
3964
- };
3965
3844
 
3966
- const createDatabaseCommand = (adapters, config, dontClose) => createOrDropDatabase("create", adapters, config, dontClose);
3967
- const dropDatabaseCommand = (adapters, config) => createOrDropDatabase("drop", adapters, config);
3968
- const createOrDropDatabase = async (action, adapters, config, dontClose) => {
3969
- const fn = action === "create" ? createDatabase : dropDatabase;
3970
- for (const adapter of adapters) {
3971
- const database = adapter.getDatabase();
3972
- const owner = adapter.getUser();
3973
- const res = await run(
3974
- adapter.reconfigure({ database: "postgres" }),
3975
- config,
3976
- {
3977
- command: (adapter2) => fn(adapter2, {
3978
- database,
3979
- owner
3980
- }),
3981
- doneMessage: () => `Database ${database} successfully ${action === "create" ? "created" : "dropped"}`,
3982
- alreadyMessage: () => `Database ${database} ${action === "create" ? "already exists" : "does not exist"}`,
3983
- deniedMessage: () => `Permission denied to ${action} database.`,
3984
- askAdminCreds: () => askForAdminCredentials(action === "create")
3985
- }
3986
- );
3987
- if (!res) continue;
3988
- if (action === "create") {
3989
- await adapter.transaction(async (tx) => {
3990
- const schema = tx.getSchema();
3991
- if (schema) {
3992
- const quoted = `"${typeof schema === "function" ? schema() : schema}"`;
3993
- const res2 = await createSchema$1(tx, quoted);
3994
- if (res2 === "done") {
3995
- config.logger?.log(`Created schema ${quoted}`);
3996
- }
3997
- }
3998
- await createMigrationsSchemaAndTable(tx, config);
3999
- });
4000
- if (!dontClose) {
4001
- await adapter.close();
4002
- }
4003
- }
4004
- }
4005
- };
4006
- const resetDatabaseCommand = async (adapters, config) => {
4007
- await createOrDropDatabase("drop", adapters, config);
4008
- await createOrDropDatabase("create", adapters, config, true);
4009
- for (const adapter of adapters) {
4010
- await migrate(adapter, config);
4011
- }
4012
- if (config.recurrentPath) {
4013
- await runRecurrentMigrations(adapters, config);
4014
- }
4015
- await Promise.all(adapters.map((adapter) => adapter.close()));
4016
- };
4017
- const run = async (adapter, config, params) => {
4018
- try {
4019
- const res = await params.command(adapter);
4020
- config.logger?.log(
4021
- res === "done" ? params.doneMessage() : params.alreadyMessage()
4022
- );
4023
- await adapter.close();
4024
- return true;
4025
- } catch (err) {
4026
- if (err instanceof CreateOrDropError) {
4027
- if (err.status === "ssl-required") {
4028
- config.logger?.log(
4029
- "SSL is required: append ?ssl=true to the database url string"
4030
- );
4031
- return false;
4032
- }
4033
- if (err.status === "forbidden" || err.status === "auth-failed") {
4034
- let message = params.deniedMessage();
4035
- const host = adapter.getHost();
4036
- const isLocal = host === "localhost";
4037
- if (!isLocal) {
4038
- message += `
4039
- Don't use this command for database service providers, only for a local db.`;
4040
- }
4041
- config.logger?.log(message);
4042
- const creds = await params.askAdminCreds();
4043
- if (!creds) return false;
4044
- return run(adapter.reconfigure(creds), config, params);
4045
- }
3845
+ const ESC = "\x1B";
3846
+ const CSI = `${ESC}[`;
3847
+ const cursorShow = `${CSI}?25h`;
3848
+ const cursorHide = `${CSI}?25l`;
3849
+ const { stdin, stdout } = process;
3850
+ const visibleChars = (s) => s.replace(
3851
+ // eslint-disable-next-line no-control-regex
3852
+ /[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PRZcf-ntqry=><~]))/g,
3853
+ ""
3854
+ ).length;
3855
+ const clear = (text) => {
3856
+ const rows = text.split(/\r?\n/).reduce(
3857
+ (rows2, line) => rows2 + 1 + Math.floor(Math.max(visibleChars(line) - 1, 0) / stdout.columns),
3858
+ 0
3859
+ );
3860
+ let clear2 = "";
3861
+ for (let i = 0; i < rows; i++) {
3862
+ clear2 += `${CSI}2K`;
3863
+ if (i < rows - 1) {
3864
+ clear2 += `${CSI}${i < rows - 1 ? "1A" : "G"}`;
4046
3865
  }
4047
- throw err;
4048
3866
  }
3867
+ return clear2;
4049
3868
  };
4050
- const askForAdminCredentials = async (create) => {
4051
- const ok = await promptConfirm({
4052
- message: `Would you like to share admin credentials to ${create ? "create" : "drop"} a database?`
3869
+ const prompt = async ({
3870
+ render,
3871
+ onKeyPress,
3872
+ validate,
3873
+ value,
3874
+ cursor: showCursor
3875
+ }) => {
3876
+ stdin.resume();
3877
+ if (stdin.isTTY) stdin.setRawMode(true);
3878
+ stdin.setEncoding("utf-8");
3879
+ if (!showCursor) stdout.write(cursorHide);
3880
+ return new Promise((res) => {
3881
+ let prevText;
3882
+ const ctx = {
3883
+ value,
3884
+ submitted: false,
3885
+ render() {
3886
+ let text = (ctx.submitted ? colors.greenBold("\u2714") : colors.yellowBold("?")) + " " + render(ctx);
3887
+ if (ctx.submitted) text += "\n";
3888
+ stdout.write(prevText ? clear(prevText) + "\r" + text : text);
3889
+ prevText = text;
3890
+ },
3891
+ submit(value2) {
3892
+ if (value2 !== void 0) ctx.value = value2;
3893
+ if (ctx.value === void 0 || validate && !validate?.(ctx)) return;
3894
+ ctx.submitted = true;
3895
+ ctx.render();
3896
+ close();
3897
+ res(ctx.value);
3898
+ }
3899
+ };
3900
+ const close = () => {
3901
+ if (!showCursor) stdout.write(cursorShow);
3902
+ if (stdin.isTTY) stdin.setRawMode(false);
3903
+ stdin.off("data", keypress);
3904
+ stdin.pause();
3905
+ };
3906
+ const keypress = (s) => {
3907
+ if (s === "" || s === "") {
3908
+ close?.();
3909
+ process.exit(0);
3910
+ }
3911
+ if (s === "\r" || s === "\n" || s === "\r\n") {
3912
+ ctx.submit();
3913
+ } else {
3914
+ onKeyPress(ctx, s);
3915
+ }
3916
+ };
3917
+ stdin.on("data", keypress);
3918
+ ctx.render();
3919
+ });
3920
+ };
3921
+ const defaultActive = (s) => `${colors.blueBold("\u276F")} ${s}`;
3922
+ const defaultInactive = (s) => ` ${s}`;
3923
+ const promptSelect = ({
3924
+ message,
3925
+ options,
3926
+ active = defaultActive,
3927
+ inactive = defaultInactive
3928
+ }) => prompt({
3929
+ value: 0,
3930
+ render(ctx) {
3931
+ let text = `${message} ${colors.pale(
3932
+ "Use arrows or jk. Press enter to submit."
3933
+ )}
3934
+ `;
3935
+ for (let i = 0; i < options.length; i++) {
3936
+ text += (ctx.value === i ? active : inactive)(options[i]) + "\n";
3937
+ }
3938
+ return text;
3939
+ },
3940
+ onKeyPress(ctx, s) {
3941
+ ctx.value = s === "\x1B[H" ? 0 : s === "\x1B[F" ? options.length - 1 : s === "\x1B[A" || s === "k" ? ctx.value === 0 ? options.length - 1 : ctx.value - 1 : s === "\x1B[B" || s === "j" || s === " " ? ctx.value === options.length - 1 ? 0 : ctx.value + 1 : ctx.value;
3942
+ ctx.render();
3943
+ }
3944
+ });
3945
+ const promptConfirm = ({
3946
+ message
3947
+ }) => prompt({
3948
+ value: true,
3949
+ render(ctx) {
3950
+ return `${colors.bright(message)}
3951
+ ${ctx.submitted ? `> ${ctx.value ? colors.greenBold("Yes") : colors.yellowBold("No")}` : colors.pale(`> (Y/n)`)}
3952
+ `;
3953
+ },
3954
+ onKeyPress(ctx, s) {
3955
+ let ok;
3956
+ if (s === "y" || s === "Y") ok = true;
3957
+ else if (s === "n" || s === "N") ok = false;
3958
+ if (ok !== void 0) {
3959
+ ctx.submit(ok);
3960
+ }
3961
+ }
3962
+ });
3963
+ const promptText = ({
3964
+ message,
3965
+ default: def = "",
3966
+ password,
3967
+ min
3968
+ }) => {
3969
+ let showDefault = true;
3970
+ let x = 0;
3971
+ const renderValue = (ctx) => password ? "*".repeat(ctx.value.length) : ctx.value;
3972
+ return prompt({
3973
+ value: def,
3974
+ cursor: true,
3975
+ validate: (ctx) => !min || ctx.value.length >= min,
3976
+ render(ctx) {
3977
+ let text = `${colors.bright(message)}
3978
+ > ${ctx.submitted ? renderValue(ctx) : showDefault ? colors.pale(def) + "\b".repeat(def.length) : ctx.value}`;
3979
+ if (ctx.submitted) text += "\n";
3980
+ return text;
3981
+ },
3982
+ onKeyPress(ctx, s) {
3983
+ let value = showDefault ? "" : ctx.value;
3984
+ if (s === "\x1B[D" && x > 0) {
3985
+ x--;
3986
+ stdout.write("\b");
3987
+ } else if (s === "\x1B[C" && x < value.length) {
3988
+ stdout.write(value[x]);
3989
+ x++;
3990
+ }
3991
+ if (s !== "\x7F" && s !== "\x1B[3~" && !visibleChars(s)) return;
3992
+ if (showDefault) {
3993
+ showDefault = false;
3994
+ stdout.write(" ".repeat(def.length) + "\b".repeat(def.length));
3995
+ }
3996
+ const prev = value;
3997
+ const prevX = x;
3998
+ if (s === "\x7F") {
3999
+ if (x > 0) {
4000
+ value = value.slice(0, x - 1) + value.slice(x);
4001
+ x--;
4002
+ }
4003
+ } else if (s === "\x1B[3~") {
4004
+ if (x < value.length) {
4005
+ value = value.slice(0, x) + value.slice(x + 1);
4006
+ }
4007
+ } else {
4008
+ value = value.slice(0, x) + s + value.slice(x);
4009
+ x++;
4010
+ }
4011
+ ctx.value = value;
4012
+ const spaces = prev.length - value.length;
4013
+ stdout.write(
4014
+ "\b".repeat(prevX) + renderValue(ctx) + (spaces > 0 ? " ".repeat(spaces) + "\b".repeat(spaces) : "") + "\b".repeat(value.length - x)
4015
+ );
4016
+ }
4017
+ });
4018
+ };
4019
+
4020
+ const runRecurrentMigrations = async (adapters, config) => {
4021
+ let dbs;
4022
+ let files = 0;
4023
+ await readdirRecursive(config.recurrentPath, async (path) => {
4024
+ files++;
4025
+ dbs ?? (dbs = adapters.map((adapter) => createDbWithAdapter({ adapter })));
4026
+ const sql = await readFile(path, "utf-8");
4027
+ await Promise.all(
4028
+ dbs.map(async (db) => {
4029
+ await db.adapter.arrays(sql);
4030
+ })
4031
+ );
4032
+ });
4033
+ if (files > 0) {
4034
+ config.logger?.log(
4035
+ `Applied ${files} recurrent migration file${files > 1 ? "s" : ""}`
4036
+ );
4037
+ }
4038
+ };
4039
+ const readdirRecursive = async (dirPath, cb) => {
4040
+ const list = await readdir(dirPath).catch((err) => {
4041
+ if (err.code !== "ENOENT") throw err;
4042
+ return;
4043
+ });
4044
+ if (!list) return;
4045
+ await Promise.all(
4046
+ list.map(async (item) => {
4047
+ const path = join(dirPath, item);
4048
+ const info = await stat(path);
4049
+ if (info.isDirectory()) {
4050
+ await readdirRecursive(path, cb);
4051
+ } else if (info.isFile() && path.endsWith(".sql")) {
4052
+ await cb(path);
4053
+ }
4054
+ })
4055
+ );
4056
+ };
4057
+
4058
+ const createDatabaseCommand = (adapters, config, dontClose) => createOrDropDatabase("create", adapters, config, dontClose);
4059
+ const dropDatabaseCommand = (adapters, config) => createOrDropDatabase("drop", adapters, config);
4060
+ const createOrDropDatabase = async (action, adapters, config, dontClose) => {
4061
+ const fn = action === "create" ? createDatabase : dropDatabase;
4062
+ for (const adapter of adapters) {
4063
+ const database = adapter.getDatabase();
4064
+ const owner = adapter.getUser();
4065
+ const res = await run(
4066
+ adapter.reconfigure({ database: "postgres" }),
4067
+ config,
4068
+ {
4069
+ command: (adapter2) => fn(adapter2, {
4070
+ database,
4071
+ owner
4072
+ }),
4073
+ doneMessage: () => `Database ${database} successfully ${action === "create" ? "created" : "dropped"}`,
4074
+ alreadyMessage: () => `Database ${database} ${action === "create" ? "already exists" : "does not exist"}`,
4075
+ deniedMessage: () => `Permission denied to ${action} database.`,
4076
+ askAdminCreds: () => askForAdminCredentials(action === "create")
4077
+ }
4078
+ );
4079
+ if (!res) continue;
4080
+ if (action === "create") {
4081
+ await adapter.transaction(async (tx) => {
4082
+ const schema = tx.getSchema();
4083
+ if (schema) {
4084
+ const quoted = `"${typeof schema === "function" ? schema() : schema}"`;
4085
+ const res2 = await createSchema(tx, quoted);
4086
+ if (res2 === "done") {
4087
+ config.logger?.log(`Created schema ${quoted}`);
4088
+ }
4089
+ }
4090
+ await createMigrationsSchemaAndTable(tx, config);
4091
+ });
4092
+ if (!dontClose) {
4093
+ await adapter.close();
4094
+ }
4095
+ }
4096
+ }
4097
+ };
4098
+ const resetDatabaseCommand = async (adapters, config) => {
4099
+ await createOrDropDatabase("drop", adapters, config);
4100
+ await createOrDropDatabase("create", adapters, config, true);
4101
+ for (const adapter of adapters) {
4102
+ await migrate(adapter, config);
4103
+ }
4104
+ if (config.recurrentPath) {
4105
+ await runRecurrentMigrations(adapters, config);
4106
+ }
4107
+ await Promise.all(adapters.map((adapter) => adapter.close()));
4108
+ };
4109
+ const run = async (adapter, config, params) => {
4110
+ try {
4111
+ const res = await params.command(adapter);
4112
+ config.logger?.log(
4113
+ res === "done" ? params.doneMessage() : params.alreadyMessage()
4114
+ );
4115
+ await adapter.close();
4116
+ return true;
4117
+ } catch (err) {
4118
+ if (err instanceof CreateOrDropError) {
4119
+ if (err.status === "ssl-required") {
4120
+ config.logger?.log(
4121
+ "SSL is required: append ?ssl=true to the database url string"
4122
+ );
4123
+ return false;
4124
+ }
4125
+ if (err.status === "forbidden" || err.status === "auth-failed") {
4126
+ let message = params.deniedMessage();
4127
+ const host = adapter.getHost();
4128
+ const isLocal = host === "localhost";
4129
+ if (!isLocal) {
4130
+ message += `
4131
+ Don't use this command for database service providers, only for a local db.`;
4132
+ }
4133
+ config.logger?.log(message);
4134
+ const creds = await params.askAdminCreds();
4135
+ if (!creds) return false;
4136
+ return run(adapter.reconfigure(creds), config, params);
4137
+ }
4138
+ }
4139
+ throw err;
4140
+ }
4141
+ };
4142
+ const askForAdminCredentials = async (create) => {
4143
+ const ok = await promptConfirm({
4144
+ message: `Would you like to share admin credentials to ${create ? "create" : "drop"} a database?`
4053
4145
  });
4054
4146
  if (!ok) {
4055
4147
  return;
@@ -6004,195 +6096,83 @@ const dbConstraintToTableConstraint = (ctx, table, item) => {
6004
6096
  references: references ? {
6005
6097
  columns: references.columns,
6006
6098
  fnOrTable: getReferencesTable(ctx, references),
6007
- foreignColumns: references.foreignColumns,
6008
- options: {
6009
- match: matchMap[references.match],
6010
- onUpdate: fkeyActionMap[references.onUpdate],
6011
- onDelete: fkeyActionMap[references.onDelete]
6012
- }
6013
- } : void 0,
6014
- check: check ? raw({ raw: check.expression }) : void 0
6015
- };
6016
- const name = item.name && item.name !== getConstraintName(table.name, constraint, ctx.snakeCase) ? item.name : void 0;
6017
- if (name) {
6018
- constraint.name = name;
6019
- if (constraint.references?.options) {
6020
- constraint.references.options.name = name;
6021
- }
6022
- }
6023
- return constraint;
6024
- };
6025
- const makeIndexOrExcludeOptions = (tableName, index, key) => {
6026
- return {
6027
- name: index.name !== (key === "indexes" ? getIndexName : getExcludeName)(
6028
- tableName,
6029
- index.columns
6030
- ) ? index.name : void 0,
6031
- using: index.using === "btree" ? void 0 : index.using,
6032
- unique: index.unique || void 0,
6033
- include: index.include,
6034
- nullsNotDistinct: index.nullsNotDistinct || void 0,
6035
- with: index.with,
6036
- tablespace: index.tablespace,
6037
- where: index.where
6038
- };
6039
- };
6040
- const checkIfIsOuterRecursiveFkey = (data, table, references) => {
6041
- const referencesId = `${references.foreignSchema}.${references.foreignTable}`;
6042
- const tableId = `${table.schemaName}.${table.name}`;
6043
- for (const other of data.tables) {
6044
- const id = `${other.schemaName}.${other.name}`;
6045
- if (referencesId === id) {
6046
- for (const c of data.constraints) {
6047
- if (c.tableName === other.name && c.schemaName === other.schemaName && c.references?.foreignTable === table.name && c.references.foreignSchema === table.schemaName && tableId < id) {
6048
- return true;
6049
- }
6050
- }
6051
- break;
6052
- }
6053
- }
6054
- return false;
6055
- };
6056
-
6057
- const pullDbStructure = async (adapter, config) => {
6058
- const currentSchema = adapter.searchPath || "public";
6059
- const ctx = makeStructureToAstCtx(config, currentSchema);
6060
- const ast = await structureToAst(ctx, adapter, config);
6061
- const result = astToMigration(currentSchema, config, ast);
6062
- if (!result) return;
6063
- const version = await makeFileVersion({}, config);
6064
- await writeMigrationFile(config, version, "pull", result);
6065
- const silentQueries = Object.assign(adapter, {
6066
- silentQuery: adapter.query,
6067
- silentArrays: adapter.arrays
6068
- });
6069
- await saveMigratedVersion(silentQueries, version, "pull", config);
6070
- const unsupportedEntries = Object.entries(ctx.unsupportedTypes);
6071
- const len = unsupportedEntries.length;
6072
- if (len) {
6073
- let count = 0;
6074
- config.logger?.warn(
6075
- `Found unsupported types:
6076
- ${unsupportedEntries.map(([type, columns]) => {
6077
- count += columns.length;
6078
- return `- ${type} is used for column${columns.length > 1 ? "s" : ""} ${columns.join(", ")}`;
6079
- }).join("\n")}
6080
- Append \`as\` method manually to ${count > 1 ? "these" : "this"} column${count > 1 ? "s" : ""} to treat ${count > 1 ? "them" : "it"} as other column type`
6081
- );
6082
- }
6083
- config.logger?.log("Database pulled successfully");
6084
- };
6085
-
6086
- const migrationConfigDefaults = {
6087
- schemaConfig: defaultSchemaConfig,
6088
- migrationsPath: path.join("src", "db", "migrations"),
6089
- migrationId: { serial: 4 },
6090
- migrationsTable: "schemaMigrations",
6091
- snakeCase: false,
6092
- commands: {},
6093
- log: true,
6094
- logger: console,
6095
- import() {
6096
- throw new Error(
6097
- "Add `import: (path) => import(path),` setting to `rakeDb` config"
6098
- );
6099
- }
6100
- };
6101
- const ensureMigrationsPath = (config) => {
6102
- if (!config.migrationsPath) {
6103
- config.migrationsPath = migrationConfigDefaults.migrationsPath;
6104
- }
6105
- if (!path.isAbsolute(config.migrationsPath)) {
6106
- config.migrationsPath = path.resolve(
6107
- config.basePath,
6108
- config.migrationsPath
6109
- );
6110
- }
6111
- return config;
6112
- };
6113
- const ensureBasePathAndDbScript = (config, intermediateCallers2 = 0) => {
6114
- if (config.basePath && config.dbScript) return config;
6115
- let filePath = getStackTrace()?.[3 + intermediateCallers2]?.getFileName();
6116
- if (!filePath) {
6117
- throw new Error(
6118
- "Failed to determine path to db script. Please set basePath option of rakeDb"
6119
- );
6120
- }
6121
- if (filePath.startsWith("file://")) {
6122
- filePath = fileURLToPath(filePath);
6123
- }
6124
- const ext = path.extname(filePath);
6125
- if (ext !== ".ts" && ext !== ".js" && ext !== ".mjs") {
6126
- throw new Error(
6127
- `Add a .ts suffix to the "${path.basename(filePath)}" when calling it`
6128
- );
6129
- }
6130
- config.basePath = path.dirname(filePath);
6131
- config.dbScript = path.basename(filePath);
6132
- return config;
6133
- };
6134
- let intermediateCallers = 0;
6135
- const incrementIntermediateCaller = () => {
6136
- intermediateCallers++;
6137
- };
6138
- const makeRakeDbConfig = (config, args) => {
6139
- const ic = intermediateCallers;
6140
- intermediateCallers = 0;
6141
- const result = {
6142
- ...migrationConfigDefaults,
6143
- ...config,
6144
- __rakeDbConfig: true
6145
- };
6146
- if (!result.log) {
6147
- delete result.logger;
6148
- }
6149
- ensureBasePathAndDbScript(result, ic);
6150
- ensureMigrationsPath(result);
6151
- if (!result.recurrentPath) {
6152
- result.recurrentPath = path.join(
6153
- result.migrationsPath,
6154
- "recurrent"
6155
- );
6156
- }
6157
- if ("recurrentPath" in result && !path.isAbsolute(result.recurrentPath)) {
6158
- result.recurrentPath = path.resolve(result.basePath, result.recurrentPath);
6159
- }
6160
- if ("baseTable" in config && config.baseTable) {
6161
- const { types, snakeCase, language } = config.baseTable.prototype;
6162
- result.columnTypes = types || makeColumnTypes(defaultSchemaConfig);
6163
- if (snakeCase) result.snakeCase = true;
6164
- if (language) result.language = language;
6165
- } else {
6166
- const ct = "columnTypes" in config && config.columnTypes;
6167
- result.columnTypes = (typeof ct === "function" ? ct(
6168
- makeColumnTypes(defaultSchemaConfig)
6169
- ) : ct) || makeColumnTypes(defaultSchemaConfig);
6170
- }
6171
- if (config.migrationId === "serial") {
6172
- result.migrationId = { serial: 4 };
6173
- }
6174
- const transaction = getCliParam(args, "transaction");
6175
- if (transaction) {
6176
- if (transaction !== "single" && transaction !== "per-migration") {
6177
- throw new Error(
6178
- `Unsupported transaction param ${transaction}, expected single or per-migration`
6179
- );
6099
+ foreignColumns: references.foreignColumns,
6100
+ options: {
6101
+ match: matchMap[references.match],
6102
+ onUpdate: fkeyActionMap[references.onUpdate],
6103
+ onDelete: fkeyActionMap[references.onDelete]
6104
+ }
6105
+ } : void 0,
6106
+ check: check ? raw({ raw: check.expression }) : void 0
6107
+ };
6108
+ const name = item.name && item.name !== getConstraintName(table.name, constraint, ctx.snakeCase) ? item.name : void 0;
6109
+ if (name) {
6110
+ constraint.name = name;
6111
+ if (constraint.references?.options) {
6112
+ constraint.references.options.name = name;
6180
6113
  }
6181
- result.transaction = transaction;
6182
- } else if (!result.transaction) {
6183
- result.transaction = "single";
6184
6114
  }
6185
- let c = rakeDbCommands;
6186
- if (config.commands) {
6187
- c = { ...c };
6188
- const commands = config.commands;
6189
- for (const key in commands) {
6190
- const command = commands[key];
6191
- c[key] = typeof command === "function" ? { run: command } : command;
6115
+ return constraint;
6116
+ };
6117
+ const makeIndexOrExcludeOptions = (tableName, index, key) => {
6118
+ return {
6119
+ name: index.name !== (key === "indexes" ? getIndexName : getExcludeName)(
6120
+ tableName,
6121
+ index.columns
6122
+ ) ? index.name : void 0,
6123
+ using: index.using === "btree" ? void 0 : index.using,
6124
+ unique: index.unique || void 0,
6125
+ include: index.include,
6126
+ nullsNotDistinct: index.nullsNotDistinct || void 0,
6127
+ with: index.with,
6128
+ tablespace: index.tablespace,
6129
+ where: index.where
6130
+ };
6131
+ };
6132
+ const checkIfIsOuterRecursiveFkey = (data, table, references) => {
6133
+ const referencesId = `${references.foreignSchema}.${references.foreignTable}`;
6134
+ const tableId = `${table.schemaName}.${table.name}`;
6135
+ for (const other of data.tables) {
6136
+ const id = `${other.schemaName}.${other.name}`;
6137
+ if (referencesId === id) {
6138
+ for (const c of data.constraints) {
6139
+ if (c.tableName === other.name && c.schemaName === other.schemaName && c.references?.foreignTable === table.name && c.references.foreignSchema === table.schemaName && tableId < id) {
6140
+ return true;
6141
+ }
6142
+ }
6143
+ break;
6192
6144
  }
6193
6145
  }
6194
- result.commands = c;
6195
- return result;
6146
+ return false;
6147
+ };
6148
+
6149
+ const pullDbStructure = async (adapter, config) => {
6150
+ const currentSchema = adapter.searchPath || "public";
6151
+ const ctx = makeStructureToAstCtx(config, currentSchema);
6152
+ const ast = await structureToAst(ctx, adapter, config);
6153
+ const result = astToMigration(currentSchema, config, ast);
6154
+ if (!result) return;
6155
+ const version = await makeFileVersion({}, config);
6156
+ await writeMigrationFile(config, version, "pull", result);
6157
+ const silentQueries = Object.assign(adapter, {
6158
+ silentQuery: adapter.query,
6159
+ silentArrays: adapter.arrays
6160
+ });
6161
+ await saveMigratedVersion(silentQueries, version, "pull", config);
6162
+ const unsupportedEntries = Object.entries(ctx.unsupportedTypes);
6163
+ const len = unsupportedEntries.length;
6164
+ if (len) {
6165
+ let count = 0;
6166
+ config.logger?.warn(
6167
+ `Found unsupported types:
6168
+ ${unsupportedEntries.map(([type, columns]) => {
6169
+ count += columns.length;
6170
+ return `- ${type} is used for column${columns.length > 1 ? "s" : ""} ${columns.join(", ")}`;
6171
+ }).join("\n")}
6172
+ Append \`as\` method manually to ${count > 1 ? "these" : "this"} column${count > 1 ? "s" : ""} to treat ${count > 1 ? "them" : "it"} as other column type`
6173
+ );
6174
+ }
6175
+ config.logger?.log("Database pulled successfully");
6196
6176
  };
6197
6177
 
6198
6178
  const listMigrationsStatuses = async (adapters, config, params) => {
@@ -6358,85 +6338,275 @@ const rebase = async (adapters, config) => {
6358
6338
  renames.push(values);
6359
6339
  renamesMap[file.path] = values;
6360
6340
  }
6361
- if (moveFile === prev) {
6362
- if (prev.serial < minVersionToMigrate)
6363
- minVersionToMigrate = prev.serial;
6364
- newVersion = prev.serial + move;
6365
- let item = [prev.path, newVersion];
6366
- if (renamesMap[prev.path]) {
6367
- renamesMap[prev.path] = item;
6368
- for (let i2 = renames.length - 1; i2 >= 0; i2--) {
6369
- const rename = renames[i2];
6370
- rename[1]--;
6371
- renames[i2] = item;
6372
- if (rename[0] === prev.path) break;
6373
- renamesMap[item[0]] = item;
6374
- item = rename;
6375
- }
6376
- } else {
6377
- renames.push(item);
6378
- renamesMap[prev.path] = item;
6379
- }
6341
+ if (moveFile === prev) {
6342
+ if (prev.serial < minVersionToMigrate)
6343
+ minVersionToMigrate = prev.serial;
6344
+ newVersion = prev.serial + move;
6345
+ let item = [prev.path, newVersion];
6346
+ if (renamesMap[prev.path]) {
6347
+ renamesMap[prev.path] = item;
6348
+ for (let i2 = renames.length - 1; i2 >= 0; i2--) {
6349
+ const rename = renames[i2];
6350
+ rename[1]--;
6351
+ renames[i2] = item;
6352
+ if (rename[0] === prev.path) break;
6353
+ renamesMap[item[0]] = item;
6354
+ item = rename;
6355
+ }
6356
+ } else {
6357
+ renames.push(item);
6358
+ renamesMap[prev.path] = item;
6359
+ }
6360
+ }
6361
+ }
6362
+ if (file.name !== migratedName && newVersion > maxNewVersion) {
6363
+ maxNewVersion = newVersion;
6364
+ }
6365
+ }
6366
+ if (!renames.length && !migratedFiles.length) return;
6367
+ maxNewVersion++;
6368
+ renames.push(
6369
+ ...migratedFiles.map((file, i) => {
6370
+ const rename = [file.path, maxNewVersion + i];
6371
+ renamesMap[file.path] = rename;
6372
+ return rename;
6373
+ })
6374
+ );
6375
+ if (!renames.length) return;
6376
+ const migrationsDown = files.filter(
6377
+ (file) => combinedVersionsMap[file.version] === file.name && file.serial >= minVersionToMigrate
6378
+ );
6379
+ const migrationsUp = files.reduce((files2, file) => {
6380
+ const rename = renamesMap[file.path];
6381
+ if (rename) {
6382
+ const version = String(rename[1]).padStart(4, "0");
6383
+ files2.push({
6384
+ ...file,
6385
+ path: path.join(
6386
+ path.dirname(rename[0]),
6387
+ version + path.basename(rename[0]).slice(version.length)
6388
+ ),
6389
+ version
6390
+ });
6391
+ } else if (!combinedVersionsMap[file.version] || file.serial >= minVersionToMigrate) {
6392
+ files2.push(file);
6393
+ }
6394
+ return files2;
6395
+ }, []).sort((a, b) => +b.version - +a.version);
6396
+ set.migrations = migrationsDown;
6397
+ const redoConfig = {
6398
+ ...config,
6399
+ async afterRollback() {
6400
+ set.migrations = migrationsUp;
6401
+ },
6402
+ async afterMigrate() {
6403
+ set.migrations = migrationsDown;
6404
+ }
6405
+ };
6406
+ for (const adapter of adapters) {
6407
+ await redo(adapter, redoConfig, { ctx, count: Infinity });
6408
+ }
6409
+ for (let i = renames.length - 1; i >= 0; i--) {
6410
+ const [from, version] = renames[i];
6411
+ const prefix = String(version).padStart(4, "0");
6412
+ await fs.rename(
6413
+ from,
6414
+ path.join(
6415
+ path.dirname(from),
6416
+ prefix + path.basename(from).slice(prefix.length)
6417
+ )
6418
+ );
6419
+ }
6420
+ };
6421
+
6422
+ const close = (adapters) => Promise.all(adapters.map((adapter) => adapter.close()));
6423
+ const maybeRunRecurrent = async (adapters, config) => {
6424
+ config.recurrentPath && await runRecurrentMigrations(
6425
+ adapters,
6426
+ config
6427
+ );
6428
+ };
6429
+ const rakeDbAliases$1 = {
6430
+ migrate: "up",
6431
+ rollback: "down",
6432
+ s: "status",
6433
+ rec: "recurrent"
6434
+ };
6435
+ const upCommand = {
6436
+ run: (adapters, config, args) => migrateCommand(adapters, config, args).then(() => maybeRunRecurrent(adapters, config)).then(() => close(adapters)),
6437
+ help: "migrate pending migrations",
6438
+ helpArguments: {
6439
+ "no arguments": "migrate all pending",
6440
+ "a number": "run a specific number of pending migrations",
6441
+ force: "enforce migrating a pending file in the middle"
6442
+ }
6443
+ };
6444
+ const downCommand = {
6445
+ run: (adapters, config, args) => rollbackCommand(adapters, config, args).then(() => close(adapters)),
6446
+ help: "rollback migrated migrations",
6447
+ helpArguments: {
6448
+ "no arguments": "rollback one last migration",
6449
+ "a number": "rollback a specified number",
6450
+ all: "rollback all migrations"
6451
+ }
6452
+ };
6453
+ const statusCommand = {
6454
+ run(adapters, config, args) {
6455
+ const showUrl = args.includes("p") || args.includes("path");
6456
+ return listMigrationsStatuses(adapters, config, { showUrl });
6457
+ },
6458
+ help: "list migrations statuses",
6459
+ helpArguments: {
6460
+ "no arguments": `does not print file paths`,
6461
+ "p, path": "also print file paths"
6462
+ }
6463
+ };
6464
+ const recurrent = {
6465
+ async run(adapters, config) {
6466
+ if (!config.recurrentPath) return;
6467
+ await maybeRunRecurrent(adapters, config).then(() => close(adapters));
6468
+ },
6469
+ help: "run recurrent migrations"
6470
+ };
6471
+ const rakeDbCommands = {
6472
+ create: {
6473
+ run: (adapters, config) => createDatabaseCommand(adapters, config),
6474
+ help: "create databases"
6475
+ },
6476
+ drop: {
6477
+ run: dropDatabaseCommand,
6478
+ help: "drop databases"
6479
+ },
6480
+ reset: {
6481
+ run: (adapters, config) => resetDatabaseCommand(adapters, config),
6482
+ help: "drop, create and migrate databases"
6483
+ },
6484
+ up: upCommand,
6485
+ down: downCommand,
6486
+ redo: {
6487
+ run: (adapters, config, args) => redoCommand(adapters, config, args).then(() => maybeRunRecurrent(adapters, config)).then(() => close(adapters)),
6488
+ help: "rollback and migrate, run recurrent"
6489
+ },
6490
+ pull: {
6491
+ run: ([adapter], config) => pullDbStructure(adapter, config).then(() => close([adapter])),
6492
+ help: "generate a combined migration for an existing database"
6493
+ },
6494
+ new: {
6495
+ run(_, config, args) {
6496
+ const [name] = args;
6497
+ if (!name) throw new Error("Migration name is missing");
6498
+ return newMigration(config, name);
6499
+ },
6500
+ help: "create new migration file"
6501
+ },
6502
+ status: statusCommand,
6503
+ recurrent,
6504
+ rebase: {
6505
+ run: (adapters, config) => rebase(adapters, config).then(() => close(adapters)),
6506
+ help: "move local migrations below the new ones from upstream"
6507
+ },
6508
+ "change-ids": {
6509
+ run(adapters, config, [format, digitsArg]) {
6510
+ if (format !== "serial" && format !== "timestamp") {
6511
+ throw new Error(
6512
+ `Pass "serial" or "timestamp" argument to the "change-ids" command`
6513
+ );
6514
+ }
6515
+ const digits = digitsArg ? parseInt(digitsArg) : void 0;
6516
+ if (digits && isNaN(digits)) {
6517
+ throw new Error(`Second argument is optional and must be an integer`);
6380
6518
  }
6381
- }
6382
- if (file.name !== migratedName && newVersion > maxNewVersion) {
6383
- maxNewVersion = newVersion;
6519
+ return changeIds(adapters, config, { format, digits });
6520
+ },
6521
+ help: "change migrations ids format",
6522
+ helpArguments: {
6523
+ serial: "change ids to 4 digit serial",
6524
+ "serial *number*": "change ids to serial number of custom length",
6525
+ timestamp: "change timestamps"
6384
6526
  }
6385
6527
  }
6386
- if (!renames.length && !migratedFiles.length) return;
6387
- maxNewVersion++;
6388
- renames.push(
6389
- ...migratedFiles.map((file, i) => {
6390
- const rename = [file.path, maxNewVersion + i];
6391
- renamesMap[file.path] = rename;
6392
- return rename;
6393
- })
6394
- );
6395
- if (!renames.length) return;
6396
- const migrationsDown = files.filter(
6397
- (file) => combinedVersionsMap[file.version] === file.name && file.serial >= minVersionToMigrate
6398
- );
6399
- const migrationsUp = files.reduce((files2, file) => {
6400
- const rename = renamesMap[file.path];
6401
- if (rename) {
6402
- const version = String(rename[1]).padStart(4, "0");
6403
- files2.push({
6404
- ...file,
6405
- path: path.join(
6406
- path.dirname(rename[0]),
6407
- version + path.basename(rename[0]).slice(version.length)
6408
- ),
6409
- version
6410
- });
6411
- } else if (!combinedVersionsMap[file.version] || file.serial >= minVersionToMigrate) {
6412
- files2.push(file);
6413
- }
6414
- return files2;
6415
- }, []).sort((a, b) => +b.version - +a.version);
6416
- set.migrations = migrationsDown;
6417
- const redoConfig = {
6528
+ };
6529
+ for (const key in rakeDbAliases$1) {
6530
+ const command = rakeDbAliases$1[key];
6531
+ if (command) rakeDbCommands[key] = rakeDbCommands[command];
6532
+ }
6533
+ let intermediateCallers = 0;
6534
+ const incrementIntermediateCaller = () => {
6535
+ intermediateCallers++;
6536
+ };
6537
+ const ensureBasePathAndDbScript = (config, intermediateCallers2 = 0) => {
6538
+ if (config.basePath && config.dbScript) return config;
6539
+ let filePath = getStackTrace()?.[3 + intermediateCallers2]?.getFileName();
6540
+ if (!filePath) {
6541
+ throw new Error(
6542
+ "Failed to determine path to db script. Please set basePath option of rakeDb"
6543
+ );
6544
+ }
6545
+ if (filePath.startsWith("file://")) {
6546
+ filePath = fileURLToPath(filePath);
6547
+ }
6548
+ const ext = path.extname(filePath);
6549
+ if (ext !== ".ts" && ext !== ".js" && ext !== ".mjs") {
6550
+ throw new Error(
6551
+ `Add a .ts suffix to the "${path.basename(filePath)}" when calling it`
6552
+ );
6553
+ }
6554
+ config.basePath = path.dirname(filePath);
6555
+ config.dbScript = path.basename(filePath);
6556
+ return config;
6557
+ };
6558
+ const makeRakeDbConfig = (config, args) => {
6559
+ const ic = intermediateCallers;
6560
+ intermediateCallers = 0;
6561
+ const result = {
6562
+ ...rakeDbConfigDefaults,
6418
6563
  ...config,
6419
- async afterRollback() {
6420
- set.migrations = migrationsUp;
6421
- },
6422
- async afterMigrate() {
6423
- set.migrations = migrationsDown;
6424
- }
6564
+ __rakeDbConfig: true
6425
6565
  };
6426
- for (const adapter of adapters) {
6427
- await redo(adapter, redoConfig, { ctx, count: Infinity });
6566
+ ensureBasePathAndDbScript(result, ic);
6567
+ Object.assign(result, processMigrateConfig(result));
6568
+ if (!result.recurrentPath && result.migrationsPath) {
6569
+ result.recurrentPath = path.join(result.migrationsPath, "recurrent");
6428
6570
  }
6429
- for (let i = renames.length - 1; i >= 0; i--) {
6430
- const [from, version] = renames[i];
6431
- const prefix = String(version).padStart(4, "0");
6432
- await fs.rename(
6433
- from,
6434
- path.join(
6435
- path.dirname(from),
6436
- prefix + path.basename(from).slice(prefix.length)
6437
- )
6438
- );
6571
+ if (result.recurrentPath && !path.isAbsolute(result.recurrentPath)) {
6572
+ result.recurrentPath = path.resolve(result.basePath, result.recurrentPath);
6573
+ }
6574
+ if ("baseTable" in config && config.baseTable) {
6575
+ const { types, snakeCase, language } = config.baseTable.prototype;
6576
+ result.columnTypes = types || makeColumnTypes(defaultSchemaConfig);
6577
+ if (snakeCase) result.snakeCase = true;
6578
+ if (language) result.language = language;
6579
+ } else {
6580
+ const ct = "columnTypes" in config && config.columnTypes;
6581
+ result.columnTypes = (typeof ct === "function" ? ct(
6582
+ makeColumnTypes(defaultSchemaConfig)
6583
+ ) : ct) || makeColumnTypes(defaultSchemaConfig);
6584
+ }
6585
+ if (config.migrationId === "serial") {
6586
+ result.migrationId = { serial: 4 };
6587
+ }
6588
+ const transaction = getCliParam(args, "transaction");
6589
+ if (transaction) {
6590
+ if (transaction !== "single" && transaction !== "per-migration") {
6591
+ throw new Error(
6592
+ `Unsupported transaction param ${transaction}, expected single or per-migration`
6593
+ );
6594
+ }
6595
+ result.transaction = transaction;
6596
+ } else if (!result.transaction) {
6597
+ result.transaction = "single";
6598
+ }
6599
+ let c = rakeDbCommands;
6600
+ if (config.commands) {
6601
+ c = { ...c };
6602
+ const commands = config.commands;
6603
+ for (const key in commands) {
6604
+ const command = commands[key];
6605
+ c[key] = typeof command === "function" ? { run: command } : command;
6606
+ }
6439
6607
  }
6608
+ result.commands = c;
6609
+ return result;
6440
6610
  };
6441
6611
 
6442
6612
  const rakeDbAliases = {
@@ -6454,7 +6624,7 @@ const rakeDbCliWithAdapter = (inputConfig, args = process.argv.slice(2)) => {
6454
6624
  config = makeRakeDbConfig(inputConfig, args);
6455
6625
  }
6456
6626
  return {
6457
- change: makeChange(config),
6627
+ change: createMigrationChangeFn(config),
6458
6628
  async run(adapter, runArgs) {
6459
6629
  const adapters = toArray(adapter);
6460
6630
  try {
@@ -6548,111 +6718,6 @@ ${Object.entries(helpArguments).map(
6548
6718
  `);
6549
6719
  }
6550
6720
  };
6551
- const close = (adapters) => Promise.all(adapters.map((adapter) => adapter.close()));
6552
- const maybeRunRecurrent = async (adapters, config) => {
6553
- config.recurrentPath && await runRecurrentMigrations(
6554
- adapters,
6555
- config
6556
- );
6557
- };
6558
- const upCommand = {
6559
- run: (adapters, config, args) => migrateCommand(adapters, config, args).then(() => maybeRunRecurrent(adapters, config)).then(() => close(adapters)),
6560
- help: "migrate pending migrations",
6561
- helpArguments: {
6562
- "no arguments": "migrate all pending",
6563
- "a number": "run a specific number of pending migrations",
6564
- force: "enforce migrating a pending file in the middle"
6565
- }
6566
- };
6567
- const downCommand = {
6568
- run: (adapters, config, args) => rollbackCommand(adapters, config, args).then(() => close(adapters)),
6569
- help: "rollback migrated migrations",
6570
- helpArguments: {
6571
- "no arguments": "rollback one last migration",
6572
- "a number": "rollback a specified number",
6573
- all: "rollback all migrations"
6574
- }
6575
- };
6576
- const statusCommand = {
6577
- run(adapters, config, args) {
6578
- const showUrl = args.includes("p") || args.includes("path");
6579
- return listMigrationsStatuses(adapters, config, { showUrl });
6580
- },
6581
- help: "list migrations statuses",
6582
- helpArguments: {
6583
- "no arguments": `does not print file paths`,
6584
- "p, path": "also print file paths"
6585
- }
6586
- };
6587
- const recurrent = {
6588
- async run(adapters, config) {
6589
- if (!config.recurrentPath) return;
6590
- await maybeRunRecurrent(adapters, config).then(() => close(adapters));
6591
- },
6592
- help: "run recurrent migrations"
6593
- };
6594
- const rakeDbCommands = {
6595
- create: {
6596
- run: (adapters, config) => createDatabaseCommand(adapters, config),
6597
- help: "create databases"
6598
- },
6599
- drop: {
6600
- run: dropDatabaseCommand,
6601
- help: "drop databases"
6602
- },
6603
- reset: {
6604
- run: (adapters, config) => resetDatabaseCommand(adapters, config),
6605
- help: "drop, create and migrate databases"
6606
- },
6607
- up: upCommand,
6608
- down: downCommand,
6609
- redo: {
6610
- run: (adapters, config, args) => redoCommand(adapters, config, args).then(() => maybeRunRecurrent(adapters, config)).then(() => close(adapters)),
6611
- help: "rollback and migrate, run recurrent"
6612
- },
6613
- pull: {
6614
- run: ([adapter], config) => pullDbStructure(adapter, config).then(() => close([adapter])),
6615
- help: "generate a combined migration for an existing database"
6616
- },
6617
- new: {
6618
- run(_, config, args) {
6619
- const [name] = args;
6620
- if (!name) throw new Error("Migration name is missing");
6621
- return newMigration(config, name);
6622
- },
6623
- help: "create new migration file"
6624
- },
6625
- status: statusCommand,
6626
- recurrent,
6627
- rebase: {
6628
- run: (adapters, config) => rebase(adapters, config).then(() => close(adapters)),
6629
- help: "move local migrations below the new ones from upstream"
6630
- },
6631
- "change-ids": {
6632
- run(adapters, config, [format, digitsArg]) {
6633
- if (format !== "serial" && format !== "timestamp") {
6634
- throw new Error(
6635
- `Pass "serial" or "timestamp" argument to the "change-ids" command`
6636
- );
6637
- }
6638
- const digits = digitsArg ? parseInt(digitsArg) : void 0;
6639
- if (digits && isNaN(digits)) {
6640
- throw new Error(`Second argument is optional and must be an integer`);
6641
- }
6642
- return changeIds(adapters, config, { format, digits });
6643
- },
6644
- help: "change migrations ids format",
6645
- helpArguments: {
6646
- serial: "change ids to 4 digit serial",
6647
- "serial *number*": "change ids to serial number of custom length",
6648
- timestamp: "change ids to timestamps"
6649
- }
6650
- }
6651
- };
6652
- for (const key in rakeDbAliases) {
6653
- const command = rakeDbAliases[key];
6654
- if (command) rakeDbCommands[key] = rakeDbCommands[command];
6655
- }
6656
6721
 
6657
- export { RakeDbError, astToMigration, concatSchemaAndName, createDatabase, createMigrationInterface, createMigrationsSchemaAndTable, createSchema$1 as createSchema, createTable$1 as createTable, dbColumnToAst, dropDatabase, dropSchema, dropTable, encodeColumnDefault, getConstraintName, getDbStructureTableData, getDbTableColumnsChecks, getDbVersion, getExcludeName, getIndexName, getMigrationsSchemaAndTable, getSchemaAndTableFromName, incrementIntermediateCaller, instantiateDbColumn, introspectDbSchema, makeDomainsMap, makeFileVersion, makeRakeDbConfig, makeStructureToAstCtx, migrate, migrateAndClose, migrationConfigDefaults, promptSelect, rakeDbCliWithAdapter, rakeDbCommands, redo, rollback, runMigration, saveMigratedVersion, setRakeDbCliRunFn, structureToAst, tableToAst, writeMigrationFile };
6722
+ export { RakeDbError, astToMigration, concatSchemaAndName, createDatabase, createMigrationChangeFn, createMigrationInterface, createMigrationsSchemaAndTable, createSchema, createTable, dbColumnToAst, dropDatabase, dropSchema, dropTable, encodeColumnDefault, getConstraintName, getDbStructureTableData, getDbTableColumnsChecks, getDbVersion, getExcludeName, getIndexName, getMigrationsSchemaAndTable, getSchemaAndTableFromName, incrementIntermediateCaller, instantiateDbColumn, introspectDbSchema, makeDomainsMap, makeFileVersion, makeStructureToAstCtx, migrate, migrateAndClose, promptSelect, rakeDbCliWithAdapter, rakeDbCommands, rakeDbConfigDefaults, redo, rollback, runMigration, saveMigratedVersion, setRakeDbCliRunFn, structureToAst, tableToAst, writeMigrationFile };
6658
6723
  //# sourceMappingURL=index.mjs.map