bunsql-native-migrate 0.1.0 → 0.1.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 +23 -1
- package/package.json +1 -1
- package/src/api/create.ts +3 -3
- package/src/api/down.ts +5 -9
- package/src/api/install.ts +3 -8
- package/src/api/run-step.ts +13 -0
- package/src/api/run-with-driver.ts +16 -0
- package/src/api/up.ts +11 -17
- package/src/core/console.ts +1 -1
- package/src/core/driver.ts +3 -0
- package/src/core/random-name.ts +0 -68
- package/src/drivers/mariadb.ts +31 -34
- package/src/drivers/postgres.ts +6 -31
- package/src/drivers/shared.ts +35 -0
- package/src/drivers/sqlite.ts +6 -31
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
Zero-ORM SQL file migrations for [Bun](https://bun.sh): PostgreSQL, MySQL/MariaDB and SQLite through the built-in `Bun.SQL` client.
|
|
7
7
|
|
|
8
|
-
No ORM, no schema diffing, no lock-in — you write plain `.js` migration files with `up()`/`down()` exports and run them with a tiny CLI or the programmatic API.
|
|
8
|
+
No ORM, no schema diffing, no lock-in — you write plain `.js` migration files with `up()`/`down()` exports (optionally `up(tx)`/`down(tx)` for transactional migrations, see below) and run them with a tiny CLI or the programmatic API.
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
@@ -64,6 +64,28 @@ Files live in the migrations directory (default `./migrations`, override with `-
|
|
|
64
64
|
9999999999999_2026_09_13_add_users_table.js
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
### Transactional migrations
|
|
68
|
+
|
|
69
|
+
Declare a `tx` parameter on `up`/`down` and the migration runs inside a single database transaction: if any statement fails, the partial work is rolled back instead of being left half-applied, and nothing is recorded.
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
const up = async (tx) => {
|
|
73
|
+
await tx`CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)`;
|
|
74
|
+
await tx`INSERT INTO users (id, name) VALUES (1, 'admin')`;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const down = async (tx) => {
|
|
78
|
+
await tx`DROP TABLE users`;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export { up, down };
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- `bunsql-native-migrate create` generates stubs in this form by default; `tx` is a Bun `SQL` client bound to the same database the runner is connected to.
|
|
85
|
+
- Migrations declared **without** parameters keep using the global `sql` client and run without a transaction — both styles can coexist in one project, decided per migration by the declared signature.
|
|
86
|
+
- Engine caveats: PostgreSQL and SQLite roll back everything, DDL included. On MySQL/MariaDB any DDL statement implicitly commits the current transaction, so there only DML gets rollback protection.
|
|
87
|
+
- The tracking record is written right after the transaction commits. A crash in that single-statement window leaves the migration applied but unrecorded — the next `up` would re-run it, so keep critical migrations idempotent (this window exists for plain `up()` migrations too, just wider).
|
|
88
|
+
|
|
67
89
|
### Migrations directory path resolution
|
|
68
90
|
|
|
69
91
|
Relative paths — whether from `--dir`, the `listDir` option or `MIGRATION_LIST_DIR` — are always resolved against the **current working directory** of the process (the same anchor Bun uses to load `.env`). Run the CLI from your project root and plain `./migrations` works as expected.
|
package/package.json
CHANGED
package/src/api/create.ts
CHANGED
|
@@ -12,11 +12,11 @@ export interface CreateOptions {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const STUB_TEMPLATE = `import { sql } from "bun";
|
|
15
|
-
// Write your migration SQL here
|
|
16
|
-
const up = async () => {};
|
|
15
|
+
// Write your migration SQL here (tx runs inside a transaction)
|
|
16
|
+
const up = async (tx) => {};
|
|
17
17
|
|
|
18
18
|
// Write your rollback SQL here
|
|
19
|
-
const down = async () => {};
|
|
19
|
+
const down = async (tx) => {};
|
|
20
20
|
|
|
21
21
|
export { up, down };
|
|
22
22
|
`;
|
package/src/api/down.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { getDatabaseUrl } from "../core/env.js";
|
|
3
|
-
import { createDriver } from "../core/driver.js";
|
|
4
2
|
import { resolveListDir } from "../core/fs.js";
|
|
5
3
|
import { log } from "../core/console.js";
|
|
6
4
|
import type { MigrateDownResult, MigrateOptions } from "./options.js";
|
|
5
|
+
import { runWithDriver } from "./run-with-driver.js";
|
|
6
|
+
import { runMigrationStep } from "./run-step.js";
|
|
7
7
|
|
|
8
8
|
export async function migrateDown(options: MigrateOptions = {}): Promise<MigrateDownResult> {
|
|
9
|
-
const url = getDatabaseUrl(options.databaseUrl);
|
|
10
9
|
const listDir = resolveListDir(options.listDir);
|
|
11
|
-
const driver = await createDriver(url);
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
return runWithDriver(options, async (driver) => {
|
|
14
12
|
const executed = await driver.listExecuted();
|
|
15
13
|
if (executed.length === 0) {
|
|
16
14
|
log({ text: "No migrations to rollback.", type: "warn" });
|
|
@@ -27,11 +25,9 @@ export async function migrateDown(options: MigrateOptions = {}): Promise<Migrate
|
|
|
27
25
|
return { reverted: file };
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
await mod.down
|
|
28
|
+
await runMigrationStep(driver, mod.down);
|
|
31
29
|
await driver.remove(file);
|
|
32
30
|
log({ text: `${file} rolled back`, type: "success" });
|
|
33
31
|
return { reverted: file };
|
|
34
|
-
}
|
|
35
|
-
await driver.close();
|
|
36
|
-
}
|
|
32
|
+
});
|
|
37
33
|
}
|
package/src/api/install.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import { getDatabaseUrl } from "../core/env.js";
|
|
2
|
-
import { createDriver } from "../core/driver.js";
|
|
3
1
|
import { log } from "../core/console.js";
|
|
4
2
|
import type { MigrateOptions } from "./options.js";
|
|
3
|
+
import { runWithDriver } from "./run-with-driver.js";
|
|
5
4
|
|
|
6
5
|
export async function installMigrations(options: MigrateOptions = {}): Promise<void> {
|
|
7
|
-
|
|
8
|
-
const driver = await createDriver(url);
|
|
9
|
-
try {
|
|
6
|
+
await runWithDriver(options, async (driver) => {
|
|
10
7
|
await driver.install();
|
|
11
8
|
log({ text: "Migration table created!", type: "success" });
|
|
12
|
-
}
|
|
13
|
-
await driver.close();
|
|
14
|
-
}
|
|
9
|
+
});
|
|
15
10
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type SQL } from "bun";
|
|
2
|
+
import type { MigrationDriver } from "../core/driver.js";
|
|
3
|
+
|
|
4
|
+
export async function runMigrationStep(
|
|
5
|
+
driver: MigrationDriver,
|
|
6
|
+
step: (tx?: SQL) => Promise<void>,
|
|
7
|
+
): Promise<void> {
|
|
8
|
+
if (step.length > 0) {
|
|
9
|
+
await driver.transaction((tx) => step(tx));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
await step();
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getDatabaseUrl } from "../core/env.js";
|
|
2
|
+
import { createDriver, type MigrationDriver } from "../core/driver.js";
|
|
3
|
+
import type { MigrateOptions } from "./options.js";
|
|
4
|
+
|
|
5
|
+
export async function runWithDriver<T>(
|
|
6
|
+
options: MigrateOptions,
|
|
7
|
+
run: (driver: MigrationDriver) => Promise<T>,
|
|
8
|
+
): Promise<T> {
|
|
9
|
+
const url = getDatabaseUrl(options.databaseUrl);
|
|
10
|
+
const driver = await createDriver(url);
|
|
11
|
+
try {
|
|
12
|
+
return await run(driver);
|
|
13
|
+
} finally {
|
|
14
|
+
await driver.close();
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/api/up.ts
CHANGED
|
@@ -1,34 +1,30 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { getDatabaseUrl } from "../core/env.js";
|
|
3
|
-
import { createDriver } from "../core/driver.js";
|
|
4
2
|
import { checksumFile, listFiles, resolveListDir } from "../core/fs.js";
|
|
5
3
|
import { log } from "../core/console.js";
|
|
6
4
|
import { type MigrateOptions, type MigrateUpResult, ChecksumDriftError } from "./options.js";
|
|
5
|
+
import { runWithDriver } from "./run-with-driver.js";
|
|
6
|
+
import { runMigrationStep } from "./run-step.js";
|
|
7
7
|
|
|
8
8
|
export async function migrateUp(options: MigrateOptions = {}): Promise<MigrateUpResult> {
|
|
9
|
-
const url = getDatabaseUrl(options.databaseUrl);
|
|
10
9
|
const listDir = resolveListDir(options.listDir);
|
|
11
|
-
const driver = await createDriver(url);
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
return runWithDriver(options, async (driver) => {
|
|
14
12
|
await driver.install();
|
|
15
13
|
|
|
16
14
|
const allFiles = await listFiles(listDir, "js");
|
|
17
|
-
const checksums = new Map
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
const checksums = new Map(
|
|
16
|
+
await Promise.all(
|
|
17
|
+
allFiles.map(async (file) => [file, await checksumFile(path.join(listDir, file))] as const),
|
|
18
|
+
),
|
|
19
|
+
);
|
|
21
20
|
|
|
22
21
|
const executed = await driver.listExecuted();
|
|
23
22
|
const executedByName = new Map(executed.map((entry) => [entry.name, entry]));
|
|
24
23
|
|
|
25
|
-
for (const file of
|
|
24
|
+
for (const [file, checksum] of checksums) {
|
|
26
25
|
const record = executedByName.get(file);
|
|
27
26
|
if (!record) continue;
|
|
28
27
|
|
|
29
|
-
const checksum = checksums.get(file);
|
|
30
|
-
if (!checksum) continue;
|
|
31
|
-
|
|
32
28
|
if (record.checksum === null) {
|
|
33
29
|
await driver.setChecksum(file, checksum);
|
|
34
30
|
log({ text: `${file} checksum saved (legacy record)`, type: "info" });
|
|
@@ -57,7 +53,7 @@ export async function migrateUp(options: MigrateOptions = {}): Promise<MigrateUp
|
|
|
57
53
|
log({ text: `${file} has no up() export, skipping`, type: "warn" });
|
|
58
54
|
continue;
|
|
59
55
|
}
|
|
60
|
-
await mod.up
|
|
56
|
+
await runMigrationStep(driver, mod.up);
|
|
61
57
|
await driver.record(file, checksum);
|
|
62
58
|
applied.push(file);
|
|
63
59
|
log({ text: `${file} migrated up`, type: "success" });
|
|
@@ -68,7 +64,5 @@ export async function migrateUp(options: MigrateOptions = {}): Promise<MigrateUp
|
|
|
68
64
|
}
|
|
69
65
|
|
|
70
66
|
return { applied };
|
|
71
|
-
}
|
|
72
|
-
await driver.close();
|
|
73
|
-
}
|
|
67
|
+
});
|
|
74
68
|
}
|
package/src/core/console.ts
CHANGED
package/src/core/driver.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type SQL } from "bun";
|
|
2
|
+
|
|
1
3
|
export interface ExecutedMigration {
|
|
2
4
|
name: string;
|
|
3
5
|
checksum: string | null;
|
|
@@ -9,6 +11,7 @@ export interface MigrationDriver {
|
|
|
9
11
|
record(migration: string, checksum: string): Promise<void>;
|
|
10
12
|
setChecksum(migration: string, checksum: string): Promise<void>;
|
|
11
13
|
remove(migration: string): Promise<void>;
|
|
14
|
+
transaction<T>(run: (tx: SQL) => Promise<T>): Promise<T>;
|
|
12
15
|
close(): Promise<void>;
|
|
13
16
|
}
|
|
14
17
|
|
package/src/core/random-name.ts
CHANGED
|
@@ -1,54 +1,20 @@
|
|
|
1
1
|
const adjectives = [
|
|
2
2
|
"brave",
|
|
3
3
|
"calm",
|
|
4
|
-
"dusty",
|
|
5
4
|
"eager",
|
|
6
|
-
"fair",
|
|
7
5
|
"golden",
|
|
8
|
-
"hasty",
|
|
9
6
|
"jolly",
|
|
10
7
|
"keen",
|
|
11
8
|
"lucky",
|
|
12
9
|
"merry",
|
|
13
10
|
"noble",
|
|
14
|
-
"proud",
|
|
15
11
|
"quick",
|
|
16
12
|
"sharp",
|
|
17
13
|
"swift",
|
|
18
|
-
"tall",
|
|
19
14
|
"vivid",
|
|
20
15
|
"warm",
|
|
21
|
-
"young",
|
|
22
16
|
"bold",
|
|
23
17
|
"crisp",
|
|
24
|
-
"dry",
|
|
25
|
-
"fine",
|
|
26
|
-
"glad",
|
|
27
|
-
"kind",
|
|
28
|
-
"light",
|
|
29
|
-
"mild",
|
|
30
|
-
"neat",
|
|
31
|
-
"prime",
|
|
32
|
-
"rare",
|
|
33
|
-
"safe",
|
|
34
|
-
"true",
|
|
35
|
-
"vast",
|
|
36
|
-
"wise",
|
|
37
|
-
"apt",
|
|
38
|
-
"bright",
|
|
39
|
-
"deep",
|
|
40
|
-
"free",
|
|
41
|
-
"grand",
|
|
42
|
-
"honest",
|
|
43
|
-
"just",
|
|
44
|
-
"lean",
|
|
45
|
-
"open",
|
|
46
|
-
"plain",
|
|
47
|
-
"still",
|
|
48
|
-
"wild",
|
|
49
|
-
"cool",
|
|
50
|
-
"soft",
|
|
51
|
-
"dark",
|
|
52
18
|
];
|
|
53
19
|
|
|
54
20
|
const nouns = [
|
|
@@ -61,47 +27,13 @@ const nouns = [
|
|
|
61
27
|
"breeze",
|
|
62
28
|
"creek",
|
|
63
29
|
"ember",
|
|
64
|
-
"glacier",
|
|
65
|
-
"harbor",
|
|
66
|
-
"island",
|
|
67
|
-
"jasper",
|
|
68
|
-
"kettle",
|
|
69
30
|
"lantern",
|
|
70
31
|
"meadow",
|
|
71
|
-
"nectar",
|
|
72
|
-
"orchid",
|
|
73
|
-
"prism",
|
|
74
32
|
"quartz",
|
|
75
33
|
"ridge",
|
|
76
34
|
"tide",
|
|
77
35
|
"valley",
|
|
78
36
|
"willow",
|
|
79
|
-
"zephyr",
|
|
80
|
-
"bolt",
|
|
81
|
-
"cliff",
|
|
82
|
-
"dawn",
|
|
83
|
-
"fern",
|
|
84
|
-
"grove",
|
|
85
|
-
"haze",
|
|
86
|
-
"iris",
|
|
87
|
-
"jet",
|
|
88
|
-
"kite",
|
|
89
|
-
"lark",
|
|
90
|
-
"marsh",
|
|
91
|
-
"nest",
|
|
92
|
-
"opal",
|
|
93
|
-
"peak",
|
|
94
|
-
"reed",
|
|
95
|
-
"sage",
|
|
96
|
-
"thorn",
|
|
97
|
-
"vine",
|
|
98
|
-
"wolf",
|
|
99
|
-
"ash",
|
|
100
|
-
"bay",
|
|
101
|
-
"cape",
|
|
102
|
-
"dune",
|
|
103
|
-
"flint",
|
|
104
|
-
"gleam",
|
|
105
37
|
];
|
|
106
38
|
|
|
107
39
|
export function randomName(): string {
|
package/src/drivers/mariadb.ts
CHANGED
|
@@ -1,45 +1,42 @@
|
|
|
1
|
-
import { SQL } from "bun";
|
|
2
|
-
import type {
|
|
1
|
+
import { type SQL } from "bun";
|
|
2
|
+
import type { MigrationDriver } from "../core/driver.js";
|
|
3
|
+
import { createSqlDriver } from "./shared.js";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
const
|
|
5
|
+
async function checksumColumnExists(db: SQL): Promise<boolean> {
|
|
6
|
+
const rows = await db`SELECT column_name FROM information_schema.columns
|
|
7
|
+
WHERE table_schema = DATABASE()
|
|
8
|
+
AND table_name = 'migrations'
|
|
9
|
+
AND column_name = 'checksum'`;
|
|
10
|
+
return rows.length > 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function uniqueIndexExists(db: SQL): Promise<boolean> {
|
|
14
|
+
const rows = await db`SELECT index_name FROM information_schema.statistics
|
|
15
|
+
WHERE table_schema = DATABASE()
|
|
16
|
+
AND table_name = 'migrations'
|
|
17
|
+
AND index_name = 'migrations_migration_unique'`;
|
|
18
|
+
return rows.length > 0;
|
|
19
|
+
}
|
|
6
20
|
|
|
7
|
-
|
|
8
|
-
|
|
21
|
+
export function create(databaseUrl: string): MigrationDriver {
|
|
22
|
+
return createSqlDriver(databaseUrl, {
|
|
23
|
+
async install(db) {
|
|
9
24
|
await db`CREATE TABLE IF NOT EXISTS migrations (
|
|
10
25
|
id INTEGER PRIMARY KEY AUTO_INCREMENT,
|
|
11
26
|
migration VARCHAR(255) NOT NULL,
|
|
12
|
-
checksum VARCHAR(64)
|
|
27
|
+
checksum VARCHAR(64),
|
|
28
|
+
CONSTRAINT migrations_migration_unique UNIQUE (migration)
|
|
13
29
|
)`;
|
|
14
|
-
await db
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return rows.map(
|
|
21
|
-
(r: { migration: string; checksum: string | null }): ExecutedMigration => ({
|
|
22
|
-
name: r.migration,
|
|
23
|
-
checksum: r.checksum ?? null,
|
|
24
|
-
}),
|
|
25
|
-
);
|
|
30
|
+
if (!(await checksumColumnExists(db))) {
|
|
31
|
+
await db`ALTER TABLE migrations ADD COLUMN checksum VARCHAR(64)`;
|
|
32
|
+
}
|
|
33
|
+
if (!(await uniqueIndexExists(db))) {
|
|
34
|
+
await db`CREATE UNIQUE INDEX migrations_migration_unique ON migrations (migration)`;
|
|
35
|
+
}
|
|
26
36
|
},
|
|
27
|
-
|
|
28
|
-
async record(migration: string, checksum: string) {
|
|
37
|
+
async record(db, migration, checksum) {
|
|
29
38
|
await db`INSERT IGNORE INTO migrations (migration, checksum)
|
|
30
39
|
VALUES (${migration}, ${checksum})`;
|
|
31
40
|
},
|
|
32
|
-
|
|
33
|
-
async setChecksum(migration: string, checksum: string) {
|
|
34
|
-
await db`UPDATE migrations SET checksum = ${checksum} WHERE migration = ${migration}`;
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
async remove(migration: string) {
|
|
38
|
-
await db`DELETE FROM migrations WHERE migration = ${migration}`;
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
async close() {
|
|
42
|
-
db.close({ timeout: 0 });
|
|
43
|
-
},
|
|
44
|
-
};
|
|
41
|
+
});
|
|
45
42
|
}
|
package/src/drivers/postgres.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 SERIAL PRIMARY KEY,
|
|
11
9
|
migration VARCHAR(255) NOT NULL,
|
|
@@ -14,33 +12,10 @@ export function create(databaseUrl: string): MigrationDriver {
|
|
|
14
12
|
await db`ALTER TABLE migrations ADD COLUMN IF NOT EXISTS checksum VARCHAR(64)`;
|
|
15
13
|
await db`CREATE UNIQUE INDEX IF NOT EXISTS migrations_migration_unique ON migrations (migration)`;
|
|
16
14
|
},
|
|
17
|
-
|
|
18
|
-
async listExecuted() {
|
|
19
|
-
const rows = await db`SELECT migration, checksum FROM migrations ORDER BY id ASC`;
|
|
20
|
-
return rows.map(
|
|
21
|
-
(r: { migration: string; checksum: string | null }): ExecutedMigration => ({
|
|
22
|
-
name: r.migration,
|
|
23
|
-
checksum: r.checksum ?? null,
|
|
24
|
-
}),
|
|
25
|
-
);
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
async record(migration: string, checksum: string) {
|
|
15
|
+
async record(db, migration, checksum) {
|
|
29
16
|
await db`INSERT INTO migrations (migration, checksum)
|
|
30
17
|
VALUES (${migration}, ${checksum})
|
|
31
18
|
ON CONFLICT (migration) DO NOTHING`;
|
|
32
19
|
},
|
|
33
|
-
|
|
34
|
-
async setChecksum(migration: string, checksum: string) {
|
|
35
|
-
await db`UPDATE migrations SET checksum = ${checksum} WHERE migration = ${migration}`;
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
async remove(migration: string) {
|
|
39
|
-
await db`DELETE FROM migrations WHERE migration = ${migration}`;
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
async close() {
|
|
43
|
-
db.close({ timeout: 0 });
|
|
44
|
-
},
|
|
45
|
-
};
|
|
20
|
+
});
|
|
46
21
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SQL } from "bun";
|
|
2
|
+
import type { ExecutedMigration, MigrationDriver } from "../core/driver.js";
|
|
3
|
+
|
|
4
|
+
export interface SqlDialect {
|
|
5
|
+
install(db: SQL): Promise<void>;
|
|
6
|
+
record(db: SQL, migration: string, checksum: string): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createSqlDriver(databaseUrl: string, dialect: SqlDialect): MigrationDriver {
|
|
10
|
+
const db = new SQL(databaseUrl);
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
install: () => dialect.install(db),
|
|
14
|
+
async listExecuted() {
|
|
15
|
+
const rows = await db`SELECT migration, checksum FROM migrations ORDER BY id ASC`;
|
|
16
|
+
return rows.map(
|
|
17
|
+
(r: { migration: string; checksum: string | null }): ExecutedMigration => ({
|
|
18
|
+
name: r.migration,
|
|
19
|
+
checksum: r.checksum ?? null,
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
22
|
+
},
|
|
23
|
+
record: (migration, checksum) => dialect.record(db, migration, checksum),
|
|
24
|
+
async setChecksum(migration, checksum) {
|
|
25
|
+
await db`UPDATE migrations SET checksum = ${checksum} WHERE migration = ${migration}`;
|
|
26
|
+
},
|
|
27
|
+
async remove(migration) {
|
|
28
|
+
await db`DELETE FROM migrations WHERE migration = ${migration}`;
|
|
29
|
+
},
|
|
30
|
+
transaction: (run) => db.begin(run),
|
|
31
|
+
async close() {
|
|
32
|
+
db.close({ timeout: 0 });
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
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,9 @@ 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
|
-
async setChecksum(migration: string, checksum: string) {
|
|
40
|
-
await db`UPDATE migrations SET checksum = ${checksum} WHERE migration = ${migration}`;
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
async remove(migration: string) {
|
|
44
|
-
await db`DELETE FROM migrations WHERE migration = ${migration}`;
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
async close() {
|
|
48
|
-
db.close({ timeout: 0 });
|
|
49
|
-
},
|
|
50
|
-
};
|
|
25
|
+
});
|
|
51
26
|
}
|