bunsql-native-migrate 0.1.1 → 0.2.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 +89 -27
- package/package.json +1 -1
- package/src/api/create.ts +29 -11
- package/src/api/down.ts +40 -14
- package/src/api/lock.ts +50 -0
- package/src/api/options.ts +39 -1
- package/src/api/run-step.ts +13 -0
- package/src/api/status.ts +20 -0
- package/src/api/up.ts +68 -45
- package/src/cli/main.ts +106 -7
- package/src/core/console.ts +1 -1
- package/src/core/driver.ts +5 -0
- package/src/core/fs.ts +4 -2
- package/src/core/random-name.ts +0 -68
- package/src/drivers/mariadb.ts +48 -34
- package/src/drivers/postgres.ts +31 -31
- package/src/drivers/shared.ts +91 -0
- package/src/drivers/sqlite.ts +16 -31
- package/src/index.ts +12 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { SQL, type ReservedSQL } from "bun";
|
|
2
|
+
import type { ExecutedMigration, MigrationDriver } from "../core/driver.js";
|
|
3
|
+
|
|
4
|
+
export interface SqlLock {
|
|
5
|
+
tryLock(timeoutSeconds: number): Promise<boolean>;
|
|
6
|
+
releaseLock(): Promise<void>;
|
|
7
|
+
dispose(): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SqlDialect {
|
|
11
|
+
install(db: SQL): Promise<void>;
|
|
12
|
+
record(db: SQL, migration: string, checksum: string): Promise<void>;
|
|
13
|
+
createLock?(db: SQL): SqlLock;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createReservedLock(
|
|
17
|
+
db: SQL,
|
|
18
|
+
acquire: (lock: SQL) => Promise<boolean>,
|
|
19
|
+
release: (lock: SQL) => Promise<void>,
|
|
20
|
+
): SqlLock {
|
|
21
|
+
let lockConnection: ReservedSQL | null = null;
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
async tryLock() {
|
|
25
|
+
const connection = await db.reserve();
|
|
26
|
+
lockConnection = connection;
|
|
27
|
+
try {
|
|
28
|
+
const acquired = await acquire(connection);
|
|
29
|
+
if (!acquired) {
|
|
30
|
+
connection.release();
|
|
31
|
+
lockConnection = null;
|
|
32
|
+
}
|
|
33
|
+
return acquired;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
connection.release();
|
|
36
|
+
lockConnection = null;
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
async releaseLock() {
|
|
41
|
+
const connection = lockConnection;
|
|
42
|
+
if (!connection) return;
|
|
43
|
+
lockConnection = null;
|
|
44
|
+
try {
|
|
45
|
+
await release(connection);
|
|
46
|
+
} finally {
|
|
47
|
+
connection.release();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
dispose() {
|
|
51
|
+
lockConnection?.release();
|
|
52
|
+
lockConnection = null;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function createSqlDriver(databaseUrl: string, dialect: SqlDialect): MigrationDriver {
|
|
58
|
+
const db = new SQL(databaseUrl);
|
|
59
|
+
const lock = dialect.createLock?.(db);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
install: () => dialect.install(db),
|
|
63
|
+
async listExecuted() {
|
|
64
|
+
const rows = await db`SELECT migration, checksum FROM migrations ORDER BY id ASC`;
|
|
65
|
+
return rows.map(
|
|
66
|
+
(r: { migration: string; checksum: string | null }): ExecutedMigration => ({
|
|
67
|
+
name: r.migration,
|
|
68
|
+
checksum: r.checksum ?? null,
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
record: (migration, checksum) => dialect.record(db, migration, checksum),
|
|
73
|
+
async setChecksum(migration, checksum) {
|
|
74
|
+
await db`UPDATE migrations SET checksum = ${checksum} WHERE migration = ${migration}`;
|
|
75
|
+
},
|
|
76
|
+
async remove(migration) {
|
|
77
|
+
await db`DELETE FROM migrations WHERE migration = ${migration}`;
|
|
78
|
+
},
|
|
79
|
+
transaction: (run) => db.begin(run),
|
|
80
|
+
...(lock
|
|
81
|
+
? {
|
|
82
|
+
tryLock: (timeoutSeconds: number) => lock.tryLock(timeoutSeconds),
|
|
83
|
+
releaseLock: () => lock.releaseLock(),
|
|
84
|
+
}
|
|
85
|
+
: {}),
|
|
86
|
+
async close() {
|
|
87
|
+
lock?.dispose();
|
|
88
|
+
db.close({ timeout: 0 });
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
package/src/drivers/sqlite.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { MigrationDriver } from "../core/driver.js";
|
|
2
|
+
import { createSqlDriver } from "./shared.js";
|
|
3
3
|
|
|
4
4
|
export function create(databaseUrl: string): MigrationDriver {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
async install() {
|
|
5
|
+
return createSqlDriver(databaseUrl, {
|
|
6
|
+
async install(db) {
|
|
9
7
|
await db`CREATE TABLE IF NOT EXISTS migrations (
|
|
10
8
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
11
9
|
migration TEXT NOT NULL,
|
|
@@ -20,32 +18,19 @@ export function create(databaseUrl: string): MigrationDriver {
|
|
|
20
18
|
}
|
|
21
19
|
await db`CREATE UNIQUE INDEX IF NOT EXISTS migrations_migration_unique ON migrations (migration)`;
|
|
22
20
|
},
|
|
23
|
-
|
|
24
|
-
async listExecuted() {
|
|
25
|
-
const rows = await db`SELECT migration, checksum FROM migrations ORDER BY id ASC`;
|
|
26
|
-
return rows.map(
|
|
27
|
-
(r: { migration: string; checksum: string | null }): ExecutedMigration => ({
|
|
28
|
-
name: r.migration,
|
|
29
|
-
checksum: r.checksum ?? null,
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
async record(migration: string, checksum: string) {
|
|
21
|
+
async record(db, migration, checksum) {
|
|
35
22
|
await db`INSERT OR IGNORE INTO migrations (migration, checksum)
|
|
36
23
|
VALUES (${migration}, ${checksum})`;
|
|
37
24
|
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
};
|
|
25
|
+
createLock: (db) => ({
|
|
26
|
+
async tryLock(timeoutSeconds: number) {
|
|
27
|
+
await db.unsafe(`PRAGMA busy_timeout = ${timeoutSeconds * 1000}`);
|
|
28
|
+
return true;
|
|
29
|
+
},
|
|
30
|
+
async releaseLock() {
|
|
31
|
+
await db.unsafe("PRAGMA busy_timeout = 0");
|
|
32
|
+
},
|
|
33
|
+
dispose() {},
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
51
36
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,29 +1,41 @@
|
|
|
1
1
|
import { createDriver, type ExecutedMigration, type MigrationDriver } from "./core/driver.js";
|
|
2
2
|
import { migrateUp } from "./api/up.js";
|
|
3
3
|
import { migrateDown } from "./api/down.js";
|
|
4
|
+
import { migrateStatus } from "./api/status.js";
|
|
4
5
|
import { installMigrations } from "./api/install.js";
|
|
5
6
|
import { createMigration } from "./api/create.js";
|
|
6
7
|
import {
|
|
8
|
+
type MigrateDownOptions,
|
|
7
9
|
type MigrateDownResult,
|
|
8
10
|
type MigrateOptions,
|
|
11
|
+
type MigrateStatusResult,
|
|
12
|
+
type MigrateUpOptions,
|
|
9
13
|
type MigrateUpResult,
|
|
10
14
|
ChecksumDriftError,
|
|
11
15
|
GitStageError,
|
|
16
|
+
MigrationLockError,
|
|
17
|
+
MigrationNotFoundError,
|
|
12
18
|
} from "./api/options.js";
|
|
13
19
|
|
|
14
20
|
export {
|
|
15
21
|
createDriver,
|
|
16
22
|
migrateUp,
|
|
17
23
|
migrateDown,
|
|
24
|
+
migrateStatus,
|
|
18
25
|
installMigrations,
|
|
19
26
|
createMigration,
|
|
20
27
|
ChecksumDriftError,
|
|
21
28
|
GitStageError,
|
|
29
|
+
MigrationLockError,
|
|
30
|
+
MigrationNotFoundError,
|
|
22
31
|
};
|
|
23
32
|
export type {
|
|
24
33
|
ExecutedMigration,
|
|
25
34
|
MigrationDriver,
|
|
26
35
|
MigrateOptions,
|
|
36
|
+
MigrateUpOptions,
|
|
27
37
|
MigrateUpResult,
|
|
38
|
+
MigrateDownOptions,
|
|
28
39
|
MigrateDownResult,
|
|
40
|
+
MigrateStatusResult,
|
|
29
41
|
};
|