@chidchanun/bcp 0.2.1 → 0.2.3

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.md CHANGED
@@ -1,18 +1,24 @@
1
1
  # Database
2
2
 
3
- BCP `0.1.14` adds the first framework-native database API through the server-only `bcp/database` entrypoint.
3
+ BCP `0.2.3` completes Database Platform v2 behind the server-only `bcp/database` entrypoint.
4
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.
5
+ The public database facade is provider-neutral and currently includes built-in adapters for:
6
6
 
7
- ## Create an app with MySQL
7
+ - MySQL,
8
+ - PostgreSQL,
9
+ - SQLite.
10
+
11
+ Applications can keep the same `db.query()`, `db.execute()`, `db.transaction()` and lifecycle API while selecting the provider through environment or explicit options.
12
+
13
+ ## MySQL
14
+
15
+ Install the optional driver:
8
16
 
9
17
  ```bash
10
- npx create-bcp-app my-app
18
+ npm install mysql2
11
19
  ```
12
20
 
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
21
+ Environment:
16
22
 
17
23
  ```env
18
24
  DB_DRIVER=mysql
@@ -22,20 +28,105 @@ DB_USER=root
22
28
  DB_PASSWORD=
23
29
  DB_NAME=bcp_app
24
30
  DB_CONNECTION_LIMIT=10
25
- DB_WAIT_FOR_CONNECTIONS=1
26
- DB_QUEUE_LIMIT=0
27
- DB_CHARSET=utf8mb4
28
31
  ```
29
32
 
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.
33
+ MySQL remains the default when no provider can be inferred.
31
34
 
32
- ## Query
35
+ Parameterized query:
33
36
 
34
37
  ```ts
35
38
  import {
36
39
  db,
37
40
  } from "bcp/database";
38
41
 
42
+ const users =
43
+ await db.query(
44
+ "SELECT id, email FROM users WHERE active = ?",
45
+ [
46
+ 1,
47
+ ]
48
+ );
49
+ ```
50
+
51
+ ## PostgreSQL
52
+
53
+ Install the optional driver:
54
+
55
+ ```bash
56
+ npm install pg
57
+ ```
58
+
59
+ BCP can infer PostgreSQL from a connection URL:
60
+
61
+ ```env
62
+ DATABASE_URL=postgresql://postgres:password@localhost:5432/bcp_app
63
+ ```
64
+
65
+ or use explicit fields:
66
+
67
+ ```env
68
+ DB_DRIVER=postgresql
69
+ DB_HOST=localhost
70
+ DB_PORT=5432
71
+ DB_USER=postgres
72
+ DB_PASSWORD=password
73
+ DB_NAME=bcp_app
74
+ ```
75
+
76
+ PostgreSQL parameters use `$1`, `$2`, and later placeholders:
77
+
78
+ ```ts
79
+ const users =
80
+ await db.query(
81
+ "SELECT id, email FROM users WHERE active = $1",
82
+ [
83
+ true,
84
+ ]
85
+ );
86
+ ```
87
+
88
+ `postgres` and `pg` are accepted as environment aliases for the PostgreSQL driver and are normalized to `postgresql`.
89
+
90
+ ## SQLite
91
+
92
+ Install the optional driver:
93
+
94
+ ```bash
95
+ npm install better-sqlite3
96
+ ```
97
+
98
+ Recommended environment:
99
+
100
+ ```env
101
+ DB_DRIVER=sqlite
102
+ DATABASE_URL=./data/bcp.sqlite
103
+ ```
104
+
105
+ BCP also recognizes `:memory:`, `sqlite:` / `file:` URLs and common `.sqlite`, `.sqlite3`, and `.db` file names.
106
+
107
+ Example:
108
+
109
+ ```ts
110
+ import {
111
+ createDatabase,
112
+ } from "bcp/database";
113
+
114
+ const database =
115
+ createDatabase({
116
+ driver:
117
+ "sqlite",
118
+ database:
119
+ "./data/app.sqlite",
120
+ });
121
+ ```
122
+
123
+ SQLite operations are serialized by the built-in adapter so async transaction callbacks cannot interleave unrelated queries on the same native connection.
124
+
125
+ ## Query and execute
126
+
127
+ `query<T>()` is intended for row-returning statements:
128
+
129
+ ```ts
39
130
  interface UserRow {
40
131
  id: number;
41
132
  email: string;
@@ -43,28 +134,27 @@ interface UserRow {
43
134
 
44
135
  const users =
45
136
  await db.query<UserRow[]>(
46
- "SELECT id, email FROM users WHERE active = ?",
47
- [
48
- 1,
49
- ]
137
+ "SELECT id, email FROM users"
50
138
  );
51
139
  ```
52
140
 
53
- ## Execute
141
+ `execute<T>()` is intended for mutations and DDL:
54
142
 
55
143
  ```ts
56
144
  const result =
57
145
  await db.execute(
58
- "INSERT INTO users (email) VALUES (?)",
146
+ "DELETE FROM sessions WHERE expired_at < ?",
59
147
  [
60
- "user@example.com",
148
+ new Date(),
61
149
  ]
62
150
  );
63
151
  ```
64
152
 
65
- Use placeholders and parameter arrays instead of concatenating untrusted values into SQL strings.
153
+ Use the placeholder syntax of the active database provider. BCP does not rewrite application SQL between dialects.
66
154
 
67
- ## Transaction
155
+ ## Transactions
156
+
157
+ The transaction callback receives a provider-scoped `TransactionDatabase`:
68
158
 
69
159
  ```ts
70
160
  await db.transaction(
@@ -88,7 +178,33 @@ await db.transaction(
88
178
  );
89
179
  ```
90
180
 
91
- BCP commits the transaction when the callback resolves and rolls it back when the callback throws.
181
+ The example above uses MySQL/SQLite placeholders. PostgreSQL migrations and application statements should use `$1`, `$2`, and so on.
182
+
183
+ BCP commits when the callback resolves and rolls back when it throws.
184
+
185
+ ## Connection lifecycle
186
+
187
+ Database instances remain lazy by default. Importing `bcp/database` does not connect to a provider.
188
+
189
+ BCP `0.2.3` adds explicit lifecycle methods:
190
+
191
+ ```ts
192
+ await db.connect();
193
+
194
+ // application work
195
+
196
+ await db.disconnect();
197
+ ```
198
+
199
+ `close()` remains available as the backward-compatible shutdown method and is equivalent to `disconnect()`:
200
+
201
+ ```ts
202
+ await db.close();
203
+ ```
204
+
205
+ After disconnecting, the next operation or `connect()` call creates a fresh adapter connection/pool.
206
+
207
+ If adapter initialization fails, BCP resets the pending lifecycle state so a later attempt can retry cleanly. Calling `close()` after a failed initialization is safe.
92
208
 
93
209
  ## Custom database instance
94
210
 
@@ -99,6 +215,8 @@ import {
99
215
 
100
216
  export const reportingDb =
101
217
  createDatabase({
218
+ driver:
219
+ "postgresql",
102
220
  host:
103
221
  "reporting-db.internal",
104
222
  database:
@@ -110,18 +228,69 @@ export const reportingDb =
110
228
 
111
229
  Explicit options override environment values for that database instance.
112
230
 
113
- ## Lifecycle
231
+ ## Database adapter contract
114
232
 
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.
233
+ Provider implementations use the shared adapter contract:
234
+
235
+ ```ts
236
+ import type {
237
+ DatabaseAdapter,
238
+ TransactionDatabase,
239
+ } from "bcp/database";
116
240
 
117
- For custom shutdown handling, close the pool with:
241
+ const adapter: DatabaseAdapter = {
242
+ driver: "custom",
243
+
244
+ async connect() {},
245
+
246
+ async query<T>(sql, parameters) {
247
+ throw new Error("Not implemented");
248
+ },
249
+
250
+ async execute<T>(sql, parameters) {
251
+ throw new Error("Not implemented");
252
+ },
253
+
254
+ async transaction<T>(
255
+ callback: (
256
+ database: TransactionDatabase
257
+ ) => Promise<T>
258
+ ) {
259
+ throw new Error("Not implemented");
260
+ },
261
+
262
+ async disconnect() {},
263
+ };
264
+ ```
265
+
266
+ Inject an adapter directly or through a lazy factory:
118
267
 
119
268
  ```ts
120
- await db.close();
269
+ export const customDb =
270
+ createDatabase({
271
+ adapter: () => adapter,
272
+ });
273
+ ```
274
+
275
+ The contract lets future providers integrate without changing application-facing database calls.
276
+
277
+ ## Migrations
278
+
279
+ Framework migration bookkeeping supports MySQL, PostgreSQL and SQLite in `0.2.3`.
280
+
281
+ ```bash
282
+ bcp db create create_users
283
+ bcp db migrate
284
+ bcp db status
285
+ bcp db rollback
121
286
  ```
122
287
 
288
+ BCP selects the internal migration-table dialect from the same active database environment. Migration files themselves remain normal provider SQL and are not automatically translated between SQL dialects.
289
+
290
+ Read more: [Database Migrations](database-migrations.md)
291
+
123
292
  ## Server-only boundary
124
293
 
125
- `bcp/database` is a server-only package export. Importing it into a browser bundle is blocked by the framework's browser export boundary.
294
+ `bcp/database` is a server-only package export. Importing it into a browser bundle is blocked by the framework browser export boundary.
126
295
 
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.
296
+ Provider drivers are optional and loaded lazily. Projects only need to install the driver they actually use.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "framework": "bcp",
4
- "versionTarget": "0.2.1",
4
+ "versionTarget": "0.2.3",
5
5
  "releaseState": "unreleased",
6
6
  "sections": [
7
7
  {
@@ -11,6 +11,7 @@
11
11
  "pages": [
12
12
  { "route": "/docs/getting-started", "source": "getting-started.md", "title": "Getting Started" },
13
13
  { "route": "/docs/configuration", "source": "configuration.md", "title": "Configuration" },
14
+ { "route": "/docs/environment-validation", "source": "environment-validation.md", "title": "Environment Validation" },
14
15
  { "route": "/docs/application-modules", "source": "application-modules.md", "title": "Application Modules" },
15
16
  { "route": "/docs/project-metadata", "source": "project-metadata.md", "title": "Project Metadata" },
16
17
  { "route": "/docs/deployment", "source": "deployment.md", "title": "Deployment" },
@@ -44,7 +45,7 @@
44
45
  {
45
46
  "id": "database",
46
47
  "title": "Database",
47
- "description": "Database primitives, transactions and migration workflows.",
48
+ "description": "Provider-neutral MySQL, PostgreSQL and SQLite primitives, lifecycle and migrations.",
48
49
  "pages": [
49
50
  { "route": "/docs/database", "source": "database.md", "title": "Database" },
50
51
  { "route": "/docs/database-migrations", "source": "database-migrations.md", "title": "Database Migrations" }
@@ -103,7 +104,9 @@
103
104
  }
104
105
  ],
105
106
  "releases": [
106
- { "route": "/releases/0.2.1", "source": "releases/0.2.1.md", "version": "0.2.1", "state": "unreleased" },
107
+ { "route": "/releases/0.2.3", "source": "releases/0.2.3.md", "version": "0.2.3", "state": "unreleased" },
108
+ { "route": "/releases/0.2.2", "source": "releases/0.2.2.md", "version": "0.2.2" },
109
+ { "route": "/releases/0.2.1", "source": "releases/0.2.1.md", "version": "0.2.1" },
107
110
  { "route": "/releases/0.2.0", "source": "releases/0.2.0.md", "version": "0.2.0" },
108
111
  { "route": "/releases/0.1.29", "source": "releases/0.1.29.md", "version": "0.1.29" },
109
112
  { "route": "/releases/0.1.28", "source": "releases/0.1.28.md", "version": "0.1.28" },
@@ -0,0 +1,224 @@
1
+ # Environment Validation
2
+
3
+ BCP Framework `0.2.2` adds an optional typed environment schema for application-specific environment variables.
4
+
5
+ The schema is separate from `bcp.config.*` and uses the project convention:
6
+
7
+ ```text
8
+ bcp.environment.ts
9
+ bcp.environment.mts
10
+ bcp.environment.js
11
+ bcp.environment.mjs
12
+ ```
13
+
14
+ Keep only one environment schema file in an application.
15
+
16
+ ## Define a schema
17
+
18
+ ```ts
19
+ import {
20
+ defineEnvironment,
21
+ } from "bcp/config";
22
+
23
+ export default defineEnvironment({
24
+ DB_HOST: {
25
+ type: "string",
26
+ required: true,
27
+ },
28
+
29
+ DB_PORT: {
30
+ type: "number",
31
+ default: 3306,
32
+ min: 1,
33
+ max: 65535,
34
+ },
35
+
36
+ SESSION_SECRET: {
37
+ type: "string",
38
+ required: true,
39
+ secret: true,
40
+ minLength: 32,
41
+ },
42
+
43
+ FEATURE_ENABLED: {
44
+ type: "boolean",
45
+ default: false,
46
+ },
47
+
48
+ BCP_PUBLIC_API_URL: {
49
+ type: "url",
50
+ required: true,
51
+ },
52
+ });
53
+ ```
54
+
55
+ Supported types:
56
+
57
+ ```text
58
+ string
59
+ number
60
+ boolean
61
+ url
62
+ ```
63
+
64
+ Supported rule fields:
65
+
66
+ ```text
67
+ type
68
+ required
69
+ secret
70
+ minLength
71
+ maxLength
72
+ min
73
+ max
74
+ default
75
+ description
76
+ ```
77
+
78
+ `minLength` / `maxLength` are intended for string and URL values. `min` / `max` are intended for numbers.
79
+
80
+ ## Validate from the CLI
81
+
82
+ ```bash
83
+ bcp config check
84
+ ```
85
+
86
+ On Windows where another `bcp.exe` may exist:
87
+
88
+ ```powershell
89
+ npm exec -- bcp-framework config check
90
+ ```
91
+
92
+ JSON output:
93
+
94
+ ```powershell
95
+ npm exec -- bcp-framework config check --json
96
+ ```
97
+
98
+ The command validates:
99
+
100
+ - `bcp.config.*`,
101
+ - `bcp.environment.*`,
102
+ - loaded `.env` files,
103
+ - configured BCP runtime values,
104
+ - application environment-schema values,
105
+ - selected production safety diagnostics.
106
+
107
+ Development is the default mode.
108
+
109
+ To inspect production diagnostics from a shell where `NODE_ENV` can be set:
110
+
111
+ ```bash
112
+ NODE_ENV=production bcp config check
113
+ ```
114
+
115
+ PowerShell example:
116
+
117
+ ```powershell
118
+ $env:NODE_ENV = "production"
119
+ npm exec -- bcp-framework config check
120
+ Remove-Item Env:NODE_ENV
121
+ ```
122
+
123
+ ## Startup validation
124
+
125
+ When a project contains `bcp.environment.*`, `bcp dev` and `bcp build` validate the schema before the application starts or a production build is produced.
126
+
127
+ A missing or invalid required environment variable stops startup/build with a configuration error.
128
+
129
+ Example:
130
+
131
+ ```text
132
+ BCP Configuration Error:
133
+ - SESSION_SECRET must contain at least 32 character(s).
134
+ ```
135
+
136
+ Changing `bcp.environment.*` while the development supervisor is running automatically restarts the dev worker, the same way changes to `.env*` or `bcp.config.*` do.
137
+
138
+ ## Public variables and secrets
139
+
140
+ Variables beginning with:
141
+
142
+ ```text
143
+ BCP_PUBLIC_
144
+ ```
145
+
146
+ can be embedded into browser bundles.
147
+
148
+ Do not use that prefix for credentials, session secrets, access keys or private service tokens.
149
+
150
+ BCP treats this as an error:
151
+
152
+ ```ts
153
+ export default defineEnvironment({
154
+ BCP_PUBLIC_TOKEN: {
155
+ type: "string",
156
+ required: true,
157
+ secret: true,
158
+ },
159
+ });
160
+ ```
161
+
162
+ The `secret: true` flag is metadata used by validation/tooling. It does not encrypt the environment value. Secrets must still be stored in an appropriate environment/secret manager and kept out of public client variables.
163
+
164
+ ## Programmatic validation
165
+
166
+ The same primitives are public through `bcp/config`:
167
+
168
+ ```ts
169
+ import {
170
+ defineEnvironment,
171
+ validateEnvironment,
172
+ } from "bcp/config";
173
+
174
+ const schema = defineEnvironment({
175
+ API_URL: {
176
+ type: "url",
177
+ required: true,
178
+ },
179
+ });
180
+
181
+ const result = validateEnvironment(
182
+ schema,
183
+ process.env
184
+ );
185
+
186
+ if (!result.ok) {
187
+ console.error(
188
+ result.issues
189
+ );
190
+ }
191
+ ```
192
+
193
+ The parsed `result.values` contains only keys declared by the schema. BCP configuration diagnostics do not print raw secret values.
194
+
195
+ ## Production diagnostics
196
+
197
+ Production configuration checks can emit warnings for potentially risky choices such as:
198
+
199
+ - production source maps enabled,
200
+ - `security.poweredByHeader` enabled,
201
+ - Content-Security-Policy disabled,
202
+ - trusted proxy mode enabled.
203
+
204
+ Warnings do not block a build. Environment-schema errors do.
205
+
206
+ Trusted proxy warnings are informational safety reminders. Enable `BCP_TRUST_PROXY` only when untrusted clients cannot bypass the trusted reverse proxy/load balancer.
207
+
208
+ ## Existing applications
209
+
210
+ Environment schemas are optional.
211
+
212
+ Projects without `bcp.environment.*` continue to use the existing `.env` loading and BCP configuration behavior. The `0.2.2` schema system is additive and does not change the established configuration precedence:
213
+
214
+ ```text
215
+ CLI override
216
+
217
+ BCP_* environment
218
+
219
+ bcp.config.*
220
+
221
+ framework defaults
222
+ ```
223
+
224
+ Application-specific variables are validated against `bcp.environment.*` after environment files are loaded.
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "framework": "bcp",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
5
5
  "releaseState": "unreleased",
6
- "baseline": "framework-platform",
6
+ "baseline": "database-platform-v2",
7
7
  "runtime": {
8
8
  "node": ">=24.11.0",
9
9
  "react": "19",
@@ -30,6 +30,7 @@
30
30
  "update",
31
31
  "db",
32
32
  "generate",
33
+ "config",
33
34
  "doctor",
34
35
  "inspect",
35
36
  "help",
@@ -45,6 +46,11 @@
45
46
  "middlewareV2": true,
46
47
  "jwtCookieSessions": true,
47
48
  "databaseMigrations": true,
49
+ "databaseAdapterContract": true,
50
+ "databasePostgresql": true,
51
+ "databaseSqlite": true,
52
+ "databaseLifecycleV2": true,
53
+ "databaseMigrationProviderConsistency": true,
48
54
  "validation": true,
49
55
  "structuredErrors": true,
50
56
  "logging": true,
@@ -55,8 +61,16 @@
55
61
  "projectGenerators": true,
56
62
  "projectDiagnostics": true,
57
63
  "documentationPlatform": true,
58
- "apiManifest": true
64
+ "apiManifest": true,
65
+ "typedEnvironmentSchema": true,
66
+ "configurationDiagnostics": true,
67
+ "configCheckCli": true
59
68
  },
69
+ "databaseProviders": [
70
+ "mysql",
71
+ "postgresql",
72
+ "sqlite"
73
+ ],
60
74
  "storageProviders": [
61
75
  "local",
62
76
  "amazon-s3",
@@ -64,7 +78,7 @@
64
78
  "s3-compatible"
65
79
  ],
66
80
  "compatibility": {
67
- "previousBaseline": "0.2.0",
81
+ "previousBaseline": "0.2.2",
68
82
  "intentionalBreakingChangesFromPreviousBaseline": false,
69
83
  "migrationGuide": "migration-0.2.md"
70
84
  },
@@ -75,7 +89,8 @@
75
89
  "platformContract": "platform-contract.md",
76
90
  "documentationPlatform": "documentation-platform.md",
77
91
  "apiReference": "api-reference.md",
92
+ "environmentValidation": "environment-validation.md",
78
93
  "migrationGuide": "migration-0.2.md",
79
- "releaseNotes": "releases/0.2.1.md"
94
+ "releaseNotes": "releases/0.2.3.md"
80
95
  }
81
96
  }