@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/docs/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  The `docs/` directory is the documentation source of truth for BCP Framework and is intentionally organized for the future **`bcp-docs-web`** website.
4
4
 
5
- > **Documentation target:** BCP Framework `0.1.26`
5
+ > **Documentation target:** BCP Framework `0.1.27`
6
6
  >
7
- > **Release state:** release-candidate source. Do not label `0.1.26` as published until RC validation and npm publication complete.
7
+ > **Release state:** unreleased development target. Do not label `0.1.27` as published until RC validation and npm publication complete.
8
8
 
9
9
  ## Documentation flow
10
10
 
@@ -95,7 +95,8 @@ Releases
95
95
  | --- | --- | --- |
96
96
  | `/docs/file-upload` | `file-upload.md` | Buffered + streaming multipart uploads |
97
97
  | `/docs/storage` | `storage.md` | StorageAdapter, streaming and file delivery |
98
- | `/docs/s3-storage` | `s3-storage.md` | S3-compatible adapter/configuration |
98
+ | `/docs/storage-ecosystem` | `storage-ecosystem.md` | Listing, copy/move, metadata, bulk delete, signed URLs |
99
+ | `/docs/s3-storage` | `s3-storage.md` | S3/R2/MinIO adapter and direct object access |
99
100
 
100
101
  ## API Reference ownership
101
102
 
@@ -116,7 +117,7 @@ bcp/middleware
116
117
  | Entrypoint | Primary guide |
117
118
  | --- | --- |
118
119
  | `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` |
120
+ | `bcp/server` | `server-request-apis.md`, `file-upload.md`, `storage.md`, `storage-ecosystem.md`, `s3-storage.md`, `development-logging.md` |
120
121
  | `bcp/auth` | `authentication.md`, `auth-route-guards.md` |
121
122
  | `bcp/database` | `database.md`, `database-migrations.md` |
122
123
  | `bcp/validation` | `validation.md` |
@@ -151,13 +152,17 @@ bcp/middleware
151
152
 
152
153
  - buffered multipart `FormData` upload APIs
153
154
  - production streaming multipart upload (`storeMultipartFile()`)
154
- - `StorageAdapter` contract
155
- - local filesystem adapter
156
- - S3-compatible adapter
157
- - streaming reads/writes
158
- - byte ranges
155
+ - backward-compatible `StorageAdapter` contract
156
+ - local filesystem storage
157
+ - S3/R2/MinIO-compatible storage
158
+ - streaming reads/writes and byte ranges
159
159
  - ETag/Last-Modified file delivery
160
160
  - upload/storage limits and abort cleanup
161
+ - object listing with cursors
162
+ - native/fallback copy and move
163
+ - portable user metadata
164
+ - bulk deletion
165
+ - S3 presigned read/write URLs
161
166
 
162
167
  ### Database
163
168
 
@@ -177,64 +182,66 @@ bcp/middleware
177
182
  - standalone production build
178
183
  - unit/integration/E2E/package/RC checks
179
184
 
180
- ## BCP 0.1.26 documentation focus
185
+ ## BCP 0.1.27 documentation focus
181
186
 
182
- ### Storage streaming
187
+ ### Storage ecosystem
183
188
 
184
- Public additions include:
189
+ New generic helpers include:
185
190
 
186
191
  ```ts
187
192
  import {
188
- getStorageCapabilities,
189
- putStorageStream,
190
- readStorageStream,
193
+ copyStorageObject,
194
+ createStorageSignedReadUrl,
195
+ createStorageSignedWriteUrl,
196
+ deleteStorageObjects,
197
+ getStorageEcosystemCapabilities,
198
+ getStorageMetadata,
199
+ listStorageObjects,
200
+ moveStorageObject,
201
+ setStorageMetadata,
191
202
  } from "bcp/server";
192
203
  ```
193
204
 
194
- Streaming methods remain optional on custom adapters for `0.1.25` compatibility.
205
+ Document these boundaries clearly:
195
206
 
196
- ### S3-compatible storage
207
+ - ecosystem methods are additive and do not invalidate older `StorageAdapter` implementations,
208
+ - listing cursors are opaque,
209
+ - overwrite remains opt-in,
210
+ - move is copy + delete rather than an atomic distributed transaction,
211
+ - application metadata is portable string metadata and not authorization/business state,
212
+ - `bcp_sha256` is reserved for framework checksum metadata,
213
+ - local storage does not emulate signed URLs,
214
+ - S3 direct-transfer URLs are temporary credentials and require application authorization before issuance.
197
215
 
198
- ```ts
199
- import {
200
- createS3Storage,
201
- } from "bcp/server";
202
- ```
216
+ See `storage-ecosystem.md`.
203
217
 
204
- Document:
218
+ ### S3 ecosystem
205
219
 
206
- - bucket/region/endpoint setup,
207
- - optional explicit credentials,
208
- - AWS default credential chain behavior,
209
- - `forcePathStyle` for compatible services such as MinIO,
210
- - prefix handling,
211
- - multipart streaming,
212
- - ranges/ETag metadata,
213
- - overwrite limitations for concurrent multipart writers,
214
- - server-only credential/security requirements.
220
+ `createS3Storage()` keeps the same public constructor and now adds:
215
221
 
216
- See `s3-storage.md`.
222
+ ```text
223
+ ListObjectsV2
224
+ CopyObject
225
+ DeleteObjects
226
+ presigned GetObject
227
+ presigned PutObject
228
+ application metadata
229
+ ```
217
230
 
218
- ### Production multipart streaming
231
+ See `s3-storage.md`.
219
232
 
220
- ```ts
221
- import {
222
- storeMultipartFile,
223
- } from "bcp/server";
224
- ```
233
+ ### 0.1.26 foundations remain supported
225
234
 
226
- Document:
235
+ The following remain part of the storage stack:
227
236
 
228
- - one named streamed file field per operation,
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.
237
+ ```text
238
+ putStorageStream()
239
+ readStorageStream()
240
+ storeMultipartFile()
241
+ createStorageResponse()
242
+ ```
236
243
 
237
- See `file-upload.md`.
244
+ `0.1.27` builds on these rather than replacing them.
238
245
 
239
246
  ## CLI commands
240
247
 
@@ -278,6 +285,7 @@ docs/releases/
278
285
  Recommended routes:
279
286
 
280
287
  ```text
288
+ /releases/0.1.27
281
289
  /releases/0.1.26
282
290
  /releases/0.1.25
283
291
  /releases/0.1.24
@@ -357,15 +365,16 @@ npm run test:package
357
365
  npm run rc:check
358
366
  ```
359
367
 
360
- For `0.1.26`, package validation must also confirm the staged npm artifact contains:
368
+ For `0.1.27`, package validation must also confirm the staged artifact includes:
361
369
 
362
370
  ```text
363
371
  @aws-sdk/client-s3
364
372
  @aws-sdk/lib-storage
373
+ @aws-sdk/s3-request-presigner
365
374
  busboy
366
375
  ```
367
376
 
368
- and the S3/multipart streaming source files/public exports.
377
+ and the ecosystem runtime/public exports.
369
378
 
370
379
  ## Documentation QA checklist
371
380
 
@@ -377,19 +386,22 @@ and the S3/multipart streaming source files/public exports.
377
386
  - route names match the current router,
378
387
  - release notes match the framework version,
379
388
  - S3 credentials are never shown as public browser variables,
389
+ - signed URLs are documented as temporary credentials,
380
390
  - roadmap APIs are not presented as published guarantees.
381
391
 
382
- ## Next direction after 0.1.26
392
+ ## Next direction after 0.1.27
383
393
 
384
- Recommended `0.1.27` direction:
394
+ Planned `0.1.28 — Production Hardening` focus:
385
395
 
386
- - signed URLs where needed,
387
- - object listing/copy/move capabilities,
388
- - broader production hardening,
389
- - graceful shutdown/resource cleanup,
390
- - stronger storage/provider diagnostics.
396
+ - graceful HTTP shutdown and active-request draining,
397
+ - database/S3 resource cleanup,
398
+ - trusted proxy and forwarded-header handling,
399
+ - timeout controls,
400
+ - security hardening,
401
+ - Docker/standalone runtime regression coverage,
402
+ - startup/runtime dependency diagnostics.
391
403
 
392
- These are roadmap items, not `0.1.26` guarantees.
404
+ These remain roadmap items until their source/tests land.
393
405
 
394
406
  ## Repository authority
395
407
 
@@ -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.