@sqb/migrator 4.10.1 → 4.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PgMigrationAdapter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path_1 = tslib_1.__importDefault(require("path"));
4
6
  const postgresql_client_1 = require("postgresql-client");
5
7
  const postgres_1 = require("@sqb/postgres");
6
8
  const migration_adapter_js_1 = require("../migration-adapter.js");
@@ -11,7 +13,6 @@ class PgMigrationAdapter extends migration_adapter_js_1.MigrationAdapter {
11
13
  constructor() {
12
14
  super(...arguments);
13
15
  this._infoSchema = 'public';
14
- this._packageName = '';
15
16
  this._version = 0;
16
17
  this._status = types_js_1.MigrationStatus.idle;
17
18
  this.defaultVariables = {
@@ -23,7 +24,7 @@ class PgMigrationAdapter extends migration_adapter_js_1.MigrationAdapter {
23
24
  this.eventTable = 'migration_events';
24
25
  }
25
26
  get packageName() {
26
- return this._packageName;
27
+ return this._migrationPackage.name;
27
28
  }
28
29
  get version() {
29
30
  return this._version;
@@ -46,6 +47,7 @@ class PgMigrationAdapter extends migration_adapter_js_1.MigrationAdapter {
46
47
  try {
47
48
  const adapter = new PgMigrationAdapter();
48
49
  adapter._connection = connection;
50
+ adapter._migrationPackage = options.migrationPackage;
49
51
  adapter._infoSchema = options.infoSchema || '__migration';
50
52
  adapter.defaultVariables.schema = options.connection.schema || '';
51
53
  if (!adapter.defaultVariables.schema) {
@@ -74,7 +76,9 @@ CREATE TABLE IF NOT EXISTS ${adapter.eventTableFull}
74
76
  version integer not null default 0,
75
77
  event varchar(16) not null,
76
78
  event_time timestamp without time zone not null,
79
+ title text,
77
80
  message text not null,
81
+ filename text,
78
82
  details text,
79
83
  CONSTRAINT pk_${adapter.eventTable} PRIMARY KEY (id)
80
84
  )`);
@@ -108,14 +112,16 @@ CREATE TABLE IF NOT EXISTS ${adapter.eventTableFull}
108
112
  const params = [];
109
113
  if (info.status && info.status !== this.status) {
110
114
  params.push(info.status);
111
- sql += ', status = $' + (params.length);
115
+ sql += ',\n status = $' + (params.length);
112
116
  }
113
117
  if (info.version && info.version !== this.version) {
114
118
  params.push(info.version);
115
- sql += ', current_version = $' + (params.length);
119
+ sql += ',\n current_version = $' + (params.length);
116
120
  }
117
121
  if (sql) {
118
- sql = `update ${this.summaryTableFull} set updated_at = current_timestamp` + sql;
122
+ params.push(this.packageName);
123
+ sql = `update ${this.summaryTableFull} set updated_at = current_timestamp` + sql +
124
+ `\n where package_name =$` + (params.length);
119
125
  await this._connection.query(sql, { params });
120
126
  if (info.status)
121
127
  this._status = info.status;
@@ -125,30 +131,38 @@ CREATE TABLE IF NOT EXISTS ${adapter.eventTableFull}
125
131
  }
126
132
  async writeEvent(event) {
127
133
  const sql = `insert into ${this.eventTableFull} ` +
128
- '(package_name, version, event, event_time, message, details) ' +
129
- 'values ($1, $2, $3, CURRENT_TIMESTAMP, $4, $5)';
134
+ '(package_name, version, event, event_time, title, message, filename, details) ' +
135
+ 'values ($1, $2, $3, CURRENT_TIMESTAMP, $4, $5, $6, $7)';
130
136
  await this._connection.query(sql, {
131
137
  params: [
132
138
  this.packageName, event.version, event.event,
133
- event.message, event.details
139
+ event.title, event.message, event.filename, event.details
134
140
  ]
135
141
  });
136
142
  }
137
- async executeTask(task, variables) {
143
+ async executeTask(migrationPackage, migration, task, variables) {
138
144
  variables = {
139
145
  ...this.defaultVariables,
140
146
  ...variables
141
147
  };
142
148
  if ((0, migration_package_js_1.isSqlScriptMigrationTask)(task)) {
143
149
  try {
144
- const script = task.script
145
- .replace(/(\${(\w+)})/g, (s, ...args) => variables[args[1]] || s);
150
+ let script;
151
+ if (typeof task.script === 'function') {
152
+ script = await task.script({ migrationPackage, migration, task, variables });
153
+ }
154
+ else
155
+ script = task.script;
156
+ if (typeof script !== 'string')
157
+ return;
158
+ script = script
159
+ .replace(/(\$\((\w+)\))/g, (s, ...args) => variables[args[1]] || s);
146
160
  await this._connection.execute(script);
147
161
  }
148
162
  catch (e) {
149
163
  let msg = `Error in task "${task.title}"`;
150
164
  if (task.filename)
151
- msg += '\n at ' + task.filename;
165
+ msg += '\n at ' + path_1.default.relative(migrationPackage.baseDir, task.filename);
152
166
  if (e.lineNr) {
153
167
  if (!task.filename)
154
168
  e.message += '\n at';
@@ -25,7 +25,7 @@ class DbMigrator extends strict_typed_events_1.AsyncEventEmitter {
25
25
  let migrationAdapter;
26
26
  switch (options.connection.dialect) {
27
27
  case 'postgres': {
28
- migrationAdapter = await pg_migration_adapter_js_1.PgMigrationAdapter.create(options);
28
+ migrationAdapter = await pg_migration_adapter_js_1.PgMigrationAdapter.create({ ...options, migrationPackage });
29
29
  break;
30
30
  }
31
31
  default:
@@ -49,41 +49,63 @@ class DbMigrator extends strict_typed_events_1.AsyncEventEmitter {
49
49
  await migrationAdapter.backupDatabase();
50
50
  }
51
51
  // Execute migration tasks
52
+ let migrationIndex = -1;
52
53
  for (const migration of migrations) {
54
+ migrationIndex++;
53
55
  if (migration.version > targetVersion || migrationAdapter.version >= migration.version)
54
56
  continue;
57
+ await this.emitAsync('migration-start', {
58
+ migration,
59
+ total: migrations.length,
60
+ index: migrationIndex
61
+ });
55
62
  for (let index = 0; index < migration.tasks.length; index++) {
56
63
  task = migration.tasks[index];
57
- await this.emitAsync('task-start', { task: task.title, total, index });
64
+ await this.emitAsync('task-start', { migration, task, total, index });
58
65
  await migrationAdapter.update({ status: types_js_1.MigrationStatus.busy });
59
66
  await migrationAdapter.writeEvent({
60
67
  event: migration_adapter_js_1.MigrationAdapter.EventKind.started,
61
68
  version: migration.version,
69
+ title: task.title,
70
+ filename: task.filename,
62
71
  message: `Task "${task.title}" started`
63
72
  });
64
73
  try {
65
- await migrationAdapter.executeTask(task, {
74
+ await migrationAdapter.executeTask(migrationPackage, migration, task, {
66
75
  schema: options.connection.schema,
67
76
  ...options.scriptVariables,
68
77
  });
69
78
  await migrationAdapter.writeEvent({
70
79
  event: migration_adapter_js_1.MigrationAdapter.EventKind.success,
71
80
  version: migration.version,
72
- message: `Task "${task.title}" completed`
81
+ title: task.title,
82
+ filename: task.filename,
83
+ message: `Task "${task.title}" completed`,
73
84
  });
74
85
  }
75
86
  catch (e) {
76
87
  await migrationAdapter.writeEvent({
77
88
  event: migration_adapter_js_1.MigrationAdapter.EventKind.error,
78
89
  version: migration.version,
79
- message: String(e)
90
+ title: task.title,
91
+ filename: task.filename,
92
+ message: String(e),
93
+ details: e.message + '\n\n' +
94
+ Object.keys(e)
95
+ .filter(k => e[k] != null)
96
+ .map(k => k + ': ' + e[k]).join('\n')
80
97
  });
81
98
  // noinspection ExceptionCaughtLocallyJS
82
99
  throw e;
83
100
  }
84
- await this.emitAsync('task-finish', { task: task.title, total, index });
101
+ await this.emitAsync('task-finish', { migration, task, total, index });
85
102
  }
86
- await migrationAdapter.update({ version: migration.version });
103
+ await migrationAdapter.update({ version: migration.version, status: types_js_1.MigrationStatus.idle });
104
+ await this.emitAsync('migration-finish', {
105
+ migration,
106
+ total: migrations.length,
107
+ index: migrationIndex
108
+ });
87
109
  }
88
110
  }
89
111
  catch (e) {
@@ -7,7 +7,8 @@ const promises_1 = tslib_1.__importDefault(require("fs/promises"));
7
7
  const path_1 = tslib_1.__importDefault(require("path"));
8
8
  const get_calling_filename_js_1 = require("./utils/get-calling-filename.js");
9
9
  function isSqlScriptMigrationTask(x) {
10
- return typeof x === 'object' && typeof x.script === 'string';
10
+ return typeof x === 'object' &&
11
+ (typeof x.script === 'string' || typeof x.script === 'function');
11
12
  }
12
13
  exports.isSqlScriptMigrationTask = isSqlScriptMigrationTask;
13
14
  function isInsertDataMigrationTask(x) {
@@ -24,13 +25,14 @@ exports.isCustomMigrationTask = isCustomMigrationTask;
24
25
  var MigrationPackage;
25
26
  (function (MigrationPackage) {
26
27
  async function load(asyncConfig) {
28
+ const baseDir = asyncConfig.baseDir || path_1.default.dirname((0, get_calling_filename_js_1.getCallingFilename)(1));
27
29
  const out = {
28
30
  ...asyncConfig,
31
+ baseDir,
29
32
  migrations: []
30
33
  };
31
34
  if (!Array.isArray(asyncConfig.migrations))
32
35
  throw new TypeError('You must provide array of MigrationConfig in "migrations" property');
33
- const baseDir = path_1.default.dirname((0, get_calling_filename_js_1.getCallingFilename)(1));
34
36
  if (asyncConfig.migrations?.length) {
35
37
  const srcMigrations = [];
36
38
  const trgMigrations = [];
@@ -41,12 +43,12 @@ var MigrationPackage;
41
43
  if (typeof x === 'object' && x.tasks)
42
44
  srcMigrations.push(x);
43
45
  else if (typeof x === 'string') {
44
- srcMigrations.push(...await loadMigrations(path_1.default.resolve(baseDir, x.replace(/\\/g, '/'))));
46
+ srcMigrations.push(...await loadMigrations(baseDir, x.replace(/\\/g, '/')));
45
47
  }
46
48
  }
47
49
  srcMigrations.sort((a, b) => a.version - b.version);
48
50
  for (const migration of srcMigrations) {
49
- const trgMigration = { ...migration, tasks: [] };
51
+ const trgMigration = { baseDir: '', ...migration, tasks: [] };
50
52
  trgMigrations.push(trgMigration);
51
53
  const srcTasks = migration.tasks;
52
54
  trgMigration.tasks = [];
@@ -56,8 +58,12 @@ var MigrationPackage;
56
58
  }
57
59
  else if (typeof t === 'string') {
58
60
  let pattern = t.replace(/\\/g, '/');
59
- pattern = path_1.default.resolve(migration.dirname || baseDir, pattern);
60
- const files = await (0, fast_glob_1.default)(pattern, { absolute: true, onlyFiles: true });
61
+ pattern = path_1.default.resolve(path_1.default.join(baseDir, trgMigration.baseDir, pattern));
62
+ const files = await (0, fast_glob_1.default)(pattern, {
63
+ absolute: true,
64
+ onlyFiles: true
65
+ });
66
+ files.sort();
61
67
  for (const filename of files) {
62
68
  const ext = path_1.default.extname(filename).toLowerCase();
63
69
  if (!path_1.default.basename(filename, ext).endsWith('.task'))
@@ -65,16 +71,20 @@ var MigrationPackage;
65
71
  if (ext === '.sql') {
66
72
  const script = await promises_1.default.readFile(filename, 'utf-8');
67
73
  trgMigration.tasks.push({
68
- title: path_1.default.basename(filename),
74
+ title: path_1.default.basename(filename, ext),
69
75
  filename,
70
76
  script
71
77
  });
72
78
  }
73
- else if (ext === '.json') {
79
+ else if (['.json', '.js', '.ts', '.cjs', '.mjs'].includes(ext)) {
74
80
  try {
75
- const json = JSON.parse(await promises_1.default.readFile(filename, 'utf-8'));
81
+ let json = ext === '.json'
82
+ ? JSON.parse(await promises_1.default.readFile(filename, 'utf-8'))
83
+ : await Promise.resolve(`${filename}`).then(s => tslib_1.__importStar(require(s)));
76
84
  if (typeof json !== 'object')
77
85
  continue;
86
+ if (json.__esModule)
87
+ json = json.default;
78
88
  if (json.script) {
79
89
  json.title = json.title || 'Run sql script';
80
90
  json.filename = filename;
@@ -106,9 +116,9 @@ var MigrationPackage;
106
116
  return out;
107
117
  }
108
118
  MigrationPackage.load = load;
109
- async function loadMigrations(pattern) {
119
+ async function loadMigrations(baseDir, pattern) {
110
120
  const out = [];
111
- const files = await (0, fast_glob_1.default)(pattern, { absolute: true, onlyFiles: true });
121
+ const files = await (0, fast_glob_1.default)(path_1.default.join(baseDir, pattern), { absolute: true, onlyFiles: true });
112
122
  for (const filename of files) {
113
123
  const ext = path_1.default.extname(filename).toLowerCase();
114
124
  if (path_1.default.basename(filename, ext) !== 'migration')
@@ -129,7 +139,7 @@ var MigrationPackage;
129
139
  }
130
140
  }
131
141
  if (json && typeof json === 'object' && json.version && Array.isArray(json.tasks)) {
132
- json.dirname = path_1.default.dirname(filename);
142
+ json.baseDir = path_1.default.relative(baseDir, path_1.default.dirname(filename));
133
143
  out.push(json);
134
144
  }
135
145
  }
@@ -1,3 +1,4 @@
1
+ import path from 'path';
1
2
  import { stringifyValueForSQL } from 'postgresql-client';
2
3
  import { PgAdapter } from '@sqb/postgres';
3
4
  import { MigrationAdapter } from '../migration-adapter.js';
@@ -8,7 +9,6 @@ export class PgMigrationAdapter extends MigrationAdapter {
8
9
  constructor() {
9
10
  super(...arguments);
10
11
  this._infoSchema = 'public';
11
- this._packageName = '';
12
12
  this._version = 0;
13
13
  this._status = MigrationStatus.idle;
14
14
  this.defaultVariables = {
@@ -20,7 +20,7 @@ export class PgMigrationAdapter extends MigrationAdapter {
20
20
  this.eventTable = 'migration_events';
21
21
  }
22
22
  get packageName() {
23
- return this._packageName;
23
+ return this._migrationPackage.name;
24
24
  }
25
25
  get version() {
26
26
  return this._version;
@@ -43,6 +43,7 @@ export class PgMigrationAdapter extends MigrationAdapter {
43
43
  try {
44
44
  const adapter = new PgMigrationAdapter();
45
45
  adapter._connection = connection;
46
+ adapter._migrationPackage = options.migrationPackage;
46
47
  adapter._infoSchema = options.infoSchema || '__migration';
47
48
  adapter.defaultVariables.schema = options.connection.schema || '';
48
49
  if (!adapter.defaultVariables.schema) {
@@ -71,7 +72,9 @@ CREATE TABLE IF NOT EXISTS ${adapter.eventTableFull}
71
72
  version integer not null default 0,
72
73
  event varchar(16) not null,
73
74
  event_time timestamp without time zone not null,
75
+ title text,
74
76
  message text not null,
77
+ filename text,
75
78
  details text,
76
79
  CONSTRAINT pk_${adapter.eventTable} PRIMARY KEY (id)
77
80
  )`);
@@ -105,14 +108,16 @@ CREATE TABLE IF NOT EXISTS ${adapter.eventTableFull}
105
108
  const params = [];
106
109
  if (info.status && info.status !== this.status) {
107
110
  params.push(info.status);
108
- sql += ', status = $' + (params.length);
111
+ sql += ',\n status = $' + (params.length);
109
112
  }
110
113
  if (info.version && info.version !== this.version) {
111
114
  params.push(info.version);
112
- sql += ', current_version = $' + (params.length);
115
+ sql += ',\n current_version = $' + (params.length);
113
116
  }
114
117
  if (sql) {
115
- sql = `update ${this.summaryTableFull} set updated_at = current_timestamp` + sql;
118
+ params.push(this.packageName);
119
+ sql = `update ${this.summaryTableFull} set updated_at = current_timestamp` + sql +
120
+ `\n where package_name =$` + (params.length);
116
121
  await this._connection.query(sql, { params });
117
122
  if (info.status)
118
123
  this._status = info.status;
@@ -122,30 +127,38 @@ CREATE TABLE IF NOT EXISTS ${adapter.eventTableFull}
122
127
  }
123
128
  async writeEvent(event) {
124
129
  const sql = `insert into ${this.eventTableFull} ` +
125
- '(package_name, version, event, event_time, message, details) ' +
126
- 'values ($1, $2, $3, CURRENT_TIMESTAMP, $4, $5)';
130
+ '(package_name, version, event, event_time, title, message, filename, details) ' +
131
+ 'values ($1, $2, $3, CURRENT_TIMESTAMP, $4, $5, $6, $7)';
127
132
  await this._connection.query(sql, {
128
133
  params: [
129
134
  this.packageName, event.version, event.event,
130
- event.message, event.details
135
+ event.title, event.message, event.filename, event.details
131
136
  ]
132
137
  });
133
138
  }
134
- async executeTask(task, variables) {
139
+ async executeTask(migrationPackage, migration, task, variables) {
135
140
  variables = {
136
141
  ...this.defaultVariables,
137
142
  ...variables
138
143
  };
139
144
  if (isSqlScriptMigrationTask(task)) {
140
145
  try {
141
- const script = task.script
142
- .replace(/(\${(\w+)})/g, (s, ...args) => variables[args[1]] || s);
146
+ let script;
147
+ if (typeof task.script === 'function') {
148
+ script = await task.script({ migrationPackage, migration, task, variables });
149
+ }
150
+ else
151
+ script = task.script;
152
+ if (typeof script !== 'string')
153
+ return;
154
+ script = script
155
+ .replace(/(\$\((\w+)\))/g, (s, ...args) => variables[args[1]] || s);
143
156
  await this._connection.execute(script);
144
157
  }
145
158
  catch (e) {
146
159
  let msg = `Error in task "${task.title}"`;
147
160
  if (task.filename)
148
- msg += '\n at ' + task.filename;
161
+ msg += '\n at ' + path.relative(migrationPackage.baseDir, task.filename);
149
162
  if (e.lineNr) {
150
163
  if (!task.filename)
151
164
  e.message += '\n at';
@@ -22,7 +22,7 @@ export class DbMigrator extends AsyncEventEmitter {
22
22
  let migrationAdapter;
23
23
  switch (options.connection.dialect) {
24
24
  case 'postgres': {
25
- migrationAdapter = await PgMigrationAdapter.create(options);
25
+ migrationAdapter = await PgMigrationAdapter.create({ ...options, migrationPackage });
26
26
  break;
27
27
  }
28
28
  default:
@@ -46,41 +46,63 @@ export class DbMigrator extends AsyncEventEmitter {
46
46
  await migrationAdapter.backupDatabase();
47
47
  }
48
48
  // Execute migration tasks
49
+ let migrationIndex = -1;
49
50
  for (const migration of migrations) {
51
+ migrationIndex++;
50
52
  if (migration.version > targetVersion || migrationAdapter.version >= migration.version)
51
53
  continue;
54
+ await this.emitAsync('migration-start', {
55
+ migration,
56
+ total: migrations.length,
57
+ index: migrationIndex
58
+ });
52
59
  for (let index = 0; index < migration.tasks.length; index++) {
53
60
  task = migration.tasks[index];
54
- await this.emitAsync('task-start', { task: task.title, total, index });
61
+ await this.emitAsync('task-start', { migration, task, total, index });
55
62
  await migrationAdapter.update({ status: MigrationStatus.busy });
56
63
  await migrationAdapter.writeEvent({
57
64
  event: MigrationAdapter.EventKind.started,
58
65
  version: migration.version,
66
+ title: task.title,
67
+ filename: task.filename,
59
68
  message: `Task "${task.title}" started`
60
69
  });
61
70
  try {
62
- await migrationAdapter.executeTask(task, {
71
+ await migrationAdapter.executeTask(migrationPackage, migration, task, {
63
72
  schema: options.connection.schema,
64
73
  ...options.scriptVariables,
65
74
  });
66
75
  await migrationAdapter.writeEvent({
67
76
  event: MigrationAdapter.EventKind.success,
68
77
  version: migration.version,
69
- message: `Task "${task.title}" completed`
78
+ title: task.title,
79
+ filename: task.filename,
80
+ message: `Task "${task.title}" completed`,
70
81
  });
71
82
  }
72
83
  catch (e) {
73
84
  await migrationAdapter.writeEvent({
74
85
  event: MigrationAdapter.EventKind.error,
75
86
  version: migration.version,
76
- message: String(e)
87
+ title: task.title,
88
+ filename: task.filename,
89
+ message: String(e),
90
+ details: e.message + '\n\n' +
91
+ Object.keys(e)
92
+ .filter(k => e[k] != null)
93
+ .map(k => k + ': ' + e[k]).join('\n')
77
94
  });
78
95
  // noinspection ExceptionCaughtLocallyJS
79
96
  throw e;
80
97
  }
81
- await this.emitAsync('task-finish', { task: task.title, total, index });
98
+ await this.emitAsync('task-finish', { migration, task, total, index });
82
99
  }
83
- await migrationAdapter.update({ version: migration.version });
100
+ await migrationAdapter.update({ version: migration.version, status: MigrationStatus.idle });
101
+ await this.emitAsync('migration-finish', {
102
+ migration,
103
+ total: migrations.length,
104
+ index: migrationIndex
105
+ });
84
106
  }
85
107
  }
86
108
  catch (e) {
@@ -3,7 +3,8 @@ import fs from 'fs/promises';
3
3
  import path from 'path';
4
4
  import { getCallingFilename } from './utils/get-calling-filename.js';
5
5
  export function isSqlScriptMigrationTask(x) {
6
- return typeof x === 'object' && typeof x.script === 'string';
6
+ return typeof x === 'object' &&
7
+ (typeof x.script === 'string' || typeof x.script === 'function');
7
8
  }
8
9
  export function isInsertDataMigrationTask(x) {
9
10
  return typeof x === 'object' &&
@@ -17,13 +18,14 @@ export function isCustomMigrationTask(x) {
17
18
  export var MigrationPackage;
18
19
  (function (MigrationPackage) {
19
20
  async function load(asyncConfig) {
21
+ const baseDir = asyncConfig.baseDir || path.dirname(getCallingFilename(1));
20
22
  const out = {
21
23
  ...asyncConfig,
24
+ baseDir,
22
25
  migrations: []
23
26
  };
24
27
  if (!Array.isArray(asyncConfig.migrations))
25
28
  throw new TypeError('You must provide array of MigrationConfig in "migrations" property');
26
- const baseDir = path.dirname(getCallingFilename(1));
27
29
  if (asyncConfig.migrations?.length) {
28
30
  const srcMigrations = [];
29
31
  const trgMigrations = [];
@@ -34,12 +36,12 @@ export var MigrationPackage;
34
36
  if (typeof x === 'object' && x.tasks)
35
37
  srcMigrations.push(x);
36
38
  else if (typeof x === 'string') {
37
- srcMigrations.push(...await loadMigrations(path.resolve(baseDir, x.replace(/\\/g, '/'))));
39
+ srcMigrations.push(...await loadMigrations(baseDir, x.replace(/\\/g, '/')));
38
40
  }
39
41
  }
40
42
  srcMigrations.sort((a, b) => a.version - b.version);
41
43
  for (const migration of srcMigrations) {
42
- const trgMigration = { ...migration, tasks: [] };
44
+ const trgMigration = { baseDir: '', ...migration, tasks: [] };
43
45
  trgMigrations.push(trgMigration);
44
46
  const srcTasks = migration.tasks;
45
47
  trgMigration.tasks = [];
@@ -49,8 +51,12 @@ export var MigrationPackage;
49
51
  }
50
52
  else if (typeof t === 'string') {
51
53
  let pattern = t.replace(/\\/g, '/');
52
- pattern = path.resolve(migration.dirname || baseDir, pattern);
53
- const files = await glob(pattern, { absolute: true, onlyFiles: true });
54
+ pattern = path.resolve(path.join(baseDir, trgMigration.baseDir, pattern));
55
+ const files = await glob(pattern, {
56
+ absolute: true,
57
+ onlyFiles: true
58
+ });
59
+ files.sort();
54
60
  for (const filename of files) {
55
61
  const ext = path.extname(filename).toLowerCase();
56
62
  if (!path.basename(filename, ext).endsWith('.task'))
@@ -58,16 +64,20 @@ export var MigrationPackage;
58
64
  if (ext === '.sql') {
59
65
  const script = await fs.readFile(filename, 'utf-8');
60
66
  trgMigration.tasks.push({
61
- title: path.basename(filename),
67
+ title: path.basename(filename, ext),
62
68
  filename,
63
69
  script
64
70
  });
65
71
  }
66
- else if (ext === '.json') {
72
+ else if (['.json', '.js', '.ts', '.cjs', '.mjs'].includes(ext)) {
67
73
  try {
68
- const json = JSON.parse(await fs.readFile(filename, 'utf-8'));
74
+ let json = ext === '.json'
75
+ ? JSON.parse(await fs.readFile(filename, 'utf-8'))
76
+ : await import(filename);
69
77
  if (typeof json !== 'object')
70
78
  continue;
79
+ if (json.__esModule)
80
+ json = json.default;
71
81
  if (json.script) {
72
82
  json.title = json.title || 'Run sql script';
73
83
  json.filename = filename;
@@ -99,9 +109,9 @@ export var MigrationPackage;
99
109
  return out;
100
110
  }
101
111
  MigrationPackage.load = load;
102
- async function loadMigrations(pattern) {
112
+ async function loadMigrations(baseDir, pattern) {
103
113
  const out = [];
104
- const files = await glob(pattern, { absolute: true, onlyFiles: true });
114
+ const files = await glob(path.join(baseDir, pattern), { absolute: true, onlyFiles: true });
105
115
  for (const filename of files) {
106
116
  const ext = path.extname(filename).toLowerCase();
107
117
  if (path.basename(filename, ext) !== 'migration')
@@ -122,7 +132,7 @@ export var MigrationPackage;
122
132
  }
123
133
  }
124
134
  if (json && typeof json === 'object' && json.version && Array.isArray(json.tasks)) {
125
- json.dirname = path.dirname(filename);
135
+ json.baseDir = path.relative(baseDir, path.dirname(filename));
126
136
  out.push(json);
127
137
  }
128
138
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sqb/migrator",
3
3
  "description": "Database migrator for SQB",
4
- "version": "4.10.1",
4
+ "version": "4.10.3",
5
5
  "author": "Panates",
6
6
  "contributors": [
7
7
  "Eray Hanoglu <e.hanoglu@panates.com>",
@@ -36,9 +36,9 @@
36
36
  "ts-gems": "^2.5.0"
37
37
  },
38
38
  "peerDependencies": {
39
- "@sqb/builder": "^4.10.1",
40
- "@sqb/connect": "^4.10.1",
41
- "@sqb/postgres": "^4.10.1"
39
+ "@sqb/builder": "^4.10.3",
40
+ "@sqb/connect": "^4.10.3",
41
+ "@sqb/postgres": "^4.10.3"
42
42
  },
43
43
  "engines": {
44
44
  "node": ">=16.0",
@@ -1,12 +1,13 @@
1
1
  import { Connection } from 'postgresql-client';
2
+ import { StrictOmit } from 'ts-gems';
2
3
  import type { DbMigratorOptions } from '../db-migrator.js';
3
4
  import { MigrationAdapter } from '../migration-adapter.js';
4
- import { MigrationTask } from '../migration-package.js';
5
+ import { Migration, MigrationPackage, MigrationTask } from '../migration-package.js';
5
6
  import { MigrationStatus } from '../types.js';
6
7
  export declare class PgMigrationAdapter extends MigrationAdapter {
7
8
  protected _connection: Connection;
8
9
  protected _infoSchema: string;
9
- protected _packageName: string;
10
+ protected _migrationPackage: MigrationPackage;
10
11
  protected _version: number;
11
12
  protected _status: MigrationStatus;
12
13
  protected defaultVariables: {
@@ -22,7 +23,9 @@ export declare class PgMigrationAdapter extends MigrationAdapter {
22
23
  get infoSchema(): string;
23
24
  get summaryTableFull(): string;
24
25
  get eventTableFull(): string;
25
- static create(options: DbMigratorOptions): Promise<PgMigrationAdapter>;
26
+ static create(options: StrictOmit<DbMigratorOptions, 'migrationPackage'> & {
27
+ migrationPackage: MigrationPackage;
28
+ }): Promise<PgMigrationAdapter>;
26
29
  close(): Promise<void>;
27
30
  refresh(): Promise<void>;
28
31
  update(info: {
@@ -30,7 +33,7 @@ export declare class PgMigrationAdapter extends MigrationAdapter {
30
33
  version?: number;
31
34
  }): Promise<void>;
32
35
  writeEvent(event: MigrationAdapter.Event): Promise<void>;
33
- executeTask(task: MigrationTask, variables: Record<string, any>): Promise<void>;
36
+ executeTask(migrationPackage: MigrationPackage, migration: Migration, task: MigrationTask, variables: Record<string, any>): Promise<void>;
34
37
  backupDatabase(): Promise<void>;
35
38
  lockSchema(): Promise<void>;
36
39
  restoreDatabase(): Promise<void>;
@@ -1,10 +1,10 @@
1
1
  import { AsyncEventEmitter } from 'strict-typed-events';
2
2
  import { ClientConfiguration } from '@sqb/connect';
3
3
  import { MigrationAdapter } from './migration-adapter.js';
4
- import { MigrationPackage, MigrationPackageAsync } from './migration-package.js';
4
+ import { MigrationPackage, MigrationPackageConfig } from './migration-package.js';
5
5
  export interface DbMigratorOptions {
6
6
  connection: ClientConfiguration;
7
- migrationPackage: MigrationPackage | MigrationPackageAsync;
7
+ migrationPackage: MigrationPackage | MigrationPackageConfig;
8
8
  infoSchema?: string;
9
9
  scriptVariables?: Record<string, string>;
10
10
  targetVersion?: number;
@@ -1,5 +1,5 @@
1
- import type { MigrationTask } from './migration-package.js';
2
- import { MigrationStatus } from './types.js';
1
+ import type { Migration, MigrationPackage, MigrationTask } from './migration-package.js';
2
+ import type { MigrationStatus } from './types.js';
3
3
  export declare abstract class MigrationAdapter {
4
4
  abstract readonly packageName: string;
5
5
  abstract readonly status: MigrationStatus;
@@ -11,7 +11,7 @@ export declare abstract class MigrationAdapter {
11
11
  version?: number;
12
12
  }): Promise<void>;
13
13
  abstract writeEvent(event: MigrationAdapter.Event): Promise<void>;
14
- abstract executeTask(task: MigrationTask, variables: Record<string, any>): Promise<void>;
14
+ abstract executeTask(migrationPackage: MigrationPackage, migration: Migration, task: MigrationTask, variables: Record<string, any>): Promise<void>;
15
15
  abstract lockSchema(): Promise<void>;
16
16
  abstract unlockSchema(): Promise<void>;
17
17
  abstract backupDatabase(): Promise<void>;
@@ -27,6 +27,8 @@ export declare namespace MigrationAdapter {
27
27
  event: EventKind;
28
28
  version: number;
29
29
  message: string;
30
+ title?: string;
31
+ filename?: string;
30
32
  details?: string;
31
33
  }
32
34
  }
@@ -1,44 +1,42 @@
1
- import { StrictOmit } from 'ts-gems';
1
+ import { PartialSome, StrictOmit } from 'ts-gems';
2
2
  import type { MigrationAdapter } from './migration-adapter.js';
3
3
  export interface MigrationPackage {
4
4
  name: string;
5
5
  description?: string;
6
6
  migrations: Migration[];
7
+ baseDir: string;
7
8
  informationTableName?: string;
8
9
  }
9
10
  export interface Migration {
10
11
  version: number;
11
12
  tasks: MigrationTask[];
12
- dirname?: string;
13
+ baseDir: string;
13
14
  backup?: boolean;
14
15
  }
15
16
  export type MigrationTask = SqlScriptMigrationTask | CustomMigrationTask | InsertDataMigrationTask;
16
17
  export interface BaseMigrationTask {
17
- title: string;
18
+ title?: string;
18
19
  filename?: string;
19
20
  }
20
21
  export interface SqlScriptMigrationTask extends BaseMigrationTask {
21
- title: string;
22
- script: string;
22
+ script: string | Function;
23
23
  }
24
24
  export interface InsertDataMigrationTask extends BaseMigrationTask {
25
- title: string;
26
25
  tableName: string;
27
26
  rows: Record<string, any>[];
28
27
  }
29
28
  export interface CustomMigrationTask extends BaseMigrationTask {
30
- title: string;
31
29
  fn: (connection: any, adapter: MigrationAdapter) => void | Promise<void>;
32
30
  }
33
31
  export declare function isSqlScriptMigrationTask(x: any): x is SqlScriptMigrationTask;
34
32
  export declare function isInsertDataMigrationTask(x: any): x is InsertDataMigrationTask;
35
33
  export declare function isCustomMigrationTask(x: any): x is CustomMigrationTask;
36
- export interface MigrationPackageAsync extends StrictOmit<MigrationPackage, 'migrations'> {
37
- migrations: (string | MigrationAsync | (() => MigrationAsync) | (() => Promise<MigrationAsync>))[];
34
+ export interface MigrationPackageConfig extends PartialSome<StrictOmit<MigrationPackage, 'migrations'>, 'baseDir'> {
35
+ migrations: (string | MigrationConfig | (() => MigrationConfig) | (() => Promise<MigrationConfig>))[];
38
36
  }
39
- export interface MigrationAsync extends StrictOmit<Migration, 'tasks'> {
37
+ export interface MigrationConfig extends StrictOmit<Migration, 'tasks' | 'baseDir'> {
40
38
  tasks: (string | MigrationTask | (() => MigrationTask) | (() => Promise<MigrationTask>))[];
41
39
  }
42
40
  export declare namespace MigrationPackage {
43
- function load(asyncConfig: MigrationPackageAsync): Promise<MigrationPackage>;
41
+ function load(asyncConfig: MigrationPackageConfig): Promise<MigrationPackage>;
44
42
  }