@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 +211 -647
- package/docs/README.md +160 -210
- package/docs/file-upload.md +194 -32
- package/docs/releases/0.1.26.md +281 -0
- package/docs/releases/0.1.27.md +350 -0
- package/docs/s3-storage.md +433 -0
- package/docs/storage-ecosystem.md +434 -0
- package/docs/storage.md +278 -162
- package/package.json +6 -2
- package/packages/client/src/database.mjs +236 -0
- package/packages/client/src/server.ts +54 -1
- package/packages/server/src/file-delivery.ts +8 -24
- package/packages/server/src/storage-ecosystem.ts +1326 -0
- package/packages/server/src/storage-s3-ecosystem.ts +947 -0
- package/packages/server/src/storage-s3.ts +1159 -0
- package/packages/server/src/storage.ts +740 -54
- package/packages/server/src/upload-stream.ts +846 -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,
|
|
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.
|
|
5
|
+
> **Development target:** `0.1.27`
|
|
6
6
|
>
|
|
7
|
-
> BCP is
|
|
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
|
|
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 |
|
|
61
|
-
| Storage |
|
|
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
|
-
|
|
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
|
-
|
|
64
|
+
Keep only one framework dependency named `bcp`. Installing both `bcp` and `@chidchanun/bcp` directly can load duplicate framework/React contexts.
|
|
105
65
|
|
|
106
|
-
|
|
66
|
+
## Application model
|
|
107
67
|
|
|
108
68
|
```text
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
148
|
+
cookies,
|
|
149
|
+
headers,
|
|
150
|
+
requestId,
|
|
151
|
+
requestMethod,
|
|
152
|
+
requestUrl,
|
|
317
153
|
} from "bcp/server";
|
|
318
154
|
```
|
|
319
155
|
|
|
320
|
-
|
|
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
|
|
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
|
-
##
|
|
186
|
+
## Uploads and streaming
|
|
431
187
|
|
|
432
|
-
|
|
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
|
-
|
|
437
|
-
requestLogger,
|
|
192
|
+
storeMultipartFile,
|
|
438
193
|
} from "bcp/server";
|
|
439
194
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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
|
-
|
|
213
|
+
`storeMultipartFile()` consumes `Request.body` incrementally and streams the selected file directly into storage. Request/body infrastructure limits still apply.
|
|
470
214
|
|
|
471
|
-
|
|
472
|
-
BCP_LOG_LEVEL=debug
|
|
473
|
-
BCP_LOG_FORMAT=json
|
|
474
|
-
```
|
|
215
|
+
Read more: [File Upload](docs/file-upload.md)
|
|
475
216
|
|
|
476
|
-
|
|
217
|
+
## Storage
|
|
477
218
|
|
|
478
|
-
|
|
479
|
-
debug
|
|
480
|
-
info
|
|
481
|
-
warn
|
|
482
|
-
error
|
|
483
|
-
silent
|
|
484
|
-
```
|
|
219
|
+
### Local filesystem
|
|
485
220
|
|
|
486
|
-
|
|
221
|
+
```ts
|
|
222
|
+
import {
|
|
223
|
+
createLocalStorage,
|
|
224
|
+
} from "bcp/server";
|
|
487
225
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
226
|
+
const storage =
|
|
227
|
+
createLocalStorage({
|
|
228
|
+
directory: "./uploads",
|
|
229
|
+
});
|
|
491
230
|
```
|
|
492
231
|
|
|
493
|
-
|
|
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
|
-
|
|
502
|
-
requireUploadedFile,
|
|
503
|
-
saveUploadedFile,
|
|
236
|
+
createS3Storage,
|
|
504
237
|
} from "bcp/server";
|
|
505
238
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
-
|
|
265
|
+
Both built-in local and S3 adapters support streaming reads/writes and byte ranges.
|
|
579
266
|
|
|
580
|
-
Read more: [File
|
|
267
|
+
Read more: [Storage and File Delivery](docs/storage.md)
|
|
581
268
|
|
|
582
|
-
|
|
269
|
+
### Storage Ecosystem — 0.1.27
|
|
583
270
|
|
|
584
|
-
BCP `0.1.
|
|
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
|
-
|
|
589
|
-
|
|
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
|
-
|
|
593
|
-
createLocalStorage({
|
|
594
|
-
directory:
|
|
595
|
-
"./uploads",
|
|
596
|
-
});
|
|
287
|
+
Listing supports prefix filtering, limits and opaque cursors:
|
|
597
288
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
289
|
+
```ts
|
|
290
|
+
const page =
|
|
291
|
+
await listStorageObjects(
|
|
292
|
+
storage,
|
|
601
293
|
{
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
"avatars/user-101.webp",
|
|
294
|
+
prefix: "documents/",
|
|
295
|
+
limit: 50,
|
|
605
296
|
}
|
|
606
297
|
);
|
|
607
298
|
```
|
|
608
299
|
|
|
609
|
-
|
|
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
|
-
|
|
302
|
+
```ts
|
|
303
|
+
await copyStorageObject(
|
|
304
|
+
storage,
|
|
305
|
+
"incoming/report.pdf",
|
|
306
|
+
"archive/report.pdf"
|
|
307
|
+
);
|
|
620
308
|
|
|
621
|
-
|
|
309
|
+
await moveStorageObject(
|
|
310
|
+
storage,
|
|
311
|
+
"tmp/avatar.webp",
|
|
312
|
+
"users/42/avatar.webp"
|
|
313
|
+
);
|
|
314
|
+
```
|
|
622
315
|
|
|
623
|
-
|
|
316
|
+
Portable user metadata is string-to-string metadata and is distinct from application authorization/business state:
|
|
624
317
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
-
|
|
329
|
+
S3-compatible storage also supports short-lived presigned direct-transfer URLs:
|
|
630
330
|
|
|
631
331
|
```ts
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
332
|
+
const downloadUrl =
|
|
333
|
+
await createStorageSignedReadUrl(
|
|
334
|
+
storage,
|
|
335
|
+
"videos/demo.mp4",
|
|
336
|
+
{
|
|
337
|
+
expiresIn: 300,
|
|
338
|
+
}
|
|
339
|
+
);
|
|
635
340
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
) {
|
|
639
|
-
return createStorageResponse(
|
|
640
|
-
request,
|
|
341
|
+
const uploadUrl =
|
|
342
|
+
await createStorageSignedWriteUrl(
|
|
641
343
|
storage,
|
|
642
|
-
"
|
|
344
|
+
"uploads/demo.mp4",
|
|
643
345
|
{
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
downloadName:
|
|
647
|
-
"report.pdf",
|
|
346
|
+
expiresIn: 300,
|
|
347
|
+
contentType: "video/mp4",
|
|
648
348
|
}
|
|
649
349
|
);
|
|
650
|
-
}
|
|
651
350
|
```
|
|
652
351
|
|
|
653
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
356
|
+
## File delivery
|
|
668
357
|
|
|
669
|
-
|
|
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
|
-
|
|
360
|
+
```ts
|
|
361
|
+
import {
|
|
362
|
+
createStorageResponse,
|
|
363
|
+
} from "bcp/server";
|
|
674
364
|
|
|
675
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
376
|
+
BCP includes Middleware System v2, typed validation helpers, structured HTTP errors, structured logging and response caching/revalidation.
|
|
690
377
|
|
|
691
|
-
Read more:
|
|
378
|
+
Read more:
|
|
692
379
|
|
|
693
|
-
|
|
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
|
-
|
|
387
|
+
## Production build
|
|
696
388
|
|
|
697
|
-
```
|
|
698
|
-
|
|
389
|
+
```bash
|
|
390
|
+
npm run build
|
|
391
|
+
npm run start
|
|
699
392
|
```
|
|
700
393
|
|
|
701
|
-
|
|
394
|
+
Standalone output is written under:
|
|
702
395
|
|
|
703
396
|
```text
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
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
|
-
|
|
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
|
|
408
|
+
Microsoft SQL Server can install another executable named `bcp.exe`. BCP publishes the collision-free alias `bcp-framework`.
|
|
737
409
|
|
|
738
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
455
|
+
The `docs/` directory is the authored source for **`bcp-docs-web`**.
|
|
864
456
|
|
|
865
|
-
|
|
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
|
-
##
|
|
491
|
+
## Next direction
|
|
931
492
|
|
|
932
|
-
|
|
493
|
+
After `0.1.27`, the planned milestone is **`0.1.28 — Production Hardening`**:
|
|
933
494
|
|
|
934
|
-
1.
|
|
935
|
-
2.
|
|
936
|
-
3.
|
|
937
|
-
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.
|
|
938
502
|
|
|
939
|
-
|
|
503
|
+
These are roadmap items, not `0.1.27` guarantees.
|
|
940
504
|
|
|
941
505
|
## License
|
|
942
506
|
|