pgroll 0.0.2

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,55 @@
1
+ #
2
+ # pgroll
3
+
4
+ `pgroll` is a lightweight and flexible database migration tool for PostgreSQL
5
+
6
+ PostgreSQL clients currently supporting:
7
+
8
+ - [x] PostgresJS
9
+ - [ ] pg
10
+ - [ ] ...
11
+
12
+ `postgresjs` client. It offers simple commands to manage your database schema changes with `up`, `down`, `create`, and `go` features.
13
+
14
+ ## Features
15
+
16
+ - **up**: Apply all pending migrations.
17
+ - **down**: Rollback the last applied migration.
18
+ - **create**: Create new migration files.
19
+ - **go**: Migrate the database schema to a specific version.
20
+
21
+ ## Installation
22
+
23
+ You can install `pgroll` via npm:
24
+
25
+ ```bash
26
+ npm install pgroll
27
+ ```
28
+
29
+ ## Usage
30
+ ### Command Line Interface (CLI)
31
+ `pgroll` provides a CLI to manage your database migrations. Below are the available commands:
32
+
33
+ #### Running the CLI
34
+ 1. Run Migrations Up:
35
+
36
+ ```bash
37
+ npx pgroll up
38
+ ```
39
+
40
+ 2. Run Migrations Down:
41
+
42
+ ```bash
43
+ npx pgroll down
44
+ ```
45
+
46
+ 3. Navigate to a Specific Version:
47
+
48
+ ```bash
49
+ npx pgroll go <version>
50
+ ```
51
+ 4. Create New Migration Files:
52
+
53
+ ```bash
54
+ npx roll create <migration-name>
55
+ ```
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const process = tslib_1.__importStar(require("node:process"));
5
+ const commander_1 = require("commander");
6
+ const postgres_1 = tslib_1.__importDefault(require("postgres"));
7
+ const index_1 = require("@/index");
8
+ const utils_1 = require("./utils");
9
+ const program = new commander_1.Command();
10
+ let migrator;
11
+ program
12
+ .version('0.0.1')
13
+ .description('Database migration tool')
14
+ .option('-d, --migrationDir <filepath>', 'Specify migration directory(Default: ./migration)')
15
+ .hook('preAction', cmd => {
16
+ const opts = cmd.opts();
17
+ migrator = new index_1.Migrator((0, postgres_1.default)(), opts.migrationDir || `${process.cwd()}/migrations`);
18
+ });
19
+ program
20
+ .command('up')
21
+ .description('Run all up migrations')
22
+ .action(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
23
+ try {
24
+ yield migrator.up({ eventHandler: console.log });
25
+ console.log('Migrations up completed successfully.');
26
+ }
27
+ catch (error) {
28
+ console.error('Error during migrations up:', error);
29
+ process.exit(1);
30
+ }
31
+ }));
32
+ program
33
+ .command('down')
34
+ .description('Run all down migrations')
35
+ .action(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
36
+ try {
37
+ yield migrator.down({ eventHandler: console.log });
38
+ console.log('Migrations down completed successfully.');
39
+ }
40
+ catch (error) {
41
+ console.error('Error during migrations down:', error);
42
+ process.exit(1);
43
+ }
44
+ }));
45
+ program
46
+ .command('create')
47
+ .description('Generate migration files in the migrations folder: one for applying changes (up), and one for reverting them (down).')
48
+ .argument('<filename>', 'file name to be created')
49
+ .action((fileName) => {
50
+ const result = (0, utils_1.createFile)(migrator.migrationsDir, fileName);
51
+ for (const f of result)
52
+ console.log(`Successfully created migration files: ${f}`);
53
+ });
54
+ program
55
+ .command('go')
56
+ .description('Navigate to a specific version; version 0 performs a rollback, reverting all migrations.')
57
+ .argument('<version>', 'version to migrate to')
58
+ .action((version) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
59
+ const parsedVersion = Number.parseInt(version, 10);
60
+ if (Number.isNaN(parsedVersion)) {
61
+ console.error('Invalid version number.');
62
+ process.exit(1);
63
+ }
64
+ try {
65
+ yield migrator.go(parsedVersion, { eventHandler: console.log });
66
+ }
67
+ catch (error) {
68
+ console.error('Error during migrations:', error);
69
+ process.exit(1);
70
+ }
71
+ }));
72
+ program.parse(process.argv);
@@ -0,0 +1,30 @@
1
+ import { ReservedSql, Sql } from 'postgres';
2
+ import { Direction } from '@/utils';
3
+ interface Option {
4
+ eventHandler: (info: string) => void;
5
+ }
6
+ export interface IMigrator {
7
+ migrationsDir: string;
8
+ up: (opts?: Option) => Promise<void>;
9
+ down: (opts?: Option) => Promise<void>;
10
+ go: (version: number, opts?: Option) => Promise<void>;
11
+ getCurrentVersion: () => Promise<number>;
12
+ }
13
+ export declare class Migrator implements IMigrator {
14
+ private readonly dbClient;
15
+ readonly migrationsDir: string;
16
+ constructor(dbClient: Sql, migrationsDir: string);
17
+ ensureMigrationTable(tx: ReservedSql): Promise<void>;
18
+ up(): Promise<void>;
19
+ down(): Promise<void>;
20
+ go(version: number, opts?: Option): Promise<void>;
21
+ migrate(direction: Direction, opts?: Option): Promise<void>;
22
+ getCurrentVersion(): Promise<number>;
23
+ getCurrentVersionWithTx(tx: ReservedSql): Promise<number>;
24
+ acquireLock(tx: ReservedSql): Promise<void>;
25
+ releaseLock(tx: ReservedSql): Promise<void>;
26
+ begin(tx: ReservedSql): Promise<void>;
27
+ commit(tx: ReservedSql): Promise<void>;
28
+ rollback(tx: ReservedSql): Promise<void>;
29
+ }
30
+ export {};
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migrator = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
6
+ const utils_1 = require("@/utils");
7
+ class Migrator {
8
+ constructor(dbClient, migrationsDir) {
9
+ Object.defineProperty(this, "dbClient", {
10
+ enumerable: true,
11
+ configurable: true,
12
+ writable: true,
13
+ value: void 0
14
+ });
15
+ Object.defineProperty(this, "migrationsDir", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: void 0
20
+ });
21
+ this.dbClient = dbClient;
22
+ this.migrationsDir = migrationsDir;
23
+ }
24
+ ensureMigrationTable(tx) {
25
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
26
+ yield tx `CREATE TABLE IF NOT EXISTS migrations(
27
+ name varchar(500) PRIMARY KEY,
28
+ version smallint NOT NULL,
29
+ applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`;
30
+ });
31
+ }
32
+ up() {
33
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
34
+ return this.migrate('up');
35
+ });
36
+ }
37
+ down() {
38
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
39
+ return this.migrate('down');
40
+ });
41
+ }
42
+ go(version, opts) {
43
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
44
+ var _a, _b;
45
+ const tx = yield this.dbClient.reserve();
46
+ try {
47
+ yield this.acquireLock(tx);
48
+ yield this.begin(tx);
49
+ yield this.ensureMigrationTable(tx);
50
+ const currentVersion = yield this.getCurrentVersionWithTx(tx);
51
+ if (currentVersion === version) {
52
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Already at version ${version}`);
53
+ return;
54
+ }
55
+ const direction = version > currentVersion ? 'up' : 'down';
56
+ const fileNames = (0, utils_1.getMigrationFiles)(this.migrationsDir, direction);
57
+ if (direction === 'up') {
58
+ const fileVersion = Math.min(fileNames.length, version);
59
+ for (let i = currentVersion; i < fileVersion; i++) {
60
+ const file = (_a = fileNames[i]) !== null && _a !== void 0 ? _a : '';
61
+ yield Promise.all([
62
+ tx.file(node_path_1.default.join(this.migrationsDir, file)).execute(),
63
+ tx `INSERT INTO migrations(name, version) VALUES (${file}, ${i} + 1)`
64
+ ]);
65
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Successfully migrated: ${file}`);
66
+ }
67
+ if (version > fileNames.length) {
68
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Currently at latest version: ${currentVersion}`);
69
+ }
70
+ }
71
+ else {
72
+ const start = fileNames.length - currentVersion;
73
+ const end = start + (currentVersion - version);
74
+ for (let i = start; i < end; i++) {
75
+ const file = (_b = fileNames[i]) !== null && _b !== void 0 ? _b : '';
76
+ yield Promise.all([
77
+ tx.file(node_path_1.default.join(this.migrationsDir, file)).execute(),
78
+ tx `DELETE FROM migrations WHERE version = ${fileNames.length - i}`
79
+ ]);
80
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Successfully migrated: ${file}`);
81
+ }
82
+ }
83
+ yield this.commit(tx);
84
+ }
85
+ catch (error) {
86
+ yield this.rollback(tx);
87
+ throw error;
88
+ }
89
+ finally {
90
+ yield this.releaseLock(tx);
91
+ tx.release();
92
+ }
93
+ });
94
+ }
95
+ migrate(direction, opts) {
96
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
97
+ var _a;
98
+ const tx = yield this.dbClient.reserve();
99
+ try {
100
+ yield this.acquireLock(tx);
101
+ yield this.begin(tx);
102
+ yield this.ensureMigrationTable(tx);
103
+ const currentVersion = yield this.getCurrentVersionWithTx(tx);
104
+ const fileNames = (0, utils_1.getMigrationFiles)(this.migrationsDir, direction);
105
+ if (direction === 'up') {
106
+ for (const fileName of fileNames) {
107
+ const id = fileNames.indexOf(fileName);
108
+ if (id >= currentVersion) {
109
+ yield Promise.all([
110
+ tx.file(node_path_1.default.join(this.migrationsDir, fileName)).execute(),
111
+ tx `INSERT INTO migrations(name, version) VALUES (${fileName}, ${id} + 1)`
112
+ ]);
113
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Successfully migrated: ${fileName}`);
114
+ }
115
+ }
116
+ }
117
+ else {
118
+ const start = fileNames.length - currentVersion;
119
+ for (let i = start; i < fileNames.length; i++) {
120
+ const file = (_a = fileNames[i]) !== null && _a !== void 0 ? _a : '';
121
+ yield Promise.all([
122
+ tx.file(node_path_1.default.join(this.migrationsDir, file)).execute(),
123
+ tx `DELETE FROM migrations WHERE version = ${fileNames.length - i}`
124
+ ]);
125
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Successfully migrated: ${file}`);
126
+ }
127
+ }
128
+ yield this.commit(tx);
129
+ }
130
+ catch (error) {
131
+ yield this.rollback(tx);
132
+ throw error;
133
+ }
134
+ finally {
135
+ yield this.releaseLock(tx);
136
+ tx.release();
137
+ }
138
+ });
139
+ }
140
+ getCurrentVersion() {
141
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
142
+ var _a;
143
+ const result = yield this
144
+ .dbClient `SELECT version FROM migrations ORDER BY version DESC LIMIT 1`;
145
+ return result.length > 0 ? (_a = result[0]) === null || _a === void 0 ? void 0 : _a['version'] : 0;
146
+ });
147
+ }
148
+ getCurrentVersionWithTx(tx) {
149
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
150
+ var _a;
151
+ const result = yield tx `SELECT version FROM migrations ORDER BY version DESC LIMIT 1`;
152
+ return result.length > 0 ? (_a = result[0]) === null || _a === void 0 ? void 0 : _a['version'] : 0;
153
+ });
154
+ }
155
+ acquireLock(tx) {
156
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
157
+ yield tx `SELECT pg_advisory_lock(21421431414441411)`;
158
+ });
159
+ }
160
+ releaseLock(tx) {
161
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
162
+ yield tx `SELECT pg_advisory_unlock(21421431414441411)`;
163
+ });
164
+ }
165
+ begin(tx) {
166
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
167
+ yield tx `BEGIN`;
168
+ });
169
+ }
170
+ commit(tx) {
171
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
172
+ yield tx `COMMIT`;
173
+ });
174
+ }
175
+ rollback(tx) {
176
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
177
+ yield tx `ROLLBACK`;
178
+ });
179
+ }
180
+ }
181
+ exports.Migrator = Migrator;
@@ -0,0 +1,4 @@
1
+ export type Direction = 'up' | 'down';
2
+ export declare const getMigrationFiles: (dir: string, direction: Direction) => string[];
3
+ export declare const createFolderIfNotExists: (filePath: string) => void;
4
+ export declare const createFile: (migrationDir: string, name: string) => string[];
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createFile = exports.createFolderIfNotExists = exports.getMigrationFiles = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
6
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
7
+ const getMigrationFiles = (dir, direction) => {
8
+ const files = node_fs_1.default
9
+ .readdirSync(dir)
10
+ .filter(file => file.endsWith(`_${direction}.sql`));
11
+ if (direction == 'up') {
12
+ return files.sort();
13
+ }
14
+ return files.sort((a, b) => b.localeCompare(a));
15
+ };
16
+ exports.getMigrationFiles = getMigrationFiles;
17
+ const createFolderIfNotExists = (filePath) => {
18
+ if (!node_fs_1.default.existsSync(filePath)) {
19
+ node_fs_1.default.mkdirSync(filePath, { recursive: true });
20
+ }
21
+ };
22
+ exports.createFolderIfNotExists = createFolderIfNotExists;
23
+ const createFile = (migrationDir, name) => {
24
+ (0, exports.createFolderIfNotExists)(migrationDir);
25
+ const timestamp = new Date().toISOString().replaceAll(/[.:TZ-]/g, '');
26
+ const ressult = [];
27
+ let fileName = `${timestamp}_${name}_up.sql`;
28
+ let filePath = node_path_1.default.join(migrationDir, fileName);
29
+ node_fs_1.default.writeFileSync(filePath, '-- up SQL here');
30
+ ressult.push(filePath);
31
+ fileName = `${timestamp}_${name}_down.sql`;
32
+ filePath = node_path_1.default.join(migrationDir, fileName);
33
+ node_fs_1.default.writeFileSync(filePath, '-- down SQL here');
34
+ ressult.push(filePath);
35
+ return ressult;
36
+ };
37
+ exports.createFile = createFile;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,70 @@
1
+ import { __awaiter } from "tslib";
2
+ import * as process from 'node:process';
3
+ import { Command } from 'commander';
4
+ import postgres from 'postgres';
5
+ import { Migrator } from '@/index';
6
+ import { createFile } from './utils';
7
+ const program = new Command();
8
+ let migrator;
9
+ program
10
+ .version('0.0.1')
11
+ .description('Database migration tool')
12
+ .option('-d, --migrationDir <filepath>', 'Specify migration directory(Default: ./migration)')
13
+ .hook('preAction', cmd => {
14
+ const opts = cmd.opts();
15
+ migrator = new Migrator(postgres(), opts.migrationDir || `${process.cwd()}/migrations`);
16
+ });
17
+ program
18
+ .command('up')
19
+ .description('Run all up migrations')
20
+ .action(() => __awaiter(void 0, void 0, void 0, function* () {
21
+ try {
22
+ yield migrator.up({ eventHandler: console.log });
23
+ console.log('Migrations up completed successfully.');
24
+ }
25
+ catch (error) {
26
+ console.error('Error during migrations up:', error);
27
+ process.exit(1);
28
+ }
29
+ }));
30
+ program
31
+ .command('down')
32
+ .description('Run all down migrations')
33
+ .action(() => __awaiter(void 0, void 0, void 0, function* () {
34
+ try {
35
+ yield migrator.down({ eventHandler: console.log });
36
+ console.log('Migrations down completed successfully.');
37
+ }
38
+ catch (error) {
39
+ console.error('Error during migrations down:', error);
40
+ process.exit(1);
41
+ }
42
+ }));
43
+ program
44
+ .command('create')
45
+ .description('Generate migration files in the migrations folder: one for applying changes (up), and one for reverting them (down).')
46
+ .argument('<filename>', 'file name to be created')
47
+ .action((fileName) => {
48
+ const result = createFile(migrator.migrationsDir, fileName);
49
+ for (const f of result)
50
+ console.log(`Successfully created migration files: ${f}`);
51
+ });
52
+ program
53
+ .command('go')
54
+ .description('Navigate to a specific version; version 0 performs a rollback, reverting all migrations.')
55
+ .argument('<version>', 'version to migrate to')
56
+ .action((version) => __awaiter(void 0, void 0, void 0, function* () {
57
+ const parsedVersion = Number.parseInt(version, 10);
58
+ if (Number.isNaN(parsedVersion)) {
59
+ console.error('Invalid version number.');
60
+ process.exit(1);
61
+ }
62
+ try {
63
+ yield migrator.go(parsedVersion, { eventHandler: console.log });
64
+ }
65
+ catch (error) {
66
+ console.error('Error during migrations:', error);
67
+ process.exit(1);
68
+ }
69
+ }));
70
+ program.parse(process.argv);
@@ -0,0 +1,30 @@
1
+ import { ReservedSql, Sql } from 'postgres';
2
+ import { Direction } from '@/utils';
3
+ interface Option {
4
+ eventHandler: (info: string) => void;
5
+ }
6
+ export interface IMigrator {
7
+ migrationsDir: string;
8
+ up: (opts?: Option) => Promise<void>;
9
+ down: (opts?: Option) => Promise<void>;
10
+ go: (version: number, opts?: Option) => Promise<void>;
11
+ getCurrentVersion: () => Promise<number>;
12
+ }
13
+ export declare class Migrator implements IMigrator {
14
+ private readonly dbClient;
15
+ readonly migrationsDir: string;
16
+ constructor(dbClient: Sql, migrationsDir: string);
17
+ ensureMigrationTable(tx: ReservedSql): Promise<void>;
18
+ up(): Promise<void>;
19
+ down(): Promise<void>;
20
+ go(version: number, opts?: Option): Promise<void>;
21
+ migrate(direction: Direction, opts?: Option): Promise<void>;
22
+ getCurrentVersion(): Promise<number>;
23
+ getCurrentVersionWithTx(tx: ReservedSql): Promise<number>;
24
+ acquireLock(tx: ReservedSql): Promise<void>;
25
+ releaseLock(tx: ReservedSql): Promise<void>;
26
+ begin(tx: ReservedSql): Promise<void>;
27
+ commit(tx: ReservedSql): Promise<void>;
28
+ rollback(tx: ReservedSql): Promise<void>;
29
+ }
30
+ export {};
@@ -0,0 +1,177 @@
1
+ import { __awaiter } from "tslib";
2
+ import path from 'node:path';
3
+ import { getMigrationFiles } from '@/utils';
4
+ export class Migrator {
5
+ constructor(dbClient, migrationsDir) {
6
+ Object.defineProperty(this, "dbClient", {
7
+ enumerable: true,
8
+ configurable: true,
9
+ writable: true,
10
+ value: void 0
11
+ });
12
+ Object.defineProperty(this, "migrationsDir", {
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true,
16
+ value: void 0
17
+ });
18
+ this.dbClient = dbClient;
19
+ this.migrationsDir = migrationsDir;
20
+ }
21
+ ensureMigrationTable(tx) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ yield tx `CREATE TABLE IF NOT EXISTS migrations(
24
+ name varchar(500) PRIMARY KEY,
25
+ version smallint NOT NULL,
26
+ applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`;
27
+ });
28
+ }
29
+ up() {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ return this.migrate('up');
32
+ });
33
+ }
34
+ down() {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ return this.migrate('down');
37
+ });
38
+ }
39
+ go(version, opts) {
40
+ return __awaiter(this, void 0, void 0, function* () {
41
+ var _a, _b;
42
+ const tx = yield this.dbClient.reserve();
43
+ try {
44
+ yield this.acquireLock(tx);
45
+ yield this.begin(tx);
46
+ yield this.ensureMigrationTable(tx);
47
+ const currentVersion = yield this.getCurrentVersionWithTx(tx);
48
+ if (currentVersion === version) {
49
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Already at version ${version}`);
50
+ return;
51
+ }
52
+ const direction = version > currentVersion ? 'up' : 'down';
53
+ const fileNames = getMigrationFiles(this.migrationsDir, direction);
54
+ if (direction === 'up') {
55
+ const fileVersion = Math.min(fileNames.length, version);
56
+ for (let i = currentVersion; i < fileVersion; i++) {
57
+ const file = (_a = fileNames[i]) !== null && _a !== void 0 ? _a : '';
58
+ yield Promise.all([
59
+ tx.file(path.join(this.migrationsDir, file)).execute(),
60
+ tx `INSERT INTO migrations(name, version) VALUES (${file}, ${i} + 1)`
61
+ ]);
62
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Successfully migrated: ${file}`);
63
+ }
64
+ if (version > fileNames.length) {
65
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Currently at latest version: ${currentVersion}`);
66
+ }
67
+ }
68
+ else {
69
+ const start = fileNames.length - currentVersion;
70
+ const end = start + (currentVersion - version);
71
+ for (let i = start; i < end; i++) {
72
+ const file = (_b = fileNames[i]) !== null && _b !== void 0 ? _b : '';
73
+ yield Promise.all([
74
+ tx.file(path.join(this.migrationsDir, file)).execute(),
75
+ tx `DELETE FROM migrations WHERE version = ${fileNames.length - i}`
76
+ ]);
77
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Successfully migrated: ${file}`);
78
+ }
79
+ }
80
+ yield this.commit(tx);
81
+ }
82
+ catch (error) {
83
+ yield this.rollback(tx);
84
+ throw error;
85
+ }
86
+ finally {
87
+ yield this.releaseLock(tx);
88
+ tx.release();
89
+ }
90
+ });
91
+ }
92
+ migrate(direction, opts) {
93
+ return __awaiter(this, void 0, void 0, function* () {
94
+ var _a;
95
+ const tx = yield this.dbClient.reserve();
96
+ try {
97
+ yield this.acquireLock(tx);
98
+ yield this.begin(tx);
99
+ yield this.ensureMigrationTable(tx);
100
+ const currentVersion = yield this.getCurrentVersionWithTx(tx);
101
+ const fileNames = getMigrationFiles(this.migrationsDir, direction);
102
+ if (direction === 'up') {
103
+ for (const fileName of fileNames) {
104
+ const id = fileNames.indexOf(fileName);
105
+ if (id >= currentVersion) {
106
+ yield Promise.all([
107
+ tx.file(path.join(this.migrationsDir, fileName)).execute(),
108
+ tx `INSERT INTO migrations(name, version) VALUES (${fileName}, ${id} + 1)`
109
+ ]);
110
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Successfully migrated: ${fileName}`);
111
+ }
112
+ }
113
+ }
114
+ else {
115
+ const start = fileNames.length - currentVersion;
116
+ for (let i = start; i < fileNames.length; i++) {
117
+ const file = (_a = fileNames[i]) !== null && _a !== void 0 ? _a : '';
118
+ yield Promise.all([
119
+ tx.file(path.join(this.migrationsDir, file)).execute(),
120
+ tx `DELETE FROM migrations WHERE version = ${fileNames.length - i}`
121
+ ]);
122
+ opts === null || opts === void 0 ? void 0 : opts.eventHandler(`Successfully migrated: ${file}`);
123
+ }
124
+ }
125
+ yield this.commit(tx);
126
+ }
127
+ catch (error) {
128
+ yield this.rollback(tx);
129
+ throw error;
130
+ }
131
+ finally {
132
+ yield this.releaseLock(tx);
133
+ tx.release();
134
+ }
135
+ });
136
+ }
137
+ getCurrentVersion() {
138
+ return __awaiter(this, void 0, void 0, function* () {
139
+ var _a;
140
+ const result = yield this
141
+ .dbClient `SELECT version FROM migrations ORDER BY version DESC LIMIT 1`;
142
+ return result.length > 0 ? (_a = result[0]) === null || _a === void 0 ? void 0 : _a['version'] : 0;
143
+ });
144
+ }
145
+ getCurrentVersionWithTx(tx) {
146
+ return __awaiter(this, void 0, void 0, function* () {
147
+ var _a;
148
+ const result = yield tx `SELECT version FROM migrations ORDER BY version DESC LIMIT 1`;
149
+ return result.length > 0 ? (_a = result[0]) === null || _a === void 0 ? void 0 : _a['version'] : 0;
150
+ });
151
+ }
152
+ acquireLock(tx) {
153
+ return __awaiter(this, void 0, void 0, function* () {
154
+ yield tx `SELECT pg_advisory_lock(21421431414441411)`;
155
+ });
156
+ }
157
+ releaseLock(tx) {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ yield tx `SELECT pg_advisory_unlock(21421431414441411)`;
160
+ });
161
+ }
162
+ begin(tx) {
163
+ return __awaiter(this, void 0, void 0, function* () {
164
+ yield tx `BEGIN`;
165
+ });
166
+ }
167
+ commit(tx) {
168
+ return __awaiter(this, void 0, void 0, function* () {
169
+ yield tx `COMMIT`;
170
+ });
171
+ }
172
+ rollback(tx) {
173
+ return __awaiter(this, void 0, void 0, function* () {
174
+ yield tx `ROLLBACK`;
175
+ });
176
+ }
177
+ }
@@ -0,0 +1,4 @@
1
+ export type Direction = 'up' | 'down';
2
+ export declare const getMigrationFiles: (dir: string, direction: Direction) => string[];
3
+ export declare const createFolderIfNotExists: (filePath: string) => void;
4
+ export declare const createFile: (migrationDir: string, name: string) => string[];