@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/docs/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# BCP Framework Documentation Source
|
|
2
2
|
|
|
3
|
-
The `docs/` directory is the documentation source of truth for BCP Framework and is intentionally organized for the
|
|
3
|
+
The `docs/` directory is the documentation source of truth for BCP Framework and is intentionally organized for the **`bcp-docs-web`** website.
|
|
4
4
|
|
|
5
|
-
> **Documentation target:** BCP Framework `0.1.
|
|
5
|
+
> **Documentation target:** BCP Framework `0.1.28`
|
|
6
6
|
>
|
|
7
|
-
> **Release state:**
|
|
7
|
+
> **Release state:** unreleased development target. Do not label `0.1.28` as published until RC validation and npm publication complete.
|
|
8
8
|
|
|
9
9
|
## Documentation flow
|
|
10
10
|
|
|
@@ -88,6 +88,7 @@ Releases
|
|
|
88
88
|
| `/docs/development-logging` | `development-logging.md` | Logging/observability |
|
|
89
89
|
| `/docs/caching` | `caching.md` | Cache/revalidation |
|
|
90
90
|
| `/docs/security` | `security.md` | Security/body limits |
|
|
91
|
+
| `/docs/production-hardening` | `production-hardening.md` | Graceful shutdown, trusted proxy and production timeouts |
|
|
91
92
|
|
|
92
93
|
### Storage & Uploads
|
|
93
94
|
|
|
@@ -95,7 +96,8 @@ Releases
|
|
|
95
96
|
| --- | --- | --- |
|
|
96
97
|
| `/docs/file-upload` | `file-upload.md` | Buffered + streaming multipart uploads |
|
|
97
98
|
| `/docs/storage` | `storage.md` | StorageAdapter, streaming and file delivery |
|
|
98
|
-
| `/docs/
|
|
99
|
+
| `/docs/storage-ecosystem` | `storage-ecosystem.md` | Listing, copy/move, metadata, bulk delete, signed URLs |
|
|
100
|
+
| `/docs/s3-storage` | `s3-storage.md` | S3/R2/MinIO adapter and direct object access |
|
|
99
101
|
|
|
100
102
|
## API Reference ownership
|
|
101
103
|
|
|
@@ -116,7 +118,7 @@ bcp/middleware
|
|
|
116
118
|
| Entrypoint | Primary guide |
|
|
117
119
|
| --- | --- |
|
|
118
120
|
| `bcp` | `routing.md`, `server-data-loaders.md`, `form-actions.md` |
|
|
119
|
-
| `bcp/server` | `server-request-apis.md`, `file-upload.md`, `storage.md`, `s3-storage.md`, `development-logging.md` |
|
|
121
|
+
| `bcp/server` | `server-request-apis.md`, `file-upload.md`, `storage.md`, `storage-ecosystem.md`, `s3-storage.md`, `development-logging.md`, `production-hardening.md` |
|
|
120
122
|
| `bcp/auth` | `authentication.md`, `auth-route-guards.md` |
|
|
121
123
|
| `bcp/database` | `database.md`, `database-migrations.md` |
|
|
122
124
|
| `bcp/validation` | `validation.md` |
|
|
@@ -138,7 +140,7 @@ bcp/middleware
|
|
|
138
140
|
- client islands / partial hydration
|
|
139
141
|
- Fast Refresh
|
|
140
142
|
|
|
141
|
-
### Server
|
|
143
|
+
### Server and production runtime
|
|
142
144
|
|
|
143
145
|
- request-scoped APIs/cookies/redirects/request IDs
|
|
144
146
|
- JWT sessions/authentication
|
|
@@ -146,18 +148,28 @@ bcp/middleware
|
|
|
146
148
|
- validation/structured errors
|
|
147
149
|
- structured logging
|
|
148
150
|
- response caching/security gateway
|
|
151
|
+
- production hardening gateway
|
|
152
|
+
- configurable request/header/keep-alive/shutdown timeouts
|
|
153
|
+
- `SIGTERM` / `SIGINT` graceful shutdown
|
|
154
|
+
- application shutdown hooks
|
|
155
|
+
- trusted-proxy forwarding-header controls
|
|
149
156
|
|
|
150
157
|
### Storage & Uploads
|
|
151
158
|
|
|
152
159
|
- buffered multipart `FormData` upload APIs
|
|
153
160
|
- production streaming multipart upload (`storeMultipartFile()`)
|
|
154
|
-
- `StorageAdapter` contract
|
|
155
|
-
- local filesystem
|
|
156
|
-
- S3-compatible
|
|
157
|
-
- streaming reads/writes
|
|
158
|
-
- byte ranges
|
|
161
|
+
- backward-compatible `StorageAdapter` contract
|
|
162
|
+
- local filesystem storage
|
|
163
|
+
- S3/R2/MinIO-compatible storage
|
|
164
|
+
- streaming reads/writes and byte ranges
|
|
159
165
|
- ETag/Last-Modified file delivery
|
|
160
166
|
- upload/storage limits and abort cleanup
|
|
167
|
+
- object listing with cursors
|
|
168
|
+
- native/fallback copy and move
|
|
169
|
+
- portable user metadata
|
|
170
|
+
- bulk deletion
|
|
171
|
+
- S3 presigned read/write URLs
|
|
172
|
+
- create-app Local Server / Amazon S3 / Cloudflare R2 presets
|
|
161
173
|
|
|
162
174
|
### Database
|
|
163
175
|
|
|
@@ -177,64 +189,56 @@ bcp/middleware
|
|
|
177
189
|
- standalone production build
|
|
178
190
|
- unit/integration/E2E/package/RC checks
|
|
179
191
|
|
|
180
|
-
## BCP 0.1.
|
|
192
|
+
## BCP 0.1.28 documentation focus
|
|
181
193
|
|
|
182
|
-
###
|
|
194
|
+
### Production hardening
|
|
183
195
|
|
|
184
|
-
Public
|
|
196
|
+
Public cleanup API:
|
|
185
197
|
|
|
186
198
|
```ts
|
|
187
199
|
import {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
readStorageStream,
|
|
200
|
+
getProductionHardeningConfig,
|
|
201
|
+
registerShutdownHook,
|
|
191
202
|
} from "bcp/server";
|
|
192
203
|
```
|
|
193
204
|
|
|
194
|
-
|
|
205
|
+
Production environment controls:
|
|
195
206
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
207
|
+
```dotenv
|
|
208
|
+
BCP_REQUEST_TIMEOUT_MS=120000
|
|
209
|
+
BCP_HEADERS_TIMEOUT_MS=66000
|
|
210
|
+
BCP_KEEP_ALIVE_TIMEOUT_MS=65000
|
|
211
|
+
BCP_SHUTDOWN_TIMEOUT_MS=10000
|
|
212
|
+
BCP_TRUST_PROXY=false
|
|
202
213
|
```
|
|
203
214
|
|
|
204
|
-
Document:
|
|
215
|
+
Document these boundaries clearly:
|
|
205
216
|
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
209
|
-
- `
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
- ranges/ETag metadata,
|
|
213
|
-
- overwrite limitations for concurrent multipart writers,
|
|
214
|
-
- server-only credential/security requirements.
|
|
217
|
+
- trusted proxy mode is off by default,
|
|
218
|
+
- forwarding headers are sanitized when trust is disabled,
|
|
219
|
+
- enable trust only behind a trusted reverse proxy/load balancer,
|
|
220
|
+
- Docker/process-manager `SIGTERM` triggers graceful shutdown,
|
|
221
|
+
- shutdown hooks are process-lifetime resource cleanup, not request cleanup,
|
|
222
|
+
- shutdown force-closes remaining public connections after the configured timeout.
|
|
215
223
|
|
|
216
|
-
See `
|
|
224
|
+
See `production-hardening.md`.
|
|
217
225
|
|
|
218
|
-
###
|
|
226
|
+
### Local Server generator fix
|
|
219
227
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
228
|
+
`create-bcp-app --storage local` now generates:
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
lib/storage.ts
|
|
232
|
+
storage/
|
|
233
|
+
├─ .gitkeep
|
|
234
|
+
└─ README.md
|
|
224
235
|
```
|
|
225
236
|
|
|
226
|
-
|
|
237
|
+
Runtime objects remain ignored while the scaffold stays visible/tracked.
|
|
227
238
|
|
|
228
|
-
|
|
229
|
-
- total request limit,
|
|
230
|
-
- per-file limit,
|
|
231
|
-
- MIME/extension validation,
|
|
232
|
-
- fixed/callback storage keys,
|
|
233
|
-
- AbortSignal behavior,
|
|
234
|
-
- direct local/S3 streaming,
|
|
235
|
-
- difference from `request.formData()` / buffered APIs.
|
|
239
|
+
### 0.1.27 storage ecosystem remains supported
|
|
236
240
|
|
|
237
|
-
|
|
241
|
+
The storage ecosystem continues to include listing, copy/move, portable metadata, bulk deletion and S3 signed URLs. `0.1.28` hardens deployment/runtime behavior rather than replacing those APIs.
|
|
238
242
|
|
|
239
243
|
## CLI commands
|
|
240
244
|
|
|
@@ -278,11 +282,11 @@ docs/releases/
|
|
|
278
282
|
Recommended routes:
|
|
279
283
|
|
|
280
284
|
```text
|
|
285
|
+
/releases/0.1.28
|
|
286
|
+
/releases/0.1.27
|
|
281
287
|
/releases/0.1.26
|
|
282
288
|
/releases/0.1.25
|
|
283
289
|
/releases/0.1.24
|
|
284
|
-
/releases/0.1.23
|
|
285
|
-
/releases/0.1.22
|
|
286
290
|
```
|
|
287
291
|
|
|
288
292
|
Use one of these release states:
|
|
@@ -311,8 +315,6 @@ interface DocPage {
|
|
|
311
315
|
}
|
|
312
316
|
```
|
|
313
317
|
|
|
314
|
-
The first `bcp-docs-web` implementation can keep this mapping in a route manifest and consume Markdown directly.
|
|
315
|
-
|
|
316
318
|
## Suggested website features
|
|
317
319
|
|
|
318
320
|
1. Sidebar navigation from this source map.
|
|
@@ -357,15 +359,9 @@ npm run test:package
|
|
|
357
359
|
npm run rc:check
|
|
358
360
|
```
|
|
359
361
|
|
|
360
|
-
For `0.1.
|
|
361
|
-
|
|
362
|
-
```text
|
|
363
|
-
@aws-sdk/client-s3
|
|
364
|
-
@aws-sdk/lib-storage
|
|
365
|
-
busboy
|
|
366
|
-
```
|
|
362
|
+
For `0.1.28`, package validation must confirm the staged artifact includes the production hardening runtime/gateway, shutdown APIs, Local Server generator fix, and the existing S3 runtime dependencies.
|
|
367
363
|
|
|
368
|
-
and
|
|
364
|
+
A representative Docker app should also be built and stopped with `docker stop` to verify graceful `SIGTERM` shutdown.
|
|
369
365
|
|
|
370
366
|
## Documentation QA checklist
|
|
371
367
|
|
|
@@ -374,33 +370,24 @@ and the S3/multipart streaming source files/public exports.
|
|
|
374
370
|
- version/release states are current,
|
|
375
371
|
- Windows direct CLI examples use `npm exec -- bcp-framework`,
|
|
376
372
|
- upload/storage/auth security caveats are present,
|
|
377
|
-
- route names match the current router,
|
|
378
373
|
- release notes match the framework version,
|
|
379
374
|
- S3 credentials are never shown as public browser variables,
|
|
375
|
+
- signed URLs are documented as temporary credentials,
|
|
376
|
+
- proxy trust is documented as opt-in,
|
|
380
377
|
- roadmap APIs are not presented as published guarantees.
|
|
381
378
|
|
|
382
|
-
## Next direction after 0.1.
|
|
379
|
+
## Next direction after 0.1.28
|
|
383
380
|
|
|
384
|
-
|
|
381
|
+
Planned `0.1.29 — Developer Experience` focus:
|
|
385
382
|
|
|
386
|
-
-
|
|
387
|
-
-
|
|
388
|
-
-
|
|
389
|
-
-
|
|
390
|
-
-
|
|
383
|
+
- generators for common framework files,
|
|
384
|
+
- richer `doctor` and `inspect`,
|
|
385
|
+
- improved build/runtime diagnostics,
|
|
386
|
+
- create-app preset improvements,
|
|
387
|
+
- production configuration diagnostics.
|
|
391
388
|
|
|
392
|
-
These
|
|
389
|
+
These remain roadmap items until their source/tests land.
|
|
393
390
|
|
|
394
391
|
## Repository authority
|
|
395
392
|
|
|
396
|
-
The framework repository remains authoritative for
|
|
397
|
-
|
|
398
|
-
```text
|
|
399
|
-
source
|
|
400
|
-
public exports
|
|
401
|
-
tests
|
|
402
|
-
docs
|
|
403
|
-
release notes
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
`bcp-docs-web` is the presentation/search/navigation layer for this content.
|
|
393
|
+
The framework repository remains authoritative for source, public exports, tests, docs and release notes. `bcp-docs-web` is the presentation/search/navigation layer for this content.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Production Hardening
|
|
2
|
+
|
|
3
|
+
BCP Framework `0.1.28` adds a production hardening gateway around the standalone runtime.
|
|
4
|
+
|
|
5
|
+
The gateway is the public HTTP listener. Existing security, cache, critical CSS, page/API and SSR runtimes remain bound to internal loopback ports behind it.
|
|
6
|
+
|
|
7
|
+
## Default production behavior
|
|
8
|
+
|
|
9
|
+
The hardening gateway configures Node HTTP timeouts and graceful shutdown defaults:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
Request timeout: 120000 ms
|
|
13
|
+
Headers timeout: 66000 ms
|
|
14
|
+
Keep-alive timeout: 65000 ms
|
|
15
|
+
Shutdown timeout: 10000 ms
|
|
16
|
+
Trust proxy: false
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Override them with server-only environment variables:
|
|
20
|
+
|
|
21
|
+
```dotenv
|
|
22
|
+
BCP_REQUEST_TIMEOUT_MS=120000
|
|
23
|
+
BCP_HEADERS_TIMEOUT_MS=66000
|
|
24
|
+
BCP_KEEP_ALIVE_TIMEOUT_MS=65000
|
|
25
|
+
BCP_SHUTDOWN_TIMEOUT_MS=10000
|
|
26
|
+
BCP_TRUST_PROXY=false
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`BCP_HEADERS_TIMEOUT_MS` must be greater than `BCP_KEEP_ALIVE_TIMEOUT_MS`.
|
|
30
|
+
|
|
31
|
+
## Graceful shutdown
|
|
32
|
+
|
|
33
|
+
Standalone production now handles `SIGTERM` and `SIGINT`.
|
|
34
|
+
|
|
35
|
+
This is especially important for Docker, Kubernetes and process managers that send `SIGTERM` before stopping a process.
|
|
36
|
+
|
|
37
|
+
Shutdown order:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
stop accepting public traffic
|
|
41
|
+
↓
|
|
42
|
+
drain/close public HTTP connections
|
|
43
|
+
↓
|
|
44
|
+
run application shutdown hooks
|
|
45
|
+
↓
|
|
46
|
+
stop internal BCP runtime layers
|
|
47
|
+
↓
|
|
48
|
+
process exits naturally
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If the public server does not close before `BCP_SHUTDOWN_TIMEOUT_MS`, BCP force-closes remaining connections.
|
|
52
|
+
|
|
53
|
+
## Application shutdown hooks
|
|
54
|
+
|
|
55
|
+
Register cleanup work through `bcp/server`:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import {
|
|
59
|
+
registerShutdownHook,
|
|
60
|
+
} from "bcp/server";
|
|
61
|
+
|
|
62
|
+
const unregister =
|
|
63
|
+
registerShutdownHook(
|
|
64
|
+
async () => {
|
|
65
|
+
await closeApplicationResources();
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "application-resources",
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Hooks run in reverse registration order. Calling the returned function unregisters the hook.
|
|
74
|
+
|
|
75
|
+
For S3-compatible storage, an application can register the adapter client cleanup when it owns the adapter for the lifetime of the process:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
registerShutdownHook(
|
|
79
|
+
() => {
|
|
80
|
+
storage.destroy();
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "s3-storage",
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Do not register request-scoped resources as process shutdown hooks.
|
|
89
|
+
|
|
90
|
+
## Trusted proxy mode
|
|
91
|
+
|
|
92
|
+
The hardening gateway sanitizes forwarding headers by default.
|
|
93
|
+
|
|
94
|
+
```dotenv
|
|
95
|
+
BCP_TRUST_PROXY=false
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This prevents a direct client from spoofing headers such as:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
Forwarded
|
|
102
|
+
X-Forwarded-For
|
|
103
|
+
X-Forwarded-Host
|
|
104
|
+
X-Forwarded-Proto
|
|
105
|
+
X-Real-IP
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
When BCP is deployed only behind a trusted reverse proxy such as Nginx, Cloudflare Tunnel or a trusted load balancer, enable:
|
|
109
|
+
|
|
110
|
+
```dotenv
|
|
111
|
+
BCP_TRUST_PROXY=true
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
BCP then preserves the trusted proxy forwarding chain and appends the immediate peer address where appropriate.
|
|
115
|
+
|
|
116
|
+
Only enable trusted proxy mode when direct untrusted traffic cannot bypass the trusted proxy.
|
|
117
|
+
|
|
118
|
+
## Docker
|
|
119
|
+
|
|
120
|
+
A production compose service can use:
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
services:
|
|
124
|
+
app:
|
|
125
|
+
init: true
|
|
126
|
+
restart: unless-stopped
|
|
127
|
+
environment:
|
|
128
|
+
NODE_ENV: production
|
|
129
|
+
BCP_HOSTNAME: 0.0.0.0
|
|
130
|
+
BCP_PORT: 3000
|
|
131
|
+
BCP_SHUTDOWN_TIMEOUT_MS: 10000
|
|
132
|
+
BCP_TRUST_PROXY: "true"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Set `BCP_TRUST_PROXY=true` only when the container receives requests exclusively from a trusted reverse proxy/network path.
|
|
136
|
+
|
|
137
|
+
Docker's stop grace period should be longer than `BCP_SHUTDOWN_TIMEOUT_MS` so BCP gets time to drain connections and cleanup resources.
|
|
138
|
+
|
|
139
|
+
## Local Server storage scaffold
|
|
140
|
+
|
|
141
|
+
`create-bcp-app --storage local` now creates:
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
lib/storage.ts
|
|
145
|
+
storage/
|
|
146
|
+
├─ .gitkeep
|
|
147
|
+
└─ README.md
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Runtime objects inside `storage/` are ignored while the scaffold files remain tracked. The generated README explains that BCP writes objects and `.bcp-storage-meta` metadata there at runtime.
|
|
151
|
+
|
|
152
|
+
The directory therefore no longer appears as an unexplained empty folder before the first upload.
|