@chidchanun/bcp 0.1.26 → 0.1.28
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 +151 -523
- package/docs/README.md +69 -82
- package/docs/production-hardening.md +152 -0
- package/docs/releases/0.1.27.md +350 -0
- package/docs/releases/0.1.28.md +134 -0
- package/docs/s3-storage.md +212 -55
- package/docs/storage-ecosystem.md +434 -0
- package/package.json +2 -1
- package/packages/client/src/server.ts +42 -2
- package/packages/server/src/hardening-proxy.ts +393 -0
- package/packages/server/src/production-hardening.ts +374 -0
- package/packages/server/src/standalone-production-runtime-v7.ts +299 -0
- package/packages/server/src/standalone-production-server.ts +1 -1
- package/packages/server/src/storage-ecosystem.ts +1326 -0
- package/packages/server/src/storage-s3-ecosystem.ts +947 -0
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, SSR, server data loading, guarded application flows, API routes, authentication, database access, validation, logging, uploads, storage
|
|
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
|
-
> **
|
|
5
|
+
> **Development target:** `0.1.28`
|
|
6
6
|
>
|
|
7
|
-
> BCP is pre-1.0. The `0.1.
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
BCP keeps React pages and their server behavior close to the route that owns them:
|
|
12
|
-
|
|
13
|
-
```text
|
|
14
|
-
Browser
|
|
15
|
-
↓
|
|
16
|
-
BCP security / middleware / cache
|
|
17
|
-
↓
|
|
18
|
-
Route guard
|
|
19
|
-
↓
|
|
20
|
-
Loader / action / API route
|
|
21
|
-
↓
|
|
22
|
-
React SSR
|
|
23
|
-
↓
|
|
24
|
-
Hydration / SPA navigation
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
A typical route can colocate its page, loader, guard and actions:
|
|
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.28` source adds Production Hardening 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
|
|
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>` |
|
|
@@ -58,10 +22,10 @@ app/
|
|
|
58
22
|
| Database | MySQL pool/query helpers, transactions and migrations |
|
|
59
23
|
| Logging | Structured logger, request logger and request IDs |
|
|
60
24
|
| Uploads | Buffered multipart helpers and production multipart streaming |
|
|
61
|
-
| Storage | Local + S3-compatible adapters, 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
|
-
| Production | Standalone
|
|
28
|
+
| Production | Standalone build, hardening gateway, graceful shutdown, trusted-proxy controls and HTTP timeouts |
|
|
65
29
|
|
|
66
30
|
## Requirements
|
|
67
31
|
|
|
@@ -79,16 +43,13 @@ cd my-app
|
|
|
79
43
|
npm run dev
|
|
80
44
|
```
|
|
81
45
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```text
|
|
85
|
-
http://localhost:3000
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Generated projects normally include:
|
|
46
|
+
Generated projects normally use the application dependency key `bcp`:
|
|
89
47
|
|
|
90
48
|
```json
|
|
91
49
|
{
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"bcp": "npm:@chidchanun/bcp@latest"
|
|
52
|
+
},
|
|
92
53
|
"scripts": {
|
|
93
54
|
"dev": "bcp dev",
|
|
94
55
|
"build": "bcp build",
|
|
@@ -100,68 +61,45 @@ Generated projects normally include:
|
|
|
100
61
|
}
|
|
101
62
|
```
|
|
102
63
|
|
|
103
|
-
|
|
64
|
+
Keep only one framework dependency named `bcp`. Installing both `bcp` and `@chidchanun/bcp` directly can load duplicate framework/React contexts.
|
|
104
65
|
|
|
105
|
-
|
|
66
|
+
## Application model
|
|
106
67
|
|
|
107
68
|
```text
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Server-only APIs use dedicated entrypoints:
|
|
122
|
-
|
|
123
|
-
```ts
|
|
124
|
-
import {
|
|
125
|
-
cookies,
|
|
126
|
-
logger,
|
|
127
|
-
requestId,
|
|
128
|
-
} 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
|
|
129
80
|
```
|
|
130
81
|
|
|
131
|
-
|
|
82
|
+
A route can keep page, loader, guard and actions together:
|
|
132
83
|
|
|
133
84
|
```text
|
|
134
85
|
app/
|
|
135
86
|
├─ layout.tsx
|
|
136
87
|
├─ page.tsx
|
|
137
|
-
├─ login/
|
|
138
|
-
│ └─ page.tsx
|
|
139
88
|
├─ dashboard/
|
|
140
89
|
│ ├─ guard.ts
|
|
141
|
-
│ ├─ page.tsx
|
|
142
90
|
│ └─ users/
|
|
143
91
|
│ └─ [id]/
|
|
144
92
|
│ ├─ loader.ts
|
|
145
93
|
│ ├─ actions.ts
|
|
146
94
|
│ └─ page.tsx
|
|
147
95
|
└─ api/
|
|
148
|
-
├─ auth/
|
|
149
|
-
│ └─ login/
|
|
150
|
-
│ └─ route.ts
|
|
151
96
|
└─ upload/
|
|
152
97
|
└─ route.ts
|
|
153
|
-
|
|
154
|
-
lib/
|
|
155
|
-
public/
|
|
156
|
-
migrations/
|
|
157
|
-
bcp.config.ts
|
|
158
|
-
package.json
|
|
159
|
-
tsconfig.json
|
|
160
98
|
```
|
|
161
99
|
|
|
162
100
|
## Routing
|
|
163
101
|
|
|
164
|
-
Page routes are discovered from `app/**/page.tsx
|
|
102
|
+
Page routes are discovered from `app/**/page.tsx` and API routes from `app/**/route.ts`.
|
|
165
103
|
|
|
166
104
|
```text
|
|
167
105
|
app/page.tsx /
|
|
@@ -170,65 +108,16 @@ app/users/[id]/page.tsx /users/:id
|
|
|
170
108
|
app/docs/[...slug]/page.tsx /docs/*
|
|
171
109
|
app/catalog/[[...slug]]/page.tsx /catalog and /catalog/*
|
|
172
110
|
app/(admin)/settings/page.tsx /settings
|
|
173
|
-
```
|
|
174
111
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
API routes use `route.ts`:
|
|
178
|
-
|
|
179
|
-
```text
|
|
180
|
-
app/api/users/route.ts /api/users
|
|
181
|
-
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
|
|
182
114
|
```
|
|
183
115
|
|
|
184
116
|
Read more: [Routing](docs/routing.md)
|
|
185
117
|
|
|
186
|
-
##
|
|
187
|
-
|
|
188
|
-
Routes inherit layouts from parent directories. BCP resolves the layout chain in development and standalone production. Document metadata is route-aware and can be generated alongside the page tree.
|
|
189
|
-
|
|
190
|
-
Read more: [Routing](docs/routing.md)
|
|
191
|
-
|
|
192
|
-
## Server data loaders
|
|
193
|
-
|
|
194
|
-
Place `loader.ts` next to a page when the route needs server-side data:
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
// app/users/[id]/loader.ts
|
|
198
|
-
export async function loader({
|
|
199
|
-
params,
|
|
200
|
-
}) {
|
|
201
|
-
return {
|
|
202
|
-
id:
|
|
203
|
-
params.id,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Consume serializable loader data from a client page:
|
|
209
|
-
|
|
210
|
-
```tsx
|
|
211
|
-
"use client";
|
|
212
|
-
|
|
213
|
-
import {
|
|
214
|
-
useLoaderData,
|
|
215
|
-
} from "bcp";
|
|
216
|
-
|
|
217
|
-
export default function UserPage() {
|
|
218
|
-
const data =
|
|
219
|
-
useLoaderData<{
|
|
220
|
-
id: string;
|
|
221
|
-
}>();
|
|
222
|
-
|
|
223
|
-
return <main>User {data.id}</main>;
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
Read more: [Server Data Loaders](docs/server-data-loaders.md)
|
|
118
|
+
## Server data, guards and actions
|
|
228
119
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
A route tree can define `guard.ts` to authorize access before rendering:
|
|
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`.
|
|
232
121
|
|
|
233
122
|
```ts
|
|
234
123
|
import {
|
|
@@ -239,40 +128,14 @@ export const guard =
|
|
|
239
128
|
requireRole("admin");
|
|
240
129
|
```
|
|
241
130
|
|
|
242
|
-
The standalone production pipeline preserves the same active request context used by authentication and server request APIs.
|
|
243
|
-
|
|
244
131
|
Read more:
|
|
245
132
|
|
|
133
|
+
- [Server Data Loaders](docs/server-data-loaders.md)
|
|
246
134
|
- [Route Guards](docs/route-guards.md)
|
|
247
135
|
- [Auth Route Guards](docs/auth-route-guards.md)
|
|
136
|
+
- [Form Actions](docs/form-actions.md)
|
|
248
137
|
|
|
249
|
-
##
|
|
250
|
-
|
|
251
|
-
Route-owned mutations live in `actions.ts` and can be invoked through the public `<Form>` API. BCP supports progressive form submission and SPA action transport while keeping mutation code server-only.
|
|
252
|
-
|
|
253
|
-
Read more: [Form Actions](docs/form-actions.md)
|
|
254
|
-
|
|
255
|
-
## Server request APIs
|
|
256
|
-
|
|
257
|
-
```ts
|
|
258
|
-
import {
|
|
259
|
-
bearerToken,
|
|
260
|
-
clientIp,
|
|
261
|
-
cookies,
|
|
262
|
-
headers,
|
|
263
|
-
requestId,
|
|
264
|
-
requestMethod,
|
|
265
|
-
requestUrl,
|
|
266
|
-
} from "bcp/server";
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
`requestId()` accepts a valid incoming `X-Request-Id` or generates a stable UUID for the active request.
|
|
270
|
-
|
|
271
|
-
Read more: [Server Request APIs](docs/server-request-apis.md)
|
|
272
|
-
|
|
273
|
-
## Authentication and sessions
|
|
274
|
-
|
|
275
|
-
High-level authentication helpers:
|
|
138
|
+
## Authentication and server APIs
|
|
276
139
|
|
|
277
140
|
```ts
|
|
278
141
|
import {
|
|
@@ -280,89 +143,23 @@ import {
|
|
|
280
143
|
requireAuth,
|
|
281
144
|
requireRole,
|
|
282
145
|
} from "bcp/auth";
|
|
283
|
-
```
|
|
284
146
|
|
|
285
|
-
Lower-level JWT cookie session primitives:
|
|
286
|
-
|
|
287
|
-
```ts
|
|
288
147
|
import {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
148
|
+
cookies,
|
|
149
|
+
headers,
|
|
150
|
+
requestId,
|
|
151
|
+
requestMethod,
|
|
152
|
+
requestUrl,
|
|
294
153
|
} from "bcp/server";
|
|
295
154
|
```
|
|
296
155
|
|
|
297
|
-
|
|
156
|
+
BCP also exposes lower-level JWT cookie session primitives through `bcp/server`.
|
|
298
157
|
|
|
299
158
|
Read more:
|
|
300
159
|
|
|
301
160
|
- [Authentication](docs/authentication.md)
|
|
302
|
-
- [JWT
|
|
303
|
-
|
|
304
|
-
## Middleware
|
|
305
|
-
|
|
306
|
-
Middleware System v2 uses onion-style execution:
|
|
307
|
-
|
|
308
|
-
```ts
|
|
309
|
-
export async function middleware(
|
|
310
|
-
request,
|
|
311
|
-
context,
|
|
312
|
-
next
|
|
313
|
-
) {
|
|
314
|
-
const response =
|
|
315
|
-
await next();
|
|
316
|
-
|
|
317
|
-
response.headers.set(
|
|
318
|
-
"x-app",
|
|
319
|
-
"example"
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
return response;
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
Existing middleware v1 behavior remains supported for compatibility.
|
|
327
|
-
|
|
328
|
-
Read more: [Middleware](docs/middleware.md)
|
|
329
|
-
|
|
330
|
-
## Validation
|
|
331
|
-
|
|
332
|
-
```ts
|
|
333
|
-
import {
|
|
334
|
-
v,
|
|
335
|
-
validateFormData,
|
|
336
|
-
} from "bcp/validation";
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
Read more: [Validation](docs/validation.md)
|
|
340
|
-
|
|
341
|
-
## Error handling
|
|
342
|
-
|
|
343
|
-
```ts
|
|
344
|
-
import {
|
|
345
|
-
badRequest,
|
|
346
|
-
forbidden,
|
|
347
|
-
notFoundResponse,
|
|
348
|
-
toErrorResponse,
|
|
349
|
-
unauthorized,
|
|
350
|
-
} from "bcp/error";
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
The common error envelope is:
|
|
354
|
-
|
|
355
|
-
```json
|
|
356
|
-
{
|
|
357
|
-
"error": {
|
|
358
|
-
"status": 400,
|
|
359
|
-
"code": "BAD_REQUEST",
|
|
360
|
-
"message": "Invalid request"
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
Read more: [Error Handling](docs/error-handling.md)
|
|
161
|
+
- [JWT Sessions](docs/session-auth.md)
|
|
162
|
+
- [Server Request APIs](docs/server-request-apis.md)
|
|
366
163
|
|
|
367
164
|
## Database
|
|
368
165
|
|
|
@@ -372,7 +169,7 @@ import {
|
|
|
372
169
|
} from "bcp/database";
|
|
373
170
|
```
|
|
374
171
|
|
|
375
|
-
The database layer provides
|
|
172
|
+
The database layer provides lazy MySQL pool creation, prepared execution, query helpers, transactions and migrations.
|
|
376
173
|
|
|
377
174
|
```bash
|
|
378
175
|
bcp db create create_users
|
|
@@ -386,71 +183,9 @@ Read more:
|
|
|
386
183
|
- [Database](docs/database.md)
|
|
387
184
|
- [Database Migrations](docs/database-migrations.md)
|
|
388
185
|
|
|
389
|
-
##
|
|
390
|
-
|
|
391
|
-
```ts
|
|
392
|
-
import {
|
|
393
|
-
logger,
|
|
394
|
-
requestLogger,
|
|
395
|
-
} from "bcp/server";
|
|
396
|
-
|
|
397
|
-
logger.info(
|
|
398
|
-
"Application event",
|
|
399
|
-
{
|
|
400
|
-
feature:
|
|
401
|
-
"catalog",
|
|
402
|
-
}
|
|
403
|
-
);
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
Environment controls:
|
|
407
|
-
|
|
408
|
-
```env
|
|
409
|
-
BCP_LOG_LEVEL=debug
|
|
410
|
-
BCP_LOG_FORMAT=json
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
Supported levels are `debug`, `info`, `warn`, `error` and `silent`. Formats are `pretty` and `json`.
|
|
414
|
-
|
|
415
|
-
Read more: [Logging and Observability](docs/development-logging.md)
|
|
416
|
-
|
|
417
|
-
## File uploads
|
|
418
|
-
|
|
419
|
-
BCP keeps the buffered multipart API for small forms:
|
|
420
|
-
|
|
421
|
-
```ts
|
|
422
|
-
import {
|
|
423
|
-
parseMultipartFormData,
|
|
424
|
-
requireUploadedFile,
|
|
425
|
-
saveUploadedFile,
|
|
426
|
-
} from "bcp/server";
|
|
427
|
-
|
|
428
|
-
const formData =
|
|
429
|
-
await parseMultipartFormData(
|
|
430
|
-
request,
|
|
431
|
-
{
|
|
432
|
-
maxBytes:
|
|
433
|
-
8 * 1024 * 1024,
|
|
434
|
-
}
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
const file =
|
|
438
|
-
requireUploadedFile(
|
|
439
|
-
formData,
|
|
440
|
-
"file",
|
|
441
|
-
{
|
|
442
|
-
maxBytes:
|
|
443
|
-
5 * 1024 * 1024,
|
|
444
|
-
allowedTypes: [
|
|
445
|
-
"image/png",
|
|
446
|
-
"image/jpeg",
|
|
447
|
-
"image/webp",
|
|
448
|
-
],
|
|
449
|
-
}
|
|
450
|
-
);
|
|
451
|
-
```
|
|
186
|
+
## Uploads and streaming
|
|
452
187
|
|
|
453
|
-
BCP
|
|
188
|
+
BCP supports the original buffered multipart helpers as well as production multipart-to-storage streaming:
|
|
454
189
|
|
|
455
190
|
```ts
|
|
456
191
|
import {
|
|
@@ -462,35 +197,24 @@ const stored =
|
|
|
462
197
|
request,
|
|
463
198
|
{
|
|
464
199
|
storage,
|
|
465
|
-
fieldName:
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
"documents/report.pdf",
|
|
469
|
-
maxBytes:
|
|
470
|
-
100 * 1024 * 1024,
|
|
200
|
+
fieldName: "file",
|
|
201
|
+
key: "documents/report.pdf",
|
|
202
|
+
maxBytes: 100 * 1024 * 1024,
|
|
471
203
|
constraints: {
|
|
472
|
-
maxBytes:
|
|
473
|
-
80 * 1024 * 1024,
|
|
204
|
+
maxBytes: 80 * 1024 * 1024,
|
|
474
205
|
allowedTypes: [
|
|
475
206
|
"application/pdf",
|
|
476
207
|
],
|
|
477
|
-
allowedExtensions: [
|
|
478
|
-
".pdf",
|
|
479
|
-
],
|
|
480
208
|
},
|
|
481
209
|
}
|
|
482
210
|
);
|
|
483
211
|
```
|
|
484
212
|
|
|
485
|
-
`storeMultipartFile()`
|
|
486
|
-
|
|
487
|
-
The security gateway still applies `server.bodyLimit` / `BCP_BODY_LIMIT` as the outer request limit. Streaming controls memory use; it does not bypass proxy/platform/body limits.
|
|
488
|
-
|
|
489
|
-
MIME type and extension validation are metadata checks, not content-signature verification.
|
|
213
|
+
`storeMultipartFile()` consumes `Request.body` incrementally and streams the selected file directly into storage. Request/body infrastructure limits still apply.
|
|
490
214
|
|
|
491
215
|
Read more: [File Upload](docs/file-upload.md)
|
|
492
216
|
|
|
493
|
-
## Storage
|
|
217
|
+
## Storage
|
|
494
218
|
|
|
495
219
|
### Local filesystem
|
|
496
220
|
|
|
@@ -501,14 +225,13 @@ import {
|
|
|
501
225
|
|
|
502
226
|
const storage =
|
|
503
227
|
createLocalStorage({
|
|
504
|
-
directory:
|
|
505
|
-
"./uploads",
|
|
228
|
+
directory: "./uploads",
|
|
506
229
|
});
|
|
507
230
|
```
|
|
508
231
|
|
|
509
|
-
|
|
232
|
+
`create-bcp-app --storage local` creates `lib/storage.ts` plus a visible `storage/README.md` / `.gitkeep` scaffold. Runtime objects remain ignored by Git.
|
|
510
233
|
|
|
511
|
-
|
|
234
|
+
### S3 / R2 / MinIO
|
|
512
235
|
|
|
513
236
|
```ts
|
|
514
237
|
import {
|
|
@@ -517,40 +240,21 @@ import {
|
|
|
517
240
|
|
|
518
241
|
const storage =
|
|
519
242
|
createS3Storage({
|
|
520
|
-
bucket:
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
process.env.S3_ENDPOINT,
|
|
526
|
-
accessKeyId:
|
|
527
|
-
process.env.S3_ACCESS_KEY_ID,
|
|
528
|
-
secretAccessKey:
|
|
529
|
-
process.env.S3_SECRET_ACCESS_KEY,
|
|
243
|
+
bucket: process.env.S3_BUCKET!,
|
|
244
|
+
region: process.env.S3_REGION!,
|
|
245
|
+
endpoint: process.env.S3_ENDPOINT,
|
|
246
|
+
accessKeyId: process.env.S3_ACCESS_KEY_ID,
|
|
247
|
+
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
|
|
530
248
|
});
|
|
531
249
|
```
|
|
532
250
|
|
|
533
|
-
Custom endpoints and `forcePathStyle`
|
|
534
|
-
|
|
535
|
-
Keep cloud credentials server-only. Never expose them through `BCP_PUBLIC_*` variables.
|
|
251
|
+
Custom endpoints and `forcePathStyle` support common S3-compatible deployments such as MinIO. Keep credentials server-only.
|
|
536
252
|
|
|
537
253
|
Read more: [S3-Compatible Storage](docs/s3-storage.md)
|
|
538
254
|
|
|
539
255
|
### Streaming storage
|
|
540
256
|
|
|
541
|
-
The `StorageAdapter`
|
|
542
|
-
|
|
543
|
-
```text
|
|
544
|
-
put
|
|
545
|
-
putStream? ← 0.1.26
|
|
546
|
-
stat
|
|
547
|
-
read
|
|
548
|
-
readStream? ← 0.1.26
|
|
549
|
-
exists
|
|
550
|
-
delete
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
Use generic helpers so legacy and native-streaming adapters can share application code:
|
|
257
|
+
The stable minimum `StorageAdapter` retains its buffered API while adapters can expose optional `putStream()` and `readStream()` implementations.
|
|
554
258
|
|
|
555
259
|
```ts
|
|
556
260
|
import {
|
|
@@ -560,109 +264,122 @@ import {
|
|
|
560
264
|
} from "bcp/server";
|
|
561
265
|
```
|
|
562
266
|
|
|
563
|
-
Both built-in
|
|
564
|
-
|
|
565
|
-
Streaming writes support `maxBytes` and `AbortSignal`. Local incomplete objects are removed on failure; S3 multipart uploads are configured to clean up parts on failed completion.
|
|
267
|
+
Both built-in local and S3 adapters support streaming reads/writes and byte ranges.
|
|
566
268
|
|
|
567
269
|
Read more: [Storage and File Delivery](docs/storage.md)
|
|
568
270
|
|
|
569
|
-
|
|
271
|
+
### Storage Ecosystem — 0.1.27
|
|
272
|
+
|
|
273
|
+
BCP `0.1.27` adds a richer, additive object-storage API without invalidating older `StorageAdapter` implementations:
|
|
570
274
|
|
|
571
275
|
```ts
|
|
572
276
|
import {
|
|
573
|
-
|
|
277
|
+
copyStorageObject,
|
|
278
|
+
createStorageSignedReadUrl,
|
|
279
|
+
createStorageSignedWriteUrl,
|
|
280
|
+
deleteStorageObjects,
|
|
281
|
+
getStorageEcosystemCapabilities,
|
|
282
|
+
getStorageMetadata,
|
|
283
|
+
listStorageObjects,
|
|
284
|
+
moveStorageObject,
|
|
285
|
+
setStorageMetadata,
|
|
574
286
|
} from "bcp/server";
|
|
575
|
-
|
|
576
|
-
export function GET(
|
|
577
|
-
request: Request
|
|
578
|
-
) {
|
|
579
|
-
return createStorageResponse(
|
|
580
|
-
request,
|
|
581
|
-
storage,
|
|
582
|
-
"documents/report.pdf",
|
|
583
|
-
{
|
|
584
|
-
disposition:
|
|
585
|
-
"attachment",
|
|
586
|
-
downloadName:
|
|
587
|
-
"report.pdf",
|
|
588
|
-
}
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
287
|
```
|
|
592
288
|
|
|
593
|
-
|
|
289
|
+
Listing supports prefix filtering, limits and opaque cursors. Copy and move use native adapter operations when available, with portable fallback behavior where practical. Portable user metadata is string-to-string metadata and is distinct from authorization/business state.
|
|
594
290
|
|
|
595
|
-
-
|
|
596
|
-
- ETag / Last-Modified validators,
|
|
597
|
-
- `If-Range`,
|
|
598
|
-
- single byte ranges with `206 Partial Content`,
|
|
599
|
-
- `304 Not Modified`,
|
|
600
|
-
- `416 Range Not Satisfiable`,
|
|
601
|
-
- safe `Content-Disposition` filenames,
|
|
602
|
-
- configurable cache control.
|
|
291
|
+
S3-compatible storage also supports short-lived presigned direct-transfer URLs. Local storage intentionally does not emulate signed URLs.
|
|
603
292
|
|
|
604
|
-
|
|
293
|
+
Read more: [Storage Ecosystem](docs/storage-ecosystem.md)
|
|
605
294
|
|
|
606
|
-
|
|
607
|
-
private, max-age=0, must-revalidate
|
|
608
|
-
```
|
|
295
|
+
## File delivery
|
|
609
296
|
|
|
610
|
-
|
|
297
|
+
`createStorageResponse()` serves local or cloud-backed objects through the same API with `GET`, `HEAD`, ETag/Last-Modified validators and single byte ranges.
|
|
611
298
|
|
|
612
|
-
|
|
299
|
+
```ts
|
|
300
|
+
import {
|
|
301
|
+
createStorageResponse,
|
|
302
|
+
} from "bcp/server";
|
|
613
303
|
|
|
614
|
-
|
|
304
|
+
return createStorageResponse(
|
|
305
|
+
request,
|
|
306
|
+
storage,
|
|
307
|
+
"documents/report.pdf"
|
|
308
|
+
);
|
|
309
|
+
```
|
|
615
310
|
|
|
616
|
-
|
|
311
|
+
Read more: [Storage and File Delivery](docs/storage.md)
|
|
617
312
|
|
|
618
|
-
|
|
313
|
+
## Middleware, validation, errors and caching
|
|
619
314
|
|
|
620
|
-
|
|
315
|
+
BCP includes Middleware System v2, typed validation helpers, structured HTTP errors, structured logging and response caching/revalidation.
|
|
621
316
|
|
|
622
|
-
|
|
317
|
+
Read more:
|
|
623
318
|
|
|
624
|
-
|
|
319
|
+
- [Middleware](docs/middleware.md)
|
|
320
|
+
- [Validation](docs/validation.md)
|
|
321
|
+
- [Error Handling](docs/error-handling.md)
|
|
322
|
+
- [Logging and Observability](docs/development-logging.md)
|
|
323
|
+
- [Caching](docs/caching.md)
|
|
324
|
+
- [Security](docs/security.md)
|
|
625
325
|
|
|
626
|
-
|
|
326
|
+
## Production build
|
|
627
327
|
|
|
628
|
-
|
|
328
|
+
```bash
|
|
329
|
+
npm run build
|
|
330
|
+
npm run start
|
|
331
|
+
```
|
|
629
332
|
|
|
630
|
-
|
|
333
|
+
Standalone output is written under:
|
|
631
334
|
|
|
632
335
|
```text
|
|
633
|
-
bcp
|
|
336
|
+
.bcp-framework/build/
|
|
337
|
+
├─ client/
|
|
338
|
+
├─ public/
|
|
339
|
+
└─ server/
|
|
340
|
+
└─ server.mjs
|
|
634
341
|
```
|
|
635
342
|
|
|
636
|
-
|
|
343
|
+
### Production Hardening — 0.1.28
|
|
637
344
|
|
|
638
|
-
|
|
639
|
-
BCP_PUBLIC_
|
|
640
|
-
```
|
|
345
|
+
The standalone runtime is wrapped by a public hardening gateway with configurable HTTP and shutdown behavior:
|
|
641
346
|
|
|
642
|
-
|
|
347
|
+
```dotenv
|
|
348
|
+
BCP_REQUEST_TIMEOUT_MS=120000
|
|
349
|
+
BCP_HEADERS_TIMEOUT_MS=66000
|
|
350
|
+
BCP_KEEP_ALIVE_TIMEOUT_MS=65000
|
|
351
|
+
BCP_SHUTDOWN_TIMEOUT_MS=10000
|
|
352
|
+
BCP_TRUST_PROXY=false
|
|
353
|
+
```
|
|
643
354
|
|
|
644
|
-
|
|
355
|
+
Standalone production handles `SIGTERM` and `SIGINT`, drains the public listener, runs application cleanup hooks and then stops internal runtime layers.
|
|
645
356
|
|
|
646
|
-
|
|
357
|
+
```ts
|
|
358
|
+
import {
|
|
359
|
+
registerShutdownHook,
|
|
360
|
+
} from "bcp/server";
|
|
647
361
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
362
|
+
registerShutdownHook(
|
|
363
|
+
() => {
|
|
364
|
+
storage.destroy();
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: "storage",
|
|
368
|
+
}
|
|
369
|
+
);
|
|
651
370
|
```
|
|
652
371
|
|
|
653
|
-
|
|
372
|
+
Trusted proxy mode is disabled by default. Enable `BCP_TRUST_PROXY=true` only when direct untrusted traffic cannot bypass the trusted reverse proxy/load balancer.
|
|
654
373
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
Read more: [Developer Tools](docs/developer-tools.md)
|
|
374
|
+
Read more: [Production Hardening](docs/production-hardening.md)
|
|
658
375
|
|
|
659
376
|
## Windows CLI
|
|
660
377
|
|
|
661
|
-
Microsoft SQL Server can install another executable named `bcp.exe`. BCP
|
|
378
|
+
Microsoft SQL Server can install another executable named `bcp.exe`. BCP publishes the collision-free alias `bcp-framework`.
|
|
662
379
|
|
|
663
|
-
Inside npm scripts, `bcp` is safe because npm prepends
|
|
380
|
+
Inside project npm scripts, `bcp` is safe because npm prepends `node_modules/.bin` to `PATH`.
|
|
664
381
|
|
|
665
|
-
For direct PowerShell usage
|
|
382
|
+
For direct PowerShell usage:
|
|
666
383
|
|
|
667
384
|
```powershell
|
|
668
385
|
npm exec -- bcp-framework --version
|
|
@@ -673,62 +390,7 @@ npm exec -- bcp-framework dev
|
|
|
673
390
|
npm exec -- bcp-framework build
|
|
674
391
|
```
|
|
675
392
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
## CLI reference
|
|
679
|
-
|
|
680
|
-
```bash
|
|
681
|
-
bcp dev
|
|
682
|
-
bcp routes
|
|
683
|
-
bcp build
|
|
684
|
-
bcp start
|
|
685
|
-
bcp doctor
|
|
686
|
-
bcp doctor --json
|
|
687
|
-
bcp inspect
|
|
688
|
-
bcp inspect --json
|
|
689
|
-
bcp update
|
|
690
|
-
bcp version
|
|
691
|
-
|
|
692
|
-
bcp db create create_users
|
|
693
|
-
bcp db migrate
|
|
694
|
-
bcp db status
|
|
695
|
-
bcp db rollback
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
## Development behavior
|
|
699
|
-
|
|
700
|
-
BCP includes Fast Refresh and deterministic development hydration behavior. Recent stabilization work covers:
|
|
701
|
-
|
|
702
|
-
- Windows line-ending parity,
|
|
703
|
-
- multiline JSX hydration parity,
|
|
704
|
-
- duplicate BCP installation detection,
|
|
705
|
-
- automatic page-route/client-bundle graph resynchronization,
|
|
706
|
-
- standalone authentication guard request-context parity.
|
|
707
|
-
|
|
708
|
-
Read more: [Hydration](docs/hydration.md)
|
|
709
|
-
|
|
710
|
-
## Production build
|
|
711
|
-
|
|
712
|
-
```bash
|
|
713
|
-
npm run build
|
|
714
|
-
npm run start
|
|
715
|
-
```
|
|
716
|
-
|
|
717
|
-
Production output:
|
|
718
|
-
|
|
719
|
-
```text
|
|
720
|
-
.bcp-framework/build/
|
|
721
|
-
├─ client/
|
|
722
|
-
├─ public/
|
|
723
|
-
└─ server/
|
|
724
|
-
└─ server.mjs
|
|
725
|
-
```
|
|
726
|
-
|
|
727
|
-
The standalone runtime composes production middleware, security, cache, actions, guards, loaders and page rendering into the final HTTP pipeline.
|
|
728
|
-
|
|
729
|
-
Runtime hostname/port overrides can be supplied to `bcp start` without rebuilding.
|
|
730
|
-
|
|
731
|
-
Read more: [Deployment](docs/deployment.md)
|
|
393
|
+
Read more: [Developer Tools](docs/developer-tools.md)
|
|
732
394
|
|
|
733
395
|
## Updating BCP
|
|
734
396
|
|
|
@@ -736,22 +398,14 @@ Read more: [Deployment](docs/deployment.md)
|
|
|
736
398
|
bcp update
|
|
737
399
|
bcp update --check
|
|
738
400
|
bcp update --dry-run
|
|
739
|
-
bcp update 0.1.
|
|
401
|
+
bcp update 0.1.28
|
|
740
402
|
bcp update next
|
|
741
403
|
```
|
|
742
404
|
|
|
743
|
-
Projects created before the updater was introduced can bootstrap it once with:
|
|
744
|
-
|
|
745
|
-
```bash
|
|
746
|
-
npx @chidchanun/bcp@latest update
|
|
747
|
-
```
|
|
748
|
-
|
|
749
405
|
Read more: [Updating](docs/updating.md)
|
|
750
406
|
|
|
751
407
|
## Framework development and release validation
|
|
752
408
|
|
|
753
|
-
Inside the BCP Framework repository:
|
|
754
|
-
|
|
755
409
|
```bash
|
|
756
410
|
npm install
|
|
757
411
|
npm run typecheck
|
|
@@ -759,70 +413,41 @@ npm run test:unit
|
|
|
759
413
|
npm run test:integration
|
|
760
414
|
npm run test:e2e
|
|
761
415
|
npm run test:package
|
|
762
|
-
```
|
|
763
|
-
|
|
764
|
-
Full release-candidate validation:
|
|
765
|
-
|
|
766
|
-
```bash
|
|
767
416
|
npm run rc:check
|
|
768
417
|
```
|
|
769
418
|
|
|
770
|
-
A version must not be tagged or published until RC and packed-package verification pass.
|
|
419
|
+
A version must not be tagged or published until its RC and packed-package verification pass.
|
|
771
420
|
|
|
772
421
|
Read more: [Releasing](docs/releasing.md)
|
|
773
422
|
|
|
774
423
|
## Documentation source
|
|
775
424
|
|
|
776
|
-
The `docs/` directory is the source
|
|
425
|
+
The `docs/` directory is the authored source for **`bcp-docs-web`**.
|
|
777
426
|
|
|
778
|
-
|
|
427
|
+
Recommended starting points:
|
|
779
428
|
|
|
780
429
|
- [Documentation Source Map](docs/README.md)
|
|
781
430
|
- [Getting Started](docs/getting-started.md)
|
|
782
431
|
- [Configuration](docs/configuration.md)
|
|
783
|
-
- [Application Modules](docs/application-modules.md)
|
|
784
432
|
- [Routing](docs/routing.md)
|
|
785
433
|
- [Server Data Loaders](docs/server-data-loaders.md)
|
|
786
434
|
- [Route Guards](docs/route-guards.md)
|
|
787
435
|
- [Form Actions](docs/form-actions.md)
|
|
788
436
|
- [Server Request APIs](docs/server-request-apis.md)
|
|
789
|
-
- [Validation](docs/validation.md)
|
|
790
|
-
- [Error Handling](docs/error-handling.md)
|
|
791
437
|
- [File Upload](docs/file-upload.md)
|
|
792
438
|
- [Storage and File Delivery](docs/storage.md)
|
|
439
|
+
- [Storage Ecosystem](docs/storage-ecosystem.md)
|
|
793
440
|
- [S3-Compatible Storage](docs/s3-storage.md)
|
|
441
|
+
- [Production Hardening](docs/production-hardening.md)
|
|
794
442
|
- [Authentication](docs/authentication.md)
|
|
795
|
-
- [Auth Route Guards](docs/auth-route-guards.md)
|
|
796
|
-
- [JWT Sessions](docs/session-auth.md)
|
|
797
443
|
- [Database](docs/database.md)
|
|
798
|
-
- [Database Migrations](docs/database-migrations.md)
|
|
799
444
|
- [Middleware](docs/middleware.md)
|
|
800
|
-
- [Hydration](docs/hydration.md)
|
|
801
445
|
- [Developer Tools](docs/developer-tools.md)
|
|
802
|
-
- [Logging and Observability](docs/development-logging.md)
|
|
803
|
-
- [Caching](docs/caching.md)
|
|
804
|
-
- [Security](docs/security.md)
|
|
805
446
|
- [Deployment](docs/deployment.md)
|
|
806
|
-
- [Updating](docs/updating.md)
|
|
807
447
|
- [Releasing](docs/releasing.md)
|
|
808
448
|
|
|
809
|
-
Recommended `bcp-docs-web` top-level navigation:
|
|
810
|
-
|
|
811
|
-
```text
|
|
812
|
-
Getting Started
|
|
813
|
-
Routing & Data
|
|
814
|
-
Authentication
|
|
815
|
-
Database
|
|
816
|
-
Runtime & Infrastructure
|
|
817
|
-
Storage & Uploads
|
|
818
|
-
API Reference
|
|
819
|
-
Releases
|
|
820
|
-
```
|
|
821
|
-
|
|
822
449
|
## Release history
|
|
823
450
|
|
|
824
|
-
Release notes live under `docs/releases/`.
|
|
825
|
-
|
|
826
451
|
| Version | Milestone |
|
|
827
452
|
| --- | --- |
|
|
828
453
|
| `0.1.20` | Hydration line-ending stabilization |
|
|
@@ -832,17 +457,20 @@ Release notes live under `docs/releases/`.
|
|
|
832
457
|
| `0.1.24` | File Upload Foundation |
|
|
833
458
|
| `0.1.25` | Storage Adapters and File Delivery |
|
|
834
459
|
| `0.1.26` | S3-Compatible Storage and Production Streaming |
|
|
460
|
+
| `0.1.27` | Storage Ecosystem |
|
|
461
|
+
| `0.1.28` | Production Hardening |
|
|
835
462
|
|
|
836
463
|
## Next direction
|
|
837
464
|
|
|
838
|
-
After `0.1.
|
|
465
|
+
After `0.1.28`, the planned milestone is **`0.1.29 — Developer Experience`**:
|
|
839
466
|
|
|
840
|
-
1.
|
|
841
|
-
2.
|
|
842
|
-
3.
|
|
843
|
-
4.
|
|
467
|
+
1. route/API/middleware/migration generators,
|
|
468
|
+
2. richer `doctor` and `inspect` diagnostics,
|
|
469
|
+
3. clearer build/runtime error messages,
|
|
470
|
+
4. improved create-app presets and automation flags,
|
|
471
|
+
5. production configuration diagnostics.
|
|
844
472
|
|
|
845
|
-
These are roadmap items, not `0.1.
|
|
473
|
+
These are roadmap items, not `0.1.28` guarantees.
|
|
846
474
|
|
|
847
475
|
## License
|
|
848
476
|
|