@chidchanun/bcp 0.1.24 → 0.1.25
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 +617 -138
- package/docs/README.md +333 -79
- package/docs/releases/0.1.25.md +132 -0
- package/docs/storage.md +285 -0
- package/package.json +1 -1
- package/packages/client/src/server.ts +24 -0
- package/packages/server/src/file-delivery.ts +629 -0
- package/packages/server/src/storage.ts +829 -0
package/README.md
CHANGED
|
@@ -1,11 +1,80 @@
|
|
|
1
1
|
# BCP Framework
|
|
2
2
|
|
|
3
|
-
BCP Framework is a React full-stack framework
|
|
3
|
+
BCP Framework is a React full-stack framework focused on file-based routing, server rendering, server-side data loading, guarded application flows, API routes, authentication, database access, validation, logging, file uploads, storage adapters and standalone production deployment.
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> **Development target:** `0.1.25`
|
|
6
|
+
>
|
|
7
|
+
> BCP is still pre-1.0. Features documented for the current development target should not be presented as published npm behavior until the release candidate has passed and the matching version has been published.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
BCP provides a single application model for React pages and server code:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
Browser
|
|
15
|
+
↓
|
|
16
|
+
BCP middleware / security
|
|
17
|
+
↓
|
|
18
|
+
Route guard
|
|
19
|
+
↓
|
|
20
|
+
Loader / action / API route
|
|
21
|
+
↓
|
|
22
|
+
React SSR
|
|
23
|
+
↓
|
|
24
|
+
Hydration / SPA navigation
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The framework is designed so application code can stay close to the route that owns it:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
app/
|
|
31
|
+
├─ layout.tsx
|
|
32
|
+
├─ page.tsx
|
|
33
|
+
├─ dashboard/
|
|
34
|
+
│ ├─ guard.ts
|
|
35
|
+
│ └─ users/
|
|
36
|
+
│ └─ [id]/
|
|
37
|
+
│ ├─ loader.ts
|
|
38
|
+
│ ├─ actions.ts
|
|
39
|
+
│ └─ page.tsx
|
|
40
|
+
└─ api/
|
|
41
|
+
└─ upload/
|
|
42
|
+
└─ route.ts
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Current capabilities
|
|
46
|
+
|
|
47
|
+
| Area | Capability |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| Application | React SSR, hydration, layouts, metadata, SPA navigation |
|
|
50
|
+
| Routing | Static, dynamic, catch-all, optional catch-all and route groups |
|
|
51
|
+
| Server data | `loader.ts`, request-scoped server APIs |
|
|
52
|
+
| Mutations | Route-owned `actions.ts` and `<Form>` |
|
|
53
|
+
| Authorization | `guard.ts`, `requireAuth()`, `requireRole()` |
|
|
54
|
+
| Authentication | JWT cookie sessions and auth helpers |
|
|
55
|
+
| Middleware | Middleware System v2 with onion execution |
|
|
56
|
+
| Validation | Typed validation helpers and structured validation errors |
|
|
57
|
+
| Error handling | HTTP error helpers and consistent error responses |
|
|
58
|
+
| Database | MySQL pool/query helpers, transactions and migrations |
|
|
59
|
+
| Logging | Structured logger, request logger and request IDs |
|
|
60
|
+
| Uploads | Multipart parsing, file validation and safe local persistence |
|
|
61
|
+
| Storage | `StorageAdapter`, local storage adapter and file delivery |
|
|
62
|
+
| Caching | Response cache and revalidation primitives |
|
|
63
|
+
| Developer tools | `doctor`, `inspect`, updater and route inspection |
|
|
64
|
+
| Production | Standalone server build with production middleware pipeline |
|
|
65
|
+
|
|
66
|
+
## Requirements
|
|
67
|
+
|
|
68
|
+
- Node.js `24.11` or newer
|
|
69
|
+
- React `19`
|
|
70
|
+
- npm
|
|
71
|
+
|
|
72
|
+
Database features currently target MySQL.
|
|
6
73
|
|
|
7
74
|
## Quick start
|
|
8
75
|
|
|
76
|
+
Create a new application:
|
|
77
|
+
|
|
9
78
|
```bash
|
|
10
79
|
npx create-bcp-app@latest my-app
|
|
11
80
|
cd my-app
|
|
@@ -18,65 +87,93 @@ Default development URL:
|
|
|
18
87
|
http://localhost:3000
|
|
19
88
|
```
|
|
20
89
|
|
|
21
|
-
|
|
90
|
+
A generated project normally exposes scripts such as:
|
|
22
91
|
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
bcp
|
|
27
|
-
bcp
|
|
28
|
-
bcp
|
|
29
|
-
bcp
|
|
30
|
-
bcp
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"scripts": {
|
|
95
|
+
"dev": "bcp dev",
|
|
96
|
+
"build": "bcp build",
|
|
97
|
+
"start": "bcp start",
|
|
98
|
+
"routes": "bcp routes",
|
|
99
|
+
"update": "bcp update"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
34
103
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
104
|
+
## Packages
|
|
105
|
+
|
|
106
|
+
The public framework package is published as:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
@chidchanun/bcp
|
|
39
110
|
```
|
|
40
111
|
|
|
41
|
-
|
|
112
|
+
Applications normally consume it through the dependency key:
|
|
42
113
|
|
|
43
|
-
```
|
|
44
|
-
bcp
|
|
45
|
-
bcp-framework inspect
|
|
46
|
-
bcp-framework dev
|
|
47
|
-
bcp-framework build
|
|
114
|
+
```text
|
|
115
|
+
bcp
|
|
48
116
|
```
|
|
49
117
|
|
|
50
|
-
|
|
118
|
+
This keeps imports concise:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import {
|
|
122
|
+
Form,
|
|
123
|
+
Link,
|
|
124
|
+
useLoaderData,
|
|
125
|
+
} from "bcp";
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Server-only APIs use dedicated entrypoints such as:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import {
|
|
132
|
+
cookies,
|
|
133
|
+
logger,
|
|
134
|
+
requestId,
|
|
135
|
+
} from "bcp/server";
|
|
136
|
+
```
|
|
51
137
|
|
|
52
138
|
## Project structure
|
|
53
139
|
|
|
140
|
+
A typical application can grow into this layout:
|
|
141
|
+
|
|
54
142
|
```text
|
|
55
143
|
app/
|
|
56
144
|
├─ layout.tsx
|
|
57
145
|
├─ page.tsx
|
|
146
|
+
├─ login/
|
|
147
|
+
│ └─ page.tsx
|
|
58
148
|
├─ dashboard/
|
|
59
149
|
│ ├─ guard.ts
|
|
150
|
+
│ ├─ page.tsx
|
|
60
151
|
│ └─ users/
|
|
61
152
|
│ └─ [id]/
|
|
62
153
|
│ ├─ loader.ts
|
|
63
154
|
│ ├─ actions.ts
|
|
64
155
|
│ └─ page.tsx
|
|
65
156
|
└─ api/
|
|
66
|
-
├─
|
|
67
|
-
│ └─
|
|
157
|
+
├─ auth/
|
|
158
|
+
│ └─ login/
|
|
159
|
+
│ └─ route.ts
|
|
68
160
|
└─ upload/
|
|
69
161
|
└─ route.ts
|
|
70
162
|
|
|
71
163
|
lib/
|
|
72
164
|
public/
|
|
165
|
+
migrations/
|
|
73
166
|
bcp.config.ts
|
|
74
167
|
package.json
|
|
75
168
|
tsconfig.json
|
|
76
169
|
```
|
|
77
170
|
|
|
171
|
+
BCP keeps page rendering, route authorization, server data and route mutations close together without requiring one large application router configuration file.
|
|
172
|
+
|
|
78
173
|
## Routing
|
|
79
174
|
|
|
175
|
+
Page routes are discovered from `app/**/page.tsx`.
|
|
176
|
+
|
|
80
177
|
```text
|
|
81
178
|
app/page.tsx /
|
|
82
179
|
app/about/page.tsx /about
|
|
@@ -88,9 +185,26 @@ app/(admin)/settings/page.tsx /settings
|
|
|
88
185
|
|
|
89
186
|
Static routes have priority over dynamic and catch-all routes.
|
|
90
187
|
|
|
91
|
-
|
|
188
|
+
API routes use `route.ts`:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
app/api/users/route.ts /api/users
|
|
192
|
+
app/api/users/[id]/route.ts /api/users/:id
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Read more: [Routing](docs/routing.md)
|
|
92
196
|
|
|
93
|
-
|
|
197
|
+
## Layouts and metadata
|
|
198
|
+
|
|
199
|
+
Routes can inherit layouts from parent directories. The framework resolves the layout chain while rendering both development and standalone production requests.
|
|
200
|
+
|
|
201
|
+
Document metadata is route-aware and can be generated alongside the page tree.
|
|
202
|
+
|
|
203
|
+
Read more: [Routing](docs/routing.md)
|
|
204
|
+
|
|
205
|
+
## Server data loaders
|
|
206
|
+
|
|
207
|
+
Place `loader.ts` next to a page when the route needs server-side data.
|
|
94
208
|
|
|
95
209
|
```ts
|
|
96
210
|
// app/users/[id]/loader.ts
|
|
@@ -104,7 +218,7 @@ export async function loader({
|
|
|
104
218
|
}
|
|
105
219
|
```
|
|
106
220
|
|
|
107
|
-
|
|
221
|
+
Consume the serializable result in a client page:
|
|
108
222
|
|
|
109
223
|
```tsx
|
|
110
224
|
"use client";
|
|
@@ -119,14 +233,50 @@ export default function UserPage() {
|
|
|
119
233
|
id: string;
|
|
120
234
|
}>();
|
|
121
235
|
|
|
122
|
-
return
|
|
236
|
+
return (
|
|
237
|
+
<main>
|
|
238
|
+
User {data.id}
|
|
239
|
+
</main>
|
|
240
|
+
);
|
|
123
241
|
}
|
|
124
242
|
```
|
|
125
243
|
|
|
126
|
-
|
|
244
|
+
Read more: [Server Data Loaders](docs/server-data-loaders.md)
|
|
245
|
+
|
|
246
|
+
## Route guards
|
|
247
|
+
|
|
248
|
+
A route tree can define `guard.ts` to authorize access before the protected route is rendered.
|
|
249
|
+
|
|
250
|
+
Authentication-aware guards are available through `bcp/auth`:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
import {
|
|
254
|
+
requireRole,
|
|
255
|
+
} from "bcp/auth";
|
|
256
|
+
|
|
257
|
+
export const guard =
|
|
258
|
+
requireRole("admin");
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The standalone production pipeline preserves the same active request context used by authentication and server request APIs.
|
|
262
|
+
|
|
263
|
+
Read more:
|
|
264
|
+
|
|
265
|
+
- [Route Guards](docs/route-guards.md)
|
|
266
|
+
- [Auth Route Guards](docs/auth-route-guards.md)
|
|
267
|
+
|
|
268
|
+
## Form actions
|
|
269
|
+
|
|
270
|
+
Route-owned mutations live in `actions.ts` and can be invoked through the public `<Form>` API.
|
|
271
|
+
|
|
272
|
+
This supports both progressive form submission and SPA action transport while keeping mutation logic server-only.
|
|
273
|
+
|
|
274
|
+
Read more: [Form Actions](docs/form-actions.md)
|
|
127
275
|
|
|
128
276
|
## Server request APIs
|
|
129
277
|
|
|
278
|
+
Request-scoped APIs are exposed through `bcp/server`:
|
|
279
|
+
|
|
130
280
|
```ts
|
|
131
281
|
import {
|
|
132
282
|
bearerToken,
|
|
@@ -139,11 +289,147 @@ import {
|
|
|
139
289
|
} from "bcp/server";
|
|
140
290
|
```
|
|
141
291
|
|
|
142
|
-
`requestId()`
|
|
292
|
+
`requestId()` uses a valid incoming `X-Request-Id` when available or generates a stable UUID for the active request.
|
|
293
|
+
|
|
294
|
+
Read more: [Server Request APIs](docs/server-request-apis.md)
|
|
295
|
+
|
|
296
|
+
## Authentication and sessions
|
|
297
|
+
|
|
298
|
+
High-level authentication helpers are available through:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
import {
|
|
302
|
+
auth,
|
|
303
|
+
requireAuth,
|
|
304
|
+
requireRole,
|
|
305
|
+
} from "bcp/auth";
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Lower-level JWT cookie session primitives are available through `bcp/server`:
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
import {
|
|
312
|
+
createSession,
|
|
313
|
+
createSessionToken,
|
|
314
|
+
destroySession,
|
|
315
|
+
getSession,
|
|
316
|
+
verifySessionToken,
|
|
317
|
+
} from "bcp/server";
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Authentication is intentionally separated from application-specific credential lookup so projects can connect their own user table or identity provider.
|
|
321
|
+
|
|
322
|
+
Read more:
|
|
323
|
+
|
|
324
|
+
- [Authentication](docs/authentication.md)
|
|
325
|
+
- [JWT Cookie Sessions](docs/session-auth.md)
|
|
326
|
+
|
|
327
|
+
## Middleware
|
|
328
|
+
|
|
329
|
+
Middleware System v2 uses onion-style execution:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
export async function middleware(
|
|
333
|
+
request,
|
|
334
|
+
context,
|
|
335
|
+
next
|
|
336
|
+
) {
|
|
337
|
+
const response =
|
|
338
|
+
await next();
|
|
339
|
+
|
|
340
|
+
response.headers.set(
|
|
341
|
+
"x-app",
|
|
342
|
+
"example"
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
return response;
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
This allows middleware to run logic both before and after downstream route execution.
|
|
350
|
+
|
|
351
|
+
Existing middleware v1 behavior remains supported for compatibility.
|
|
352
|
+
|
|
353
|
+
Read more: [Middleware](docs/middleware.md)
|
|
354
|
+
|
|
355
|
+
## Validation
|
|
356
|
+
|
|
357
|
+
BCP includes typed validation primitives:
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import {
|
|
361
|
+
v,
|
|
362
|
+
validateFormData,
|
|
363
|
+
} from "bcp/validation";
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Validation can be shared by API routes and form actions without coupling application schemas to the rendering layer.
|
|
367
|
+
|
|
368
|
+
Read more: [Validation](docs/validation.md)
|
|
369
|
+
|
|
370
|
+
## Error handling
|
|
371
|
+
|
|
372
|
+
Structured HTTP error helpers are exposed through `bcp/error`:
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
import {
|
|
376
|
+
badRequest,
|
|
377
|
+
forbidden,
|
|
378
|
+
notFoundResponse,
|
|
379
|
+
toErrorResponse,
|
|
380
|
+
unauthorized,
|
|
381
|
+
} from "bcp/error";
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
The common error envelope is:
|
|
385
|
+
|
|
386
|
+
```json
|
|
387
|
+
{
|
|
388
|
+
"error": {
|
|
389
|
+
"status": 400,
|
|
390
|
+
"code": "BAD_REQUEST",
|
|
391
|
+
"message": "Invalid request"
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Read more: [Error Handling](docs/error-handling.md)
|
|
397
|
+
|
|
398
|
+
## Database
|
|
399
|
+
|
|
400
|
+
Database helpers are exposed through:
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
import {
|
|
404
|
+
db,
|
|
405
|
+
} from "bcp/database";
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
The database layer provides:
|
|
409
|
+
|
|
410
|
+
- lazy MySQL pool creation,
|
|
411
|
+
- prepared execution,
|
|
412
|
+
- query helpers,
|
|
413
|
+
- transactions,
|
|
414
|
+
- migration status and rollback support.
|
|
143
415
|
|
|
144
|
-
|
|
416
|
+
Migration commands:
|
|
145
417
|
|
|
146
|
-
|
|
418
|
+
```bash
|
|
419
|
+
bcp db create create_users
|
|
420
|
+
bcp db migrate
|
|
421
|
+
bcp db status
|
|
422
|
+
bcp db rollback
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Read more:
|
|
426
|
+
|
|
427
|
+
- [Database](docs/database.md)
|
|
428
|
+
- [Database Migrations](docs/database-migrations.md)
|
|
429
|
+
|
|
430
|
+
## Logging and observability
|
|
431
|
+
|
|
432
|
+
Structured server logging is available through `bcp/server`:
|
|
147
433
|
|
|
148
434
|
```ts
|
|
149
435
|
import {
|
|
@@ -158,7 +444,11 @@ logger.info(
|
|
|
158
444
|
"catalog",
|
|
159
445
|
}
|
|
160
446
|
);
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Request-scoped logging can automatically include request identity:
|
|
161
450
|
|
|
451
|
+
```ts
|
|
162
452
|
export async function loader() {
|
|
163
453
|
const log =
|
|
164
454
|
await requestLogger({
|
|
@@ -176,18 +466,35 @@ export async function loader() {
|
|
|
176
466
|
}
|
|
177
467
|
```
|
|
178
468
|
|
|
179
|
-
|
|
469
|
+
Environment controls:
|
|
180
470
|
|
|
181
471
|
```env
|
|
182
472
|
BCP_LOG_LEVEL=debug
|
|
183
473
|
BCP_LOG_FORMAT=json
|
|
184
474
|
```
|
|
185
475
|
|
|
186
|
-
Supported levels
|
|
476
|
+
Supported levels:
|
|
477
|
+
|
|
478
|
+
```text
|
|
479
|
+
debug
|
|
480
|
+
info
|
|
481
|
+
warn
|
|
482
|
+
error
|
|
483
|
+
silent
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Supported formats:
|
|
487
|
+
|
|
488
|
+
```text
|
|
489
|
+
pretty
|
|
490
|
+
json
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Read more: [Logging and Observability](docs/development-logging.md)
|
|
187
494
|
|
|
188
|
-
## File
|
|
495
|
+
## File upload
|
|
189
496
|
|
|
190
|
-
BCP 0.1.24
|
|
497
|
+
BCP `0.1.24` introduced multipart parsing and file validation:
|
|
191
498
|
|
|
192
499
|
```ts
|
|
193
500
|
import {
|
|
@@ -229,39 +536,31 @@ export async function POST(
|
|
|
229
536
|
}
|
|
230
537
|
);
|
|
231
538
|
|
|
232
|
-
|
|
539
|
+
return Response.json(
|
|
233
540
|
await saveUploadedFile(
|
|
234
541
|
file,
|
|
235
542
|
{
|
|
236
543
|
directory:
|
|
237
544
|
"./uploads",
|
|
238
545
|
}
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
return Response.json({
|
|
242
|
-
fileName:
|
|
243
|
-
saved.fileName,
|
|
244
|
-
size:
|
|
245
|
-
saved.size,
|
|
246
|
-
checksumSha256:
|
|
247
|
-
saved.checksumSha256,
|
|
248
|
-
});
|
|
546
|
+
)
|
|
547
|
+
);
|
|
249
548
|
}
|
|
250
549
|
```
|
|
251
550
|
|
|
252
551
|
Upload helpers provide:
|
|
253
552
|
|
|
254
553
|
- multipart validation,
|
|
255
|
-
- total and per-file size limits,
|
|
256
|
-
- MIME
|
|
257
|
-
- optional
|
|
554
|
+
- total request and per-file size limits,
|
|
555
|
+
- MIME and extension allowlists,
|
|
556
|
+
- required/optional file fields,
|
|
258
557
|
- safe UUID-based storage names,
|
|
259
558
|
- filename sanitization,
|
|
260
559
|
- path traversal protection,
|
|
261
|
-
- no-overwrite-by-default
|
|
560
|
+
- no-overwrite-by-default persistence,
|
|
262
561
|
- SHA-256 checksum metadata.
|
|
263
562
|
|
|
264
|
-
The security gateway
|
|
563
|
+
The security gateway applies `server.bodyLimit` / `BCP_BODY_LIMIT` before application upload parsing. Applications accepting larger files must raise that outer limit explicitly.
|
|
265
564
|
|
|
266
565
|
```ts
|
|
267
566
|
import {
|
|
@@ -276,122 +575,234 @@ export default defineConfig({
|
|
|
276
575
|
});
|
|
277
576
|
```
|
|
278
577
|
|
|
279
|
-
MIME type and extension are metadata checks, not content-signature verification. Security-sensitive applications should additionally
|
|
578
|
+
MIME type and extension validation are metadata checks, not content-signature verification. Security-sensitive applications should additionally verify content and use malware scanning where appropriate.
|
|
280
579
|
|
|
281
|
-
|
|
580
|
+
Read more: [File Upload](docs/file-upload.md)
|
|
282
581
|
|
|
283
|
-
##
|
|
582
|
+
## Storage adapters
|
|
284
583
|
|
|
285
|
-
BCP 0.1.
|
|
584
|
+
BCP `0.1.25` adds the first application-facing storage abstraction.
|
|
286
585
|
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
|
|
586
|
+
```ts
|
|
587
|
+
import {
|
|
588
|
+
createLocalStorage,
|
|
589
|
+
storeUploadedFile,
|
|
590
|
+
} from "bcp/server";
|
|
290
591
|
|
|
291
|
-
|
|
592
|
+
const storage =
|
|
593
|
+
createLocalStorage({
|
|
594
|
+
directory:
|
|
595
|
+
"./uploads",
|
|
596
|
+
});
|
|
292
597
|
|
|
293
|
-
|
|
598
|
+
const stored =
|
|
599
|
+
await storeUploadedFile(
|
|
600
|
+
file,
|
|
601
|
+
{
|
|
602
|
+
storage,
|
|
603
|
+
key:
|
|
604
|
+
"avatars/user-101.webp",
|
|
605
|
+
}
|
|
606
|
+
);
|
|
607
|
+
```
|
|
294
608
|
|
|
295
|
-
|
|
609
|
+
The `StorageAdapter` contract contains:
|
|
296
610
|
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
|
|
611
|
+
```text
|
|
612
|
+
put
|
|
613
|
+
stat
|
|
614
|
+
read
|
|
615
|
+
exists
|
|
616
|
+
delete
|
|
300
617
|
```
|
|
301
618
|
|
|
302
|
-
|
|
619
|
+
Application code can depend on this contract instead of depending directly on filesystem paths.
|
|
620
|
+
|
|
621
|
+
The built-in adapter in `0.1.25` is local filesystem storage. Cloud/object-storage adapters are planned for a later milestone.
|
|
303
622
|
|
|
304
|
-
|
|
623
|
+
Storage keys are logical relative paths. Absolute paths and traversal segments are rejected.
|
|
305
624
|
|
|
306
|
-
|
|
625
|
+
Read more: [Storage and File Delivery](docs/storage.md)
|
|
307
626
|
|
|
308
|
-
##
|
|
627
|
+
## Production file delivery
|
|
628
|
+
|
|
629
|
+
Storage objects can be returned through a hardened HTTP response helper:
|
|
309
630
|
|
|
310
631
|
```ts
|
|
311
632
|
import {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
} from "bcp/validation";
|
|
633
|
+
createStorageResponse,
|
|
634
|
+
} from "bcp/server";
|
|
315
635
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
636
|
+
export async function GET(
|
|
637
|
+
request: Request
|
|
638
|
+
) {
|
|
639
|
+
return createStorageResponse(
|
|
640
|
+
request,
|
|
641
|
+
storage,
|
|
642
|
+
"documents/report.pdf",
|
|
643
|
+
{
|
|
644
|
+
disposition:
|
|
645
|
+
"attachment",
|
|
646
|
+
downloadName:
|
|
647
|
+
"report.pdf",
|
|
648
|
+
}
|
|
649
|
+
);
|
|
650
|
+
}
|
|
321
651
|
```
|
|
322
652
|
|
|
323
|
-
|
|
653
|
+
`createStorageResponse()` supports:
|
|
324
654
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
655
|
+
- `GET`,
|
|
656
|
+
- `HEAD`,
|
|
657
|
+
- `ETag`,
|
|
658
|
+
- `Last-Modified`,
|
|
659
|
+
- `If-None-Match`,
|
|
660
|
+
- `If-Modified-Since`,
|
|
661
|
+
- `If-Range`,
|
|
662
|
+
- single byte ranges with `206 Partial Content`,
|
|
663
|
+
- `304 Not Modified`,
|
|
664
|
+
- `416 Range Not Satisfiable`,
|
|
665
|
+
- safe `Content-Disposition` filenames.
|
|
666
|
+
|
|
667
|
+
The default cache policy is intentionally conservative:
|
|
668
|
+
|
|
669
|
+
```text
|
|
670
|
+
private, max-age=0, must-revalidate
|
|
333
671
|
```
|
|
334
672
|
|
|
335
|
-
|
|
673
|
+
Public immutable caching must be opted into explicitly.
|
|
336
674
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
675
|
+
Multiple byte ranges are intentionally not supported in `0.1.25`.
|
|
676
|
+
|
|
677
|
+
Read more: [Storage and File Delivery](docs/storage.md)
|
|
678
|
+
|
|
679
|
+
## Caching
|
|
680
|
+
|
|
681
|
+
BCP includes server response caching and revalidation primitives used by development and standalone production runtimes.
|
|
682
|
+
|
|
683
|
+
Read more: [Caching](docs/caching.md)
|
|
684
|
+
|
|
685
|
+
## Security
|
|
686
|
+
|
|
687
|
+
The framework security layer includes request body limits and production request handling defaults. Application authorization is still the responsibility of route guards and application logic.
|
|
688
|
+
|
|
689
|
+
Storage keys, filenames and MIME metadata must not be treated as authorization decisions.
|
|
690
|
+
|
|
691
|
+
Read more: [Security](docs/security.md)
|
|
692
|
+
|
|
693
|
+
## Environment and configuration
|
|
694
|
+
|
|
695
|
+
Application configuration lives in:
|
|
696
|
+
|
|
697
|
+
```text
|
|
698
|
+
bcp.config.ts
|
|
341
699
|
```
|
|
342
700
|
|
|
343
|
-
|
|
701
|
+
Public environment variables use the prefix:
|
|
344
702
|
|
|
345
|
-
|
|
703
|
+
```text
|
|
704
|
+
BCP_PUBLIC_
|
|
705
|
+
```
|
|
346
706
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
707
|
+
Server-only environment values remain server-side and are not emitted into browser bundles.
|
|
708
|
+
|
|
709
|
+
Read more: [Configuration](docs/configuration.md)
|
|
710
|
+
|
|
711
|
+
## Developer tools
|
|
712
|
+
|
|
713
|
+
BCP includes project diagnostics:
|
|
714
|
+
|
|
715
|
+
```bash
|
|
716
|
+
bcp doctor
|
|
717
|
+
bcp inspect
|
|
353
718
|
```
|
|
354
719
|
|
|
355
|
-
|
|
720
|
+
`bcp doctor` checks areas such as:
|
|
356
721
|
|
|
357
|
-
|
|
722
|
+
- project structure,
|
|
723
|
+
- BCP installation,
|
|
724
|
+
- React / ReactDOM compatibility,
|
|
725
|
+
- duplicate framework copies,
|
|
726
|
+
- environment/config loading,
|
|
727
|
+
- route conflicts,
|
|
728
|
+
- client/server boundaries.
|
|
358
729
|
|
|
359
|
-
|
|
730
|
+
`bcp inspect` reports resolved configuration, environment sources, dependencies and discovered routes.
|
|
360
731
|
|
|
361
|
-
|
|
362
|
-
export async function middleware(
|
|
363
|
-
request,
|
|
364
|
-
context,
|
|
365
|
-
next
|
|
366
|
-
) {
|
|
367
|
-
const response =
|
|
368
|
-
await next();
|
|
732
|
+
Read more: [Developer Tools](docs/developer-tools.md)
|
|
369
733
|
|
|
370
|
-
|
|
371
|
-
"x-app",
|
|
372
|
-
"example"
|
|
373
|
-
);
|
|
734
|
+
## Windows CLI
|
|
374
735
|
|
|
375
|
-
|
|
376
|
-
|
|
736
|
+
Microsoft SQL Server also installs an executable named `bcp.exe` on Windows.
|
|
737
|
+
|
|
738
|
+
BCP therefore publishes the collision-free alias:
|
|
739
|
+
|
|
740
|
+
```text
|
|
741
|
+
bcp-framework
|
|
377
742
|
```
|
|
378
743
|
|
|
379
|
-
|
|
744
|
+
Inside project npm scripts, `bcp` remains safe because npm puts `node_modules/.bin` at the front of `PATH`.
|
|
380
745
|
|
|
381
|
-
|
|
746
|
+
For direct PowerShell usage, prefer:
|
|
382
747
|
|
|
383
|
-
|
|
748
|
+
```powershell
|
|
749
|
+
npm exec -- bcp-framework doctor
|
|
750
|
+
npm exec -- bcp-framework inspect
|
|
751
|
+
npm exec -- bcp-framework dev
|
|
752
|
+
npm exec -- bcp-framework build
|
|
753
|
+
```
|
|
384
754
|
|
|
385
|
-
|
|
755
|
+
## CLI reference
|
|
386
756
|
|
|
387
|
-
|
|
757
|
+
```bash
|
|
758
|
+
bcp dev
|
|
759
|
+
bcp routes
|
|
760
|
+
bcp build
|
|
761
|
+
bcp start
|
|
762
|
+
bcp doctor
|
|
763
|
+
bcp doctor --json
|
|
764
|
+
bcp inspect
|
|
765
|
+
bcp inspect --json
|
|
766
|
+
bcp update
|
|
767
|
+
bcp version
|
|
768
|
+
|
|
769
|
+
bcp db create create_users
|
|
770
|
+
bcp db migrate
|
|
771
|
+
bcp db status
|
|
772
|
+
bcp db rollback
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
## Development behavior
|
|
776
|
+
|
|
777
|
+
BCP includes Fast Refresh and deterministic development hydration behavior.
|
|
778
|
+
|
|
779
|
+
Recent stabilization work also covers:
|
|
780
|
+
|
|
781
|
+
- Windows line-ending parity,
|
|
782
|
+
- multiline JSX hydration parity,
|
|
783
|
+
- duplicate BCP installation detection,
|
|
784
|
+
- automatic page-route/client-bundle graph resynchronization,
|
|
785
|
+
- standalone authentication guard request-context parity.
|
|
786
|
+
|
|
787
|
+
A development topology change should no longer require manually deleting `.bcp-framework` to recover a missing client route bundle.
|
|
788
|
+
|
|
789
|
+
Read more: [Hydration](docs/hydration.md)
|
|
790
|
+
|
|
791
|
+
## Production build
|
|
792
|
+
|
|
793
|
+
Build an application:
|
|
388
794
|
|
|
389
795
|
```bash
|
|
390
796
|
npm run build
|
|
391
|
-
npm start
|
|
392
797
|
```
|
|
393
798
|
|
|
394
|
-
|
|
799
|
+
Start the generated standalone runtime:
|
|
800
|
+
|
|
801
|
+
```bash
|
|
802
|
+
npm run start
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
Production output is written under:
|
|
395
806
|
|
|
396
807
|
```text
|
|
397
808
|
.bcp-framework/build/
|
|
@@ -401,44 +812,71 @@ Standalone output is generated under:
|
|
|
401
812
|
└─ server.mjs
|
|
402
813
|
```
|
|
403
814
|
|
|
404
|
-
|
|
815
|
+
The standalone runtime composes production middleware, security, cache, actions, guards, loaders and page rendering into the final HTTP pipeline.
|
|
816
|
+
|
|
817
|
+
Runtime hostname/port overrides can be supplied to `bcp start` without rebuilding the application.
|
|
818
|
+
|
|
819
|
+
Read more: [Deployment](docs/deployment.md)
|
|
820
|
+
|
|
821
|
+
## Updating BCP
|
|
405
822
|
|
|
406
823
|
```bash
|
|
407
824
|
bcp update
|
|
408
825
|
bcp update --check
|
|
409
826
|
bcp update --dry-run
|
|
410
|
-
bcp update 0.1.
|
|
827
|
+
bcp update 0.1.25
|
|
411
828
|
bcp update next
|
|
412
829
|
```
|
|
413
830
|
|
|
414
|
-
Projects
|
|
831
|
+
Projects created before the updater was introduced can bootstrap it once using the public package:
|
|
415
832
|
|
|
416
833
|
```bash
|
|
417
834
|
npx @chidchanun/bcp@latest update
|
|
418
835
|
```
|
|
419
836
|
|
|
420
|
-
|
|
837
|
+
Read more: [Updating](docs/updating.md)
|
|
838
|
+
|
|
839
|
+
## Framework development
|
|
840
|
+
|
|
841
|
+
When working inside the BCP Framework repository itself:
|
|
421
842
|
|
|
422
843
|
```bash
|
|
844
|
+
npm install
|
|
423
845
|
npm run typecheck
|
|
424
846
|
npm run test:unit
|
|
847
|
+
npm run test:e2e
|
|
848
|
+
npm run test:package
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
Full release-candidate validation:
|
|
852
|
+
|
|
853
|
+
```bash
|
|
425
854
|
npm run rc:check
|
|
426
855
|
```
|
|
427
856
|
|
|
428
|
-
|
|
857
|
+
A version must not be tagged or published until its release candidate and packed-package verification pass.
|
|
858
|
+
|
|
859
|
+
Read more: [Releasing](docs/releasing.md)
|
|
860
|
+
|
|
861
|
+
## Documentation source
|
|
429
862
|
|
|
430
|
-
|
|
863
|
+
The `docs/` directory is the source content intended to feed the future **`bcp-docs-web`** documentation website.
|
|
431
864
|
|
|
865
|
+
Start with:
|
|
866
|
+
|
|
867
|
+
- [Documentation Source Map](docs/README.md)
|
|
432
868
|
- [Getting Started](docs/getting-started.md)
|
|
869
|
+
- [Configuration](docs/configuration.md)
|
|
433
870
|
- [Application Modules](docs/application-modules.md)
|
|
434
871
|
- [Routing](docs/routing.md)
|
|
435
|
-
- [Server Request APIs](docs/server-request-apis.md)
|
|
436
872
|
- [Server Data Loaders](docs/server-data-loaders.md)
|
|
437
|
-
- [
|
|
873
|
+
- [Route Guards](docs/route-guards.md)
|
|
438
874
|
- [Form Actions](docs/form-actions.md)
|
|
875
|
+
- [Server Request APIs](docs/server-request-apis.md)
|
|
439
876
|
- [Validation](docs/validation.md)
|
|
440
877
|
- [Error Handling](docs/error-handling.md)
|
|
441
878
|
- [File Upload](docs/file-upload.md)
|
|
879
|
+
- [Storage and File Delivery](docs/storage.md)
|
|
442
880
|
- [Authentication](docs/authentication.md)
|
|
443
881
|
- [Auth Route Guards](docs/auth-route-guards.md)
|
|
444
882
|
- [JWT Sessions](docs/session-auth.md)
|
|
@@ -447,17 +885,58 @@ After RC passes, releases are tagged and published manually with the guarded rel
|
|
|
447
885
|
- [Middleware](docs/middleware.md)
|
|
448
886
|
- [Hydration](docs/hydration.md)
|
|
449
887
|
- [Developer Tools](docs/developer-tools.md)
|
|
450
|
-
- [
|
|
888
|
+
- [Logging and Observability](docs/development-logging.md)
|
|
451
889
|
- [Caching](docs/caching.md)
|
|
452
890
|
- [Security](docs/security.md)
|
|
453
891
|
- [Deployment](docs/deployment.md)
|
|
454
892
|
- [Updating](docs/updating.md)
|
|
455
893
|
- [Releasing](docs/releasing.md)
|
|
456
894
|
|
|
457
|
-
##
|
|
895
|
+
## Documentation website model
|
|
896
|
+
|
|
897
|
+
When `bcp-docs-web` is built, the recommended top-level information architecture is:
|
|
898
|
+
|
|
899
|
+
```text
|
|
900
|
+
Getting Started
|
|
901
|
+
Routing & Data
|
|
902
|
+
Authentication
|
|
903
|
+
Database
|
|
904
|
+
Runtime & Infrastructure
|
|
905
|
+
API Reference
|
|
906
|
+
Releases
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
`docs/README.md` contains the proposed route-to-source mapping for that website.
|
|
910
|
+
|
|
911
|
+
## Release history
|
|
912
|
+
|
|
913
|
+
Release notes are stored under:
|
|
914
|
+
|
|
915
|
+
```text
|
|
916
|
+
docs/releases/
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
Recent milestones:
|
|
920
|
+
|
|
921
|
+
| Version | Milestone |
|
|
922
|
+
| --- | --- |
|
|
923
|
+
| `0.1.20` | Hydration line-ending stabilization |
|
|
924
|
+
| `0.1.21` | Hydration semantic parity |
|
|
925
|
+
| `0.1.22` | Developer tools and diagnostics |
|
|
926
|
+
| `0.1.23` | Logging and observability |
|
|
927
|
+
| `0.1.24` | File Upload Foundation |
|
|
928
|
+
| `0.1.25` | Storage Adapters and File Delivery |
|
|
929
|
+
|
|
930
|
+
## Roadmap
|
|
931
|
+
|
|
932
|
+
Current planned direction after `0.1.25`:
|
|
933
|
+
|
|
934
|
+
1. S3-compatible / cloud storage adapter integration.
|
|
935
|
+
2. Production upload streaming.
|
|
936
|
+
3. Broader storage adapter ecosystem.
|
|
937
|
+
4. Additional production hardening as new workloads expose edge cases.
|
|
458
938
|
|
|
459
|
-
|
|
460
|
-
- React 19
|
|
939
|
+
Roadmap items are plans, not published API guarantees.
|
|
461
940
|
|
|
462
941
|
## License
|
|
463
942
|
|