kysely 0.29.4 → 0.30.0-beta.1
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 +1 -1
- package/dist/dialect/mysql/mysql-dialect-config.d.ts +31 -6
- package/dist/dialect/mysql/mysql-dialect.d.ts +2 -2
- package/dist/dialect/mysql/mysql-dialect.js +2 -2
- package/dist/dialect/mysql/mysql-driver.js +28 -2
- package/dist/helpers/sqlite.d.ts +11 -4
- package/dist/helpers/sqlite.js +4 -1
- package/dist/migration/file-migration-provider.d.ts +3 -2
- package/dist/migration/file-migration-provider.js +1 -1
- package/dist/migration/index.d.ts +1 -0
- package/dist/migration/index.js +1 -0
- package/dist/migration/migration.d.ts +70 -0
- package/dist/migration/migration.js +21 -0
- package/dist/migration/migrator.d.ts +91 -20
- package/dist/migration/migrator.js +166 -97
- package/dist/util/object-utils.d.ts +0 -1
- package/dist/util/object-utils.js +0 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](https://stand-with-ukraine.pp.ua)
|
|
2
2
|
|
|
3
3
|
[](https://github.com/kysely-org/kysely/releases/latest)
|
|
4
|
-
[](https://socket.dev/npm/package/kysely/overview/0.30.0-beta.1)
|
|
5
5
|
[](https://github.com/kysely-org/kysely)
|
|
6
6
|
[](https://github.com/kysely-org/kysely/blob/master/LICENSE)
|
|
7
7
|
[](https://github.com/kysely-org/kysely/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)
|
|
@@ -5,23 +5,25 @@ import type { AbortableOperationOptions } from '../../util/abort.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export interface MysqlDialectConfig {
|
|
7
7
|
/**
|
|
8
|
-
* A `
|
|
9
|
-
* outside of the `pool` to avoid
|
|
8
|
+
* A `createConnection` function (from either `mysql2` or `mysql2/promise`),
|
|
9
|
+
* to be used for connecting to the database outside of the `pool` to avoid
|
|
10
|
+
* waiting for an idle connection.
|
|
10
11
|
*
|
|
11
12
|
* This is useful for cancelling queries on the database side.
|
|
12
13
|
*/
|
|
13
14
|
controlConnection?: {
|
|
14
|
-
(connectionUri: string): MysqlConnection
|
|
15
|
-
(config: object): MysqlConnection
|
|
15
|
+
(connectionUri: string): MysqlConnection | Promise<MysqlPromiseConnection>;
|
|
16
|
+
(config: object): MysqlConnection | Promise<MysqlPromiseConnection>;
|
|
16
17
|
};
|
|
17
18
|
/**
|
|
18
|
-
* A mysql2 Pool instance
|
|
19
|
+
* A mysql2 Pool instance (from either `mysql2` or `mysql2/promise`) or a
|
|
20
|
+
* function that returns one.
|
|
19
21
|
*
|
|
20
22
|
* If a function is provided, it's called once when the first query is executed.
|
|
21
23
|
*
|
|
22
24
|
* https://github.com/sidorares/node-mysql2#using-connection-pools
|
|
23
25
|
*/
|
|
24
|
-
pool: MysqlPool | ((options?: AbortableOperationOptions) => Promise<MysqlPool>);
|
|
26
|
+
pool: MysqlPool | MysqlPromisePool | ((options?: AbortableOperationOptions) => Promise<MysqlPool | MysqlPromisePool>);
|
|
25
27
|
/**
|
|
26
28
|
* Called once for each created connection.
|
|
27
29
|
*/
|
|
@@ -43,6 +45,17 @@ export interface MysqlPool {
|
|
|
43
45
|
getConnection(callback: (error: unknown, connection: MysqlPoolConnection) => void): void;
|
|
44
46
|
end(callback: (error: unknown) => void): void;
|
|
45
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* This interface is the subset of `mysql2/promise`'s `Pool` class that
|
|
50
|
+
* kysely needs.
|
|
51
|
+
*
|
|
52
|
+
* It's a thin promise wrapper around the callback pool, which it exposes
|
|
53
|
+
* as its `pool` property. The dialect unwraps it and uses the callback
|
|
54
|
+
* pool directly.
|
|
55
|
+
*/
|
|
56
|
+
export interface MysqlPromisePool {
|
|
57
|
+
pool: MysqlPool;
|
|
58
|
+
}
|
|
46
59
|
export interface MysqlConnection {
|
|
47
60
|
config: object;
|
|
48
61
|
connect(callback?: (error: unknown) => void): void;
|
|
@@ -52,6 +65,18 @@ export interface MysqlConnection {
|
|
|
52
65
|
};
|
|
53
66
|
threadId: number;
|
|
54
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* This interface is the subset of `mysql2/promise`'s `Connection` class that
|
|
70
|
+
* kysely needs.
|
|
71
|
+
*
|
|
72
|
+
* It's a thin promise wrapper around the callback connection. It's already
|
|
73
|
+
* connected when the `createConnection` promise resolves, so the dialect
|
|
74
|
+
* uses its promise-based API directly.
|
|
75
|
+
*/
|
|
76
|
+
export interface MysqlPromiseConnection {
|
|
77
|
+
destroy(): void;
|
|
78
|
+
query(sql: string, values?: unknown[]): Promise<unknown>;
|
|
79
|
+
}
|
|
55
80
|
export type MysqlConectionConstructor = new (opts?: object) => MysqlConnection;
|
|
56
81
|
export interface MysqlPoolConnection extends MysqlConnection {
|
|
57
82
|
release(): void;
|
|
@@ -11,7 +11,7 @@ import type { MysqlDialectConfig } from './mysql-dialect-config.js';
|
|
|
11
11
|
* The constructor takes an instance of {@link MysqlDialectConfig}.
|
|
12
12
|
*
|
|
13
13
|
* ```ts
|
|
14
|
-
* import { createPool } from 'mysql2'
|
|
14
|
+
* import { createPool } from 'mysql2/promise' // or just 'mysql2' — both work
|
|
15
15
|
*
|
|
16
16
|
* new MysqlDialect({
|
|
17
17
|
* pool: createPool({
|
|
@@ -25,7 +25,7 @@ import type { MysqlDialectConfig } from './mysql-dialect-config.js';
|
|
|
25
25
|
* can be a function:
|
|
26
26
|
*
|
|
27
27
|
* ```ts
|
|
28
|
-
* import { createPool } from 'mysql2'
|
|
28
|
+
* import { createPool } from 'mysql2/promise' // or just 'mysql2' — both work
|
|
29
29
|
*
|
|
30
30
|
* new MysqlDialect({
|
|
31
31
|
* pool: async () => createPool({
|
|
@@ -9,7 +9,7 @@ import { MysqlAdapter } from './mysql-adapter.js';
|
|
|
9
9
|
* The constructor takes an instance of {@link MysqlDialectConfig}.
|
|
10
10
|
*
|
|
11
11
|
* ```ts
|
|
12
|
-
* import { createPool } from 'mysql2'
|
|
12
|
+
* import { createPool } from 'mysql2/promise' // or just 'mysql2' — both work
|
|
13
13
|
*
|
|
14
14
|
* new MysqlDialect({
|
|
15
15
|
* pool: createPool({
|
|
@@ -23,7 +23,7 @@ import { MysqlAdapter } from './mysql-adapter.js';
|
|
|
23
23
|
* can be a function:
|
|
24
24
|
*
|
|
25
25
|
* ```ts
|
|
26
|
-
* import { createPool } from 'mysql2'
|
|
26
|
+
* import { createPool } from 'mysql2/promise' // or just 'mysql2' — both work
|
|
27
27
|
*
|
|
28
28
|
* new MysqlDialect({
|
|
29
29
|
* pool: async () => createPool({
|
|
@@ -13,9 +13,12 @@ export class MysqlDriver {
|
|
|
13
13
|
this.#config = freeze({ ...config });
|
|
14
14
|
}
|
|
15
15
|
async init(options) {
|
|
16
|
-
|
|
16
|
+
const pool = isFunction(this.#config.pool)
|
|
17
17
|
? await this.#config.pool(options)
|
|
18
18
|
: this.#config.pool;
|
|
19
|
+
// A `mysql2/promise` pool's `getConnection` ignores the callback we pass
|
|
20
|
+
// it, hanging any query forever. Unwrap the callback pool it decorates.
|
|
21
|
+
this.#pool = isPromisePool(pool) ? pool.pool : pool;
|
|
19
22
|
}
|
|
20
23
|
async acquireConnection(options) {
|
|
21
24
|
const rawConnection = await this.#acquireConnection();
|
|
@@ -93,6 +96,12 @@ export class MysqlDriver {
|
|
|
93
96
|
});
|
|
94
97
|
}
|
|
95
98
|
}
|
|
99
|
+
function isPromisePool(pool) {
|
|
100
|
+
return ('pool' in pool && isObject(pool.pool) && isFunction(pool.pool.getConnection));
|
|
101
|
+
}
|
|
102
|
+
function isPromiseConnection(connection) {
|
|
103
|
+
return isFunction(connection.then);
|
|
104
|
+
}
|
|
96
105
|
function isOkPacket(obj) {
|
|
97
106
|
return isObject(obj) && 'insertId' in obj && 'affectedRows' in obj;
|
|
98
107
|
}
|
|
@@ -224,7 +233,24 @@ class MysqlConnection {
|
|
|
224
233
|
const {
|
|
225
234
|
// omitting these as they cause a warning and perhaps a future error when passed.
|
|
226
235
|
clientFlags: _, maxPacketSize: __, ...cfg } = config;
|
|
227
|
-
const
|
|
236
|
+
const controlConnectionOrPromise = this.#controlConnection(cfg);
|
|
237
|
+
// `mysql2/promise`'s `createConnection` returns a promise of a connection
|
|
238
|
+
// that's already connected, with a promise-based API. Use it directly.
|
|
239
|
+
if (isPromiseConnection(controlConnectionOrPromise)) {
|
|
240
|
+
const controlConnection = await controlConnectionOrPromise;
|
|
241
|
+
try {
|
|
242
|
+
// by the time we get the connection, another query might have been executed.
|
|
243
|
+
// we need to ensure we're not canceling the wrong query.
|
|
244
|
+
if (queryIdToCancel.queryId === this.#queryId?.queryId) {
|
|
245
|
+
await controlConnection.query(query, []);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
controlConnection.destroy();
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const controlConnection = controlConnectionOrPromise;
|
|
228
254
|
try {
|
|
229
255
|
await new Promise((resolve, reject) => {
|
|
230
256
|
controlConnection.connect((connectError) => {
|
package/dist/helpers/sqlite.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { Expression } from '../expression/expression.js';
|
|
2
2
|
import type { SelectQueryBuilderExpression } from '../query-builder/select-query-builder-expression.js';
|
|
3
3
|
import type { RawBuilder } from '../raw-builder/raw-builder.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { KyselyTypeError } from '../util/type-error.js';
|
|
5
|
+
import type { IsNever, ShallowDehydrateObject, ShallowDehydrateValue, Simplify } from '../util/type-utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Used to check for `BLOB` column existence in result records.
|
|
8
|
+
*/
|
|
9
|
+
export type ExtractBlobs<O> = {
|
|
10
|
+
[K in keyof O]: O[K] extends Expression<infer V> ? Extract<V, Uint8Array> : Extract<O[K], Uint8Array>;
|
|
11
|
+
}[keyof O];
|
|
5
12
|
/**
|
|
6
13
|
* A SQLite helper for aggregating a subquery into a JSON array.
|
|
7
14
|
*
|
|
@@ -63,7 +70,7 @@ import type { ShallowDehydrateObject, ShallowDehydrateValue, Simplify } from '..
|
|
|
63
70
|
* from "person"
|
|
64
71
|
* ```
|
|
65
72
|
*/
|
|
66
|
-
export declare function jsonArrayFrom<O>(expr: SelectQueryBuilderExpression<O>): RawBuilder<Simplify<ShallowDehydrateObject<O>>[]>;
|
|
73
|
+
export declare function jsonArrayFrom<O>(expr: IsNever<ExtractBlobs<O>> extends true ? SelectQueryBuilderExpression<O> : KyselyTypeError<'SQLite does not support passing `BLOB` values to `json_object`. Cast to `TEXT`.'>): RawBuilder<Simplify<ShallowDehydrateObject<O>>[]>;
|
|
67
74
|
/**
|
|
68
75
|
* A SQLite helper for turning a subquery into a JSON object.
|
|
69
76
|
*
|
|
@@ -127,7 +134,7 @@ export declare function jsonArrayFrom<O>(expr: SelectQueryBuilderExpression<O>):
|
|
|
127
134
|
* from "person";
|
|
128
135
|
* ```
|
|
129
136
|
*/
|
|
130
|
-
export declare function jsonObjectFrom<O>(expr: SelectQueryBuilderExpression<O>): RawBuilder<Simplify<ShallowDehydrateObject<O>> | null>;
|
|
137
|
+
export declare function jsonObjectFrom<O>(expr: IsNever<ExtractBlobs<O>> extends true ? SelectQueryBuilderExpression<O> : KyselyTypeError<'SQLite does not support passing `BLOB` values to `json_object`. Cast to `TEXT`.'>): RawBuilder<Simplify<ShallowDehydrateObject<O>> | null>;
|
|
131
138
|
/**
|
|
132
139
|
* The SQLite `json_object` function.
|
|
133
140
|
*
|
|
@@ -184,6 +191,6 @@ export declare function jsonObjectFrom<O>(expr: SelectQueryBuilderExpression<O>)
|
|
|
184
191
|
* from "person"
|
|
185
192
|
* ```
|
|
186
193
|
*/
|
|
187
|
-
export declare function jsonBuildObject<O extends Record<string, Expression<unknown>>>(obj: O): RawBuilder<Simplify<{
|
|
194
|
+
export declare function jsonBuildObject<O extends Record<string, Expression<unknown>>>(obj: IsNever<ExtractBlobs<O>> extends true ? O : KyselyTypeError<'SQLite does not support passing `BLOB` values to `json_object`. Cast to `TEXT`.'>): RawBuilder<Simplify<{
|
|
188
195
|
[K in keyof O]: O[K] extends Expression<infer V> ? ShallowDehydrateValue<V> : never;
|
|
189
196
|
}>>;
|
package/dist/helpers/sqlite.js
CHANGED
|
@@ -188,7 +188,10 @@ export function jsonObjectFrom(expr) {
|
|
|
188
188
|
* ```
|
|
189
189
|
*/
|
|
190
190
|
export function jsonBuildObject(obj) {
|
|
191
|
-
return sql `json_object(${sql.join(Object.keys(obj).flatMap((k) => [
|
|
191
|
+
return sql `json_object(${sql.join(Object.keys(obj).flatMap((k) => [
|
|
192
|
+
sql.lit(k),
|
|
193
|
+
obj[k],
|
|
194
|
+
]))})`;
|
|
192
195
|
}
|
|
193
196
|
function getSqliteJsonObjectArgs(node, table) {
|
|
194
197
|
try {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type { Migration
|
|
1
|
+
import type { Migration } from './migration.js';
|
|
2
|
+
import type { MigrationProvider } from './migrator.js';
|
|
2
3
|
/**
|
|
3
4
|
* Reads all migrations from a folder.
|
|
4
5
|
*
|
|
5
6
|
* ### Examples
|
|
6
7
|
*
|
|
7
8
|
* ```ts
|
|
8
|
-
* import
|
|
9
|
+
* import fs from 'node:fs/promises'
|
|
9
10
|
* import path from 'node:path'
|
|
10
11
|
*
|
|
11
12
|
* new FileMigrationProvider({
|
package/dist/migration/index.js
CHANGED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Kysely } from '../kysely.js';
|
|
2
|
+
/**
|
|
3
|
+
* The shape of a migration's module.
|
|
4
|
+
*/
|
|
5
|
+
export interface Migration {
|
|
6
|
+
/**
|
|
7
|
+
* Configuration object for this migration module.
|
|
8
|
+
*
|
|
9
|
+
* See {@link MigrationConfig}.
|
|
10
|
+
*/
|
|
11
|
+
config?: MigrationConfig;
|
|
12
|
+
/**
|
|
13
|
+
* The `down` method that reverts the changes made by it's sibling {@link Migration.up}
|
|
14
|
+
* method.
|
|
15
|
+
*/
|
|
16
|
+
down?(db: Kysely<any>): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* The `up` method that changes the database schema.
|
|
19
|
+
*
|
|
20
|
+
* See {@link Migration.down}.
|
|
21
|
+
*/
|
|
22
|
+
up(db: Kysely<any>): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Configuration for a single migration.
|
|
26
|
+
*/
|
|
27
|
+
export interface MigrationConfig {
|
|
28
|
+
/**
|
|
29
|
+
* Controls whether this migration runs inside its own transaction, when
|
|
30
|
+
* {@link MigrateOptions.transactionMode} is `'per-migration'` — the only
|
|
31
|
+
* mode that honors this property. Defaults to `true` in that mode: the
|
|
32
|
+
* migration runs in its own transaction unless this is set to `false`.
|
|
33
|
+
*
|
|
34
|
+
* In any other transaction mode, leaving this property out is the only
|
|
35
|
+
* valid state — providing a value, either one, results in an error: in
|
|
36
|
+
* `'per-run'` mode a single shared transaction cannot partially exclude a
|
|
37
|
+
* migration, and in `'none'` mode there are no transactions to configure.
|
|
38
|
+
* This is the one place an explicit `true` differs from the default —
|
|
39
|
+
* `true` asserts a requirement that non-`'per-migration'` modes refuse,
|
|
40
|
+
* while leaving the property out never errors.
|
|
41
|
+
*
|
|
42
|
+
* Setting `false` makes the migration run without a transaction. This is
|
|
43
|
+
* required for statements that cannot run inside a transaction, such as
|
|
44
|
+
* PostgreSQL's `CREATE INDEX CONCURRENTLY`. Note that without a
|
|
45
|
+
* transaction, a failure can leave the migration partially applied, and the
|
|
46
|
+
* migration table record is written separately after the migration
|
|
47
|
+
* completes. Prefer a single statement per non-transactional migration, and
|
|
48
|
+
* write it so it can be safely retried — for example, drop a
|
|
49
|
+
* possibly-leftover invalid index before creating it.
|
|
50
|
+
*/
|
|
51
|
+
readonly transaction?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Helps define a single migration by taking over types duties and enforcing the contract.
|
|
55
|
+
*
|
|
56
|
+
* ### Examples
|
|
57
|
+
*
|
|
58
|
+
* ```ts
|
|
59
|
+
* import { defineMigration } from 'kysely/migration'
|
|
60
|
+
*
|
|
61
|
+
* export default defineMigration({
|
|
62
|
+
* async up(db) {
|
|
63
|
+
* // ...
|
|
64
|
+
* },
|
|
65
|
+
*
|
|
66
|
+
* config: { transaction: false },
|
|
67
|
+
* })
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function defineMigration(migration: Migration): Migration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/// <reference types="./migration.d.ts" />
|
|
2
|
+
/**
|
|
3
|
+
* Helps define a single migration by taking over types duties and enforcing the contract.
|
|
4
|
+
*
|
|
5
|
+
* ### Examples
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { defineMigration } from 'kysely/migration'
|
|
9
|
+
*
|
|
10
|
+
* export default defineMigration({
|
|
11
|
+
* async up(db) {
|
|
12
|
+
* // ...
|
|
13
|
+
* },
|
|
14
|
+
*
|
|
15
|
+
* config: { transaction: false },
|
|
16
|
+
* })
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function defineMigration(migration) {
|
|
20
|
+
return migration;
|
|
21
|
+
}
|
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
import type { Kysely } from '../kysely.js';
|
|
2
|
+
import type { Migration } from './migration.js';
|
|
2
3
|
export declare const DEFAULT_MIGRATION_TABLE = "kysely_migration";
|
|
3
4
|
export declare const DEFAULT_MIGRATION_LOCK_TABLE = "kysely_migration_lock";
|
|
4
5
|
export declare const DEFAULT_ALLOW_UNORDERED_MIGRATIONS = false;
|
|
5
6
|
export declare const MIGRATION_LOCK_ID = "migration_lock";
|
|
6
7
|
export declare const NO_MIGRATIONS: NoMigrations;
|
|
7
|
-
export interface Migration {
|
|
8
|
-
up(db: Kysely<any>): Promise<void>;
|
|
9
|
-
/**
|
|
10
|
-
* An optional down method.
|
|
11
|
-
*
|
|
12
|
-
* If you don't provide a down method, the migration is skipped when
|
|
13
|
-
* migrating down.
|
|
14
|
-
*/
|
|
15
|
-
down?(db: Kysely<any>): Promise<void>;
|
|
16
|
-
}
|
|
17
8
|
/**
|
|
18
9
|
* A class for running migrations.
|
|
19
10
|
*
|
|
@@ -25,7 +16,7 @@ export interface Migration {
|
|
|
25
16
|
* other way.
|
|
26
17
|
*
|
|
27
18
|
* ```ts
|
|
28
|
-
* import
|
|
19
|
+
* import fs from 'node:fs/promises'
|
|
29
20
|
* import path from 'node:path'
|
|
30
21
|
* import * as Sqlite from 'better-sqlite3'
|
|
31
22
|
* import { Kysely, SqliteDialect } from 'kysely'
|
|
@@ -73,7 +64,7 @@ export declare class Migrator {
|
|
|
73
64
|
* ### Examples
|
|
74
65
|
*
|
|
75
66
|
* ```ts
|
|
76
|
-
* import
|
|
67
|
+
* import fs from 'node:fs/promises'
|
|
77
68
|
* import path from 'node:path'
|
|
78
69
|
* import * as Sqlite from 'better-sqlite3'
|
|
79
70
|
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
@@ -115,7 +106,7 @@ export declare class Migrator {
|
|
|
115
106
|
* ### Examples
|
|
116
107
|
*
|
|
117
108
|
* ```ts
|
|
118
|
-
* import
|
|
109
|
+
* import fs from 'node:fs/promises'
|
|
119
110
|
* import path from 'node:path'
|
|
120
111
|
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
121
112
|
*
|
|
@@ -138,7 +129,7 @@ export declare class Migrator {
|
|
|
138
129
|
* you can use a special constant `NO_MIGRATIONS`:
|
|
139
130
|
*
|
|
140
131
|
* ```ts
|
|
141
|
-
* import
|
|
132
|
+
* import fs from 'node:fs/promises'
|
|
142
133
|
* import path from 'node:path'
|
|
143
134
|
* import { FileMigrationProvider, Migrator, NO_MIGRATIONS } from 'kysely/migration'
|
|
144
135
|
*
|
|
@@ -154,8 +145,33 @@ export declare class Migrator {
|
|
|
154
145
|
*
|
|
155
146
|
* await migrator.migrateTo(NO_MIGRATIONS)
|
|
156
147
|
* ```
|
|
148
|
+
*
|
|
149
|
+
* By default, this method migrates up or down depending on which side of the
|
|
150
|
+
* current migration state the target migration is on. Pass
|
|
151
|
+
* {@link MigrateToOptions.direction} to enforce a direction — if reaching the
|
|
152
|
+
* target would require migrating the other way, nothing is executed and
|
|
153
|
+
* {@link MigrationResultSet.error} holds an error:
|
|
154
|
+
*
|
|
155
|
+
* ```ts
|
|
156
|
+
* import fs from 'node:fs/promises'
|
|
157
|
+
* import path from 'node:path'
|
|
158
|
+
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
159
|
+
*
|
|
160
|
+
* const migrator = new Migrator({
|
|
161
|
+
* db,
|
|
162
|
+
* provider: new FileMigrationProvider({
|
|
163
|
+
* fs,
|
|
164
|
+
* // Path to the folder that contains all your migrations.
|
|
165
|
+
* migrationFolder: 'some/path/to/migrations',
|
|
166
|
+
* path,
|
|
167
|
+
* })
|
|
168
|
+
* })
|
|
169
|
+
*
|
|
170
|
+
* // Errors if `some_migration` is already executed.
|
|
171
|
+
* await migrator.migrateTo('some_migration', { direction: 'Up' })
|
|
172
|
+
* ```
|
|
157
173
|
*/
|
|
158
|
-
migrateTo(targetMigrationName: string | NoMigrations, options?:
|
|
174
|
+
migrateTo(targetMigrationName: string | NoMigrations, options?: MigrateToOptions): Promise<MigrationResultSet>;
|
|
159
175
|
/**
|
|
160
176
|
* Migrate one step up.
|
|
161
177
|
*
|
|
@@ -167,7 +183,7 @@ export declare class Migrator {
|
|
|
167
183
|
* ### Examples
|
|
168
184
|
*
|
|
169
185
|
* ```ts
|
|
170
|
-
* import
|
|
186
|
+
* import fs from 'node:fs/promises'
|
|
171
187
|
* import path from 'node:path'
|
|
172
188
|
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
173
189
|
*
|
|
@@ -196,7 +212,7 @@ export declare class Migrator {
|
|
|
196
212
|
* ### Examples
|
|
197
213
|
*
|
|
198
214
|
* ```ts
|
|
199
|
-
* import
|
|
215
|
+
* import fs from 'node:fs/promises'
|
|
200
216
|
* import path from 'node:path'
|
|
201
217
|
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
202
218
|
*
|
|
@@ -221,9 +237,65 @@ export interface MigrateOptions {
|
|
|
221
237
|
*
|
|
222
238
|
* Default is `false`.
|
|
223
239
|
*
|
|
224
|
-
*
|
|
240
|
+
* @deprecated Use {@link transactionMode} instead — `disableTransactions: true`
|
|
241
|
+
* is equivalent to `transactionMode: 'none'`. Setting both to contradicting
|
|
242
|
+
* values results in an error.
|
|
225
243
|
*/
|
|
226
244
|
readonly disableTransactions?: boolean;
|
|
245
|
+
/**
|
|
246
|
+
* Controls how migrations are wrapped in transactions, when the dialect
|
|
247
|
+
* supports transactional DDL ({@link DialectAdapter.supportsTransactionalDdl}).
|
|
248
|
+
*
|
|
249
|
+
* - `'per-run'` — the entire migration run is wrapped in a single transaction.
|
|
250
|
+
* Either every migration in the run is applied, or none are. Any migration
|
|
251
|
+
* with a per-migration `transaction` configuration ({@link MigrationConfig.transaction})
|
|
252
|
+
* results in an error, since a single shared transaction cannot partially
|
|
253
|
+
* exclude a migration. Requires transactional DDL — results in an error on
|
|
254
|
+
* dialects without it (e.g. MySQL).
|
|
255
|
+
*
|
|
256
|
+
* - `'per-migration'` — each migration runs in its own transaction, together
|
|
257
|
+
* with the insertion/deletion of its migration table record. A failure rolls
|
|
258
|
+
* back only the failing migration; previously completed migrations stay
|
|
259
|
+
* applied. Individual migrations can opt out of their transaction with
|
|
260
|
+
* `config: { transaction: false }` — required for statements that cannot
|
|
261
|
+
* run inside a transaction, such as PostgreSQL's `CREATE INDEX CONCURRENTLY`
|
|
262
|
+
* or `ALTER TYPE ... ADD VALUE` (pre-12). Requires transactional DDL —
|
|
263
|
+
* results in an error on dialects without it.
|
|
264
|
+
*
|
|
265
|
+
* - `'none'` — migrations run without any transactions. Any migration with a
|
|
266
|
+
* per-migration `transaction` configuration results in an error.
|
|
267
|
+
*
|
|
268
|
+
* When not provided, the current default is `'per-run'` on dialects that
|
|
269
|
+
* support transactional DDL and `'none'` on dialects that don't. This
|
|
270
|
+
* default might change to `'per-migration'` in a future version of Kysely —
|
|
271
|
+
* running multiple migrations without an explicit `transactionMode` logs a
|
|
272
|
+
* message about this. Provide an explicit `transactionMode` to make the
|
|
273
|
+
* migrator's behavior independent of the default.
|
|
274
|
+
*
|
|
275
|
+
* Note that transactions spanning multiple migrations (`'per-run'`) can make
|
|
276
|
+
* otherwise-valid migration sequences fail. For example, a new PostgreSQL enum
|
|
277
|
+
* value cannot be used in the same transaction that added it, so a migration
|
|
278
|
+
* that runs `ALTER TYPE ... ADD VALUE` followed by a migration that uses the
|
|
279
|
+
* new value works when the two run in separate transactions, but fails when
|
|
280
|
+
* both are pending in a single `'per-run'` mode run.
|
|
281
|
+
*/
|
|
282
|
+
readonly transactionMode?: MigratorTransactionMode;
|
|
283
|
+
}
|
|
284
|
+
export type MigratorTransactionMode = 'per-run' | 'per-migration' | 'none';
|
|
285
|
+
export interface MigrateToOptions extends MigrateOptions {
|
|
286
|
+
/**
|
|
287
|
+
* Enforces a migration direction.
|
|
288
|
+
*
|
|
289
|
+
* When provided and reaching the target migration would require migrating
|
|
290
|
+
* the other way, nothing is executed and {@link MigrationResultSet.error}
|
|
291
|
+
* holds an error. For example, `direction: 'Up'` errors when the target
|
|
292
|
+
* migration is already executed.
|
|
293
|
+
*
|
|
294
|
+
* By default, {@link Migrator.migrateTo | migrateTo} migrates up or down
|
|
295
|
+
* depending on which side of the current migration state the target
|
|
296
|
+
* migration is on.
|
|
297
|
+
*/
|
|
298
|
+
readonly direction?: MigrationDirection;
|
|
227
299
|
}
|
|
228
300
|
export interface MigratorProps extends MigrateOptions {
|
|
229
301
|
readonly db: Kysely<any>;
|
|
@@ -327,7 +399,7 @@ export interface MigrationResultSet {
|
|
|
327
399
|
*/
|
|
328
400
|
readonly results?: MigrationResult[];
|
|
329
401
|
}
|
|
330
|
-
type MigrationDirection = 'Up' | 'Down';
|
|
402
|
+
export type MigrationDirection = 'Up' | 'Down';
|
|
331
403
|
export interface MigrationResult {
|
|
332
404
|
readonly migrationName: string;
|
|
333
405
|
/**
|
|
@@ -390,4 +462,3 @@ export interface MigrationInfo {
|
|
|
390
462
|
*/
|
|
391
463
|
executedAt?: Date;
|
|
392
464
|
}
|
|
393
|
-
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/// <reference types="./migrator.d.ts" />
|
|
2
2
|
import { NoopPlugin } from '../plugin/noop-plugin.js';
|
|
3
3
|
import { WithSchemaPlugin } from '../plugin/with-schema/with-schema-plugin.js';
|
|
4
|
-
import {
|
|
4
|
+
import { logOnce } from '../util/log-once.js';
|
|
5
|
+
import { freeze, isObject } from '../util/object-utils.js';
|
|
5
6
|
export const DEFAULT_MIGRATION_TABLE = 'kysely_migration';
|
|
6
7
|
export const DEFAULT_MIGRATION_LOCK_TABLE = 'kysely_migration_lock';
|
|
7
8
|
export const DEFAULT_ALLOW_UNORDERED_MIGRATIONS = false;
|
|
@@ -18,7 +19,7 @@ export const NO_MIGRATIONS = freeze({ __noMigrations__: true });
|
|
|
18
19
|
* other way.
|
|
19
20
|
*
|
|
20
21
|
* ```ts
|
|
21
|
-
* import
|
|
22
|
+
* import fs from 'node:fs/promises'
|
|
22
23
|
* import path from 'node:path'
|
|
23
24
|
* import * as Sqlite from 'better-sqlite3'
|
|
24
25
|
* import { Kysely, SqliteDialect } from 'kysely'
|
|
@@ -87,7 +88,7 @@ export class Migrator {
|
|
|
87
88
|
* ### Examples
|
|
88
89
|
*
|
|
89
90
|
* ```ts
|
|
90
|
-
* import
|
|
91
|
+
* import fs from 'node:fs/promises'
|
|
91
92
|
* import path from 'node:path'
|
|
92
93
|
* import * as Sqlite from 'better-sqlite3'
|
|
93
94
|
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
@@ -131,7 +132,7 @@ export class Migrator {
|
|
|
131
132
|
* ### Examples
|
|
132
133
|
*
|
|
133
134
|
* ```ts
|
|
134
|
-
* import
|
|
135
|
+
* import fs from 'node:fs/promises'
|
|
135
136
|
* import path from 'node:path'
|
|
136
137
|
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
137
138
|
*
|
|
@@ -154,7 +155,7 @@ export class Migrator {
|
|
|
154
155
|
* you can use a special constant `NO_MIGRATIONS`:
|
|
155
156
|
*
|
|
156
157
|
* ```ts
|
|
157
|
-
* import
|
|
158
|
+
* import fs from 'node:fs/promises'
|
|
158
159
|
* import path from 'node:path'
|
|
159
160
|
* import { FileMigrationProvider, Migrator, NO_MIGRATIONS } from 'kysely/migration'
|
|
160
161
|
*
|
|
@@ -170,11 +171,39 @@ export class Migrator {
|
|
|
170
171
|
*
|
|
171
172
|
* await migrator.migrateTo(NO_MIGRATIONS)
|
|
172
173
|
* ```
|
|
174
|
+
*
|
|
175
|
+
* By default, this method migrates up or down depending on which side of the
|
|
176
|
+
* current migration state the target migration is on. Pass
|
|
177
|
+
* {@link MigrateToOptions.direction} to enforce a direction — if reaching the
|
|
178
|
+
* target would require migrating the other way, nothing is executed and
|
|
179
|
+
* {@link MigrationResultSet.error} holds an error:
|
|
180
|
+
*
|
|
181
|
+
* ```ts
|
|
182
|
+
* import fs from 'node:fs/promises'
|
|
183
|
+
* import path from 'node:path'
|
|
184
|
+
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
185
|
+
*
|
|
186
|
+
* const migrator = new Migrator({
|
|
187
|
+
* db,
|
|
188
|
+
* provider: new FileMigrationProvider({
|
|
189
|
+
* fs,
|
|
190
|
+
* // Path to the folder that contains all your migrations.
|
|
191
|
+
* migrationFolder: 'some/path/to/migrations',
|
|
192
|
+
* path,
|
|
193
|
+
* })
|
|
194
|
+
* })
|
|
195
|
+
*
|
|
196
|
+
* // Errors if `some_migration` is already executed.
|
|
197
|
+
* await migrator.migrateTo('some_migration', { direction: 'Up' })
|
|
198
|
+
* ```
|
|
173
199
|
*/
|
|
174
200
|
async migrateTo(targetMigrationName, options) {
|
|
175
201
|
return this.#migrate(({ migrations, executedMigrations, pendingMigrations, }) => {
|
|
176
202
|
if (isObject(targetMigrationName) &&
|
|
177
203
|
targetMigrationName.__noMigrations__ === true) {
|
|
204
|
+
if (options?.direction === 'Up') {
|
|
205
|
+
throw new Error(`can't migrate up to NO_MIGRATIONS`);
|
|
206
|
+
}
|
|
178
207
|
return { direction: 'Down', step: Infinity };
|
|
179
208
|
}
|
|
180
209
|
if (!migrations.find((m) => m.name === targetMigrationName)) {
|
|
@@ -183,12 +212,18 @@ export class Migrator {
|
|
|
183
212
|
const executedIndex = executedMigrations.indexOf(targetMigrationName);
|
|
184
213
|
const pendingIndex = pendingMigrations.findIndex((m) => m.name === targetMigrationName);
|
|
185
214
|
if (executedIndex !== -1) {
|
|
215
|
+
if (options?.direction === 'Up') {
|
|
216
|
+
throw new Error(`migration "${targetMigrationName}" is already executed; can't migrate up to it`);
|
|
217
|
+
}
|
|
186
218
|
return {
|
|
187
219
|
direction: 'Down',
|
|
188
220
|
step: executedMigrations.length - executedIndex - 1,
|
|
189
221
|
};
|
|
190
222
|
}
|
|
191
223
|
if (pendingIndex !== -1) {
|
|
224
|
+
if (options?.direction === 'Down') {
|
|
225
|
+
throw new Error(`migration "${targetMigrationName}" isn't executed; can't migrate down to it`);
|
|
226
|
+
}
|
|
192
227
|
return { direction: 'Up', step: pendingIndex + 1 };
|
|
193
228
|
}
|
|
194
229
|
throw new Error(`migration "${targetMigrationName}" isn't executed or pending`);
|
|
@@ -205,7 +240,7 @@ export class Migrator {
|
|
|
205
240
|
* ### Examples
|
|
206
241
|
*
|
|
207
242
|
* ```ts
|
|
208
|
-
* import
|
|
243
|
+
* import fs from 'node:fs/promises'
|
|
209
244
|
* import path from 'node:path'
|
|
210
245
|
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
211
246
|
*
|
|
@@ -236,7 +271,7 @@ export class Migrator {
|
|
|
236
271
|
* ### Examples
|
|
237
272
|
*
|
|
238
273
|
* ```ts
|
|
239
|
-
* import
|
|
274
|
+
* import fs from 'node:fs/promises'
|
|
240
275
|
* import path from 'node:path'
|
|
241
276
|
* import { FileMigrationProvider, Migrator } from 'kysely/migration'
|
|
242
277
|
*
|
|
@@ -403,6 +438,7 @@ export class Migrator {
|
|
|
403
438
|
lockRowId: MIGRATION_LOCK_ID,
|
|
404
439
|
lockTableSchema: this.#props.migrationTableSchema,
|
|
405
440
|
});
|
|
441
|
+
const transactionMode = this.#resolveTransactionMode(options, adapter);
|
|
406
442
|
const run = async (db) => {
|
|
407
443
|
const state = await this.#getState(db);
|
|
408
444
|
if (state.migrations.length === 0) {
|
|
@@ -412,13 +448,34 @@ export class Migrator {
|
|
|
412
448
|
if (step <= 0) {
|
|
413
449
|
return { results: [] };
|
|
414
450
|
}
|
|
415
|
-
|
|
416
|
-
|
|
451
|
+
const migrationsToRun = this.#getMigrationsToRun(state, direction, step);
|
|
452
|
+
const [orchestrator, runner] = direction === 'Down'
|
|
453
|
+
? [
|
|
454
|
+
this.#rollbackMigrations.bind(this),
|
|
455
|
+
this.#rollbackMigration.bind(this),
|
|
456
|
+
]
|
|
457
|
+
: [this.#applyMigrations.bind(this), this.#applyMigration.bind(this)];
|
|
458
|
+
if (transactionMode.value === 'per-migration') {
|
|
459
|
+
return await orchestrator(migrationsToRun, async (migration) => {
|
|
460
|
+
if (migration.config?.transaction === false) {
|
|
461
|
+
return await runner(db, migration);
|
|
462
|
+
}
|
|
463
|
+
await db.transaction().execute((trx) => runner(trx, migration));
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
const migrationWithTransactionConfig = migrationsToRun.find((migration) => migration.config?.transaction != null);
|
|
467
|
+
if (migrationWithTransactionConfig) {
|
|
468
|
+
const modeDescription = transactionMode.explicit
|
|
469
|
+
? `\`transactionMode\` is '${transactionMode.value}'`
|
|
470
|
+
: `\`transactionMode\` was not provided and defaults to '${transactionMode.value}'`;
|
|
471
|
+
throw new Error(`Migration "${migrationWithTransactionConfig.name}" has a \`transaction\` configuration but ${modeDescription}. Per-migration transaction configuration is only supported when \`transactionMode\` is 'per-migration'. Set \`transactionMode: 'per-migration'\` on the migrator or the migrate call to enable it.`);
|
|
417
472
|
}
|
|
418
|
-
|
|
419
|
-
|
|
473
|
+
if (!transactionMode.explicit &&
|
|
474
|
+
adapter.supportsTransactionalDdl &&
|
|
475
|
+
migrationsToRun.length > 1) {
|
|
476
|
+
logOnce("kysely:migrator: multiple migrations were run in a single transaction ('per-run'). This default might change in a future version of Kysely. Pass an explicit `transactionMode` to the migrator to pin the behavior and silence this message: 'per-run' to keep the current behavior, or 'per-migration' to run each migration in its own transaction.");
|
|
420
477
|
}
|
|
421
|
-
return
|
|
478
|
+
return await orchestrator(migrationsToRun, (migration) => runner(db, migration));
|
|
422
479
|
};
|
|
423
480
|
const runWithLock = async (db, cb) => {
|
|
424
481
|
try {
|
|
@@ -429,51 +486,92 @@ export class Migrator {
|
|
|
429
486
|
await adapter.releaseMigrationLock(db, lockOptions);
|
|
430
487
|
}
|
|
431
488
|
};
|
|
432
|
-
const disableTransactions = options?.disableTransactions ?? this.#props.disableTransactions;
|
|
433
489
|
if (this.#props.db.isTransaction) {
|
|
434
490
|
if (!adapter.supportsTransactionalDdl) {
|
|
435
491
|
throw new Error('Transactional DDL is not supported in this dialect. Passing a transaction to this migrator would result in failure or unexpected behavior.');
|
|
436
492
|
}
|
|
437
|
-
if (
|
|
438
|
-
throw new Error(
|
|
493
|
+
if (transactionMode.value !== 'per-run') {
|
|
494
|
+
throw new Error(`The transaction mode is '${transactionMode.value}' but the migrator was given a transaction. Passing a transaction to this migrator is only supported when \`transactionMode\` is 'per-run'.`);
|
|
439
495
|
}
|
|
440
496
|
return runWithLock(this.#props.db, run);
|
|
441
497
|
}
|
|
442
|
-
if (
|
|
498
|
+
if (transactionMode.value === 'per-run') {
|
|
443
499
|
return this.#props.db
|
|
444
500
|
.connection()
|
|
445
|
-
.execute((db) => runWithLock(db, (db) => db.transaction().execute(
|
|
501
|
+
.execute((db) => runWithLock(db, (db) => db.transaction().execute(run)));
|
|
446
502
|
}
|
|
447
503
|
return this.#props.db.connection().execute((db) => runWithLock(db, run));
|
|
448
504
|
}
|
|
505
|
+
#resolveTransactionMode(options, adapter) {
|
|
506
|
+
const resolveLevel = (options, source) => {
|
|
507
|
+
if (!options) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const { disableTransactions, transactionMode } = options;
|
|
511
|
+
if (disableTransactions != null &&
|
|
512
|
+
transactionMode &&
|
|
513
|
+
(transactionMode === 'none') !== disableTransactions) {
|
|
514
|
+
throw new Error(`\`transactionMode\` is '${transactionMode}' but \`disableTransactions\` is ${disableTransactions} in the ${source}. These options contradict each other. Prefer \`transactionMode\` — \`disableTransactions\` is deprecated.`);
|
|
515
|
+
}
|
|
516
|
+
if (transactionMode) {
|
|
517
|
+
if (transactionMode !== 'none' && !adapter.supportsTransactionalDdl) {
|
|
518
|
+
throw new Error(`\`transactionMode\` is '${transactionMode}' but transactional DDL is not supported in this dialect. Migrations would fail or behave unexpectedly.`);
|
|
519
|
+
}
|
|
520
|
+
return { explicit: true, value: transactionMode };
|
|
521
|
+
}
|
|
522
|
+
if (disableTransactions == null) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (disableTransactions === true) {
|
|
526
|
+
return { explicit: true, value: 'none' };
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
explicit: false,
|
|
530
|
+
value: adapter.supportsTransactionalDdl ? 'per-run' : 'none',
|
|
531
|
+
};
|
|
532
|
+
};
|
|
533
|
+
return (resolveLevel(options, 'migrate call options') ||
|
|
534
|
+
resolveLevel(this.#props, 'migrator properties') || {
|
|
535
|
+
explicit: false,
|
|
536
|
+
value: adapter.supportsTransactionalDdl ? 'per-run' : 'none',
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
#getMigrationsToRun(state, direction, step) {
|
|
540
|
+
if (direction === 'Down') {
|
|
541
|
+
return state.executedMigrations
|
|
542
|
+
.toReversed()
|
|
543
|
+
.slice(0, step)
|
|
544
|
+
.map((name) => {
|
|
545
|
+
return state.migrations.find((it) => it.name === name);
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
return state.pendingMigrations.slice(0, step);
|
|
549
|
+
}
|
|
449
550
|
async #getState(db) {
|
|
450
|
-
const migrations = await
|
|
451
|
-
|
|
551
|
+
const [migrations, executedMigrations] = await Promise.all([
|
|
552
|
+
this.#resolveMigrations(),
|
|
553
|
+
this.#getExecutedMigrations(db),
|
|
554
|
+
]);
|
|
452
555
|
this.#ensureNoMissingMigrations(migrations, executedMigrations);
|
|
453
556
|
if (!this.#allowUnorderedMigrations) {
|
|
454
557
|
this.#ensureMigrationsInOrder(migrations, executedMigrations);
|
|
455
558
|
}
|
|
456
559
|
const pendingMigrations = this.#getPendingMigrations(migrations, executedMigrations);
|
|
457
560
|
return freeze({
|
|
458
|
-
migrations,
|
|
459
561
|
executedMigrations,
|
|
460
|
-
lastMigration:
|
|
562
|
+
lastMigration: executedMigrations.at(-1),
|
|
563
|
+
migrations,
|
|
461
564
|
pendingMigrations,
|
|
462
565
|
});
|
|
463
566
|
}
|
|
464
567
|
#getPendingMigrations(migrations, executedMigrations) {
|
|
465
|
-
return migrations.filter((migration) =>
|
|
466
|
-
return !executedMigrations.includes(migration.name);
|
|
467
|
-
});
|
|
568
|
+
return migrations.filter((migration) => !executedMigrations.includes(migration.name));
|
|
468
569
|
}
|
|
469
570
|
async #resolveMigrations() {
|
|
470
571
|
const allMigrations = await this.#props.provider.getMigrations();
|
|
471
572
|
return Object.keys(allMigrations)
|
|
472
573
|
.sort()
|
|
473
|
-
.map((name) => ({
|
|
474
|
-
...allMigrations[name],
|
|
475
|
-
name,
|
|
476
|
-
}));
|
|
574
|
+
.map((name) => ({ ...allMigrations[name], name }));
|
|
477
575
|
}
|
|
478
576
|
async #getExecutedMigrations(db) {
|
|
479
577
|
const executedMigrations = await db
|
|
@@ -509,92 +607,63 @@ export class Migrator {
|
|
|
509
607
|
}
|
|
510
608
|
}
|
|
511
609
|
}
|
|
512
|
-
async #
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
.
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
});
|
|
519
|
-
const results = migrationsToRollback.map((migration) => {
|
|
520
|
-
return {
|
|
521
|
-
migrationName: migration.name,
|
|
522
|
-
direction: 'Down',
|
|
523
|
-
status: 'NotExecuted',
|
|
524
|
-
};
|
|
525
|
-
});
|
|
610
|
+
async #rollbackMigrations(migrationsToRollback, rollbacker) {
|
|
611
|
+
const results = migrationsToRollback.map((migration) => ({
|
|
612
|
+
direction: 'Down',
|
|
613
|
+
migrationName: migration.name,
|
|
614
|
+
status: 'NotExecuted',
|
|
615
|
+
}));
|
|
526
616
|
for (let i = 0; i < results.length; ++i) {
|
|
527
617
|
const migration = migrationsToRollback[i];
|
|
618
|
+
if (!migration.down) {
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
528
621
|
try {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
await db
|
|
532
|
-
.withPlugin(this.#schemaPlugin)
|
|
533
|
-
.deleteFrom(this.#migrationTable)
|
|
534
|
-
.where('name', '=', migration.name)
|
|
535
|
-
.execute();
|
|
536
|
-
results[i] = {
|
|
537
|
-
migrationName: migration.name,
|
|
538
|
-
direction: 'Down',
|
|
539
|
-
status: 'Success',
|
|
540
|
-
};
|
|
541
|
-
}
|
|
622
|
+
await rollbacker(migration);
|
|
623
|
+
results[i].status = 'Success';
|
|
542
624
|
}
|
|
543
625
|
catch (error) {
|
|
544
|
-
results[i] =
|
|
545
|
-
|
|
546
|
-
direction: 'Down',
|
|
547
|
-
status: 'Error',
|
|
548
|
-
};
|
|
549
|
-
throw new MigrationResultSetError({
|
|
550
|
-
error,
|
|
551
|
-
results,
|
|
552
|
-
});
|
|
626
|
+
results[i].status = 'Error';
|
|
627
|
+
throw new MigrationResultSetError({ error, results });
|
|
553
628
|
}
|
|
554
629
|
}
|
|
555
630
|
return { results };
|
|
556
631
|
}
|
|
557
|
-
async #
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
status: 'NotExecuted',
|
|
564
|
-
};
|
|
565
|
-
});
|
|
632
|
+
async #applyMigrations(migrationsToRun, applier) {
|
|
633
|
+
const results = migrationsToRun.map((migration) => ({
|
|
634
|
+
direction: 'Up',
|
|
635
|
+
migrationName: migration.name,
|
|
636
|
+
status: 'NotExecuted',
|
|
637
|
+
}));
|
|
566
638
|
for (let i = 0; i < results.length; i++) {
|
|
567
|
-
const migration =
|
|
639
|
+
const migration = migrationsToRun[i];
|
|
568
640
|
try {
|
|
569
|
-
await migration
|
|
570
|
-
|
|
571
|
-
.withPlugin(this.#schemaPlugin)
|
|
572
|
-
.insertInto(this.#migrationTable)
|
|
573
|
-
.values({
|
|
574
|
-
name: migration.name,
|
|
575
|
-
timestamp: new Date().toISOString(),
|
|
576
|
-
})
|
|
577
|
-
.execute();
|
|
578
|
-
results[i] = {
|
|
579
|
-
migrationName: migration.name,
|
|
580
|
-
direction: 'Up',
|
|
581
|
-
status: 'Success',
|
|
582
|
-
};
|
|
641
|
+
await applier(migration);
|
|
642
|
+
results[i].status = 'Success';
|
|
583
643
|
}
|
|
584
644
|
catch (error) {
|
|
585
|
-
results[i] =
|
|
586
|
-
|
|
587
|
-
direction: 'Up',
|
|
588
|
-
status: 'Error',
|
|
589
|
-
};
|
|
590
|
-
throw new MigrationResultSetError({
|
|
591
|
-
error,
|
|
592
|
-
results,
|
|
593
|
-
});
|
|
645
|
+
results[i].status = 'Error';
|
|
646
|
+
throw new MigrationResultSetError({ error, results });
|
|
594
647
|
}
|
|
595
648
|
}
|
|
596
649
|
return { results };
|
|
597
650
|
}
|
|
651
|
+
async #applyMigration(db, migration) {
|
|
652
|
+
await migration.up(db);
|
|
653
|
+
await db
|
|
654
|
+
.withPlugin(this.#schemaPlugin)
|
|
655
|
+
.insertInto(this.#migrationTable)
|
|
656
|
+
.values({ name: migration.name, timestamp: new Date().toISOString() })
|
|
657
|
+
.execute();
|
|
658
|
+
}
|
|
659
|
+
async #rollbackMigration(db, migration) {
|
|
660
|
+
await migration.down(db);
|
|
661
|
+
await db
|
|
662
|
+
.withPlugin(this.#schemaPlugin)
|
|
663
|
+
.deleteFrom(this.#migrationTable)
|
|
664
|
+
.where('name', '=', migration.name)
|
|
665
|
+
.execute();
|
|
666
|
+
}
|
|
598
667
|
async #createIfNotExists(qb) {
|
|
599
668
|
if (this.#props.db.getExecutor().adapter.supportsCreateIfNotExists) {
|
|
600
669
|
qb = qb.ifNotExists();
|
|
@@ -14,7 +14,6 @@ export declare function isFunction(obj: unknown): obj is Function;
|
|
|
14
14
|
export declare function isObject(obj: unknown): obj is ShallowRecord<string, unknown>;
|
|
15
15
|
export declare function isArrayBufferOrView(obj: unknown): obj is ArrayBuffer | ArrayBufferView;
|
|
16
16
|
export declare function isPlainObject(obj: unknown): obj is Record<string, unknown>;
|
|
17
|
-
export declare function getLast<T>(arr: ArrayLike<T>): T | undefined;
|
|
18
17
|
export declare function freeze<T>(obj: T): Readonly<T>;
|
|
19
18
|
export declare function asArray<T>(arg: T | ReadonlyArray<T>): ReadonlyArray<T>;
|
|
20
19
|
export declare function asReadonlyArray<T>(arg: T | ReadonlyArray<T>): ReadonlyArray<T>;
|