@chidchanun/bcp 0.1.13 → 0.1.15
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/docs/database-migrations.md +105 -0
- package/docs/database.md +127 -0
- package/package.json +6 -1
- package/packages/cli/src/args.ts +71 -1
- package/packages/cli/src/database-migrations.ts +708 -0
- package/packages/cli/src/index.ts +112 -1
- package/packages/client/src/database.ts +492 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Database Migrations
|
|
2
|
+
|
|
3
|
+
BCP Framework `0.1.15` adds MySQL migration commands to the `bcp` CLI.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
Database migrations currently use the MySQL adapter from `bcp/database`.
|
|
8
|
+
|
|
9
|
+
Configure the same environment variables used by your application:
|
|
10
|
+
|
|
11
|
+
```env
|
|
12
|
+
DB_HOST=localhost
|
|
13
|
+
DB_PORT=3306
|
|
14
|
+
DB_USER=root
|
|
15
|
+
DB_PASSWORD=
|
|
16
|
+
DB_NAME=bcp_app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A project using migrations must have `mysql2` installed. Applications created with the MySQL preset already include it.
|
|
20
|
+
|
|
21
|
+
## Create a migration
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bcp db create create_users
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
BCP creates an ordered TypeScript file in `migrations/`:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
migrations/
|
|
31
|
+
20260827040506_create_users.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
A generated migration exports `up()` and `down()`:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import type {
|
|
38
|
+
TransactionDatabase,
|
|
39
|
+
} from "bcp/database";
|
|
40
|
+
|
|
41
|
+
export async function up(
|
|
42
|
+
db: TransactionDatabase
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
await db.execute(`
|
|
45
|
+
CREATE TABLE users (
|
|
46
|
+
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
47
|
+
email VARCHAR(255) NOT NULL UNIQUE
|
|
48
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
49
|
+
`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function down(
|
|
53
|
+
db: TransactionDatabase
|
|
54
|
+
): Promise<void> {
|
|
55
|
+
await db.execute(
|
|
56
|
+
"DROP TABLE users"
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Migration filenames use a UTC timestamp prefix so migrations have a stable execution order.
|
|
62
|
+
|
|
63
|
+
## Run pending migrations
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bcp db migrate
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
BCP creates the internal `_bcp_migrations` table when needed, detects files that have not been applied, and runs all pending migrations in filename order.
|
|
70
|
+
|
|
71
|
+
All migrations applied by one `bcp db migrate` command share the same batch number. Each individual migration runs inside its own database transaction. The migration record is inserted in the same transaction as `up()`.
|
|
72
|
+
|
|
73
|
+
## Check status
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
bcp db status
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The command reports applied and pending migration files together with the batch number for applied migrations.
|
|
80
|
+
|
|
81
|
+
## Roll back
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
bcp db rollback
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Rollback reverses only the latest migration batch. Migrations in that batch run from newest to oldest, and each `down()` runs in a transaction together with removal of its migration record.
|
|
88
|
+
|
|
89
|
+
BCP refuses to roll back an applied migration when its migration file is missing.
|
|
90
|
+
|
|
91
|
+
## Project root
|
|
92
|
+
|
|
93
|
+
All database commands support the normal BCP project-root option:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
bcp db status --root ./apps/admin
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Environment files
|
|
100
|
+
|
|
101
|
+
Database commands load the BCP development environment files before connecting, so the same local database settings used by `bcp dev` can be reused by migration commands.
|
|
102
|
+
|
|
103
|
+
## Current scope
|
|
104
|
+
|
|
105
|
+
`0.1.15` migration execution supports MySQL. PostgreSQL, SQLite and MongoDB presets remain available to `create-bcp-app`, but framework-managed migrations for those adapters are planned for later releases.
|
package/docs/database.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Database
|
|
2
|
+
|
|
3
|
+
BCP `0.1.14` adds the first framework-native database API through the server-only `bcp/database` entrypoint.
|
|
4
|
+
|
|
5
|
+
The initial adapter targets MySQL. Additional adapters can be added in later releases without changing the page, loader, guard or action APIs that consume the database layer.
|
|
6
|
+
|
|
7
|
+
## Create an app with MySQL
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx create-bcp-app my-app
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Choose **MySQL** during interactive setup. The generated project installs `mysql2`, writes the database environment variables and creates `lib/database.ts` as a thin wrapper around `bcp/database`.
|
|
14
|
+
|
|
15
|
+
## Environment
|
|
16
|
+
|
|
17
|
+
```env
|
|
18
|
+
DB_DRIVER=mysql
|
|
19
|
+
DB_HOST=localhost
|
|
20
|
+
DB_PORT=3306
|
|
21
|
+
DB_USER=root
|
|
22
|
+
DB_PASSWORD=
|
|
23
|
+
DB_NAME=bcp_app
|
|
24
|
+
DB_CONNECTION_LIMIT=10
|
|
25
|
+
DB_WAIT_FOR_CONNECTIONS=1
|
|
26
|
+
DB_QUEUE_LIMIT=0
|
|
27
|
+
DB_CHARSET=utf8mb4
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Only `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD` and `DB_NAME` are required for the standard generated preset. The remaining values have safe framework defaults.
|
|
31
|
+
|
|
32
|
+
## Query
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import {
|
|
36
|
+
db,
|
|
37
|
+
} from "bcp/database";
|
|
38
|
+
|
|
39
|
+
interface UserRow {
|
|
40
|
+
id: number;
|
|
41
|
+
email: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const users =
|
|
45
|
+
await db.query<UserRow[]>(
|
|
46
|
+
"SELECT id, email FROM users WHERE active = ?",
|
|
47
|
+
[
|
|
48
|
+
1,
|
|
49
|
+
]
|
|
50
|
+
);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Execute
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const result =
|
|
57
|
+
await db.execute(
|
|
58
|
+
"INSERT INTO users (email) VALUES (?)",
|
|
59
|
+
[
|
|
60
|
+
"user@example.com",
|
|
61
|
+
]
|
|
62
|
+
);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use placeholders and parameter arrays instead of concatenating untrusted values into SQL strings.
|
|
66
|
+
|
|
67
|
+
## Transaction
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
await db.transaction(
|
|
71
|
+
async (transaction) => {
|
|
72
|
+
await transaction.execute(
|
|
73
|
+
"UPDATE accounts SET balance = balance - ? WHERE id = ?",
|
|
74
|
+
[
|
|
75
|
+
100,
|
|
76
|
+
1,
|
|
77
|
+
]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
await transaction.execute(
|
|
81
|
+
"UPDATE accounts SET balance = balance + ? WHERE id = ?",
|
|
82
|
+
[
|
|
83
|
+
100,
|
|
84
|
+
2,
|
|
85
|
+
]
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
BCP commits the transaction when the callback resolves and rolls it back when the callback throws.
|
|
92
|
+
|
|
93
|
+
## Custom database instance
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import {
|
|
97
|
+
createDatabase,
|
|
98
|
+
} from "bcp/database";
|
|
99
|
+
|
|
100
|
+
export const reportingDb =
|
|
101
|
+
createDatabase({
|
|
102
|
+
host:
|
|
103
|
+
"reporting-db.internal",
|
|
104
|
+
database:
|
|
105
|
+
"analytics",
|
|
106
|
+
connectionLimit:
|
|
107
|
+
5,
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Explicit options override environment values for that database instance.
|
|
112
|
+
|
|
113
|
+
## Lifecycle
|
|
114
|
+
|
|
115
|
+
Connections are lazy. Importing `bcp/database` does not open a MySQL connection. The pool is created on the first `query`, `execute` or `transaction` call.
|
|
116
|
+
|
|
117
|
+
For custom shutdown handling, close the pool with:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
await db.close();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Server-only boundary
|
|
124
|
+
|
|
125
|
+
`bcp/database` is a server-only package export. Importing it into a browser bundle is blocked by the framework's browser export boundary.
|
|
126
|
+
|
|
127
|
+
The MySQL adapter loads `mysql2/promise` only when a connection is first needed. Projects that do not use MySQL do not need to install the driver.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chidchanun/bcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "BCP Framework - a React full-stack framework with file-based routing, SSR, APIs, middleware, islands, caching and standalone production builds.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,6 +43,11 @@
|
|
|
43
43
|
"types": "./packages/client/src/config.ts",
|
|
44
44
|
"default": "./packages/client/src/config.ts"
|
|
45
45
|
},
|
|
46
|
+
"./database": {
|
|
47
|
+
"types": "./packages/client/src/database.ts",
|
|
48
|
+
"browser": "./packages/client/src/server-only.browser.mjs",
|
|
49
|
+
"default": "./packages/client/src/database.ts"
|
|
50
|
+
},
|
|
46
51
|
"./server": {
|
|
47
52
|
"types": "./packages/client/src/server.ts",
|
|
48
53
|
"browser": "./packages/client/src/server-only.browser.mjs",
|
package/packages/cli/src/args.ts
CHANGED
|
@@ -7,9 +7,17 @@ export type CliCommand =
|
|
|
7
7
|
| "start"
|
|
8
8
|
| "routes"
|
|
9
9
|
| "update"
|
|
10
|
+
| "db"
|
|
10
11
|
| "help"
|
|
11
12
|
| "version";
|
|
12
13
|
|
|
14
|
+
export type DatabaseCliAction =
|
|
15
|
+
| "migrate"
|
|
16
|
+
| "status"
|
|
17
|
+
| "rollback"
|
|
18
|
+
| "create"
|
|
19
|
+
| "help";
|
|
20
|
+
|
|
13
21
|
export interface CliOptions {
|
|
14
22
|
command: CliCommand;
|
|
15
23
|
|
|
@@ -24,6 +32,10 @@ export interface CliOptions {
|
|
|
24
32
|
updateCheck?: boolean;
|
|
25
33
|
|
|
26
34
|
updateDryRun?: boolean;
|
|
35
|
+
|
|
36
|
+
dbAction?: DatabaseCliAction;
|
|
37
|
+
|
|
38
|
+
dbMigrationName?: string;
|
|
27
39
|
}
|
|
28
40
|
|
|
29
41
|
export function parseCliArgs(
|
|
@@ -49,6 +61,12 @@ export function parseCliArgs(
|
|
|
49
61
|
let updateDryRun =
|
|
50
62
|
false;
|
|
51
63
|
|
|
64
|
+
let dbAction:
|
|
65
|
+
DatabaseCliAction | undefined;
|
|
66
|
+
|
|
67
|
+
let dbMigrationName:
|
|
68
|
+
string | undefined;
|
|
69
|
+
|
|
52
70
|
let commandSet = false;
|
|
53
71
|
|
|
54
72
|
for (
|
|
@@ -62,7 +80,14 @@ export function parseCliArgs(
|
|
|
62
80
|
argument === "-h" ||
|
|
63
81
|
argument === "--help"
|
|
64
82
|
) {
|
|
65
|
-
|
|
83
|
+
if (
|
|
84
|
+
commandSet &&
|
|
85
|
+
command === "db"
|
|
86
|
+
) {
|
|
87
|
+
dbAction = "help";
|
|
88
|
+
} else {
|
|
89
|
+
command = "help";
|
|
90
|
+
}
|
|
66
91
|
continue;
|
|
67
92
|
}
|
|
68
93
|
|
|
@@ -221,6 +246,44 @@ export function parseCliArgs(
|
|
|
221
246
|
continue;
|
|
222
247
|
}
|
|
223
248
|
|
|
249
|
+
if (
|
|
250
|
+
commandSet &&
|
|
251
|
+
command === "db"
|
|
252
|
+
) {
|
|
253
|
+
if (
|
|
254
|
+
dbAction === undefined
|
|
255
|
+
) {
|
|
256
|
+
if (
|
|
257
|
+
argument === "migrate" ||
|
|
258
|
+
argument === "status" ||
|
|
259
|
+
argument === "rollback" ||
|
|
260
|
+
argument === "create" ||
|
|
261
|
+
argument === "help"
|
|
262
|
+
) {
|
|
263
|
+
dbAction =
|
|
264
|
+
argument;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Unknown database command: ${argument}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (
|
|
274
|
+
dbAction === "create" &&
|
|
275
|
+
dbMigrationName === undefined
|
|
276
|
+
) {
|
|
277
|
+
dbMigrationName =
|
|
278
|
+
argument;
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
throw new Error(
|
|
283
|
+
`Unexpected argument: ${argument}`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
224
287
|
if (commandSet) {
|
|
225
288
|
throw new Error(
|
|
226
289
|
`Unexpected argument: ${argument}`
|
|
@@ -233,6 +296,7 @@ export function parseCliArgs(
|
|
|
233
296
|
argument === "start" ||
|
|
234
297
|
argument === "routes" ||
|
|
235
298
|
argument === "update" ||
|
|
299
|
+
argument === "db" ||
|
|
236
300
|
argument === "help" ||
|
|
237
301
|
argument === "version"
|
|
238
302
|
) {
|
|
@@ -266,6 +330,12 @@ export function parseCliArgs(
|
|
|
266
330
|
updateTarget,
|
|
267
331
|
updateCheck,
|
|
268
332
|
updateDryRun,
|
|
333
|
+
...(command === "db"
|
|
334
|
+
? {
|
|
335
|
+
dbAction,
|
|
336
|
+
dbMigrationName,
|
|
337
|
+
}
|
|
338
|
+
: {}),
|
|
269
339
|
};
|
|
270
340
|
}
|
|
271
341
|
|