@chidchanun/bcp 0.1.26 → 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 +176 -518
- package/docs/README.md +71 -59
- package/docs/releases/0.1.27.md +350 -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 +33 -2
- 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.27`
|
|
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.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
|
|
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,7 +22,7 @@ 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
28
|
| Production | Standalone server build with production middleware pipeline |
|
|
@@ -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
|
-
|
|
175
|
-
Static routes have priority over dynamic and catch-all routes.
|
|
176
|
-
|
|
177
|
-
API routes use `route.ts`:
|
|
178
111
|
|
|
179
|
-
|
|
180
|
-
app/api/users/route.ts
|
|
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
|
-
##
|
|
118
|
+
## Server data, guards and actions
|
|
187
119
|
|
|
188
|
-
|
|
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)
|
|
228
|
-
|
|
229
|
-
## Route guards
|
|
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
|
-
|
|
285
|
-
Lower-level JWT cookie session primitives:
|
|
286
146
|
|
|
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
|
-
##
|
|
186
|
+
## Uploads and streaming
|
|
390
187
|
|
|
391
|
-
|
|
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
|
-
```
|
|
452
|
-
|
|
453
|
-
BCP `0.1.26` adds direct multipart-to-storage streaming for larger production uploads:
|
|
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,11 @@ import {
|
|
|
501
225
|
|
|
502
226
|
const storage =
|
|
503
227
|
createLocalStorage({
|
|
504
|
-
directory:
|
|
505
|
-
"./uploads",
|
|
228
|
+
directory: "./uploads",
|
|
506
229
|
});
|
|
507
230
|
```
|
|
508
231
|
|
|
509
|
-
### S3
|
|
510
|
-
|
|
511
|
-
BCP `0.1.26` adds an S3-compatible backend:
|
|
232
|
+
### S3 / R2 / MinIO
|
|
512
233
|
|
|
513
234
|
```ts
|
|
514
235
|
import {
|
|
@@ -517,40 +238,21 @@ import {
|
|
|
517
238
|
|
|
518
239
|
const storage =
|
|
519
240
|
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,
|
|
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,
|
|
530
246
|
});
|
|
531
247
|
```
|
|
532
248
|
|
|
533
|
-
Custom endpoints and `forcePathStyle`
|
|
534
|
-
|
|
535
|
-
Keep cloud credentials server-only. Never expose them through `BCP_PUBLIC_*` variables.
|
|
249
|
+
Custom endpoints and `forcePathStyle` support common S3-compatible deployments such as MinIO. Keep credentials server-only.
|
|
536
250
|
|
|
537
251
|
Read more: [S3-Compatible Storage](docs/s3-storage.md)
|
|
538
252
|
|
|
539
253
|
### Streaming storage
|
|
540
254
|
|
|
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:
|
|
255
|
+
The stable minimum `StorageAdapter` retains its buffered API while adapters can expose optional `putStream()` and `readStream()` implementations.
|
|
554
256
|
|
|
555
257
|
```ts
|
|
556
258
|
import {
|
|
@@ -560,152 +262,127 @@ import {
|
|
|
560
262
|
} from "bcp/server";
|
|
561
263
|
```
|
|
562
264
|
|
|
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.
|
|
265
|
+
Both built-in local and S3 adapters support streaming reads/writes and byte ranges.
|
|
566
266
|
|
|
567
267
|
Read more: [Storage and File Delivery](docs/storage.md)
|
|
568
268
|
|
|
569
|
-
|
|
269
|
+
### Storage Ecosystem — 0.1.27
|
|
270
|
+
|
|
271
|
+
BCP `0.1.27` adds a richer, additive object-storage API without invalidating older `StorageAdapter` implementations:
|
|
570
272
|
|
|
571
273
|
```ts
|
|
572
274
|
import {
|
|
573
|
-
|
|
275
|
+
copyStorageObject,
|
|
276
|
+
createStorageSignedReadUrl,
|
|
277
|
+
createStorageSignedWriteUrl,
|
|
278
|
+
deleteStorageObjects,
|
|
279
|
+
getStorageEcosystemCapabilities,
|
|
280
|
+
getStorageMetadata,
|
|
281
|
+
listStorageObjects,
|
|
282
|
+
moveStorageObject,
|
|
283
|
+
setStorageMetadata,
|
|
574
284
|
} from "bcp/server";
|
|
285
|
+
```
|
|
575
286
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
287
|
+
Listing supports prefix filtering, limits and opaque cursors:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
const page =
|
|
291
|
+
await listStorageObjects(
|
|
581
292
|
storage,
|
|
582
|
-
"documents/report.pdf",
|
|
583
293
|
{
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
downloadName:
|
|
587
|
-
"report.pdf",
|
|
294
|
+
prefix: "documents/",
|
|
295
|
+
limit: 50,
|
|
588
296
|
}
|
|
589
297
|
);
|
|
590
|
-
}
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
`createStorageResponse()` uses storage streaming and supports:
|
|
594
|
-
|
|
595
|
-
- `GET` / `HEAD`,
|
|
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.
|
|
603
|
-
|
|
604
|
-
The default cache policy remains:
|
|
605
|
-
|
|
606
|
-
```text
|
|
607
|
-
private, max-age=0, must-revalidate
|
|
608
298
|
```
|
|
609
299
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
Read more: [Storage and File Delivery](docs/storage.md)
|
|
613
|
-
|
|
614
|
-
## Caching
|
|
615
|
-
|
|
616
|
-
BCP includes response caching and revalidation primitives used by development and standalone production runtimes.
|
|
617
|
-
|
|
618
|
-
Read more: [Caching](docs/caching.md)
|
|
619
|
-
|
|
620
|
-
## Security
|
|
621
|
-
|
|
622
|
-
The framework security layer includes request body limits and production request handling defaults. Application authorization remains the responsibility of route guards and application logic.
|
|
623
|
-
|
|
624
|
-
Storage keys, filenames and MIME metadata must not be treated as authorization decisions.
|
|
625
|
-
|
|
626
|
-
Read more: [Security](docs/security.md)
|
|
627
|
-
|
|
628
|
-
## Environment and configuration
|
|
300
|
+
Copy and move use native adapter operations when available, with portable fallback behavior where practical:
|
|
629
301
|
|
|
630
|
-
|
|
302
|
+
```ts
|
|
303
|
+
await copyStorageObject(
|
|
304
|
+
storage,
|
|
305
|
+
"incoming/report.pdf",
|
|
306
|
+
"archive/report.pdf"
|
|
307
|
+
);
|
|
631
308
|
|
|
632
|
-
|
|
633
|
-
|
|
309
|
+
await moveStorageObject(
|
|
310
|
+
storage,
|
|
311
|
+
"tmp/avatar.webp",
|
|
312
|
+
"users/42/avatar.webp"
|
|
313
|
+
);
|
|
634
314
|
```
|
|
635
315
|
|
|
636
|
-
|
|
316
|
+
Portable user metadata is string-to-string metadata and is distinct from application authorization/business state:
|
|
637
317
|
|
|
638
|
-
```
|
|
639
|
-
|
|
318
|
+
```ts
|
|
319
|
+
await setStorageMetadata(
|
|
320
|
+
storage,
|
|
321
|
+
"archive/report.pdf",
|
|
322
|
+
{
|
|
323
|
+
owner: "user-42",
|
|
324
|
+
status: "approved",
|
|
325
|
+
}
|
|
326
|
+
);
|
|
640
327
|
```
|
|
641
328
|
|
|
642
|
-
|
|
329
|
+
S3-compatible storage also supports short-lived presigned direct-transfer URLs:
|
|
643
330
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
331
|
+
```ts
|
|
332
|
+
const downloadUrl =
|
|
333
|
+
await createStorageSignedReadUrl(
|
|
334
|
+
storage,
|
|
335
|
+
"videos/demo.mp4",
|
|
336
|
+
{
|
|
337
|
+
expiresIn: 300,
|
|
338
|
+
}
|
|
339
|
+
);
|
|
647
340
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
341
|
+
const uploadUrl =
|
|
342
|
+
await createStorageSignedWriteUrl(
|
|
343
|
+
storage,
|
|
344
|
+
"uploads/demo.mp4",
|
|
345
|
+
{
|
|
346
|
+
expiresIn: 300,
|
|
347
|
+
contentType: "video/mp4",
|
|
348
|
+
}
|
|
349
|
+
);
|
|
651
350
|
```
|
|
652
351
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
`bcp inspect` reports resolved configuration, environment sources, dependency versions and discovered routes.
|
|
656
|
-
|
|
657
|
-
Read more: [Developer Tools](docs/developer-tools.md)
|
|
352
|
+
Local storage intentionally does not emulate signed URLs. Signed URLs are temporary credentials and should only be generated after application authorization.
|
|
658
353
|
|
|
659
|
-
|
|
354
|
+
Read more: [Storage Ecosystem](docs/storage-ecosystem.md)
|
|
660
355
|
|
|
661
|
-
|
|
356
|
+
## File delivery
|
|
662
357
|
|
|
663
|
-
|
|
358
|
+
`createStorageResponse()` serves local or cloud-backed objects through the same API with `GET`, `HEAD`, ETag/Last-Modified validators and single byte ranges.
|
|
664
359
|
|
|
665
|
-
|
|
360
|
+
```ts
|
|
361
|
+
import {
|
|
362
|
+
createStorageResponse,
|
|
363
|
+
} from "bcp/server";
|
|
666
364
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
npm exec -- bcp-framework dev
|
|
673
|
-
npm exec -- bcp-framework build
|
|
365
|
+
return createStorageResponse(
|
|
366
|
+
request,
|
|
367
|
+
storage,
|
|
368
|
+
"documents/report.pdf"
|
|
369
|
+
);
|
|
674
370
|
```
|
|
675
371
|
|
|
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
|
-
```
|
|
372
|
+
Read more: [Storage and File Delivery](docs/storage.md)
|
|
697
373
|
|
|
698
|
-
##
|
|
374
|
+
## Middleware, validation, errors and caching
|
|
699
375
|
|
|
700
|
-
BCP includes
|
|
376
|
+
BCP includes Middleware System v2, typed validation helpers, structured HTTP errors, structured logging and response caching/revalidation.
|
|
701
377
|
|
|
702
|
-
|
|
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.
|
|
378
|
+
Read more:
|
|
707
379
|
|
|
708
|
-
|
|
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)
|
|
709
386
|
|
|
710
387
|
## Production build
|
|
711
388
|
|
|
@@ -714,7 +391,7 @@ npm run build
|
|
|
714
391
|
npm run start
|
|
715
392
|
```
|
|
716
393
|
|
|
717
|
-
|
|
394
|
+
Standalone output is written under:
|
|
718
395
|
|
|
719
396
|
```text
|
|
720
397
|
.bcp-framework/build/
|
|
@@ -724,11 +401,26 @@ Production output:
|
|
|
724
401
|
└─ server.mjs
|
|
725
402
|
```
|
|
726
403
|
|
|
727
|
-
|
|
404
|
+
Read more: [Deployment](docs/deployment.md)
|
|
728
405
|
|
|
729
|
-
|
|
406
|
+
## Windows CLI
|
|
730
407
|
|
|
731
|
-
|
|
408
|
+
Microsoft SQL Server can install another executable named `bcp.exe`. BCP publishes the collision-free alias `bcp-framework`.
|
|
409
|
+
|
|
410
|
+
Inside project npm scripts, `bcp` is safe because npm prepends `node_modules/.bin` to `PATH`.
|
|
411
|
+
|
|
412
|
+
For direct PowerShell usage:
|
|
413
|
+
|
|
414
|
+
```powershell
|
|
415
|
+
npm exec -- bcp-framework --version
|
|
416
|
+
npm exec -- bcp-framework doctor
|
|
417
|
+
npm exec -- bcp-framework inspect
|
|
418
|
+
npm exec -- bcp-framework routes
|
|
419
|
+
npm exec -- bcp-framework dev
|
|
420
|
+
npm exec -- bcp-framework build
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Read more: [Developer Tools](docs/developer-tools.md)
|
|
732
424
|
|
|
733
425
|
## Updating BCP
|
|
734
426
|
|
|
@@ -736,22 +428,14 @@ Read more: [Deployment](docs/deployment.md)
|
|
|
736
428
|
bcp update
|
|
737
429
|
bcp update --check
|
|
738
430
|
bcp update --dry-run
|
|
739
|
-
bcp update 0.1.
|
|
431
|
+
bcp update 0.1.27
|
|
740
432
|
bcp update next
|
|
741
433
|
```
|
|
742
434
|
|
|
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
435
|
Read more: [Updating](docs/updating.md)
|
|
750
436
|
|
|
751
437
|
## Framework development and release validation
|
|
752
438
|
|
|
753
|
-
Inside the BCP Framework repository:
|
|
754
|
-
|
|
755
439
|
```bash
|
|
756
440
|
npm install
|
|
757
441
|
npm run typecheck
|
|
@@ -759,70 +443,40 @@ npm run test:unit
|
|
|
759
443
|
npm run test:integration
|
|
760
444
|
npm run test:e2e
|
|
761
445
|
npm run test:package
|
|
762
|
-
```
|
|
763
|
-
|
|
764
|
-
Full release-candidate validation:
|
|
765
|
-
|
|
766
|
-
```bash
|
|
767
446
|
npm run rc:check
|
|
768
447
|
```
|
|
769
448
|
|
|
770
|
-
A version must not be tagged or published until RC and packed-package verification pass.
|
|
449
|
+
A version must not be tagged or published until its RC and packed-package verification pass.
|
|
771
450
|
|
|
772
451
|
Read more: [Releasing](docs/releasing.md)
|
|
773
452
|
|
|
774
453
|
## Documentation source
|
|
775
454
|
|
|
776
|
-
The `docs/` directory is the source
|
|
455
|
+
The `docs/` directory is the authored source for **`bcp-docs-web`**.
|
|
777
456
|
|
|
778
|
-
|
|
457
|
+
Recommended starting points:
|
|
779
458
|
|
|
780
459
|
- [Documentation Source Map](docs/README.md)
|
|
781
460
|
- [Getting Started](docs/getting-started.md)
|
|
782
461
|
- [Configuration](docs/configuration.md)
|
|
783
|
-
- [Application Modules](docs/application-modules.md)
|
|
784
462
|
- [Routing](docs/routing.md)
|
|
785
463
|
- [Server Data Loaders](docs/server-data-loaders.md)
|
|
786
464
|
- [Route Guards](docs/route-guards.md)
|
|
787
465
|
- [Form Actions](docs/form-actions.md)
|
|
788
466
|
- [Server Request APIs](docs/server-request-apis.md)
|
|
789
|
-
- [Validation](docs/validation.md)
|
|
790
|
-
- [Error Handling](docs/error-handling.md)
|
|
791
467
|
- [File Upload](docs/file-upload.md)
|
|
792
468
|
- [Storage and File Delivery](docs/storage.md)
|
|
469
|
+
- [Storage Ecosystem](docs/storage-ecosystem.md)
|
|
793
470
|
- [S3-Compatible Storage](docs/s3-storage.md)
|
|
794
471
|
- [Authentication](docs/authentication.md)
|
|
795
|
-
- [Auth Route Guards](docs/auth-route-guards.md)
|
|
796
|
-
- [JWT Sessions](docs/session-auth.md)
|
|
797
472
|
- [Database](docs/database.md)
|
|
798
|
-
- [Database Migrations](docs/database-migrations.md)
|
|
799
473
|
- [Middleware](docs/middleware.md)
|
|
800
|
-
- [Hydration](docs/hydration.md)
|
|
801
474
|
- [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
475
|
- [Deployment](docs/deployment.md)
|
|
806
|
-
- [Updating](docs/updating.md)
|
|
807
476
|
- [Releasing](docs/releasing.md)
|
|
808
477
|
|
|
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
478
|
## Release history
|
|
823
479
|
|
|
824
|
-
Release notes live under `docs/releases/`.
|
|
825
|
-
|
|
826
480
|
| Version | Milestone |
|
|
827
481
|
| --- | --- |
|
|
828
482
|
| `0.1.20` | Hydration line-ending stabilization |
|
|
@@ -832,17 +486,21 @@ Release notes live under `docs/releases/`.
|
|
|
832
486
|
| `0.1.24` | File Upload Foundation |
|
|
833
487
|
| `0.1.25` | Storage Adapters and File Delivery |
|
|
834
488
|
| `0.1.26` | S3-Compatible Storage and Production Streaming |
|
|
489
|
+
| `0.1.27` | Storage Ecosystem |
|
|
835
490
|
|
|
836
491
|
## Next direction
|
|
837
492
|
|
|
838
|
-
After `0.1.
|
|
493
|
+
After `0.1.27`, the planned milestone is **`0.1.28 — Production Hardening`**:
|
|
839
494
|
|
|
840
|
-
1.
|
|
841
|
-
2.
|
|
842
|
-
3.
|
|
843
|
-
4.
|
|
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.
|
|
844
502
|
|
|
845
|
-
These are roadmap items, not `0.1.
|
|
503
|
+
These are roadmap items, not `0.1.27` guarantees.
|
|
846
504
|
|
|
847
505
|
## License
|
|
848
506
|
|