@blackglory/pg-migrations 0.2.3 → 0.3.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 +25 -7
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/migrate.d.ts +14 -0
- package/lib/migrate.js +150 -0
- package/lib/migrate.js.map +1 -0
- package/package.json +36 -35
- package/src/index.ts +1 -1
- package/src/migrate.ts +118 -48
- package/lib/es2015/index.d.ts +0 -1
- package/lib/es2015/index.js +0 -18
- package/lib/es2015/index.js.map +0 -1
- package/lib/es2015/migrate.d.ts +0 -7
- package/lib/es2015/migrate.js +0 -145
- package/lib/es2015/migrate.js.map +0 -1
- package/lib/es2018/index.d.ts +0 -1
- package/lib/es2018/index.js +0 -18
- package/lib/es2018/index.js.map +0 -1
- package/lib/es2018/migrate.d.ts +0 -7
- package/lib/es2018/migrate.js +0 -120
- package/lib/es2018/migrate.js.map +0 -1
package/README.md
CHANGED
|
@@ -21,22 +21,40 @@ interface IMigration {
|
|
|
21
21
|
}
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
You may need [migration-files].
|
|
25
|
+
|
|
26
|
+
[migration-files]: https://github.com/BlackGlory/migration-files
|
|
27
|
+
|
|
24
28
|
### migrate
|
|
25
29
|
```ts
|
|
26
30
|
function migrate(
|
|
27
31
|
client: Client
|
|
28
32
|
, migrations: IMigration[]
|
|
29
|
-
,
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
, options?: {
|
|
34
|
+
targetVersion?: number
|
|
35
|
+
throwOnNewerVersion?: boolean = false
|
|
36
|
+
|
|
37
|
+
migrationsTable?: string = 'migrations'
|
|
38
|
+
advisoryLockKey?: bigint = BigInt('-9223372036854775808') // The smallest bigint for postgres
|
|
39
|
+
}
|
|
32
40
|
): Promise<void>
|
|
33
41
|
```
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
If `options.targetVersion` is `undefined`,
|
|
44
|
+
the maximum version of the `migrations` is used.
|
|
45
|
+
|
|
46
|
+
When the maximum known migration version is less than the database schema version,
|
|
47
|
+
it means the current instance is outdated.
|
|
48
|
+
- When `options.throwOnNewerVersion` is `false` (default),
|
|
49
|
+
it will skip the migration,
|
|
50
|
+
so your outdated instance continues to run.
|
|
51
|
+
- When `options.throwOnNewerVersion` is `true`,
|
|
52
|
+
it will throw an error,
|
|
53
|
+
so your outdated instance fails immediately.
|
|
54
|
+
|
|
55
|
+
#### Can multiple instances migrate in parallel?
|
|
37
56
|
Yes, it uses advisory lock to ensure that only one instance is migrating at a time.
|
|
38
|
-
When the maximum migration version is less than the database schema version (which means it is an obsolete instance), it will skip the migration.
|
|
39
57
|
|
|
40
|
-
|
|
58
|
+
#### What if my migration requires more than one connection?
|
|
41
59
|
You can get all connection configurations through properties to create a new `pg.Client`.
|
|
42
60
|
It is important to note that the custom client you create is not part of the migration transaction.
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './migrate.js';
|
package/lib/index.js
ADDED
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA"}
|
package/lib/migrate.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Client } from 'pg';
|
|
2
|
+
export interface IMigration {
|
|
3
|
+
version: number;
|
|
4
|
+
up: string | ((client: Client) => PromiseLike<void>);
|
|
5
|
+
down: string | ((client: Client) => PromiseLike<void>);
|
|
6
|
+
}
|
|
7
|
+
interface IMigrateOptions {
|
|
8
|
+
targetVersion?: number;
|
|
9
|
+
throwOnNewerVersion?: boolean;
|
|
10
|
+
migrationsTable?: string;
|
|
11
|
+
advisoryLockKey?: bigint;
|
|
12
|
+
}
|
|
13
|
+
export declare function migrate(client: Client, migrations: IMigration[], { targetVersion, throwOnNewerVersion, migrationsTable, advisoryLockKey }?: IMigrateOptions): Promise<void>;
|
|
14
|
+
export {};
|
package/lib/migrate.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { assert, isFunction } from '@blackglory/prelude';
|
|
2
|
+
import { first, isntUndefined, isUndefined, max } from 'extra-utils';
|
|
3
|
+
export async function migrate(client, migrations, { targetVersion = getMaximumVersion(migrations), throwOnNewerVersion = false, migrationsTable = 'migrations', advisoryLockKey = BigInt('-9223372036854775808') } = {}) {
|
|
4
|
+
const maxVersion = getMaximumVersion(migrations);
|
|
5
|
+
await lock(client, advisoryLockKey);
|
|
6
|
+
try {
|
|
7
|
+
await ensureMigrationsTable(client, migrationsTable);
|
|
8
|
+
while (true) {
|
|
9
|
+
const done = await transaction(client, async () => {
|
|
10
|
+
const currentVersion = await getDatabaseVersion(client, migrationsTable);
|
|
11
|
+
if (maxVersion < currentVersion) {
|
|
12
|
+
if (throwOnNewerVersion) {
|
|
13
|
+
throw new Error(`Database version ${currentVersion} is higher than the maximum known migration version.`);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
if (currentVersion === targetVersion) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
else if (currentVersion < targetVersion) {
|
|
24
|
+
await upgrade();
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
await downgrade();
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
if (done)
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
await unlock(client, advisoryLockKey);
|
|
39
|
+
}
|
|
40
|
+
async function upgrade() {
|
|
41
|
+
const currentVersion = await getDatabaseVersion(client, migrationsTable);
|
|
42
|
+
const targetVersion = currentVersion + 1;
|
|
43
|
+
try {
|
|
44
|
+
const migration = migrations.find(x => x.version === targetVersion);
|
|
45
|
+
assert(migration, `Cannot find migration for version ${targetVersion}`);
|
|
46
|
+
if (isFunction(migration.up)) {
|
|
47
|
+
await migration.up(client);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
await client.query(migration.up);
|
|
51
|
+
}
|
|
52
|
+
await setDatabaseVersion(client, migrationsTable, targetVersion);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
throw new Error(`Upgrade from version ${currentVersion} to version ${targetVersion} failed.`, { cause: e });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function downgrade() {
|
|
59
|
+
const currentVersion = await getDatabaseVersion(client, migrationsTable);
|
|
60
|
+
const targetVersion = currentVersion - 1;
|
|
61
|
+
try {
|
|
62
|
+
const migration = migrations.find(x => x.version === currentVersion);
|
|
63
|
+
assert(migration, `Cannot find migration for version ${targetVersion}`);
|
|
64
|
+
if (isFunction(migration.down)) {
|
|
65
|
+
await migration.down(client);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
await client.query(migration.down);
|
|
69
|
+
}
|
|
70
|
+
await setDatabaseVersion(client, migrationsTable, targetVersion);
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
throw new Error(`Downgrade from version ${currentVersion} to version ${targetVersion} failed.`, { cause: e });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function getMaximumVersion(migrations) {
|
|
78
|
+
return migrations
|
|
79
|
+
.map(x => x.version)
|
|
80
|
+
.reduce(max, 0);
|
|
81
|
+
}
|
|
82
|
+
async function getDatabaseVersion(client, migrationTable) {
|
|
83
|
+
const version = await tryGetDatabaseVersion(client, migrationTable);
|
|
84
|
+
assert(isntUndefined(version));
|
|
85
|
+
return version;
|
|
86
|
+
}
|
|
87
|
+
async function tryGetDatabaseVersion(client, migrationTable) {
|
|
88
|
+
const result = await client.query(`
|
|
89
|
+
SELECT schema_version
|
|
90
|
+
FROM "${migrationTable}"
|
|
91
|
+
LIMIT 1;
|
|
92
|
+
`);
|
|
93
|
+
return first(result.rows)?.schema_version;
|
|
94
|
+
}
|
|
95
|
+
async function ensureMigrationsTable(client, migrationTable) {
|
|
96
|
+
await transaction(client, async () => {
|
|
97
|
+
await client.query(`
|
|
98
|
+
CREATE TABLE IF NOT EXISTS "${migrationTable}" (
|
|
99
|
+
schema_version INTEGER NOT NULL
|
|
100
|
+
);
|
|
101
|
+
`);
|
|
102
|
+
if (isUndefined(await tryGetDatabaseVersion(client, migrationTable))) {
|
|
103
|
+
await client.query(`
|
|
104
|
+
INSERT INTO "${migrationTable}" (schema_version)
|
|
105
|
+
VALUES (0);
|
|
106
|
+
`);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async function setDatabaseVersion(client, migrationTable, version) {
|
|
111
|
+
await client.query(`
|
|
112
|
+
UPDATE "${migrationTable}"
|
|
113
|
+
SET schema_version = $1;
|
|
114
|
+
`, [version]);
|
|
115
|
+
}
|
|
116
|
+
async function lock(client, key) {
|
|
117
|
+
await client.query(`
|
|
118
|
+
SELECT pg_advisory_lock(${key});
|
|
119
|
+
`);
|
|
120
|
+
}
|
|
121
|
+
async function unlock(client, key) {
|
|
122
|
+
await client.query(`
|
|
123
|
+
SELECT pg_advisory_unlock(${key});
|
|
124
|
+
`);
|
|
125
|
+
}
|
|
126
|
+
const inTransactionClients = new WeakSet();
|
|
127
|
+
async function transaction(client, fn) {
|
|
128
|
+
if (inTransactionClients.has(client)) {
|
|
129
|
+
throw new Error('PostgreSQL does not support nested transactions.');
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
inTransactionClients.add(client);
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
await client.query('BEGIN');
|
|
136
|
+
try {
|
|
137
|
+
const result = await fn();
|
|
138
|
+
await client.query('COMMIT');
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
await client.query('ROLLBACK');
|
|
143
|
+
throw e;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
inTransactionClients.delete(client);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blackglory/pg-migrations",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A utility for database migrations with pg",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"migration",
|
|
@@ -14,61 +14,62 @@
|
|
|
14
14
|
"lib",
|
|
15
15
|
"src"
|
|
16
16
|
],
|
|
17
|
-
"
|
|
18
|
-
"
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "lib/index.js",
|
|
19
|
+
"types": "lib/index.d.ts",
|
|
19
20
|
"repository": "git@github.com:BlackGlory/pg-migrations.git",
|
|
20
21
|
"author": "BlackGlory <woshenmedoubuzhidao@blackglory.me>",
|
|
21
22
|
"license": "MIT",
|
|
22
23
|
"sideEffects": false,
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
23
27
|
"scripts": {
|
|
28
|
+
"prepare": "ts-patch install -s",
|
|
24
29
|
"lint": "eslint --ext .js,.jsx,.ts,.tsx --quiet src __tests__",
|
|
25
|
-
"test": "
|
|
26
|
-
"
|
|
27
|
-
"test:coverage": "jest --coverage --config jest.config.js",
|
|
28
|
-
"prepublishOnly": "run-s clean build",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"prepublishOnly": "run-s prepare clean build",
|
|
29
32
|
"clean": "rimraf lib",
|
|
30
|
-
"build": "
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"build:es2018:compile": "tsc --project tsconfig.build.json --target es2018 --outDir lib/es2018",
|
|
36
|
-
"build:es2018:patch": "tscpaths -p tsconfig.build.json -s ./src -o ./lib/es2018",
|
|
37
|
-
"docker:test": "run-s docker:test:build docker:test:run; run-s docker:test:clean",
|
|
38
|
-
"docker:test:build": "docker-compose --file docker-compose.test.yml build",
|
|
39
|
-
"docker:test:run": "docker-compose --file docker-compose.test.yml run --rm sut",
|
|
40
|
-
"docker:test:clean": "docker-compose --file docker-compose.test.yml down",
|
|
33
|
+
"build": "tsc --project tsconfig.build.json",
|
|
34
|
+
"docker:test": "run-s docker:test:clean docker:test:build docker:test:run docker:test:clean",
|
|
35
|
+
"docker:test:build": "docker compose --file docker-compose.test.yml build",
|
|
36
|
+
"docker:test:run": "docker compose --file docker-compose.test.yml run --rm test",
|
|
37
|
+
"docker:test:clean": "docker compose --file docker-compose.test.yml down",
|
|
41
38
|
"release": "standard-version"
|
|
42
39
|
},
|
|
43
40
|
"husky": {
|
|
44
41
|
"hooks": {
|
|
45
|
-
"pre-commit": "run-s lint docker:test",
|
|
42
|
+
"pre-commit": "run-s prepare lint build docker:test",
|
|
46
43
|
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
|
47
44
|
}
|
|
48
45
|
},
|
|
49
46
|
"devDependencies": {
|
|
50
|
-
"@commitlint/cli": "^
|
|
51
|
-
"@commitlint/config-conventional": "^
|
|
52
|
-
"@
|
|
53
|
-
"@types/pg": "^8.
|
|
54
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
55
|
-
"@typescript-eslint/parser": "^
|
|
56
|
-
"eslint": "^
|
|
47
|
+
"@commitlint/cli": "^20.4.1",
|
|
48
|
+
"@commitlint/config-conventional": "^20.4.1",
|
|
49
|
+
"@eslint/js": "^10.0.1",
|
|
50
|
+
"@types/pg": "^8.16.0",
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
52
|
+
"@typescript-eslint/parser": "^8.55.0",
|
|
53
|
+
"eslint": "^10.0.0",
|
|
57
54
|
"husky": "^4.3.0",
|
|
58
|
-
"jest": "^27.5.1",
|
|
59
55
|
"npm-run-all": "^4.1.5",
|
|
60
|
-
"pg": "^8.
|
|
61
|
-
"
|
|
56
|
+
"pg": "^8.18.0",
|
|
57
|
+
"return-style": "^3.0.1",
|
|
58
|
+
"rimraf": "^6.1.2",
|
|
62
59
|
"standard-version": "^9.5.0",
|
|
63
|
-
"ts-
|
|
64
|
-
"
|
|
65
|
-
"typescript": "^
|
|
60
|
+
"ts-patch": "^3.3.0",
|
|
61
|
+
"typescript": "^5.9.3",
|
|
62
|
+
"typescript-eslint": "^8.55.0",
|
|
63
|
+
"typescript-transform-paths": "^3.5.6",
|
|
64
|
+
"vite": "^7.3.1",
|
|
65
|
+
"vite-tsconfig-paths": "^6.1.0",
|
|
66
|
+
"vitest": "^4.0.18"
|
|
66
67
|
},
|
|
67
68
|
"dependencies": {
|
|
68
|
-
"@blackglory/
|
|
69
|
-
"
|
|
69
|
+
"@blackglory/prelude": "^0.4.0",
|
|
70
|
+
"extra-utils": "^5.20.0"
|
|
70
71
|
},
|
|
71
72
|
"peerDependencies": {
|
|
72
|
-
"pg": "8"
|
|
73
|
+
"pg": "^8.0.0"
|
|
73
74
|
}
|
|
74
75
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './migrate'
|
|
1
|
+
export * from './migrate.js'
|
package/src/migrate.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Client } from 'pg'
|
|
2
|
-
import { isFunction } from '@blackglory/
|
|
3
|
-
import {
|
|
2
|
+
import { assert, isFunction } from '@blackglory/prelude'
|
|
3
|
+
import { first, isntUndefined, isUndefined, max } from 'extra-utils'
|
|
4
4
|
|
|
5
5
|
export interface IMigration {
|
|
6
6
|
version: number
|
|
@@ -8,36 +8,61 @@ export interface IMigration {
|
|
|
8
8
|
down: string | ((client: Client) => PromiseLike<void>)
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
interface IMigrateOptions {
|
|
12
|
+
targetVersion?: number
|
|
13
|
+
throwOnNewerVersion?: boolean
|
|
14
|
+
|
|
15
|
+
migrationsTable?: string
|
|
16
|
+
advisoryLockKey?: bigint
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
export async function migrate(
|
|
12
20
|
client: Client
|
|
13
21
|
, migrations: IMigration[]
|
|
14
|
-
,
|
|
15
|
-
|
|
16
|
-
,
|
|
22
|
+
, {
|
|
23
|
+
targetVersion = getMaximumVersion(migrations)
|
|
24
|
+
, throwOnNewerVersion = false
|
|
25
|
+
, migrationsTable = 'migrations'
|
|
26
|
+
, advisoryLockKey = BigInt('-9223372036854775808') // The smallest bigint for postgres
|
|
27
|
+
}: IMigrateOptions = {}
|
|
17
28
|
): Promise<void> {
|
|
18
29
|
const maxVersion = getMaximumVersion(migrations)
|
|
30
|
+
|
|
19
31
|
await lock(client, advisoryLockKey)
|
|
20
32
|
try {
|
|
33
|
+
|
|
34
|
+
await ensureMigrationsTable(client, migrationsTable)
|
|
35
|
+
|
|
21
36
|
while (true) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
const done = await transaction(client, async () => {
|
|
38
|
+
const currentVersion = await getDatabaseVersion(client, migrationsTable)
|
|
39
|
+
|
|
40
|
+
if (maxVersion < currentVersion) {
|
|
41
|
+
if (throwOnNewerVersion) {
|
|
42
|
+
throw new Error(`Database version ${currentVersion} is higher than the maximum known migration version.`)
|
|
43
|
+
} else {
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
30
46
|
} else {
|
|
31
|
-
|
|
47
|
+
if (currentVersion === targetVersion) {
|
|
48
|
+
return true
|
|
49
|
+
} else if (currentVersion < targetVersion) {
|
|
50
|
+
await upgrade()
|
|
51
|
+
return false
|
|
52
|
+
} else {
|
|
53
|
+
await downgrade()
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
32
56
|
}
|
|
33
|
-
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
if (done) break
|
|
34
60
|
}
|
|
35
61
|
} finally {
|
|
36
62
|
await unlock(client, advisoryLockKey)
|
|
37
63
|
}
|
|
38
64
|
|
|
39
|
-
async function upgrade() {
|
|
40
|
-
await client.query('BEGIN')
|
|
65
|
+
async function upgrade(): Promise<void> {
|
|
41
66
|
const currentVersion: number = await getDatabaseVersion(client, migrationsTable)
|
|
42
67
|
const targetVersion = currentVersion + 1
|
|
43
68
|
try {
|
|
@@ -50,16 +75,15 @@ export async function migrate(
|
|
|
50
75
|
await client.query(migration.up)
|
|
51
76
|
}
|
|
52
77
|
await setDatabaseVersion(client, migrationsTable, targetVersion)
|
|
53
|
-
await client.query('COMMIT')
|
|
54
78
|
} catch (e) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Upgrade from version ${currentVersion} to version ${targetVersion} failed.`
|
|
81
|
+
, { cause: e }
|
|
82
|
+
)
|
|
58
83
|
}
|
|
59
84
|
}
|
|
60
85
|
|
|
61
|
-
async function downgrade() {
|
|
62
|
-
await client.query('BEGIN')
|
|
86
|
+
async function downgrade(): Promise<void> {
|
|
63
87
|
const currentVersion = await getDatabaseVersion(client, migrationsTable)
|
|
64
88
|
const targetVersion = currentVersion - 1
|
|
65
89
|
try {
|
|
@@ -72,43 +96,62 @@ export async function migrate(
|
|
|
72
96
|
await client.query(migration.down)
|
|
73
97
|
}
|
|
74
98
|
await setDatabaseVersion(client, migrationsTable, targetVersion)
|
|
75
|
-
await client.query('COMMIT')
|
|
76
99
|
} catch (e) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Downgrade from version ${currentVersion} to version ${targetVersion} failed.`
|
|
102
|
+
, { cause: e }
|
|
103
|
+
)
|
|
80
104
|
}
|
|
81
105
|
}
|
|
82
106
|
}
|
|
83
107
|
|
|
84
108
|
function getMaximumVersion(migrations: IMigration[]): number {
|
|
85
|
-
return migrations
|
|
109
|
+
return migrations
|
|
110
|
+
.map(x => x.version)
|
|
111
|
+
.reduce(max, 0)
|
|
86
112
|
}
|
|
87
113
|
|
|
88
|
-
async function getDatabaseVersion(
|
|
89
|
-
|
|
114
|
+
async function getDatabaseVersion(
|
|
115
|
+
client: Client
|
|
116
|
+
, migrationTable: string
|
|
117
|
+
): Promise<number> {
|
|
118
|
+
const version = await tryGetDatabaseVersion(client, migrationTable)
|
|
119
|
+
assert(isntUndefined(version))
|
|
120
|
+
|
|
121
|
+
return version
|
|
122
|
+
}
|
|
90
123
|
|
|
124
|
+
async function tryGetDatabaseVersion(
|
|
125
|
+
client: Client
|
|
126
|
+
, migrationTable: string
|
|
127
|
+
): Promise<number | undefined> {
|
|
91
128
|
const result = await client.query<{ schema_version: number }>(`
|
|
92
129
|
SELECT schema_version
|
|
93
|
-
FROM "${migrationTable}"
|
|
130
|
+
FROM "${migrationTable}"
|
|
131
|
+
LIMIT 1;
|
|
94
132
|
`)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
133
|
+
|
|
134
|
+
return first(result.rows)?.schema_version
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function ensureMigrationsTable(
|
|
138
|
+
client: Client
|
|
139
|
+
, migrationTable: string
|
|
140
|
+
): Promise<void> {
|
|
141
|
+
await transaction(client, async () => {
|
|
98
142
|
await client.query(`
|
|
99
|
-
|
|
100
|
-
|
|
143
|
+
CREATE TABLE IF NOT EXISTS "${migrationTable}" (
|
|
144
|
+
schema_version INTEGER NOT NULL
|
|
145
|
+
);
|
|
101
146
|
`)
|
|
102
|
-
return 0
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
147
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
148
|
+
if (isUndefined(await tryGetDatabaseVersion(client, migrationTable))) {
|
|
149
|
+
await client.query(`
|
|
150
|
+
INSERT INTO "${migrationTable}" (schema_version)
|
|
151
|
+
VALUES (0);
|
|
152
|
+
`)
|
|
153
|
+
}
|
|
154
|
+
})
|
|
112
155
|
}
|
|
113
156
|
|
|
114
157
|
async function setDatabaseVersion(
|
|
@@ -117,9 +160,9 @@ async function setDatabaseVersion(
|
|
|
117
160
|
, version: number
|
|
118
161
|
): Promise<void> {
|
|
119
162
|
await client.query(`
|
|
120
|
-
UPDATE ${migrationTable}
|
|
121
|
-
SET schema_version = $
|
|
122
|
-
|
|
163
|
+
UPDATE "${migrationTable}"
|
|
164
|
+
SET schema_version = $1;
|
|
165
|
+
`, [version])
|
|
123
166
|
}
|
|
124
167
|
|
|
125
168
|
async function lock(client: Client, key: bigint): Promise<void> {
|
|
@@ -133,3 +176,30 @@ async function unlock(client: Client, key: bigint): Promise<void> {
|
|
|
133
176
|
SELECT pg_advisory_unlock(${key});
|
|
134
177
|
`)
|
|
135
178
|
}
|
|
179
|
+
|
|
180
|
+
const inTransactionClients = new WeakSet<Client>()
|
|
181
|
+
async function transaction<T>(client: Client, fn: () => Promise<T>): Promise<T> {
|
|
182
|
+
if (inTransactionClients.has(client)) {
|
|
183
|
+
throw new Error('PostgreSQL does not support nested transactions.')
|
|
184
|
+
} else {
|
|
185
|
+
inTransactionClients.add(client)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await client.query('BEGIN')
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const result = await fn()
|
|
193
|
+
|
|
194
|
+
await client.query('COMMIT')
|
|
195
|
+
|
|
196
|
+
return result
|
|
197
|
+
} catch (e) {
|
|
198
|
+
await client.query('ROLLBACK')
|
|
199
|
+
|
|
200
|
+
throw e
|
|
201
|
+
}
|
|
202
|
+
} finally {
|
|
203
|
+
inTransactionClients.delete(client)
|
|
204
|
+
}
|
|
205
|
+
}
|
package/lib/es2015/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './migrate';
|
package/lib/es2015/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./migrate"), exports);
|
|
18
|
-
//# sourceMappingURL=index.js.map
|
package/lib/es2015/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAAyB"}
|
package/lib/es2015/migrate.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { Client } from 'pg';
|
|
2
|
-
export interface IMigration {
|
|
3
|
-
version: number;
|
|
4
|
-
up: string | ((client: Client) => PromiseLike<void>);
|
|
5
|
-
down: string | ((client: Client) => PromiseLike<void>);
|
|
6
|
-
}
|
|
7
|
-
export declare function migrate(client: Client, migrations: IMigration[], targetVersion?: number, migrationsTable?: string, advisoryLockKey?: bigint): Promise<void>;
|
package/lib/es2015/migrate.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.migrate = void 0;
|
|
13
|
-
const types_1 = require("@blackglory/types");
|
|
14
|
-
const errors_1 = require("@blackglory/errors");
|
|
15
|
-
function migrate(client, migrations, targetVersion = getMaximumVersion(migrations), migrationsTable = 'migrations', advisoryLockKey = BigInt('-9223372036854775808')) {
|
|
16
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
-
const maxVersion = getMaximumVersion(migrations);
|
|
18
|
-
yield lock(client, advisoryLockKey);
|
|
19
|
-
try {
|
|
20
|
-
while (true) {
|
|
21
|
-
const currentVersion = yield getDatabaseVersion(client, migrationsTable);
|
|
22
|
-
if (maxVersion < currentVersion) {
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
if (currentVersion === targetVersion) {
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
else if (currentVersion < targetVersion) {
|
|
30
|
-
yield upgrade();
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
yield downgrade();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
finally {
|
|
39
|
-
yield unlock(client, advisoryLockKey);
|
|
40
|
-
}
|
|
41
|
-
function upgrade() {
|
|
42
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
-
yield client.query('BEGIN');
|
|
44
|
-
const currentVersion = yield getDatabaseVersion(client, migrationsTable);
|
|
45
|
-
const targetVersion = currentVersion + 1;
|
|
46
|
-
try {
|
|
47
|
-
const migration = migrations.find(x => x.version === targetVersion);
|
|
48
|
-
(0, errors_1.assert)(migration, `Cannot find migration for version ${targetVersion}`);
|
|
49
|
-
if ((0, types_1.isFunction)(migration.up)) {
|
|
50
|
-
yield migration.up(client);
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
yield client.query(migration.up);
|
|
54
|
-
}
|
|
55
|
-
yield setDatabaseVersion(client, migrationsTable, targetVersion);
|
|
56
|
-
yield client.query('COMMIT');
|
|
57
|
-
}
|
|
58
|
-
catch (e) {
|
|
59
|
-
console.error(`Upgrade from version ${currentVersion} to version ${targetVersion} failed.`);
|
|
60
|
-
yield client.query('ROLLBACK');
|
|
61
|
-
throw e;
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
function downgrade() {
|
|
66
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
67
|
-
yield client.query('BEGIN');
|
|
68
|
-
const currentVersion = yield getDatabaseVersion(client, migrationsTable);
|
|
69
|
-
const targetVersion = currentVersion - 1;
|
|
70
|
-
try {
|
|
71
|
-
const migration = migrations.find(x => x.version === currentVersion);
|
|
72
|
-
(0, errors_1.assert)(migration, `Cannot find migration for version ${targetVersion}`);
|
|
73
|
-
if ((0, types_1.isFunction)(migration.down)) {
|
|
74
|
-
yield migration.down(client);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
yield client.query(migration.down);
|
|
78
|
-
}
|
|
79
|
-
yield setDatabaseVersion(client, migrationsTable, targetVersion);
|
|
80
|
-
yield client.query('COMMIT');
|
|
81
|
-
}
|
|
82
|
-
catch (e) {
|
|
83
|
-
console.error(`Downgrade from version ${currentVersion} to version ${targetVersion} failed.`);
|
|
84
|
-
yield client.query('ROLLBACK');
|
|
85
|
-
throw e;
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
exports.migrate = migrate;
|
|
92
|
-
function getMaximumVersion(migrations) {
|
|
93
|
-
return migrations.reduce((max, cur) => Math.max(cur.version, max), 0);
|
|
94
|
-
}
|
|
95
|
-
function getDatabaseVersion(client, migrationTable) {
|
|
96
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
97
|
-
yield ensureMigrationsTable(client, migrationTable);
|
|
98
|
-
const result = yield client.query(`
|
|
99
|
-
SELECT schema_version
|
|
100
|
-
FROM "${migrationTable}";
|
|
101
|
-
`);
|
|
102
|
-
if (result.rows.length) {
|
|
103
|
-
return result.rows[0].schema_version;
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
yield client.query(`
|
|
107
|
-
INSERT INTO "${migrationTable}" (schema_version)
|
|
108
|
-
VALUES (0);
|
|
109
|
-
`);
|
|
110
|
-
return 0;
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
function ensureMigrationsTable(client, migrationTable) {
|
|
115
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
-
yield client.query(`
|
|
117
|
-
CREATE TABLE IF NOT EXISTS "${migrationTable}" (
|
|
118
|
-
schema_version INTEGER NOT NULL
|
|
119
|
-
);
|
|
120
|
-
`);
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
function setDatabaseVersion(client, migrationTable, version) {
|
|
124
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
-
yield client.query(`
|
|
126
|
-
UPDATE ${migrationTable}
|
|
127
|
-
SET schema_version = ${version};
|
|
128
|
-
`);
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
function lock(client, key) {
|
|
132
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
133
|
-
yield client.query(`
|
|
134
|
-
SELECT pg_advisory_lock(${key});
|
|
135
|
-
`);
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
function unlock(client, key) {
|
|
139
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
140
|
-
yield client.query(`
|
|
141
|
-
SELECT pg_advisory_unlock(${key});
|
|
142
|
-
`);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
//# sourceMappingURL=migrate.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/migrate.ts"],"names":[],"mappings":"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"}
|
package/lib/es2018/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './migrate';
|
package/lib/es2018/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./migrate"), exports);
|
|
18
|
-
//# sourceMappingURL=index.js.map
|
package/lib/es2018/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAAyB"}
|
package/lib/es2018/migrate.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { Client } from 'pg';
|
|
2
|
-
export interface IMigration {
|
|
3
|
-
version: number;
|
|
4
|
-
up: string | ((client: Client) => PromiseLike<void>);
|
|
5
|
-
down: string | ((client: Client) => PromiseLike<void>);
|
|
6
|
-
}
|
|
7
|
-
export declare function migrate(client: Client, migrations: IMigration[], targetVersion?: number, migrationsTable?: string, advisoryLockKey?: bigint): Promise<void>;
|
package/lib/es2018/migrate.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.migrate = void 0;
|
|
4
|
-
const types_1 = require("@blackglory/types");
|
|
5
|
-
const errors_1 = require("@blackglory/errors");
|
|
6
|
-
async function migrate(client, migrations, targetVersion = getMaximumVersion(migrations), migrationsTable = 'migrations', advisoryLockKey = BigInt('-9223372036854775808')) {
|
|
7
|
-
const maxVersion = getMaximumVersion(migrations);
|
|
8
|
-
await lock(client, advisoryLockKey);
|
|
9
|
-
try {
|
|
10
|
-
while (true) {
|
|
11
|
-
const currentVersion = await getDatabaseVersion(client, migrationsTable);
|
|
12
|
-
if (maxVersion < currentVersion) {
|
|
13
|
-
break;
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
if (currentVersion === targetVersion) {
|
|
17
|
-
break;
|
|
18
|
-
}
|
|
19
|
-
else if (currentVersion < targetVersion) {
|
|
20
|
-
await upgrade();
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
await downgrade();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
finally {
|
|
29
|
-
await unlock(client, advisoryLockKey);
|
|
30
|
-
}
|
|
31
|
-
async function upgrade() {
|
|
32
|
-
await client.query('BEGIN');
|
|
33
|
-
const currentVersion = await getDatabaseVersion(client, migrationsTable);
|
|
34
|
-
const targetVersion = currentVersion + 1;
|
|
35
|
-
try {
|
|
36
|
-
const migration = migrations.find(x => x.version === targetVersion);
|
|
37
|
-
(0, errors_1.assert)(migration, `Cannot find migration for version ${targetVersion}`);
|
|
38
|
-
if ((0, types_1.isFunction)(migration.up)) {
|
|
39
|
-
await migration.up(client);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
await client.query(migration.up);
|
|
43
|
-
}
|
|
44
|
-
await setDatabaseVersion(client, migrationsTable, targetVersion);
|
|
45
|
-
await client.query('COMMIT');
|
|
46
|
-
}
|
|
47
|
-
catch (e) {
|
|
48
|
-
console.error(`Upgrade from version ${currentVersion} to version ${targetVersion} failed.`);
|
|
49
|
-
await client.query('ROLLBACK');
|
|
50
|
-
throw e;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
async function downgrade() {
|
|
54
|
-
await client.query('BEGIN');
|
|
55
|
-
const currentVersion = await getDatabaseVersion(client, migrationsTable);
|
|
56
|
-
const targetVersion = currentVersion - 1;
|
|
57
|
-
try {
|
|
58
|
-
const migration = migrations.find(x => x.version === currentVersion);
|
|
59
|
-
(0, errors_1.assert)(migration, `Cannot find migration for version ${targetVersion}`);
|
|
60
|
-
if ((0, types_1.isFunction)(migration.down)) {
|
|
61
|
-
await migration.down(client);
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
await client.query(migration.down);
|
|
65
|
-
}
|
|
66
|
-
await setDatabaseVersion(client, migrationsTable, targetVersion);
|
|
67
|
-
await client.query('COMMIT');
|
|
68
|
-
}
|
|
69
|
-
catch (e) {
|
|
70
|
-
console.error(`Downgrade from version ${currentVersion} to version ${targetVersion} failed.`);
|
|
71
|
-
await client.query('ROLLBACK');
|
|
72
|
-
throw e;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
exports.migrate = migrate;
|
|
77
|
-
function getMaximumVersion(migrations) {
|
|
78
|
-
return migrations.reduce((max, cur) => Math.max(cur.version, max), 0);
|
|
79
|
-
}
|
|
80
|
-
async function getDatabaseVersion(client, migrationTable) {
|
|
81
|
-
await ensureMigrationsTable(client, migrationTable);
|
|
82
|
-
const result = await client.query(`
|
|
83
|
-
SELECT schema_version
|
|
84
|
-
FROM "${migrationTable}";
|
|
85
|
-
`);
|
|
86
|
-
if (result.rows.length) {
|
|
87
|
-
return result.rows[0].schema_version;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
await client.query(`
|
|
91
|
-
INSERT INTO "${migrationTable}" (schema_version)
|
|
92
|
-
VALUES (0);
|
|
93
|
-
`);
|
|
94
|
-
return 0;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
async function ensureMigrationsTable(client, migrationTable) {
|
|
98
|
-
await client.query(`
|
|
99
|
-
CREATE TABLE IF NOT EXISTS "${migrationTable}" (
|
|
100
|
-
schema_version INTEGER NOT NULL
|
|
101
|
-
);
|
|
102
|
-
`);
|
|
103
|
-
}
|
|
104
|
-
async function setDatabaseVersion(client, migrationTable, version) {
|
|
105
|
-
await client.query(`
|
|
106
|
-
UPDATE ${migrationTable}
|
|
107
|
-
SET schema_version = ${version};
|
|
108
|
-
`);
|
|
109
|
-
}
|
|
110
|
-
async function lock(client, key) {
|
|
111
|
-
await client.query(`
|
|
112
|
-
SELECT pg_advisory_lock(${key});
|
|
113
|
-
`);
|
|
114
|
-
}
|
|
115
|
-
async function unlock(client, key) {
|
|
116
|
-
await client.query(`
|
|
117
|
-
SELECT pg_advisory_unlock(${key});
|
|
118
|
-
`);
|
|
119
|
-
}
|
|
120
|
-
//# sourceMappingURL=migrate.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/migrate.ts"],"names":[],"mappings":"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"}
|