@chidchanun/bcp 0.1.25 → 0.1.27

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 CHANGED
@@ -1,52 +1,16 @@
1
1
  # BCP Framework
2
2
 
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.
3
+ BCP Framework is a React full-stack framework focused on file-based routing, SSR, server data loading, guarded application flows, API routes, authentication, database access, validation, logging, uploads, storage and standalone production deployment.
4
4
 
5
- > **Development target:** `0.1.25`
5
+ > **Development target:** `0.1.27`
6
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
- ```
7
+ > BCP is pre-1.0. The `0.1.27` source adds the Storage Ecosystem and remains an unreleased development target until local validation, RC checks, tagging and npm publication complete.
44
8
 
45
9
  ## Current capabilities
46
10
 
47
11
  | Area | Capability |
48
12
  | --- | --- |
49
- | Application | React SSR, hydration, layouts, metadata, SPA navigation |
13
+ | Application | React SSR, hydration, layouts, metadata and SPA navigation |
50
14
  | Routing | Static, dynamic, catch-all, optional catch-all and route groups |
51
15
  | Server data | `loader.ts`, request-scoped server APIs |
52
16
  | Mutations | Route-owned `actions.ts` and `<Form>` |
@@ -57,8 +21,8 @@ app/
57
21
  | Error handling | HTTP error helpers and consistent error responses |
58
22
  | Database | MySQL pool/query helpers, transactions and migrations |
59
23
  | 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 |
24
+ | Uploads | Buffered multipart helpers and production multipart streaming |
25
+ | Storage | Local + S3-compatible adapters, streaming, listing, copy/move, metadata, bulk delete and signed S3 URLs |
62
26
  | Caching | Response cache and revalidation primitives |
63
27
  | Developer tools | `doctor`, `inspect`, updater and route inspection |
64
28
  | Production | Standalone server build with production middleware pipeline |
@@ -73,106 +37,69 @@ Database features currently target MySQL.
73
37
 
74
38
  ## Quick start
75
39
 
76
- Create a new application:
77
-
78
40
  ```bash
79
41
  npx create-bcp-app@latest my-app
80
42
  cd my-app
81
43
  npm run dev
82
44
  ```
83
45
 
84
- Default development URL:
85
-
86
- ```text
87
- http://localhost:3000
88
- ```
89
-
90
- A generated project normally exposes scripts such as:
46
+ Generated projects normally use the application dependency key `bcp`:
91
47
 
92
48
  ```json
93
49
  {
50
+ "dependencies": {
51
+ "bcp": "npm:@chidchanun/bcp@latest"
52
+ },
94
53
  "scripts": {
95
54
  "dev": "bcp dev",
96
55
  "build": "bcp build",
97
56
  "start": "bcp start",
98
57
  "routes": "bcp routes",
99
- "update": "bcp update"
58
+ "update": "bcp update",
59
+ "typecheck": "tsc --noEmit"
100
60
  }
101
61
  }
102
62
  ```
103
63
 
104
- ## Packages
64
+ Keep only one framework dependency named `bcp`. Installing both `bcp` and `@chidchanun/bcp` directly can load duplicate framework/React contexts.
105
65
 
106
- The public framework package is published as:
66
+ ## Application model
107
67
 
108
68
  ```text
109
- @chidchanun/bcp
110
- ```
111
-
112
- Applications normally consume it through the dependency key:
113
-
114
- ```text
115
- bcp
116
- ```
117
-
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";
69
+ Browser
70
+
71
+ BCP security / middleware / cache
72
+
73
+ Route guard
74
+
75
+ Loader / action / API route
76
+
77
+ React SSR
78
+
79
+ Hydration / SPA navigation
136
80
  ```
137
81
 
138
- ## Project structure
139
-
140
- A typical application can grow into this layout:
82
+ A route can keep page, loader, guard and actions together:
141
83
 
142
84
  ```text
143
85
  app/
144
86
  ├─ layout.tsx
145
87
  ├─ page.tsx
146
- ├─ login/
147
- │ └─ page.tsx
148
88
  ├─ dashboard/
149
89
  │ ├─ guard.ts
150
- │ ├─ page.tsx
151
90
  │ └─ users/
152
91
  │ └─ [id]/
153
92
  │ ├─ loader.ts
154
93
  │ ├─ actions.ts
155
94
  │ └─ page.tsx
156
95
  └─ api/
157
- ├─ auth/
158
- │ └─ login/
159
- │ └─ route.ts
160
96
  └─ upload/
161
97
  └─ route.ts
162
-
163
- lib/
164
- public/
165
- migrations/
166
- bcp.config.ts
167
- package.json
168
- tsconfig.json
169
98
  ```
170
99
 
171
- BCP keeps page rendering, route authorization, server data and route mutations close together without requiring one large application router configuration file.
172
-
173
100
  ## Routing
174
101
 
175
- Page routes are discovered from `app/**/page.tsx`.
102
+ Page routes are discovered from `app/**/page.tsx` and API routes from `app/**/route.ts`.
176
103
 
177
104
  ```text
178
105
  app/page.tsx /
@@ -181,73 +108,16 @@ app/users/[id]/page.tsx /users/:id
181
108
  app/docs/[...slug]/page.tsx /docs/*
182
109
  app/catalog/[[...slug]]/page.tsx /catalog and /catalog/*
183
110
  app/(admin)/settings/page.tsx /settings
184
- ```
185
-
186
- Static routes have priority over dynamic and catch-all routes.
187
111
 
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
112
+ app/api/users/route.ts /api/users
113
+ app/api/users/[id]/route.ts /api/users/:id
193
114
  ```
194
115
 
195
116
  Read more: [Routing](docs/routing.md)
196
117
 
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.
208
-
209
- ```ts
210
- // app/users/[id]/loader.ts
211
- export async function loader({
212
- params,
213
- }) {
214
- return {
215
- id:
216
- params.id,
217
- };
218
- }
219
- ```
220
-
221
- Consume the serializable result in a client page:
222
-
223
- ```tsx
224
- "use client";
225
-
226
- import {
227
- useLoaderData,
228
- } from "bcp";
229
-
230
- export default function UserPage() {
231
- const data =
232
- useLoaderData<{
233
- id: string;
234
- }>();
235
-
236
- return (
237
- <main>
238
- User {data.id}
239
- </main>
240
- );
241
- }
242
- ```
243
-
244
- Read more: [Server Data Loaders](docs/server-data-loaders.md)
245
-
246
- ## Route guards
118
+ ## Server data, guards and actions
247
119
 
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`:
120
+ Server loaders live next to their route in `loader.ts`. Route authorization can be defined in `guard.ts`, while route-owned mutations live in `actions.ts`.
251
121
 
252
122
  ```ts
253
123
  import {
@@ -258,44 +128,14 @@ export const guard =
258
128
  requireRole("admin");
259
129
  ```
260
130
 
261
- The standalone production pipeline preserves the same active request context used by authentication and server request APIs.
262
-
263
131
  Read more:
264
132
 
133
+ - [Server Data Loaders](docs/server-data-loaders.md)
265
134
  - [Route Guards](docs/route-guards.md)
266
135
  - [Auth Route Guards](docs/auth-route-guards.md)
136
+ - [Form Actions](docs/form-actions.md)
267
137
 
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)
275
-
276
- ## Server request APIs
277
-
278
- Request-scoped APIs are exposed through `bcp/server`:
279
-
280
- ```ts
281
- import {
282
- bearerToken,
283
- clientIp,
284
- cookies,
285
- headers,
286
- requestId,
287
- requestMethod,
288
- requestUrl,
289
- } from "bcp/server";
290
- ```
291
-
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:
138
+ ## Authentication and server APIs
299
139
 
300
140
  ```ts
301
141
  import {
@@ -303,117 +143,33 @@ import {
303
143
  requireAuth,
304
144
  requireRole,
305
145
  } from "bcp/auth";
306
- ```
307
-
308
- Lower-level JWT cookie session primitives are available through `bcp/server`:
309
146
 
310
- ```ts
311
147
  import {
312
- createSession,
313
- createSessionToken,
314
- destroySession,
315
- getSession,
316
- verifySessionToken,
148
+ cookies,
149
+ headers,
150
+ requestId,
151
+ requestMethod,
152
+ requestUrl,
317
153
  } from "bcp/server";
318
154
  ```
319
155
 
320
- Authentication is intentionally separated from application-specific credential lookup so projects can connect their own user table or identity provider.
156
+ BCP also exposes lower-level JWT cookie session primitives through `bcp/server`.
321
157
 
322
158
  Read more:
323
159
 
324
160
  - [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)
161
+ - [JWT Sessions](docs/session-auth.md)
162
+ - [Server Request APIs](docs/server-request-apis.md)
397
163
 
398
164
  ## Database
399
165
 
400
- Database helpers are exposed through:
401
-
402
166
  ```ts
403
167
  import {
404
168
  db,
405
169
  } from "bcp/database";
406
170
  ```
407
171
 
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.
415
-
416
- Migration commands:
172
+ The database layer provides lazy MySQL pool creation, prepared execution, query helpers, transactions and migrations.
417
173
 
418
174
  ```bash
419
175
  bcp db create create_users
@@ -427,396 +183,244 @@ Read more:
427
183
  - [Database](docs/database.md)
428
184
  - [Database Migrations](docs/database-migrations.md)
429
185
 
430
- ## Logging and observability
186
+ ## Uploads and streaming
431
187
 
432
- Structured server logging is available through `bcp/server`:
188
+ BCP supports the original buffered multipart helpers as well as production multipart-to-storage streaming:
433
189
 
434
190
  ```ts
435
191
  import {
436
- logger,
437
- requestLogger,
192
+ storeMultipartFile,
438
193
  } from "bcp/server";
439
194
 
440
- logger.info(
441
- "Application event",
442
- {
443
- feature:
444
- "catalog",
445
- }
446
- );
447
- ```
448
-
449
- Request-scoped logging can automatically include request identity:
450
-
451
- ```ts
452
- export async function loader() {
453
- const log =
454
- await requestLogger({
455
- feature:
456
- "categories",
457
- });
458
-
459
- log.info(
460
- "Loading categories"
195
+ const stored =
196
+ await storeMultipartFile(
197
+ request,
198
+ {
199
+ storage,
200
+ fieldName: "file",
201
+ key: "documents/report.pdf",
202
+ maxBytes: 100 * 1024 * 1024,
203
+ constraints: {
204
+ maxBytes: 80 * 1024 * 1024,
205
+ allowedTypes: [
206
+ "application/pdf",
207
+ ],
208
+ },
209
+ }
461
210
  );
462
-
463
- return {
464
- items: [],
465
- };
466
- }
467
211
  ```
468
212
 
469
- Environment controls:
213
+ `storeMultipartFile()` consumes `Request.body` incrementally and streams the selected file directly into storage. Request/body infrastructure limits still apply.
470
214
 
471
- ```env
472
- BCP_LOG_LEVEL=debug
473
- BCP_LOG_FORMAT=json
474
- ```
215
+ Read more: [File Upload](docs/file-upload.md)
475
216
 
476
- Supported levels:
217
+ ## Storage
477
218
 
478
- ```text
479
- debug
480
- info
481
- warn
482
- error
483
- silent
484
- ```
219
+ ### Local filesystem
485
220
 
486
- Supported formats:
221
+ ```ts
222
+ import {
223
+ createLocalStorage,
224
+ } from "bcp/server";
487
225
 
488
- ```text
489
- pretty
490
- json
226
+ const storage =
227
+ createLocalStorage({
228
+ directory: "./uploads",
229
+ });
491
230
  ```
492
231
 
493
- Read more: [Logging and Observability](docs/development-logging.md)
494
-
495
- ## File upload
496
-
497
- BCP `0.1.24` introduced multipart parsing and file validation:
232
+ ### S3 / R2 / MinIO
498
233
 
499
234
  ```ts
500
235
  import {
501
- parseMultipartFormData,
502
- requireUploadedFile,
503
- saveUploadedFile,
236
+ createS3Storage,
504
237
  } from "bcp/server";
505
238
 
506
- export async function POST(
507
- request: Request
508
- ) {
509
- const formData =
510
- await parseMultipartFormData(
511
- request,
512
- {
513
- maxBytes:
514
- 8 * 1024 * 1024,
515
- }
516
- );
517
-
518
- const file =
519
- requireUploadedFile(
520
- formData,
521
- "file",
522
- {
523
- maxBytes:
524
- 5 * 1024 * 1024,
525
- allowedTypes: [
526
- "image/png",
527
- "image/jpeg",
528
- "image/webp",
529
- ],
530
- allowedExtensions: [
531
- ".png",
532
- ".jpg",
533
- ".jpeg",
534
- ".webp",
535
- ],
536
- }
537
- );
538
-
539
- return Response.json(
540
- await saveUploadedFile(
541
- file,
542
- {
543
- directory:
544
- "./uploads",
545
- }
546
- )
547
- );
548
- }
239
+ const storage =
240
+ createS3Storage({
241
+ bucket: process.env.S3_BUCKET!,
242
+ region: process.env.S3_REGION!,
243
+ endpoint: process.env.S3_ENDPOINT,
244
+ accessKeyId: process.env.S3_ACCESS_KEY_ID,
245
+ secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
246
+ });
549
247
  ```
550
248
 
551
- Upload helpers provide:
249
+ Custom endpoints and `forcePathStyle` support common S3-compatible deployments such as MinIO. Keep credentials server-only.
250
+
251
+ Read more: [S3-Compatible Storage](docs/s3-storage.md)
552
252
 
553
- - multipart validation,
554
- - total request and per-file size limits,
555
- - MIME and extension allowlists,
556
- - required/optional file fields,
557
- - safe UUID-based storage names,
558
- - filename sanitization,
559
- - path traversal protection,
560
- - no-overwrite-by-default persistence,
561
- - SHA-256 checksum metadata.
253
+ ### Streaming storage
562
254
 
563
- The security gateway applies `server.bodyLimit` / `BCP_BODY_LIMIT` before application upload parsing. Applications accepting larger files must raise that outer limit explicitly.
255
+ The stable minimum `StorageAdapter` retains its buffered API while adapters can expose optional `putStream()` and `readStream()` implementations.
564
256
 
565
257
  ```ts
566
258
  import {
567
- defineConfig,
568
- } from "bcp/config";
569
-
570
- export default defineConfig({
571
- server: {
572
- bodyLimit:
573
- 10 * 1024 * 1024,
574
- },
575
- });
259
+ getStorageCapabilities,
260
+ putStorageStream,
261
+ readStorageStream,
262
+ } from "bcp/server";
576
263
  ```
577
264
 
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.
265
+ Both built-in local and S3 adapters support streaming reads/writes and byte ranges.
579
266
 
580
- Read more: [File Upload](docs/file-upload.md)
267
+ Read more: [Storage and File Delivery](docs/storage.md)
581
268
 
582
- ## Storage adapters
269
+ ### Storage Ecosystem — 0.1.27
583
270
 
584
- BCP `0.1.25` adds the first application-facing storage abstraction.
271
+ BCP `0.1.27` adds a richer, additive object-storage API without invalidating older `StorageAdapter` implementations:
585
272
 
586
273
  ```ts
587
274
  import {
588
- createLocalStorage,
589
- storeUploadedFile,
275
+ copyStorageObject,
276
+ createStorageSignedReadUrl,
277
+ createStorageSignedWriteUrl,
278
+ deleteStorageObjects,
279
+ getStorageEcosystemCapabilities,
280
+ getStorageMetadata,
281
+ listStorageObjects,
282
+ moveStorageObject,
283
+ setStorageMetadata,
590
284
  } from "bcp/server";
285
+ ```
591
286
 
592
- const storage =
593
- createLocalStorage({
594
- directory:
595
- "./uploads",
596
- });
287
+ Listing supports prefix filtering, limits and opaque cursors:
597
288
 
598
- const stored =
599
- await storeUploadedFile(
600
- file,
289
+ ```ts
290
+ const page =
291
+ await listStorageObjects(
292
+ storage,
601
293
  {
602
- storage,
603
- key:
604
- "avatars/user-101.webp",
294
+ prefix: "documents/",
295
+ limit: 50,
605
296
  }
606
297
  );
607
298
  ```
608
299
 
609
- The `StorageAdapter` contract contains:
610
-
611
- ```text
612
- put
613
- stat
614
- read
615
- exists
616
- delete
617
- ```
300
+ Copy and move use native adapter operations when available, with portable fallback behavior where practical:
618
301
 
619
- Application code can depend on this contract instead of depending directly on filesystem paths.
302
+ ```ts
303
+ await copyStorageObject(
304
+ storage,
305
+ "incoming/report.pdf",
306
+ "archive/report.pdf"
307
+ );
620
308
 
621
- The built-in adapter in `0.1.25` is local filesystem storage. Cloud/object-storage adapters are planned for a later milestone.
309
+ await moveStorageObject(
310
+ storage,
311
+ "tmp/avatar.webp",
312
+ "users/42/avatar.webp"
313
+ );
314
+ ```
622
315
 
623
- Storage keys are logical relative paths. Absolute paths and traversal segments are rejected.
316
+ Portable user metadata is string-to-string metadata and is distinct from application authorization/business state:
624
317
 
625
- Read more: [Storage and File Delivery](docs/storage.md)
626
-
627
- ## Production file delivery
318
+ ```ts
319
+ await setStorageMetadata(
320
+ storage,
321
+ "archive/report.pdf",
322
+ {
323
+ owner: "user-42",
324
+ status: "approved",
325
+ }
326
+ );
327
+ ```
628
328
 
629
- Storage objects can be returned through a hardened HTTP response helper:
329
+ S3-compatible storage also supports short-lived presigned direct-transfer URLs:
630
330
 
631
331
  ```ts
632
- import {
633
- createStorageResponse,
634
- } from "bcp/server";
332
+ const downloadUrl =
333
+ await createStorageSignedReadUrl(
334
+ storage,
335
+ "videos/demo.mp4",
336
+ {
337
+ expiresIn: 300,
338
+ }
339
+ );
635
340
 
636
- export async function GET(
637
- request: Request
638
- ) {
639
- return createStorageResponse(
640
- request,
341
+ const uploadUrl =
342
+ await createStorageSignedWriteUrl(
641
343
  storage,
642
- "documents/report.pdf",
344
+ "uploads/demo.mp4",
643
345
  {
644
- disposition:
645
- "attachment",
646
- downloadName:
647
- "report.pdf",
346
+ expiresIn: 300,
347
+ contentType: "video/mp4",
648
348
  }
649
349
  );
650
- }
651
350
  ```
652
351
 
653
- `createStorageResponse()` supports:
352
+ Local storage intentionally does not emulate signed URLs. Signed URLs are temporary credentials and should only be generated after application authorization.
654
353
 
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.
354
+ Read more: [Storage Ecosystem](docs/storage-ecosystem.md)
666
355
 
667
- The default cache policy is intentionally conservative:
356
+ ## File delivery
668
357
 
669
- ```text
670
- private, max-age=0, must-revalidate
671
- ```
358
+ `createStorageResponse()` serves local or cloud-backed objects through the same API with `GET`, `HEAD`, ETag/Last-Modified validators and single byte ranges.
672
359
 
673
- Public immutable caching must be opted into explicitly.
360
+ ```ts
361
+ import {
362
+ createStorageResponse,
363
+ } from "bcp/server";
674
364
 
675
- Multiple byte ranges are intentionally not supported in `0.1.25`.
365
+ return createStorageResponse(
366
+ request,
367
+ storage,
368
+ "documents/report.pdf"
369
+ );
370
+ ```
676
371
 
677
372
  Read more: [Storage and File Delivery](docs/storage.md)
678
373
 
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.
374
+ ## Middleware, validation, errors and caching
688
375
 
689
- Storage keys, filenames and MIME metadata must not be treated as authorization decisions.
376
+ BCP includes Middleware System v2, typed validation helpers, structured HTTP errors, structured logging and response caching/revalidation.
690
377
 
691
- Read more: [Security](docs/security.md)
378
+ Read more:
692
379
 
693
- ## Environment and configuration
380
+ - [Middleware](docs/middleware.md)
381
+ - [Validation](docs/validation.md)
382
+ - [Error Handling](docs/error-handling.md)
383
+ - [Logging and Observability](docs/development-logging.md)
384
+ - [Caching](docs/caching.md)
385
+ - [Security](docs/security.md)
694
386
 
695
- Application configuration lives in:
387
+ ## Production build
696
388
 
697
- ```text
698
- bcp.config.ts
389
+ ```bash
390
+ npm run build
391
+ npm run start
699
392
  ```
700
393
 
701
- Public environment variables use the prefix:
394
+ Standalone output is written under:
702
395
 
703
396
  ```text
704
- BCP_PUBLIC_
705
- ```
706
-
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
397
+ .bcp-framework/build/
398
+ ├─ client/
399
+ ├─ public/
400
+ └─ server/
401
+ └─ server.mjs
718
402
  ```
719
403
 
720
- `bcp doctor` checks areas such as:
721
-
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.
729
-
730
- `bcp inspect` reports resolved configuration, environment sources, dependencies and discovered routes.
731
-
732
- Read more: [Developer Tools](docs/developer-tools.md)
404
+ Read more: [Deployment](docs/deployment.md)
733
405
 
734
406
  ## Windows CLI
735
407
 
736
- Microsoft SQL Server also installs an executable named `bcp.exe` on Windows.
408
+ Microsoft SQL Server can install another executable named `bcp.exe`. BCP publishes the collision-free alias `bcp-framework`.
737
409
 
738
- BCP therefore publishes the collision-free alias:
739
-
740
- ```text
741
- bcp-framework
742
- ```
410
+ Inside project npm scripts, `bcp` is safe because npm prepends `node_modules/.bin` to `PATH`.
743
411
 
744
- Inside project npm scripts, `bcp` remains safe because npm puts `node_modules/.bin` at the front of `PATH`.
745
-
746
- For direct PowerShell usage, prefer:
412
+ For direct PowerShell usage:
747
413
 
748
414
  ```powershell
415
+ npm exec -- bcp-framework --version
749
416
  npm exec -- bcp-framework doctor
750
417
  npm exec -- bcp-framework inspect
418
+ npm exec -- bcp-framework routes
751
419
  npm exec -- bcp-framework dev
752
420
  npm exec -- bcp-framework build
753
421
  ```
754
422
 
755
- ## CLI reference
756
-
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:
794
-
795
- ```bash
796
- npm run build
797
- ```
798
-
799
- Start the generated standalone runtime:
800
-
801
- ```bash
802
- npm run start
803
- ```
804
-
805
- Production output is written under:
806
-
807
- ```text
808
- .bcp-framework/build/
809
- ├─ client/
810
- ├─ public/
811
- └─ server/
812
- └─ server.mjs
813
- ```
814
-
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)
423
+ Read more: [Developer Tools](docs/developer-tools.md)
820
424
 
821
425
  ## Updating BCP
822
426
 
@@ -824,100 +428,55 @@ Read more: [Deployment](docs/deployment.md)
824
428
  bcp update
825
429
  bcp update --check
826
430
  bcp update --dry-run
827
- bcp update 0.1.25
431
+ bcp update 0.1.27
828
432
  bcp update next
829
433
  ```
830
434
 
831
- Projects created before the updater was introduced can bootstrap it once using the public package:
832
-
833
- ```bash
834
- npx @chidchanun/bcp@latest update
835
- ```
836
-
837
435
  Read more: [Updating](docs/updating.md)
838
436
 
839
- ## Framework development
840
-
841
- When working inside the BCP Framework repository itself:
437
+ ## Framework development and release validation
842
438
 
843
439
  ```bash
844
440
  npm install
845
441
  npm run typecheck
846
442
  npm run test:unit
443
+ npm run test:integration
847
444
  npm run test:e2e
848
445
  npm run test:package
849
- ```
850
-
851
- Full release-candidate validation:
852
-
853
- ```bash
854
446
  npm run rc:check
855
447
  ```
856
448
 
857
- A version must not be tagged or published until its release candidate and packed-package verification pass.
449
+ A version must not be tagged or published until its RC and packed-package verification pass.
858
450
 
859
451
  Read more: [Releasing](docs/releasing.md)
860
452
 
861
453
  ## Documentation source
862
454
 
863
- The `docs/` directory is the source content intended to feed the future **`bcp-docs-web`** documentation website.
455
+ The `docs/` directory is the authored source for **`bcp-docs-web`**.
864
456
 
865
- Start with:
457
+ Recommended starting points:
866
458
 
867
459
  - [Documentation Source Map](docs/README.md)
868
460
  - [Getting Started](docs/getting-started.md)
869
461
  - [Configuration](docs/configuration.md)
870
- - [Application Modules](docs/application-modules.md)
871
462
  - [Routing](docs/routing.md)
872
463
  - [Server Data Loaders](docs/server-data-loaders.md)
873
464
  - [Route Guards](docs/route-guards.md)
874
465
  - [Form Actions](docs/form-actions.md)
875
466
  - [Server Request APIs](docs/server-request-apis.md)
876
- - [Validation](docs/validation.md)
877
- - [Error Handling](docs/error-handling.md)
878
467
  - [File Upload](docs/file-upload.md)
879
468
  - [Storage and File Delivery](docs/storage.md)
469
+ - [Storage Ecosystem](docs/storage-ecosystem.md)
470
+ - [S3-Compatible Storage](docs/s3-storage.md)
880
471
  - [Authentication](docs/authentication.md)
881
- - [Auth Route Guards](docs/auth-route-guards.md)
882
- - [JWT Sessions](docs/session-auth.md)
883
472
  - [Database](docs/database.md)
884
- - [Database Migrations](docs/database-migrations.md)
885
473
  - [Middleware](docs/middleware.md)
886
- - [Hydration](docs/hydration.md)
887
474
  - [Developer Tools](docs/developer-tools.md)
888
- - [Logging and Observability](docs/development-logging.md)
889
- - [Caching](docs/caching.md)
890
- - [Security](docs/security.md)
891
475
  - [Deployment](docs/deployment.md)
892
- - [Updating](docs/updating.md)
893
476
  - [Releasing](docs/releasing.md)
894
477
 
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
478
  ## Release history
912
479
 
913
- Release notes are stored under:
914
-
915
- ```text
916
- docs/releases/
917
- ```
918
-
919
- Recent milestones:
920
-
921
480
  | Version | Milestone |
922
481
  | --- | --- |
923
482
  | `0.1.20` | Hydration line-ending stabilization |
@@ -926,17 +485,22 @@ Recent milestones:
926
485
  | `0.1.23` | Logging and observability |
927
486
  | `0.1.24` | File Upload Foundation |
928
487
  | `0.1.25` | Storage Adapters and File Delivery |
488
+ | `0.1.26` | S3-Compatible Storage and Production Streaming |
489
+ | `0.1.27` | Storage Ecosystem |
929
490
 
930
- ## Roadmap
491
+ ## Next direction
931
492
 
932
- Current planned direction after `0.1.25`:
493
+ After `0.1.27`, the planned milestone is **`0.1.28 — Production Hardening`**:
933
494
 
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.
495
+ 1. graceful HTTP shutdown and active-request draining,
496
+ 2. database/S3 resource cleanup,
497
+ 3. trusted proxy and forwarded-header handling,
498
+ 4. request/server timeout controls,
499
+ 5. security hardening,
500
+ 6. Docker and standalone-runtime regression coverage,
501
+ 7. stronger startup/runtime diagnostics.
938
502
 
939
- Roadmap items are plans, not published API guarantees.
503
+ These are roadmap items, not `0.1.27` guarantees.
940
504
 
941
505
  ## License
942
506