@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.
@@ -0,0 +1,350 @@
1
+ # BCP Framework 0.1.27
2
+
3
+ BCP Framework `0.1.27` is the **Storage Ecosystem** milestone.
4
+
5
+ > Release state: unreleased development target until local validation, RC checks, tagging and npm publication complete.
6
+
7
+ ## Highlights
8
+
9
+ - Added object listing with prefix filtering, limits and opaque cursors.
10
+ - Added generic and native copy/move operations.
11
+ - Added application user metadata for local and S3-compatible storage.
12
+ - Added bulk deletion helpers.
13
+ - Added S3 presigned read and write URLs.
14
+ - Expanded storage capability discovery without breaking the `0.1.26` `StorageAdapter` contract.
15
+ - Preserved fallback copy/move/delete-many behavior for older custom adapters.
16
+ - Added `create-bcp-app` storage provider selection for Local Server, Amazon S3 and Cloudflare R2.
17
+ - Added provider-specific `lib/storage.ts` scaffolding and `.env.example` configuration.
18
+ - Added local storage ecosystem unit coverage.
19
+ - Added S3-compatible ecosystem integration coverage.
20
+ - Extended packed-package smoke checks for the new runtime and presigner dependency.
21
+
22
+ ## Public API additions
23
+
24
+ All new application APIs are available through `bcp/server`:
25
+
26
+ ```ts
27
+ import {
28
+ copyStorageObject,
29
+ createStorageSignedReadUrl,
30
+ createStorageSignedWriteUrl,
31
+ deleteStorageObjects,
32
+ getStorageEcosystemCapabilities,
33
+ getStorageMetadata,
34
+ listStorageObjects,
35
+ moveStorageObject,
36
+ setStorageMetadata,
37
+ } from "bcp/server";
38
+ ```
39
+
40
+ New public types include:
41
+
42
+ ```text
43
+ StorageEcosystemAdapter
44
+ StorageEcosystemCapabilities
45
+ StorageEcosystemPutOptions
46
+ StorageEcosystemPutStreamOptions
47
+ StorageListOptions
48
+ StorageListResult
49
+ StorageCopyOptions
50
+ StorageDeleteManyResult
51
+ StorageSignedUrlOptions
52
+ StorageSignedWriteUrlOptions
53
+ StorageUserMetadata
54
+ S3StorageEcosystemAdapter
55
+ ```
56
+
57
+ ## Backward compatibility
58
+
59
+ The original `StorageAdapter` remains valid. Ecosystem methods are additive and optional.
60
+
61
+ Generic helpers select a native adapter method when available. Operations that can be implemented portably, such as copy/move/delete-many, also have compatibility fallbacks where practical.
62
+
63
+ Custom `0.1.25` / `0.1.26` adapters therefore do not need to implement all `0.1.27` methods immediately.
64
+
65
+ ## create-bcp-app storage provider selection
66
+
67
+ Interactive project creation now includes:
68
+
69
+ ```text
70
+ Select storage provider:
71
+ None
72
+ Local Server
73
+ Amazon S3
74
+ Cloudflare R2
75
+ ```
76
+
77
+ The same choices can be automated:
78
+
79
+ ```bash
80
+ npx create-bcp-app my-app --storage local
81
+ npx create-bcp-app my-app --storage amazon-s3
82
+ npx create-bcp-app my-app --storage cloudflare-r2
83
+ ```
84
+
85
+ Supported flag values are:
86
+
87
+ ```text
88
+ none
89
+ local
90
+ amazon-s3
91
+ cloudflare-r2
92
+ ```
93
+
94
+ Selecting a provider creates `lib/storage.ts` and appends provider-specific settings to `.env.example`.
95
+
96
+ ### Local Server preset
97
+
98
+ ```dotenv
99
+ STORAGE_LOCAL_DIRECTORY=./storage
100
+ ```
101
+
102
+ The generated helper uses `createLocalStorage()` and adds `storage/` to `.gitignore`.
103
+
104
+ ### Amazon S3 preset
105
+
106
+ ```dotenv
107
+ AWS_S3_BUCKET=
108
+ AWS_REGION=ap-southeast-1
109
+ AWS_ACCESS_KEY_ID=
110
+ AWS_SECRET_ACCESS_KEY=
111
+ AWS_SESSION_TOKEN=
112
+ AWS_S3_PREFIX=
113
+ ```
114
+
115
+ The generated helper uses `createS3Storage()`. Explicit credentials are optional when the application deployment uses the AWS SDK server-side credential provider chain.
116
+
117
+ ### Cloudflare R2 preset
118
+
119
+ ```dotenv
120
+ R2_ACCOUNT_ID=
121
+ R2_BUCKET=
122
+ R2_ACCESS_KEY_ID=
123
+ R2_SECRET_ACCESS_KEY=
124
+ R2_PREFIX=
125
+ ```
126
+
127
+ The generated helper uses the S3-compatible adapter with `region: "auto"` and the Cloudflare account endpoint.
128
+
129
+ All generated storage credentials remain server-only and must not be moved into `BCP_PUBLIC_*` environment variables.
130
+
131
+ ## Listing
132
+
133
+ ```ts
134
+ const page =
135
+ await listStorageObjects(
136
+ storage,
137
+ {
138
+ prefix: "documents/",
139
+ limit: 50,
140
+ }
141
+ );
142
+ ```
143
+
144
+ The result contains:
145
+
146
+ ```ts
147
+ {
148
+ objects: StorageObjectMetadata[];
149
+ cursor?: string;
150
+ }
151
+ ```
152
+
153
+ Cursors are opaque. Applications should pass them back unchanged rather than decoding or constructing them.
154
+
155
+ The built-in local adapter uses a stable key-order cursor. The S3 adapter maps the generic cursor to the provider continuation token.
156
+
157
+ ## Copy and move
158
+
159
+ ```ts
160
+ await copyStorageObject(
161
+ storage,
162
+ "incoming/report.pdf",
163
+ "archive/report.pdf"
164
+ );
165
+
166
+ await moveStorageObject(
167
+ storage,
168
+ "incoming/avatar.webp",
169
+ "users/42/avatar.webp"
170
+ );
171
+ ```
172
+
173
+ S3 uses native `CopyObject`. Other adapters can fall back to streamed read/write copy behavior.
174
+
175
+ Overwrite remains opt-in.
176
+
177
+ ## User metadata
178
+
179
+ `0.1.27` adds portable string metadata separate from BCP's internal checksum metadata:
180
+
181
+ ```ts
182
+ await storage.put(
183
+ "documents/report.pdf",
184
+ bytes,
185
+ {
186
+ contentType: "application/pdf",
187
+ metadata: {
188
+ owner: "user-42",
189
+ category: "report",
190
+ },
191
+ }
192
+ );
193
+ ```
194
+
195
+ Read or replace metadata independently:
196
+
197
+ ```ts
198
+ const metadata =
199
+ await getStorageMetadata(
200
+ storage,
201
+ "documents/report.pdf"
202
+ );
203
+
204
+ await setStorageMetadata(
205
+ storage,
206
+ "documents/report.pdf",
207
+ {
208
+ owner: "user-42",
209
+ state: "approved",
210
+ }
211
+ );
212
+ ```
213
+
214
+ Metadata keys are normalized to lowercase and must use portable storage-safe characters. The reserved `bcp_sha256` key cannot be set by applications.
215
+
216
+ ## Bulk deletion
217
+
218
+ ```ts
219
+ const result =
220
+ await deleteStorageObjects(
221
+ storage,
222
+ [
223
+ "tmp/a.bin",
224
+ "tmp/b.bin",
225
+ ]
226
+ );
227
+ ```
228
+
229
+ The result contains:
230
+
231
+ ```ts
232
+ {
233
+ deleted: string[];
234
+ missing: string[];
235
+ failed: Array<{
236
+ key: string;
237
+ message: string;
238
+ }>;
239
+ }
240
+ ```
241
+
242
+ S3 deletion semantics treat deletion of a missing object as successful, so S3 normally reports those keys under `deleted` rather than `missing`.
243
+
244
+ ## Presigned URLs
245
+
246
+ S3-compatible adapters expose direct read/write signing:
247
+
248
+ ```ts
249
+ const downloadUrl =
250
+ await createStorageSignedReadUrl(
251
+ storage,
252
+ "videos/demo.mp4",
253
+ {
254
+ expiresIn: 300,
255
+ }
256
+ );
257
+
258
+ const uploadUrl =
259
+ await createStorageSignedWriteUrl(
260
+ storage,
261
+ "uploads/demo.mp4",
262
+ {
263
+ expiresIn: 300,
264
+ contentType: "video/mp4",
265
+ }
266
+ );
267
+ ```
268
+
269
+ Signed URLs are useful when browsers or other clients should transfer large files directly with object storage rather than proxying bytes through the application server.
270
+
271
+ The default expiry is 15 minutes. `expiresIn` is limited to the S3 SigV4 maximum of seven days.
272
+
273
+ Local filesystem storage intentionally does not emulate signed URLs.
274
+
275
+ ## Capabilities
276
+
277
+ Use:
278
+
279
+ ```ts
280
+ getStorageEcosystemCapabilities(storage)
281
+ ```
282
+
283
+ The extended capability result includes:
284
+
285
+ ```ts
286
+ {
287
+ streamingRead,
288
+ streamingWrite,
289
+ ranges,
290
+ signedUrls,
291
+ listing,
292
+ signedReadUrls,
293
+ signedWriteUrls,
294
+ copy,
295
+ move,
296
+ metadata,
297
+ bulkDelete,
298
+ }
299
+ ```
300
+
301
+ For the built-in S3 ecosystem adapter all of these capabilities are enabled.
302
+
303
+ For local storage, listing/copy/move/metadata/bulk-delete are enabled while signed URL capabilities remain disabled.
304
+
305
+ ## Dependency change
306
+
307
+ The framework package adds:
308
+
309
+ ```text
310
+ @aws-sdk/s3-request-presigner
311
+ ```
312
+
313
+ alongside the S3 client and multipart upload dependencies introduced in `0.1.26`.
314
+
315
+ ## Testing
316
+
317
+ `0.1.27` adds regression coverage for:
318
+
319
+ - local listing and pagination,
320
+ - portable metadata,
321
+ - metadata preservation during copy/move,
322
+ - bulk deletion,
323
+ - unsupported local signed URLs,
324
+ - S3 listing,
325
+ - S3 native copy/move,
326
+ - S3 metadata replacement,
327
+ - S3 bulk deletion,
328
+ - S3 signed read/write URL generation,
329
+ - create-app Local Server storage scaffolding,
330
+ - create-app Amazon S3 storage scaffolding,
331
+ - create-app Cloudflare R2 storage scaffolding,
332
+ - non-interactive `--storage` CLI selection,
333
+ - public package exports,
334
+ - packed dependency/runtime presence.
335
+
336
+ ## Remaining release work
337
+
338
+ Before publication:
339
+
340
+ 1. sync `package-lock.json` with `0.1.27` and the presigner dependency,
341
+ 2. run `npm run typecheck`,
342
+ 3. run unit/integration/E2E/package tests,
343
+ 4. run `npm run rc:check`,
344
+ 5. validate a packed `0.1.27` package in a representative application,
345
+ 6. create the `v0.1.27` tag only after the final release commit is known,
346
+ 7. publish and verify npm visibility.
347
+
348
+ ## Next milestone
349
+
350
+ The planned next milestone is `0.1.28 — Production Hardening`, focused on graceful shutdown, proxy awareness, timeouts, security and Docker/standalone runtime reliability.
@@ -0,0 +1,134 @@
1
+ # BCP Framework 0.1.28
2
+
3
+ BCP Framework `0.1.28` is the **Production Hardening** milestone.
4
+
5
+ > Release state: unreleased development target until local validation, RC checks, tagging and npm publication complete.
6
+
7
+ ## Highlights
8
+
9
+ - Added a public production hardening gateway around the existing standalone runtime.
10
+ - Added configurable request, header, keep-alive and shutdown timeouts.
11
+ - Added graceful `SIGTERM` / `SIGINT` handling for Docker and process managers.
12
+ - Added application shutdown hooks through `registerShutdownHook()`.
13
+ - Added trusted-proxy forwarding-header sanitization with secure default `BCP_TRUST_PROXY=false`.
14
+ - Added force-close fallback after the configured graceful shutdown timeout.
15
+ - Fixed `create-bcp-app --storage local` so generated projects no longer contain an unexplained empty storage directory.
16
+ - Added `storage/README.md` and `storage/.gitkeep` while continuing to ignore runtime storage objects.
17
+ - Added regression coverage for production hardening primitives and Local Server scaffolding.
18
+
19
+ ## Production environment controls
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
+ Defaults are production-safe and require no configuration for ordinary direct HTTP deployments.
30
+
31
+ `BCP_HEADERS_TIMEOUT_MS` must be greater than `BCP_KEEP_ALIVE_TIMEOUT_MS`.
32
+
33
+ ## Graceful shutdown
34
+
35
+ The standalone production runtime now handles:
36
+
37
+ ```text
38
+ SIGTERM
39
+ SIGINT
40
+ ```
41
+
42
+ The runtime first stops/drains the public hardening gateway, then runs application shutdown hooks, then stops internal BCP runtime layers.
43
+
44
+ After `BCP_SHUTDOWN_TIMEOUT_MS`, remaining public connections are force-closed.
45
+
46
+ ## Public API addition
47
+
48
+ ```ts
49
+ import {
50
+ getProductionHardeningConfig,
51
+ registerShutdownHook,
52
+ } from "bcp/server";
53
+ ```
54
+
55
+ Example:
56
+
57
+ ```ts
58
+ registerShutdownHook(
59
+ () => {
60
+ storage.destroy();
61
+ },
62
+ {
63
+ name: "storage",
64
+ }
65
+ );
66
+ ```
67
+
68
+ Hooks run in reverse registration order and can be unregistered with the cleanup function returned by `registerShutdownHook()`.
69
+
70
+ ## Trusted proxy behavior
71
+
72
+ Trusted proxy mode is disabled by default.
73
+
74
+ When disabled, BCP strips spoofable incoming forwarding headers at the public hardening gateway and writes forwarding information from the actual connection.
75
+
76
+ When the application is intentionally deployed behind a trusted reverse proxy/load balancer, enable:
77
+
78
+ ```dotenv
79
+ BCP_TRUST_PROXY=true
80
+ ```
81
+
82
+ Only enable it when direct untrusted traffic cannot bypass the trusted proxy.
83
+
84
+ ## Local Server storage fix
85
+
86
+ Before `0.1.28`, selecting Local Server generated `lib/storage.ts` and configured `./storage`, but the runtime directory had no explanatory scaffold before the first upload.
87
+
88
+ `0.1.28` generates:
89
+
90
+ ```text
91
+ storage/
92
+ ├─ .gitkeep
93
+ └─ README.md
94
+ ```
95
+
96
+ and uses Git ignore rules:
97
+
98
+ ```gitignore
99
+ storage/*
100
+ !storage/.gitkeep
101
+ !storage/README.md
102
+ ```
103
+
104
+ Runtime uploads and `.bcp-storage-meta` remain untracked while the directory structure is visible in a fresh project.
105
+
106
+ ## Testing
107
+
108
+ `0.1.28` adds coverage for:
109
+
110
+ - production timeout parsing,
111
+ - invalid timeout relationships,
112
+ - applying Node HTTP server timeout settings,
113
+ - graceful HTTP server close,
114
+ - shutdown hook ordering/unregistration,
115
+ - generated Local Server storage directory scaffold,
116
+ - generated Local Server Git ignore behavior.
117
+
118
+ ## Remaining release work
119
+
120
+ Before publication:
121
+
122
+ 1. sync `package-lock.json` to `0.1.28`,
123
+ 2. run `npm run typecheck`,
124
+ 3. run unit/integration/E2E/package tests,
125
+ 4. run `npm run rc:check`,
126
+ 5. build a representative BCP application,
127
+ 6. run the standalone artifact in Docker,
128
+ 7. verify `docker stop` produces graceful shutdown logs,
129
+ 8. create the `v0.1.28` tag only after the final release commit is known,
130
+ 9. publish and verify npm visibility.
131
+
132
+ ## Next milestone
133
+
134
+ The planned next milestone is `0.1.29 — Developer Experience`, focused on generators, richer `doctor` / `inspect`, improved diagnostics and create-app workflow improvements.