@sqb/migrator 4.1.5 → 4.5.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/README.md ADDED
@@ -0,0 +1,69 @@
1
+
2
+ <center>
3
+ <p>
4
+ <img src="https://user-images.githubusercontent.com/3836517/32965280-1a2b63ce-cbe7-11e7-8ee1-ba47313503c5.png" width="500px" alt="SQB Logo"/>
5
+ </p>
6
+ </center>
7
+
8
+ <br>
9
+
10
+ [![NPM Version][npm-image]][npm-url]
11
+ [![NPM Downloads][downloads-image]][downloads-url]
12
+ [![Build Status][travis-image]][travis-url]
13
+ [![Test Coverage][coveralls-image]][coveralls-url]
14
+ [![Dependencies][dependencies-image]][dependencies-url]
15
+ [![DevDependencies][devdependencies-image]][devdependencies-url]
16
+ [![Package Quality][quality-image]][quality-url]
17
+
18
+
19
+ ## About SQB
20
+
21
+ SQB is an extensible, multi-dialect SQL query builder and Database connection wrapper for NodeJS.
22
+
23
+ ## Main goals
24
+
25
+ - Single code base for any sql based database
26
+ - Powerful and simplified query coding scheme
27
+ - Fast applications with low memory requirements
28
+ - Let applications work with large data tables efficiently
29
+ - Support latest JavaScript language standards
30
+ - Lightweight and extensible framework.
31
+
32
+
33
+ You can report bugs and discuss features on the [GitHub issues](https://github.com/sqbjs/sqb/issues) page
34
+
35
+ Thanks to all of the great [contributions](https://github.com/sqbjs/sqb/graphs/contributors) to the project.
36
+
37
+ You may want to check detailed [DOCUMENTATION](https://sqbjs.github.io/sqb/)
38
+
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ $ npm install @sqb/migrator --save
44
+ ```
45
+
46
+ ## Node Compatibility
47
+
48
+ - node >= 16.x
49
+
50
+
51
+ ### License
52
+ SQB is available under [MIT](LICENSE) license.
53
+
54
+ [npm-image]: https://img.shields.io/npm/v/@sqb/migrator.svg
55
+ [npm-url]: https://npmjs.org/package/@sqb/migrator
56
+ [travis-image]: https://img.shields.io/travis/sqbjs/@sqb/migrator/master.svg
57
+ [travis-url]: https://travis-ci.org/sqbjs/@sqb/migrator
58
+ [coveralls-image]: https://img.shields.io/coveralls/sqbjs/@sqb/migrator/master.svg
59
+ [coveralls-url]: https://coveralls.io/r/sqbjs/@sqb/migrator
60
+ [downloads-image]: https://img.shields.io/npm/dm/@sqb/migrator.svg
61
+ [downloads-url]: https://npmjs.org/package/@sqb/migrator
62
+ [gitter-image]: https://badges.gitter.im/sqbjs/@sqb/migrator.svg
63
+ [gitter-url]: https://gitter.im/sqbjs/@sqb/migrator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
64
+ [dependencies-image]: https://david-dm.org/sqbjs/@sqb/migrator/status.svg
65
+ [dependencies-url]:https://david-dm.org/sqbjs/@sqb/migrator
66
+ [devdependencies-image]: https://david-dm.org/sqbjs/@sqb/migrator/dev-status.svg
67
+ [devdependencies-url]:https://david-dm.org/sqbjs/@sqb/migrator?type=dev
68
+ [quality-image]: http://npm.packagequality.com/shield/@sqb/migrator.png
69
+ [quality-url]: http://packagequality.com/#?package=@sqb/migrator
@@ -5,7 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const fs_1 = tslib_1.__importDefault(require("fs"));
6
6
  const path_1 = tslib_1.__importDefault(require("path"));
7
7
  const postgresql_client_1 = require("postgresql-client");
8
- const load_task_files_1 = require("./load-task-files");
8
+ const load_task_files_js_1 = require("./load-task-files.js");
9
9
  const ignoreEventListener = () => 0;
10
10
  class DbMigrator {
11
11
  async execute(options) {
@@ -108,7 +108,7 @@ class DbMigrator {
108
108
  maxVersion = Math.max(minVersion, m.version);
109
109
  for (const t of m.tasks) {
110
110
  if (typeof t === 'string') {
111
- m.tasks = (0, load_task_files_1.loadTaskFiles)(t);
111
+ m.tasks = (0, load_task_files_js_1.loadTaskFiles)(t);
112
112
  }
113
113
  }
114
114
  }
package/cjs/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./db-migrator.js"), exports);
5
+ tslib_1.__exportStar(require("./types.js"), exports);
File without changes
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
File without changes
@@ -1,5 +1,5 @@
1
1
  import { Connection } from 'postgresql-client';
2
- import { InsertDataMigrationTask, Migration, MigrationPackage } from './types';
2
+ import { InsertDataMigrationTask, Migration, MigrationPackage } from './types.js';
3
3
  export interface MigrationExecuteOptions {
4
4
  connection: Connection;
5
5
  migrationPackage: MigrationPackage;
@@ -0,0 +1,210 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { stringifyValueForSQL } from 'postgresql-client';
4
+ import { loadTaskFiles } from './load-task-files.js';
5
+ const ignoreEventListener = () => 0;
6
+ export class DbMigrator {
7
+ async execute(options) {
8
+ const { connection, schema } = options;
9
+ const migrationPackage = DbMigrator.loadMigrationPackage(options.migrationPackage);
10
+ const targetVersion = Math.min(options.targetVersion || Number.MAX_SAFE_INTEGER, migrationPackage.maxVersion);
11
+ if (targetVersion && targetVersion < migrationPackage.minVersion)
12
+ 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;
25
+ try {
26
+ await this._lockSchema();
27
+ if (needBackup) {
28
+ emit('backup');
29
+ await this._backup();
30
+ }
31
+ await connection.execute(`update ${schema}.${schemaInfo.infTable} set status = 'migration'`);
32
+ // Execute migration tasks
33
+ for (const migration of migrations) {
34
+ if (migration.version > targetVersion || schemaInfo.version >= migration.version)
35
+ continue;
36
+ for (let index = 0; index < migration.tasks.length; index++) {
37
+ 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;
53
+ });
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
+ }
67
+ else if ('tableName' in task) {
68
+ const script = DbMigrator.objectDataToSql({ ...task, schema });
69
+ await connection.execute(script);
70
+ }
71
+ emit('process', { task: task.title, total, index });
72
+ }
73
+ }
74
+ await connection.query(`update ${schema}.${schemaInfo.infTable} set status = $1, schema_version = $2, ` +
75
+ 'updated_at = current_timestamp', { params: ['ready', targetVersion] });
76
+ }
77
+ catch (e) {
78
+ if (needBackup) {
79
+ emit('backup');
80
+ await this._restore();
81
+ }
82
+ throw e;
83
+ }
84
+ finally {
85
+ await this._unlockSchema();
86
+ }
87
+ emit('finish');
88
+ return true;
89
+ }
90
+ static loadMigrationPackage(pkg) {
91
+ const migarr = typeof pkg.migrations === 'function' ? pkg.migrations() : pkg.migrations;
92
+ const migrations = [];
93
+ for (const x of migarr) {
94
+ if (typeof x === 'string')
95
+ 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
+ }
109
+ }
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]]);
182
+ }
183
+ sql += ');\n';
184
+ }
185
+ return sql;
186
+ }
187
+ }
188
+ 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 = require(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
+ locateMigrations(trg, dirname);
208
+ }
209
+ }
210
+ }
package/esm/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './db-migrator.js';
2
+ export * from './types.js';
package/esm/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './db-migrator.js';
2
+ export * from './types.js';
@@ -1,2 +1,2 @@
1
- import { MigrationTask } from './types';
1
+ import { MigrationTask } from './types.js';
2
2
  export declare function loadTaskFiles(pattern: string): MigrationTask[];
@@ -0,0 +1,34 @@
1
+ import glob from 'fast-glob';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ export function loadTaskFiles(pattern) {
5
+ const out = [];
6
+ const files = glob.sync(pattern.replace(/\\/g, '/'), { absolute: true, onlyFiles: true });
7
+ for (const filename of files) {
8
+ const ext = path.extname(filename).toLowerCase();
9
+ if (ext === '.sql') {
10
+ const script = fs.readFileSync(filename, 'utf-8');
11
+ out.push({
12
+ title: path.basename(filename),
13
+ script
14
+ });
15
+ }
16
+ else if (ext === '.json') {
17
+ try {
18
+ const json = JSON.parse(fs.readFileSync(filename, 'utf-8'));
19
+ if (typeof json === 'object' && json.tableName && json.rows) {
20
+ if (!json.tableName) { // noinspection ExceptionCaughtLocallyJS
21
+ throw new Error('"tableName" property does not exists');
22
+ }
23
+ json.title = json.title || 'Migrate data into ' + json.tableName;
24
+ out.push(json);
25
+ }
26
+ }
27
+ catch (e) {
28
+ e.message = e.message + '\n' + filename;
29
+ throw e;
30
+ }
31
+ }
32
+ }
33
+ return out;
34
+ }
@@ -1,4 +1,3 @@
1
- import { Connection } from 'postgresql-client';
2
1
  export declare type MigrationsThunk = () => (string | Migration)[];
3
2
  export interface MigrationPackage {
4
3
  description: string;
@@ -17,7 +16,7 @@ export interface Migration {
17
16
  version: number;
18
17
  tasks: string[] | MigrationTask[];
19
18
  }
20
- export declare type MigrationTaskFunction = (connection: Connection) => void | Promise<void>;
19
+ export declare type MigrationTaskFunction = (connection: any) => void | Promise<void>;
21
20
  export declare type MigrationTask = SqlScriptMigrationTask | CustomMigrationTask | InsertDataMigrationTask;
22
21
  export interface BaseMigrationTask {
23
22
  title: string;
package/esm/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,52 +1,67 @@
1
1
  {
2
- "name": "@sqb/migrator",
3
- "description": "Database migrator for SQB",
4
- "version": "4.1.5",
5
- "author": "Panates",
6
- "contributors": [
7
- "Eray Hanoglu <e.hanoglu@panates.com>"
2
+ "name" : "@sqb/migrator",
3
+ "description" : "Database migrator for SQB",
4
+ "version" : "4.5.0",
5
+ "author" : "Panates",
6
+ "contributors" : [
7
+ "Eray Hanoglu <e.hanoglu@panates.com>",
8
+ "Ilker Gurelli <i.gurelli@panates.com>"
8
9
  ],
9
- "license": "Apache-2.0",
10
- "repository": {
11
- "type": "git",
12
- "url": "https://github.com/sqbjs/sqb.git",
13
- "directory": "packages/migrator"
10
+ "license" : "Apache-2.0",
11
+ "repository" : {
12
+ "type" : "git",
13
+ "url" : "https://github.com/sqbjs/sqb.git",
14
+ "directory" : "packages/migrator"
14
15
  },
15
- "keywords": [
16
- "javascript",
17
- "typescript",
16
+ "scripts" : {
17
+ "compile" : "tsc",
18
+ "prebuild" : "npm run lint && npm run clean",
19
+ "build" : "npm run build:cjs && npm run build:esm",
20
+ "build:cjs" : "tsc -b tsconfig-build-cjs.json",
21
+ "build:esm" : "tsc -b tsconfig-build-esm.json",
22
+ "postbuild" : "cp README.md package.json ../../LICENSE ../../build/migrator && cp ../../package.cjs.json ../../build/migrator/cjs/package.json",
23
+ "lint" : "eslint .",
24
+ "test" : "jest",
25
+ "cover" : "jest --collect-coverage",
26
+ "clean" : "npm run clean:src | npm run clean:dist | npm run clean:cover",
27
+ "clean:src" : "ts-cleanup -s src --all",
28
+ "clean:dist" : "rimraf ../../build/migrator",
29
+ "clean:cover" : "rimraf ../../coverage/migrator"
30
+ },
31
+ "peerDependencies" : {
32
+ "@sqb/builder" : "^4.5.0",
33
+ "@sqb/connect" : "^4.5.0"
34
+ },
35
+ "type" : "module",
36
+ "main" : "cjs/index.js",
37
+ "module" : "esm/index.js",
38
+ "types" : "esm/index.d.ts",
39
+ "exports" : {
40
+ "." : {
41
+ "require" : "./cjs/index.js",
42
+ "default" : "./esm/index.js"
43
+ },
44
+ "./cjs" : "./cjs/index.js",
45
+ "./esm" : "./esm/index.js"
46
+ },
47
+ "engines" : {
48
+ "node" : ">=16.0",
49
+ "npm" : ">=7.0.0"
50
+ },
51
+ "files" : [
52
+ "bin/",
53
+ "cjs/",
54
+ "esm/",
55
+ "LICENSE",
56
+ "README.md"
57
+ ],
58
+ "keywords" : [
59
+ "sqb",
18
60
  "sql",
19
61
  "query",
20
62
  "database",
21
63
  "db",
22
64
  "migrate",
23
65
  "migrator"
24
- ],
25
- "peerDependencies": {
26
- "@sqb/builder": "^4.1.4"
27
- },
28
- "main": "dist/index.js",
29
- "types": "dist/index.d.ts",
30
- "engines": {
31
- "node": ">= 16.0"
32
- },
33
- "directories": {
34
- "lib": "dist",
35
- "test": "test"
36
- },
37
- "files": [
38
- "LICENSE",
39
- "README",
40
- "dist/"
41
- ],
42
- "scripts": {
43
- "build": "tsc -b tsconfig-build.json",
44
- "prebuild": "npm run lint && npm run clean:dist",
45
- "postbuild": "copyfiles package.json README.md LICENSE ../../build/migrator",
46
- "clean": "ts-cleanup -s src --all & ts-cleanup -s test",
47
- "clean:dist": "rimraf ../../build/migrator",
48
- "compile": "tsc",
49
- "lint": "eslint src/** --no-error-on-unmatched-pattern",
50
- "test": "TS_NODE_PROJECT='./test/tsconfig.json' mocha -r ts-node/register -r tsconfig-paths/register --reporter spec test/**/*.spec.ts"
51
- }
52
- }
66
+ ]
67
+ }
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './db-migrator';
2
- export * from './types';
package/dist/index.js DELETED
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- tslib_1.__exportStar(require("./db-migrator"), exports);
5
- tslib_1.__exportStar(require("./types"), exports);