@sqb/migrator 4.9.0 → 4.10.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.
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCallingFilename = void 0;
4
+ const PATH_PATTERN = /^(?:file:\/\/)?(.+)$/;
5
+ function getCallingFilename(position = 0) {
6
+ position++;
7
+ if (position >= Error.stackTraceLimit)
8
+ return '';
9
+ const oldPrepareStackTrace = Error.prepareStackTrace;
10
+ Error.prepareStackTrace = (_, stack) => stack;
11
+ const stack = new Error().stack;
12
+ Error.prepareStackTrace = oldPrepareStackTrace;
13
+ if (stack !== null && typeof stack === 'object') {
14
+ // stack[0] holds this file
15
+ // stack[1] holds where this function was called
16
+ const s = stack[position] ?
17
+ stack[position].getFileName() : undefined;
18
+ const m = s ? PATH_PATTERN.exec(s) : undefined;
19
+ return m ? m[1] : '';
20
+ }
21
+ return '';
22
+ }
23
+ exports.getCallingFilename = getCallingFilename;
@@ -0,0 +1,194 @@
1
+ import { stringifyValueForSQL } from 'postgresql-client';
2
+ import { PgAdapter } from '@sqb/postgres';
3
+ import { MigrationAdapter } from '../migration-adapter.js';
4
+ import { isCustomMigrationTask, isInsertDataMigrationTask, isSqlScriptMigrationTask } from '../migration-package.js';
5
+ import { MigrationStatus } from '../types.js';
6
+ const pgAdapter = new PgAdapter();
7
+ export class PgMigrationAdapter extends MigrationAdapter {
8
+ constructor() {
9
+ super(...arguments);
10
+ this._infoSchema = 'public';
11
+ this._packageName = '';
12
+ this._version = 0;
13
+ this._status = MigrationStatus.idle;
14
+ this.defaultVariables = {
15
+ tablespace: 'pg_default',
16
+ schema: 'public',
17
+ owner: 'postgres'
18
+ };
19
+ this.summaryTable = 'migration_summary';
20
+ this.eventTable = 'migration_events';
21
+ }
22
+ get packageName() {
23
+ return this._packageName;
24
+ }
25
+ get version() {
26
+ return this._version;
27
+ }
28
+ get status() {
29
+ return this._status;
30
+ }
31
+ get infoSchema() {
32
+ return this._infoSchema;
33
+ }
34
+ get summaryTableFull() {
35
+ return this.infoSchema + '.' + this.summaryTable;
36
+ }
37
+ get eventTableFull() {
38
+ return this.infoSchema + '.' + this.eventTable;
39
+ }
40
+ static async create(options) {
41
+ // Create connection
42
+ const connection = (await pgAdapter.connect(options.connection)).intlcon;
43
+ try {
44
+ const adapter = new PgMigrationAdapter();
45
+ adapter._connection = connection;
46
+ adapter._infoSchema = options.infoSchema || '__migration';
47
+ adapter.defaultVariables.schema = options.connection.schema || '';
48
+ if (!adapter.defaultVariables.schema) {
49
+ const r = await connection.query('SELECT CURRENT_SCHEMA ', { objectRows: true });
50
+ adapter.defaultVariables.schema = (r.rows?.[0]?.current_schema) || 'public';
51
+ }
52
+ // Check if migration schema
53
+ await connection.query(`CREATE SCHEMA IF NOT EXISTS ${adapter.infoSchema} AUTHORIZATION postgres;`);
54
+ // Create summary table if not exists
55
+ await connection.execute(`
56
+ CREATE TABLE IF NOT EXISTS ${adapter.summaryTableFull}
57
+ (
58
+ package_name varchar not null,
59
+ status varchar(16) not null,
60
+ current_version integer not null default 0,
61
+ created_at timestamp without time zone not null default current_timestamp,
62
+ updated_at timestamp without time zone,
63
+ CONSTRAINT pk_${adapter.summaryTable} PRIMARY KEY (package_name)
64
+ )`);
65
+ // Create events table if not exists
66
+ await connection.execute(`
67
+ CREATE TABLE IF NOT EXISTS ${adapter.eventTableFull}
68
+ (
69
+ id serial not null,
70
+ package_name varchar not null,
71
+ version integer not null default 0,
72
+ event varchar(16) not null,
73
+ event_time timestamp without time zone not null,
74
+ message text not null,
75
+ details text,
76
+ CONSTRAINT pk_${adapter.eventTable} PRIMARY KEY (id)
77
+ )`);
78
+ // Insert summary record if not exists
79
+ const r = await connection.query(`SELECT status FROM ${adapter.summaryTableFull} WHERE package_name = $1`, { params: [adapter.packageName], objectRows: true });
80
+ if (!(r && r.rows?.length)) {
81
+ await connection.query(`insert into ${adapter.summaryTableFull} ` +
82
+ '(package_name, status) values ($1, $2)', { params: [adapter.packageName, MigrationStatus.idle] });
83
+ }
84
+ await adapter.refresh();
85
+ return adapter;
86
+ }
87
+ catch (e) {
88
+ await connection.close(0);
89
+ throw e;
90
+ }
91
+ }
92
+ async close() {
93
+ await this._connection.close();
94
+ }
95
+ async refresh() {
96
+ const r = await this._connection.query(`SELECT * FROM ${this.summaryTableFull} WHERE package_name = $1`, { params: [this.packageName], objectRows: true });
97
+ const row = r.rows && r.rows[0];
98
+ if (!row)
99
+ throw new Error('Summary record did not created');
100
+ this._version = row.current_version;
101
+ this._status = row.status;
102
+ }
103
+ async update(info) {
104
+ let sql = '';
105
+ const params = [];
106
+ if (info.status && info.status !== this.status) {
107
+ params.push(info.status);
108
+ sql += ', status = $' + (params.length);
109
+ }
110
+ if (info.version && info.version !== this.version) {
111
+ params.push(info.version);
112
+ sql += ', current_version = $' + (params.length);
113
+ }
114
+ if (sql) {
115
+ sql = `update ${this.summaryTableFull} set updated_at = current_timestamp` + sql;
116
+ await this._connection.query(sql, { params });
117
+ if (info.status)
118
+ this._status = info.status;
119
+ if (info.version)
120
+ this._version = info.version;
121
+ }
122
+ }
123
+ async writeEvent(event) {
124
+ const sql = `insert into ${this.eventTableFull} ` +
125
+ '(package_name, version, event, event_time, message, details) ' +
126
+ 'values ($1, $2, $3, CURRENT_TIMESTAMP, $4, $5)';
127
+ await this._connection.query(sql, {
128
+ params: [
129
+ this.packageName, event.version, event.event,
130
+ event.message, event.details
131
+ ]
132
+ });
133
+ }
134
+ async executeTask(task, variables) {
135
+ variables = {
136
+ ...this.defaultVariables,
137
+ ...variables
138
+ };
139
+ if (isSqlScriptMigrationTask(task)) {
140
+ try {
141
+ const script = task.script
142
+ .replace(/(\${(\w+)})/g, (s, ...args) => variables[args[1]] || s);
143
+ await this._connection.execute(script);
144
+ }
145
+ catch (e) {
146
+ let msg = `Error in task "${task.title}"`;
147
+ if (task.filename)
148
+ msg += '\n at ' + task.filename;
149
+ if (e.lineNr) {
150
+ if (!task.filename)
151
+ e.message += '\n at';
152
+ msg += ` (${e.lineNr},${e.colNr}):\n` + e.line;
153
+ if (e.colNr)
154
+ msg += '\n' + ' '.repeat(e.colNr - 1) + '^';
155
+ }
156
+ e.message = msg + '\n\n' + e.message;
157
+ throw e;
158
+ }
159
+ return;
160
+ }
161
+ if (isCustomMigrationTask(task)) {
162
+ await task.fn(this._connection, this);
163
+ return;
164
+ }
165
+ if (isInsertDataMigrationTask(task)) {
166
+ const script = task.rows
167
+ .map(row => this.rowToSql(variables.schema + '.' + task.tableName, row))
168
+ .join('\n');
169
+ await this._connection.execute(script);
170
+ }
171
+ }
172
+ backupDatabase() {
173
+ return Promise.resolve(undefined);
174
+ }
175
+ lockSchema() {
176
+ return Promise.resolve(undefined);
177
+ }
178
+ restoreDatabase() {
179
+ return Promise.resolve(undefined);
180
+ }
181
+ unlockSchema() {
182
+ return Promise.resolve(undefined);
183
+ }
184
+ rowToSql(tableName, row) {
185
+ let sql = '';
186
+ const keys = Object.keys(row);
187
+ sql += `insert into ${tableName} (${keys}) values (`;
188
+ for (let i = 0; i < keys.length; i++) {
189
+ sql += (i ? ', ' : '') + stringifyValueForSQL(row[keys[i]]);
190
+ }
191
+ sql += ');\n';
192
+ return sql;
193
+ }
194
+ }
@@ -1,210 +1,104 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { stringifyValueForSQL } from 'postgresql-client';
4
- import { loadTaskFiles } from './load-task-files.js';
5
- const ignoreEventListener = () => 0;
6
- export class DbMigrator {
1
+ import { AsyncEventEmitter } from 'strict-typed-events';
2
+ import { PgMigrationAdapter } from './adapters/pg-migration-adapter.js';
3
+ import { MigrationAdapter } from './migration-adapter.js';
4
+ import { MigrationPackage } from './migration-package.js';
5
+ import { MigrationStatus } from './types.js';
6
+ export class DbMigrator extends AsyncEventEmitter {
7
7
  async execute(options) {
8
- const { connection, schema } = options;
9
- const migrationPackage = await DbMigrator.loadMigrationPackage(options.migrationPackage);
10
- const targetVersion = Math.min(options.targetVersion || Number.MAX_SAFE_INTEGER, migrationPackage.maxVersion);
11
- if (targetVersion && targetVersion < migrationPackage.minVersion)
8
+ if (!options.connection.dialect)
9
+ throw new TypeError(`You must provide connection.dialect`);
10
+ const migrationPackage = await MigrationPackage.load(options.migrationPackage);
11
+ let minVersion = migrationPackage.migrations
12
+ .reduce((a, m) => Math.min(a, m.version), Number.MAX_SAFE_INTEGER);
13
+ if (minVersion === Number.MAX_SAFE_INTEGER)
14
+ minVersion = 0;
15
+ const maxVersion = migrationPackage.migrations
16
+ .reduce((a, m) => Math.max(a, m.version), 0);
17
+ const targetVersion = Math.min(options?.targetVersion || Number.MAX_SAFE_INTEGER, maxVersion);
18
+ if (targetVersion && targetVersion < minVersion) { // noinspection ExceptionCaughtLocallyJS
12
19
  throw new Error(`Version mismatch. Target schema version (${targetVersion}) is lower than ` +
13
- `migration package min version (${migrationPackage.minVersion})`);
14
- const emit = options.eventListener || ignoreEventListener;
15
- const schemaInfo = await this._getSchemaInfo(options);
16
- if (schemaInfo.version && schemaInfo.version < migrationPackage.minVersion - 1)
17
- throw new Error(`This package can migrate starting from ${migrationPackage.minVersion - 1} but current version is ${schemaInfo.version}`);
18
- const { migrations } = migrationPackage;
19
- // calculate total scripts;
20
- const total = migrations
21
- .reduce((i, x) => i + x.tasks.length, 0);
22
- const needBackup = !!migrations.find(x => !!x.backup);
23
- emit('start');
24
- let task;
20
+ `migration package min version (${minVersion})`);
21
+ }
22
+ let migrationAdapter;
23
+ switch (options.connection.dialect) {
24
+ case 'postgres': {
25
+ migrationAdapter = await PgMigrationAdapter.create(options);
26
+ break;
27
+ }
28
+ default:
29
+ throw new TypeError(`Migration adapter for "${options.connection.dialect}" dialect is not implemented yet`);
30
+ }
31
+ let needBackup = false;
25
32
  try {
26
- await this._lockSchema();
33
+ if (migrationAdapter.version && migrationAdapter.version < minVersion - 1) { // noinspection ExceptionCaughtLocallyJS
34
+ throw new Error(`This package can migrate starting from ${minVersion - 1} but current version is ${migrationAdapter.version}`);
35
+ }
36
+ const { migrations } = migrationPackage;
37
+ // calculate total scripts;
38
+ const total = migrations
39
+ .reduce((i, x) => i + x.tasks.length, 0);
40
+ needBackup = !!migrations.find(x => !!x.backup);
41
+ await this.emitAsync('start');
42
+ let task;
43
+ await migrationAdapter.lockSchema();
27
44
  if (needBackup) {
28
- emit('backup');
29
- await this._backup();
45
+ await this.emitAsync('backup');
46
+ await migrationAdapter.backupDatabase();
30
47
  }
31
- await connection.execute(`update ${schema}.${schemaInfo.infTable} set status = 'migration'`);
32
48
  // Execute migration tasks
33
49
  for (const migration of migrations) {
34
- if (migration.version > targetVersion || schemaInfo.version >= migration.version)
50
+ if (migration.version > targetVersion || migrationAdapter.version >= migration.version)
35
51
  continue;
36
52
  for (let index = 0; index < migration.tasks.length; index++) {
37
53
  task = migration.tasks[index];
38
- emit('processing', { task: task.title, total, index });
39
- if ('fn' in task && typeof task.fn === 'function') {
40
- await task.fn(connection);
41
- }
42
- else if (task.script) {
43
- const script = task.script.replace(/(\${(\w+)})/gi, (s, ...args) => {
44
- switch (args[1].toLowerCase()) {
45
- case 'schema':
46
- return schema;
47
- case 'owner':
48
- return schemaInfo.owner;
49
- case 'tablespace':
50
- return schemaInfo.tablespace;
51
- }
52
- return s;
54
+ await this.emitAsync('task-start', { task: task.title, total, index });
55
+ await migrationAdapter.update({ status: MigrationStatus.busy });
56
+ await migrationAdapter.writeEvent({
57
+ event: MigrationAdapter.EventKind.started,
58
+ version: migration.version,
59
+ message: `Task "${task.title}" started`
60
+ });
61
+ try {
62
+ await migrationAdapter.executeTask(task, {
63
+ schema: options.connection.schema,
64
+ ...options.scriptVariables,
65
+ });
66
+ await migrationAdapter.writeEvent({
67
+ event: MigrationAdapter.EventKind.success,
68
+ version: migration.version,
69
+ message: `Task "${task.title}" completed`
53
70
  });
54
- try {
55
- await connection.execute(script);
56
- }
57
- catch (e) {
58
- e.message += `\n${task.title}`;
59
- if (e.lineNr)
60
- e.message += ` (${e.lineNr},${e.colNr}):\n` + e.line;
61
- if (e.colNr)
62
- e.message += '\n' + ' '.repeat(e.colNr - 1) + '^';
63
- // noinspection ExceptionCaughtLocallyJS
64
- throw e;
65
- }
66
71
  }
67
- else if ('tableName' in task) {
68
- const script = DbMigrator.objectDataToSql({ ...task, schema });
69
- await connection.execute(script);
72
+ catch (e) {
73
+ await migrationAdapter.writeEvent({
74
+ event: MigrationAdapter.EventKind.error,
75
+ version: migration.version,
76
+ message: String(e)
77
+ });
78
+ // noinspection ExceptionCaughtLocallyJS
79
+ throw e;
70
80
  }
71
- emit('process', { task: task.title, total, index });
81
+ await this.emitAsync('task-finish', { task: task.title, total, index });
72
82
  }
83
+ await migrationAdapter.update({ version: migration.version });
73
84
  }
74
- await connection.query(`update ${schema}.${schemaInfo.infTable} set status = $1, schema_version = $2, ` +
75
- 'updated_at = current_timestamp', { params: ['ready', targetVersion] });
76
85
  }
77
86
  catch (e) {
78
87
  if (needBackup) {
79
- emit('backup');
80
- await this._restore();
88
+ await this.emitAsync('restore');
89
+ await migrationAdapter.restoreDatabase();
81
90
  }
82
91
  throw e;
83
92
  }
84
93
  finally {
85
- await this._unlockSchema();
86
- }
87
- emit('finish');
88
- return true;
89
- }
90
- static async loadMigrationPackage(pkg) {
91
- const migarr = typeof pkg.migrations === 'function' ? await pkg.migrations() : pkg.migrations;
92
- const migrations = [];
93
- for (const x of migarr) {
94
- if (typeof x === 'string')
95
- await locateMigrations(migrations, x);
96
- else
97
- migrations.push(x);
98
- }
99
- let minVersion = 0;
100
- let maxVersion = 0;
101
- migrations.sort((a, b) => a.version - b.version);
102
- for (const m of migrations) {
103
- minVersion = !minVersion ? m.version : Math.min(minVersion, m.version);
104
- maxVersion = Math.max(minVersion, m.version);
105
- for (const t of m.tasks) {
106
- if (typeof t === 'string') {
107
- m.tasks = loadTaskFiles(t);
108
- }
94
+ try {
95
+ await migrationAdapter.unlockSchema();
109
96
  }
110
- }
111
- return {
112
- minVersion,
113
- maxVersion,
114
- migrations
115
- };
116
- }
117
- async _lockSchema() {
118
- // todo
119
- }
120
- async _unlockSchema() {
121
- // todo
122
- }
123
- async _backup() {
124
- // todo
125
- }
126
- async _restore() {
127
- // todo
128
- }
129
- async _getSchemaInfo(options) {
130
- const { connection, schema } = options;
131
- let owner = options.owner || 'postgres';
132
- let tablespace = options.tablespace || 'pg_default';
133
- let version = 0;
134
- let status = '';
135
- let r = await connection.query('select schema_owner FROM information_schema.schemata WHERE schema_name = $1', { params: [schema], objectRows: true });
136
- if (r.rows && r.rows[0]) {
137
- owner = r.rows[0].schema_owner;
138
- }
139
- else {
140
- await connection.execute(`CREATE SCHEMA ${schema} AUTHORIZATION ${owner};`);
141
- }
142
- // Determine tablespace of schema
143
- const infTable = options.migrationPackage.informationTableName || '__schema_info';
144
- r = await connection.query('SELECT tablespace FROM pg_tables WHERE schemaname = $1 AND tablename = $2', { params: [schema, infTable], objectRows: true });
145
- if (r.rows && r.rows[0]) {
146
- tablespace = r.rows[0].tablespace || tablespace;
147
- r = await connection.query(`SELECT schema_version, status FROM ${schema}.${infTable}`, { objectRows: true });
148
- if (r.rows && r.rows[0]) {
149
- version = r.rows[0].schema_version;
150
- status = r.rows[0].status;
151
- }
152
- }
153
- else {
154
- // Create if not exists
155
- await connection.execute(`
156
- CREATE TABLE ${schema}.${infTable}
157
- (
158
- status varchar(16) not null,
159
- schema_version integer not null default 0,
160
- created_at timestamp without time zone not null default current_timestamp,
161
- updated_at timestamp without time zone
162
- ) TABLESPACE ${tablespace};
163
- insert into ${schema}.${infTable} (status) values ('init');
164
- `);
165
- status = 'init';
166
- }
167
- return {
168
- infTable,
169
- tablespace,
170
- owner,
171
- version,
172
- status
173
- };
174
- }
175
- static objectDataToSql(task) {
176
- let sql = '';
177
- for (const rows of task.rows) {
178
- const keys = Object.keys(rows);
179
- sql += `insert into ${task.schema}.${task.tableName} (${keys}) values (`;
180
- for (let i = 0; i < keys.length; i++) {
181
- sql += (i ? ', ' : '') + stringifyValueForSQL(rows[keys[i]]);
97
+ finally {
98
+ await migrationAdapter.close();
182
99
  }
183
- sql += ');\n';
184
- }
185
- return sql;
186
- }
187
- }
188
- async function locateMigrations(trg, dir) {
189
- for (const f of ['migration.ts', 'migration.js', 'migration.json']) {
190
- const filename = path.join(dir, f);
191
- if (fs.existsSync(filename)) {
192
- const x = await import(filename);
193
- const fileDir = path.dirname(filename);
194
- const obj = x.default || x;
195
- if (obj.version && obj.tasks) {
196
- const migration = { ...obj };
197
- migration.tasks = obj.tasks.map(k => (typeof k === 'string' ? path.resolve(fileDir, k) : k));
198
- trg.push(migration);
199
- }
200
- return;
201
- }
202
- }
203
- const files = fs.readdirSync(dir);
204
- for (const f of files) {
205
- const dirname = path.join(dir, f);
206
- if (fs.statSync(dirname).isDirectory()) {
207
- await locateMigrations(trg, dirname);
208
100
  }
101
+ await this.emitAsync('finish');
102
+ return true;
209
103
  }
210
104
  }
package/esm/index.js CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from './db-migrator.js';
2
+ export * from './migration-package.js';
3
+ export * from './migration-adapter.js';
2
4
  export * from './types.js';
@@ -0,0 +1,10 @@
1
+ export class MigrationAdapter {
2
+ }
3
+ (function (MigrationAdapter) {
4
+ let EventKind;
5
+ (function (EventKind) {
6
+ EventKind["started"] = "started";
7
+ EventKind["success"] = "success";
8
+ EventKind["error"] = "error";
9
+ })(EventKind = MigrationAdapter.EventKind || (MigrationAdapter.EventKind = {}));
10
+ })(MigrationAdapter || (MigrationAdapter = {}));
@@ -0,0 +1,131 @@
1
+ import glob from 'fast-glob';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import { getCallingFilename } from './utils/get-calling-filename.js';
5
+ export function isSqlScriptMigrationTask(x) {
6
+ return typeof x === 'object' && typeof x.script === 'string';
7
+ }
8
+ export function isInsertDataMigrationTask(x) {
9
+ return typeof x === 'object' &&
10
+ typeof x.tableName === 'string' &&
11
+ Array.isArray(x.rows);
12
+ }
13
+ export function isCustomMigrationTask(x) {
14
+ return typeof x === 'object' &&
15
+ typeof x.fn === 'function';
16
+ }
17
+ export var MigrationPackage;
18
+ (function (MigrationPackage) {
19
+ async function load(asyncConfig) {
20
+ const out = {
21
+ ...asyncConfig,
22
+ migrations: []
23
+ };
24
+ if (!Array.isArray(asyncConfig.migrations))
25
+ throw new TypeError('You must provide array of MigrationConfig in "migrations" property');
26
+ const baseDir = path.dirname(getCallingFilename(1));
27
+ if (asyncConfig.migrations?.length) {
28
+ const srcMigrations = [];
29
+ const trgMigrations = [];
30
+ out.migrations = trgMigrations;
31
+ let x;
32
+ for (x of asyncConfig.migrations) {
33
+ x = typeof x === 'function' ? await x() : x;
34
+ if (typeof x === 'object' && x.tasks)
35
+ srcMigrations.push(x);
36
+ else if (typeof x === 'string') {
37
+ srcMigrations.push(...await loadMigrations(path.resolve(baseDir, x.replace(/\\/g, '/'))));
38
+ }
39
+ }
40
+ srcMigrations.sort((a, b) => a.version - b.version);
41
+ for (const migration of srcMigrations) {
42
+ const trgMigration = { ...migration, tasks: [] };
43
+ trgMigrations.push(trgMigration);
44
+ const srcTasks = migration.tasks;
45
+ trgMigration.tasks = [];
46
+ for (const t of srcTasks) {
47
+ if (typeof t === 'object') {
48
+ trgMigration.tasks.push(t);
49
+ }
50
+ else if (typeof t === 'string') {
51
+ let pattern = t.replace(/\\/g, '/');
52
+ pattern = path.resolve(migration.dirname || baseDir, pattern);
53
+ const files = await glob(pattern, { absolute: true, onlyFiles: true });
54
+ for (const filename of files) {
55
+ const ext = path.extname(filename).toLowerCase();
56
+ if (!path.basename(filename, ext).endsWith('.task'))
57
+ continue;
58
+ if (ext === '.sql') {
59
+ const script = await fs.readFile(filename, 'utf-8');
60
+ trgMigration.tasks.push({
61
+ title: path.basename(filename),
62
+ filename,
63
+ script
64
+ });
65
+ }
66
+ else if (ext === '.json') {
67
+ try {
68
+ const json = JSON.parse(await fs.readFile(filename, 'utf-8'));
69
+ if (typeof json !== 'object')
70
+ continue;
71
+ if (json.script) {
72
+ json.title = json.title || 'Run sql script';
73
+ json.filename = filename;
74
+ trgMigration.tasks.push(json);
75
+ continue;
76
+ }
77
+ if (json.tableName && json.rows) {
78
+ json.title = json.title || 'Migrate data into ' + json.tableName;
79
+ json.filename = filename;
80
+ trgMigration.tasks.push(json);
81
+ continue;
82
+ }
83
+ if (typeof json.fn === 'function') {
84
+ json.title = json.title || 'Run custom function';
85
+ json.filename = filename;
86
+ trgMigration.tasks.push(json);
87
+ }
88
+ }
89
+ catch (e) {
90
+ e.message = `Error in ${filename}\n` + e.message;
91
+ throw e;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ return out;
100
+ }
101
+ MigrationPackage.load = load;
102
+ async function loadMigrations(pattern) {
103
+ const out = [];
104
+ const files = await glob(pattern, { absolute: true, onlyFiles: true });
105
+ for (const filename of files) {
106
+ const ext = path.extname(filename).toLowerCase();
107
+ if (path.basename(filename, ext) !== 'migration')
108
+ continue;
109
+ let json;
110
+ if (['.js', '.ts', '.cjs', '.mjs'].includes(ext)) {
111
+ json = await import(filename);
112
+ if (json.__esModule)
113
+ json = json.default;
114
+ }
115
+ else if (ext === '.json') {
116
+ try {
117
+ json = JSON.parse(await fs.readFile(filename, 'utf-8'));
118
+ }
119
+ catch (e) {
120
+ e.message = `Error in ${filename}\n` + e.message;
121
+ throw e;
122
+ }
123
+ }
124
+ if (json && typeof json === 'object' && json.version && Array.isArray(json.tasks)) {
125
+ json.dirname = path.dirname(filename);
126
+ out.push(json);
127
+ }
128
+ }
129
+ return out;
130
+ }
131
+ })(MigrationPackage || (MigrationPackage = {}));
package/esm/types.js CHANGED
@@ -1 +1,5 @@
1
- export {};
1
+ export var MigrationStatus;
2
+ (function (MigrationStatus) {
3
+ MigrationStatus["idle"] = "idle";
4
+ MigrationStatus["busy"] = "busy";
5
+ })(MigrationStatus || (MigrationStatus = {}));