rake-db 2.27.18 → 2.27.20

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,6 +1,6 @@
1
- import { defaultSchemaConfig, makeColumnTypes, getStackTrace, singleQuote, isRawSQL, escapeForMigration, ArrayColumn, toSnakeCase, toCamelCase, DomainColumn, toArray, EnumColumn, snakeCaseKey, getColumnTypes, parseTableData, emptyObject, escapeString, tableDataMethods, setCurrentColumnName, consumeColumnName, ColumnType, ColumnTypeBase, parseTableDataInput, UnknownColumn, setDefaultLanguage, deepCompare, raw, logParamToLogObject, createDbWithAdapter, getImportPath, pathToLog, emptyArray, colors, makeColumnsByType, RawSQL, CustomTypeColumn, assignDbDataToColumn, PostgisGeographyPointColumn, exhaustive, codeToString, addCode, quoteObjectKey, pushTableDataCode, primaryKeyInnerToCode, indexInnerToCode, excludeInnerToCode, constraintInnerToCode, referencesArgsToCode, backtickQuote, TimestampTZColumn, TimestampColumn } from 'pqb';
1
+ import { isRawSQL, escapeForMigration, ArrayColumn, toSnakeCase, toCamelCase, DomainColumn, toArray, EnumColumn, defaultSchemaConfig, snakeCaseKey, getColumnTypes, parseTableData, emptyObject, escapeString, tableDataMethods, setCurrentColumnName, consumeColumnName, ColumnType, ColumnTypeBase, parseTableDataInput, UnknownColumn, setDefaultLanguage, deepCompare, raw, singleQuote, logParamToLogObject, createDbWithAdapter, getImportPath, pathToLog, emptyArray, makeColumnTypes, getStackTrace, colors, makeColumnsByType, RawSQL, CustomTypeColumn, assignDbDataToColumn, PostgisGeographyPointColumn, exhaustive, codeToString, addCode, quoteObjectKey, pushTableDataCode, primaryKeyInnerToCode, indexInnerToCode, excludeInnerToCode, constraintInnerToCode, referencesArgsToCode, backtickQuote, TimestampTZColumn, TimestampColumn } from 'pqb';
2
2
  import path, { join } from 'path';
3
- import { fileURLToPath, pathToFileURL } from 'node:url';
3
+ import { pathToFileURL, fileURLToPath } from 'node:url';
4
4
  import fs, { mkdir, writeFile, readdir, stat, readFile } from 'fs/promises';
5
5
 
6
6
  class RakeDbError extends Error {
@@ -15,154 +15,6 @@ const clearChanges = () => {
15
15
  const getCurrentChanges = () => currentChanges;
16
16
  const pushChange = (change) => currentChanges.push(change);
17
17
 
18
- const migrationConfigDefaults = {
19
- schemaConfig: defaultSchemaConfig,
20
- migrationsPath: path.join("src", "db", "migrations"),
21
- migrationId: { serial: 4 },
22
- migrationsTable: "schemaMigrations",
23
- snakeCase: false,
24
- commands: {},
25
- log: true,
26
- logger: console,
27
- import() {
28
- throw new Error(
29
- "Add `import: (path) => import(path),` setting to `rakeDb` config"
30
- );
31
- }
32
- };
33
- const ensureMigrationsPath = (config) => {
34
- if (!config.migrationsPath) {
35
- config.migrationsPath = migrationConfigDefaults.migrationsPath;
36
- }
37
- if (!path.isAbsolute(config.migrationsPath)) {
38
- config.migrationsPath = path.resolve(
39
- config.basePath,
40
- config.migrationsPath
41
- );
42
- }
43
- return config;
44
- };
45
- const ensureBasePathAndDbScript = (config, intermediateCallers = 0) => {
46
- if (config.basePath && config.dbScript) return config;
47
- let filePath = getStackTrace()?.[3 + intermediateCallers].getFileName();
48
- if (!filePath) {
49
- throw new Error(
50
- "Failed to determine path to db script. Please set basePath option of rakeDb"
51
- );
52
- }
53
- if (filePath.startsWith("file://")) {
54
- filePath = fileURLToPath(filePath);
55
- }
56
- const ext = path.extname(filePath);
57
- if (ext !== ".ts" && ext !== ".js" && ext !== ".mjs") {
58
- throw new Error(
59
- `Add a .ts suffix to the "${path.basename(filePath)}" when calling it`
60
- );
61
- }
62
- config.basePath = path.dirname(filePath);
63
- config.dbScript = path.basename(filePath);
64
- return config;
65
- };
66
- const processRakeDbConfig = (config) => {
67
- const result = { ...migrationConfigDefaults, ...config };
68
- if (!result.log) {
69
- delete result.logger;
70
- }
71
- ensureBasePathAndDbScript(result, 1);
72
- ensureMigrationsPath(result);
73
- if (!result.recurrentPath) {
74
- result.recurrentPath = path.join(
75
- result.migrationsPath,
76
- "recurrent"
77
- );
78
- }
79
- if ("recurrentPath" in result && !path.isAbsolute(result.recurrentPath)) {
80
- result.recurrentPath = path.resolve(result.basePath, result.recurrentPath);
81
- }
82
- if ("baseTable" in config && config.baseTable) {
83
- const { types, snakeCase, language } = config.baseTable.prototype;
84
- result.columnTypes = types || makeColumnTypes(defaultSchemaConfig);
85
- if (snakeCase) result.snakeCase = true;
86
- if (language) result.language = language;
87
- } else {
88
- const ct = "columnTypes" in config && config.columnTypes;
89
- result.columnTypes = (typeof ct === "function" ? ct(
90
- makeColumnTypes(defaultSchemaConfig)
91
- ) : ct) || makeColumnTypes(defaultSchemaConfig);
92
- }
93
- if (config.migrationId === "serial") {
94
- result.migrationId = { serial: 4 };
95
- }
96
- return result;
97
- };
98
-
99
- const getFirstWordAndRest = (input) => {
100
- const i = input.search(/(?=[A-Z])|[-_ ]/);
101
- if (i !== -1) {
102
- const restStart = input[i] === "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
103
- const rest = input.slice(restStart);
104
- return [input.slice(0, i), rest[0].toLowerCase() + rest.slice(1)];
105
- } else {
106
- return [input];
107
- }
108
- };
109
- const getTextAfterRegExp = (input, regex, length) => {
110
- let i = input.search(regex);
111
- if (i === -1) return;
112
- if (input[i] === "-" || input[i] === "_" || input[i] === " ") i++;
113
- i += length;
114
- const start = input[i] == "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
115
- const text = input.slice(start);
116
- return text[0].toLowerCase() + text.slice(1);
117
- };
118
- const getTextAfterTo = (input) => {
119
- return getTextAfterRegExp(input, /(To|-to|_to| to)[A-Z-_ ]/, 2);
120
- };
121
- const getTextAfterFrom = (input) => {
122
- return getTextAfterRegExp(input, /(From|-from|_from| from)[A-Z-_ ]/, 4);
123
- };
124
- const joinColumns = (columns) => {
125
- return columns.map((column) => `"${column}"`).join(", ");
126
- };
127
- const quoteWithSchema = ({
128
- schema,
129
- name
130
- }) => quoteTable(schema, name);
131
- const quoteTable = (schema, table) => schema ? `"${schema}"."${table}"` : `"${table}"`;
132
- const getSchemaAndTableFromName = (name) => {
133
- const i = name.indexOf(".");
134
- return i !== -1 ? [name.slice(0, i), name.slice(i + 1)] : [void 0, name];
135
- };
136
- const quoteNameFromString = (string) => {
137
- return quoteTable(...getSchemaAndTableFromName(string));
138
- };
139
- const quoteCustomType = (s) => {
140
- const [schema, type] = getSchemaAndTableFromName(s);
141
- return schema ? '"' + schema + '".' + type : type;
142
- };
143
- const quoteSchemaTable = (arg) => {
144
- return singleQuote(concatSchemaAndName(arg));
145
- };
146
- const concatSchemaAndName = ({
147
- schema,
148
- name
149
- }) => {
150
- return schema ? `${schema}.${name}` : name;
151
- };
152
- const makePopulateEnumQuery = (item) => {
153
- const [schema, name] = getSchemaAndTableFromName(item.enumName);
154
- return {
155
- text: `SELECT unnest(enum_range(NULL::${quoteTable(schema, name)}))::text`,
156
- then(result) {
157
- item.options.push(...result.rows.map(([value]) => value));
158
- }
159
- };
160
- };
161
- const transaction = (adapter, fn) => {
162
- return adapter.transaction(void 0, fn);
163
- };
164
- const queryLock = (trx) => trx.query(`SELECT pg_advisory_xact_lock('${RAKE_DB_LOCK_KEY}')`);
165
-
166
18
  const versionToString = (config, version) => config.migrationId === "timestamp" ? `${version}` : `${version}`.padStart(config.migrationId.serial, "0");
167
19
  const columnTypeToSql = (item) => {
168
20
  return item.data.isOfCustomType ? item instanceof DomainColumn ? quoteNameFromString(item.dataType) : quoteCustomType(item.toSQL()) : item.toSQL();
@@ -3066,6 +2918,9 @@ const createMigrationsTable = async (db, config) => {
3066
2918
  };
3067
2919
 
3068
2920
  const RAKE_DB_LOCK_KEY = "8582141715823621641";
2921
+ const transactionIfSingle = (params, fn) => {
2922
+ return params.config.transaction === "single" ? transaction(params.adapter, fn) : fn(params.adapter);
2923
+ };
3069
2924
  function makeMigrateFn(up, fn) {
3070
2925
  return async (params) => {
3071
2926
  const ctx = params.ctx || {};
@@ -3074,7 +2929,7 @@ function makeMigrateFn(up, fn) {
3074
2929
  const force = params.force ?? false;
3075
2930
  let migrations;
3076
2931
  try {
3077
- await transaction(params.adapter, async (trx) => {
2932
+ await transactionIfSingle(params, async (trx) => {
3078
2933
  const versions = await getMigratedVersionsMap(
3079
2934
  ctx,
3080
2935
  trx,
@@ -3085,7 +2940,7 @@ function makeMigrateFn(up, fn) {
3085
2940
  });
3086
2941
  } catch (err) {
3087
2942
  if (err instanceof NoMigrationsTableError) {
3088
- await transaction(params.adapter, async (trx) => {
2943
+ await transactionIfSingle(params, async (trx) => {
3089
2944
  await createMigrationsTable(trx, params.config);
3090
2945
  const versions = await getMigratedVersionsMap(
3091
2946
  ctx,
@@ -3204,6 +3059,7 @@ const migrateOrRollback = async (trx, config, set, versions, count, up, redo2, f
3204
3059
  }
3205
3060
  let loggedAboutStarting = false;
3206
3061
  let migrations;
3062
+ const migrationRunner = config.transaction === "single" ? runMigration : runMigrationInOwnTransaction;
3207
3063
  for (const file of set.migrations) {
3208
3064
  if (up && versionsMap[file.version] || !up && !versionsMap[file.version]) {
3209
3065
  continue;
@@ -3217,7 +3073,7 @@ const migrateOrRollback = async (trx, config, set, versions, count, up, redo2, f
3217
3073
  );
3218
3074
  }
3219
3075
  const changes = await getChanges(file, config);
3220
- const adapter = await runMigration(trx, up, changes, config);
3076
+ const adapter = await migrationRunner(trx, up, changes, config);
3221
3077
  await changeMigratedVersion(adapter, up, file, config);
3222
3078
  (migrations ?? (migrations = [])).push(file);
3223
3079
  if (up) {
@@ -3296,6 +3152,9 @@ const getChanges = async (file, config) => {
3296
3152
  }
3297
3153
  return changes;
3298
3154
  };
3155
+ const runMigrationInOwnTransaction = (adapter, ...rest) => {
3156
+ return transaction(adapter, (trx) => runMigration(trx, ...rest));
3157
+ };
3299
3158
  const runMigration = async (trx, up, changes, config) => {
3300
3159
  const db = createMigrationInterface(trx, up, config);
3301
3160
  if (changes.length) {
@@ -3317,6 +3176,176 @@ const changeMigratedVersion = async (adapter, up, file, config) => {
3317
3176
  );
3318
3177
  };
3319
3178
 
3179
+ const getFirstWordAndRest = (input) => {
3180
+ const i = input.search(/(?=[A-Z])|[-_ ]/);
3181
+ if (i !== -1) {
3182
+ const restStart = input[i] === "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
3183
+ const rest = input.slice(restStart);
3184
+ return [input.slice(0, i), rest[0].toLowerCase() + rest.slice(1)];
3185
+ } else {
3186
+ return [input];
3187
+ }
3188
+ };
3189
+ const getTextAfterRegExp = (input, regex, length) => {
3190
+ let i = input.search(regex);
3191
+ if (i === -1) return;
3192
+ if (input[i] === "-" || input[i] === "_" || input[i] === " ") i++;
3193
+ i += length;
3194
+ const start = input[i] == "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
3195
+ const text = input.slice(start);
3196
+ return text[0].toLowerCase() + text.slice(1);
3197
+ };
3198
+ const getTextAfterTo = (input) => {
3199
+ return getTextAfterRegExp(input, /(To|-to|_to| to)[A-Z-_ ]/, 2);
3200
+ };
3201
+ const getTextAfterFrom = (input) => {
3202
+ return getTextAfterRegExp(input, /(From|-from|_from| from)[A-Z-_ ]/, 4);
3203
+ };
3204
+ const joinColumns = (columns) => {
3205
+ return columns.map((column) => `"${column}"`).join(", ");
3206
+ };
3207
+ const quoteWithSchema = ({
3208
+ schema,
3209
+ name
3210
+ }) => quoteTable(schema, name);
3211
+ const quoteTable = (schema, table) => schema ? `"${schema}"."${table}"` : `"${table}"`;
3212
+ const getSchemaAndTableFromName = (name) => {
3213
+ const i = name.indexOf(".");
3214
+ return i !== -1 ? [name.slice(0, i), name.slice(i + 1)] : [void 0, name];
3215
+ };
3216
+ const quoteNameFromString = (string) => {
3217
+ return quoteTable(...getSchemaAndTableFromName(string));
3218
+ };
3219
+ const quoteCustomType = (s) => {
3220
+ const [schema, type] = getSchemaAndTableFromName(s);
3221
+ return schema ? '"' + schema + '".' + type : type;
3222
+ };
3223
+ const quoteSchemaTable = (arg) => {
3224
+ return singleQuote(concatSchemaAndName(arg));
3225
+ };
3226
+ const concatSchemaAndName = ({
3227
+ schema,
3228
+ name
3229
+ }) => {
3230
+ return schema ? `${schema}.${name}` : name;
3231
+ };
3232
+ const makePopulateEnumQuery = (item) => {
3233
+ const [schema, name] = getSchemaAndTableFromName(item.enumName);
3234
+ return {
3235
+ text: `SELECT unnest(enum_range(NULL::${quoteTable(schema, name)}))::text`,
3236
+ then(result) {
3237
+ item.options.push(...result.rows.map(([value]) => value));
3238
+ }
3239
+ };
3240
+ };
3241
+ const transaction = (adapter, fn) => {
3242
+ return adapter.transaction(void 0, fn);
3243
+ };
3244
+ const queryLock = (trx) => trx.query(`SELECT pg_advisory_xact_lock('${RAKE_DB_LOCK_KEY}')`);
3245
+ const getCliParam = (args, name) => {
3246
+ if (args) {
3247
+ const key = "--" + name;
3248
+ for (let i = 0; i < args.length; i += 1) {
3249
+ const arg = args[i];
3250
+ if (arg === key) return args[i + 1];
3251
+ else if (arg.startsWith(key)) return arg.slice(key.length + 1);
3252
+ }
3253
+ }
3254
+ return;
3255
+ };
3256
+
3257
+ const migrationConfigDefaults = {
3258
+ schemaConfig: defaultSchemaConfig,
3259
+ migrationsPath: path.join("src", "db", "migrations"),
3260
+ migrationId: { serial: 4 },
3261
+ migrationsTable: "schemaMigrations",
3262
+ snakeCase: false,
3263
+ commands: {},
3264
+ log: true,
3265
+ logger: console,
3266
+ import() {
3267
+ throw new Error(
3268
+ "Add `import: (path) => import(path),` setting to `rakeDb` config"
3269
+ );
3270
+ }
3271
+ };
3272
+ const ensureMigrationsPath = (config) => {
3273
+ if (!config.migrationsPath) {
3274
+ config.migrationsPath = migrationConfigDefaults.migrationsPath;
3275
+ }
3276
+ if (!path.isAbsolute(config.migrationsPath)) {
3277
+ config.migrationsPath = path.resolve(
3278
+ config.basePath,
3279
+ config.migrationsPath
3280
+ );
3281
+ }
3282
+ return config;
3283
+ };
3284
+ const ensureBasePathAndDbScript = (config, intermediateCallers = 0) => {
3285
+ if (config.basePath && config.dbScript) return config;
3286
+ let filePath = getStackTrace()?.[3 + intermediateCallers].getFileName();
3287
+ if (!filePath) {
3288
+ throw new Error(
3289
+ "Failed to determine path to db script. Please set basePath option of rakeDb"
3290
+ );
3291
+ }
3292
+ if (filePath.startsWith("file://")) {
3293
+ filePath = fileURLToPath(filePath);
3294
+ }
3295
+ const ext = path.extname(filePath);
3296
+ if (ext !== ".ts" && ext !== ".js" && ext !== ".mjs") {
3297
+ throw new Error(
3298
+ `Add a .ts suffix to the "${path.basename(filePath)}" when calling it`
3299
+ );
3300
+ }
3301
+ config.basePath = path.dirname(filePath);
3302
+ config.dbScript = path.basename(filePath);
3303
+ return config;
3304
+ };
3305
+ const processRakeDbConfig = (config, args) => {
3306
+ const result = { ...migrationConfigDefaults, ...config };
3307
+ if (!result.log) {
3308
+ delete result.logger;
3309
+ }
3310
+ ensureBasePathAndDbScript(result, 1);
3311
+ ensureMigrationsPath(result);
3312
+ if (!result.recurrentPath) {
3313
+ result.recurrentPath = path.join(
3314
+ result.migrationsPath,
3315
+ "recurrent"
3316
+ );
3317
+ }
3318
+ if ("recurrentPath" in result && !path.isAbsolute(result.recurrentPath)) {
3319
+ result.recurrentPath = path.resolve(result.basePath, result.recurrentPath);
3320
+ }
3321
+ if ("baseTable" in config && config.baseTable) {
3322
+ const { types, snakeCase, language } = config.baseTable.prototype;
3323
+ result.columnTypes = types || makeColumnTypes(defaultSchemaConfig);
3324
+ if (snakeCase) result.snakeCase = true;
3325
+ if (language) result.language = language;
3326
+ } else {
3327
+ const ct = "columnTypes" in config && config.columnTypes;
3328
+ result.columnTypes = (typeof ct === "function" ? ct(
3329
+ makeColumnTypes(defaultSchemaConfig)
3330
+ ) : ct) || makeColumnTypes(defaultSchemaConfig);
3331
+ }
3332
+ if (config.migrationId === "serial") {
3333
+ result.migrationId = { serial: 4 };
3334
+ }
3335
+ const transaction = getCliParam(args, "transaction");
3336
+ if (transaction) {
3337
+ if (transaction !== "single" && transaction !== "per-migration") {
3338
+ throw new Error(
3339
+ `Unsupported transaction param ${transaction}, expected single or per-migration`
3340
+ );
3341
+ }
3342
+ result.transaction = transaction;
3343
+ } else if (!result.transaction) {
3344
+ result.transaction = "single";
3345
+ }
3346
+ return result;
3347
+ };
3348
+
3320
3349
  const ESC = "\x1B";
3321
3350
  const CSI = `${ESC}[`;
3322
3351
  const cursorShow = `${CSI}?25h`;
@@ -5784,7 +5813,7 @@ const rakeDbCommands = {
5784
5813
  };
5785
5814
 
5786
5815
  const rakeDbWithAdapters = (adapters, partialConfig, args = process.argv.slice(2)) => {
5787
- const config = processRakeDbConfig(partialConfig);
5816
+ const config = processRakeDbConfig(partialConfig, args);
5788
5817
  const promise = runCommand(
5789
5818
  adapters,
5790
5819
  config,
@@ -5838,7 +5867,8 @@ const makeMigrateAdapter = (config) => {
5838
5867
  columnTypes: conf.columnTypes || makeColumnTypes(defaultSchemaConfig),
5839
5868
  migrationId: conf.migrationId || migrationConfigDefaults.migrationId,
5840
5869
  migrationsTable: conf.migrationsTable || migrationConfigDefaults.migrationsTable,
5841
- import: conf.import || migrationConfigDefaults.import
5870
+ import: conf.import || migrationConfigDefaults.import,
5871
+ transaction: conf.transaction || "single"
5842
5872
  }
5843
5873
  });
5844
5874
  };