@chidchanun/bcp 0.1.25 → 0.1.26
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 +169 -263
- package/docs/README.md +148 -210
- package/docs/file-upload.md +194 -32
- package/docs/releases/0.1.26.md +281 -0
- package/docs/s3-storage.md +276 -0
- package/docs/storage.md +278 -162
- package/package.json +5 -2
- package/packages/client/src/database.mjs +236 -0
- package/packages/client/src/server.ts +22 -0
- package/packages/server/src/file-delivery.ts +8 -24
- 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/docs/file-upload.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# File Upload
|
|
2
2
|
|
|
3
|
-
BCP Framework
|
|
3
|
+
BCP Framework provides two server-only multipart upload paths through `bcp/server`:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- the buffered `FormData` helpers introduced in `0.1.24`,
|
|
6
|
+
- direct multipart-to-storage streaming added in `0.1.26`.
|
|
7
|
+
|
|
8
|
+
Use the buffered API for small forms and workflows that need normal `FormData` access. Use `storeMultipartFile()` when large files should flow directly from the request body into a `StorageAdapter` without materializing the complete file in application memory.
|
|
9
|
+
|
|
10
|
+
## Buffered API route
|
|
6
11
|
|
|
7
12
|
```ts
|
|
8
13
|
import {
|
|
@@ -64,6 +69,125 @@ export async function POST(
|
|
|
64
69
|
}
|
|
65
70
|
```
|
|
66
71
|
|
|
72
|
+
## Production streaming upload
|
|
73
|
+
|
|
74
|
+
BCP `0.1.26` adds `storeMultipartFile()`:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import {
|
|
78
|
+
createLocalStorage,
|
|
79
|
+
storeMultipartFile,
|
|
80
|
+
} from "bcp/server";
|
|
81
|
+
|
|
82
|
+
const uploads =
|
|
83
|
+
createLocalStorage({
|
|
84
|
+
directory:
|
|
85
|
+
"./uploads",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export async function POST(
|
|
89
|
+
request: Request
|
|
90
|
+
) {
|
|
91
|
+
const stored =
|
|
92
|
+
await storeMultipartFile(
|
|
93
|
+
request,
|
|
94
|
+
{
|
|
95
|
+
storage:
|
|
96
|
+
uploads,
|
|
97
|
+
fieldName:
|
|
98
|
+
"file",
|
|
99
|
+
key:
|
|
100
|
+
"incoming/upload.bin",
|
|
101
|
+
maxBytes:
|
|
102
|
+
100 * 1024 * 1024,
|
|
103
|
+
constraints: {
|
|
104
|
+
maxBytes:
|
|
105
|
+
80 * 1024 * 1024,
|
|
106
|
+
allowedTypes: [
|
|
107
|
+
"application/octet-stream",
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
return Response.json(
|
|
114
|
+
stored
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The request body is parsed incrementally. The target file stream is handed to the selected `StorageAdapter` while multipart data is still arriving.
|
|
120
|
+
|
|
121
|
+
This path is intended for large uploads and shared/object storage. It avoids calling `request.formData()` for the uploaded file.
|
|
122
|
+
|
|
123
|
+
## Streaming directly to S3-compatible storage
|
|
124
|
+
|
|
125
|
+
The same API works with `createS3Storage()`:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import {
|
|
129
|
+
createS3Storage,
|
|
130
|
+
storeMultipartFile,
|
|
131
|
+
} from "bcp/server";
|
|
132
|
+
|
|
133
|
+
const uploads =
|
|
134
|
+
createS3Storage({
|
|
135
|
+
bucket:
|
|
136
|
+
process.env.S3_BUCKET!,
|
|
137
|
+
region:
|
|
138
|
+
process.env.S3_REGION!,
|
|
139
|
+
endpoint:
|
|
140
|
+
process.env.S3_ENDPOINT,
|
|
141
|
+
accessKeyId:
|
|
142
|
+
process.env.S3_ACCESS_KEY_ID,
|
|
143
|
+
secretAccessKey:
|
|
144
|
+
process.env.S3_SECRET_ACCESS_KEY,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
export async function POST(
|
|
148
|
+
request: Request
|
|
149
|
+
) {
|
|
150
|
+
return Response.json(
|
|
151
|
+
await storeMultipartFile(
|
|
152
|
+
request,
|
|
153
|
+
{
|
|
154
|
+
storage:
|
|
155
|
+
uploads,
|
|
156
|
+
fieldName:
|
|
157
|
+
"file",
|
|
158
|
+
key: (
|
|
159
|
+
file
|
|
160
|
+
) =>
|
|
161
|
+
`documents/${crypto.randomUUID()}${file.originalName.endsWith(".pdf") ? ".pdf" : ""}`,
|
|
162
|
+
constraints: {
|
|
163
|
+
maxBytes:
|
|
164
|
+
50 * 1024 * 1024,
|
|
165
|
+
allowedTypes: [
|
|
166
|
+
"application/pdf",
|
|
167
|
+
],
|
|
168
|
+
allowedExtensions: [
|
|
169
|
+
".pdf",
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The `key` option can be a fixed string or an async callback receiving:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
{
|
|
182
|
+
fieldName,
|
|
183
|
+
originalName,
|
|
184
|
+
encoding,
|
|
185
|
+
contentType,
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
When `key` is omitted, BCP creates a UUID-based key and preserves a short sanitized extension.
|
|
190
|
+
|
|
67
191
|
## Request body limit
|
|
68
192
|
|
|
69
193
|
BCP's security gateway enforces the framework-wide request body limit before the request reaches the application server.
|
|
@@ -79,16 +203,20 @@ import {
|
|
|
79
203
|
export default defineConfig({
|
|
80
204
|
server: {
|
|
81
205
|
bodyLimit:
|
|
82
|
-
|
|
206
|
+
100 * 1024 * 1024,
|
|
83
207
|
},
|
|
84
208
|
});
|
|
85
209
|
```
|
|
86
210
|
|
|
87
211
|
The same setting can be supplied with `BCP_BODY_LIMIT`.
|
|
88
212
|
|
|
89
|
-
|
|
213
|
+
For buffered uploads, keep `server.bodyLimit` greater than or equal to the multipart limit used by `parseMultipartFormData()`.
|
|
214
|
+
|
|
215
|
+
For streaming uploads, keep the infrastructure/framework body limit greater than or equal to `storeMultipartFile({ maxBytes })`. Streaming avoids full application-memory buffering, but it does not bypass reverse-proxy, framework or platform request-size limits.
|
|
216
|
+
|
|
217
|
+
## Multipart request limits
|
|
90
218
|
|
|
91
|
-
|
|
219
|
+
Buffered parsing:
|
|
92
220
|
|
|
93
221
|
```ts
|
|
94
222
|
const formData =
|
|
@@ -101,16 +229,31 @@ const formData =
|
|
|
101
229
|
);
|
|
102
230
|
```
|
|
103
231
|
|
|
104
|
-
|
|
232
|
+
Streaming parsing:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
await storeMultipartFile(
|
|
236
|
+
request,
|
|
237
|
+
{
|
|
238
|
+
storage,
|
|
239
|
+
fieldName:
|
|
240
|
+
"file",
|
|
241
|
+
maxBytes:
|
|
242
|
+
100 * 1024 * 1024,
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Both helpers:
|
|
105
248
|
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
249
|
+
- require `multipart/form-data`,
|
|
250
|
+
- reject a declared `Content-Length` above the configured request limit,
|
|
251
|
+
- enforce the request limit while processing,
|
|
252
|
+
- throw `UploadError` with HTTP-oriented status information on validation failures.
|
|
110
253
|
|
|
111
|
-
`
|
|
254
|
+
`storeMultipartFile()` additionally applies the file-level `constraints.maxBytes` while bytes are flowing into storage. Native streaming adapters can abort incomplete writes rather than keeping a partial object.
|
|
112
255
|
|
|
113
|
-
## Reading files
|
|
256
|
+
## Reading buffered files
|
|
114
257
|
|
|
115
258
|
Use `getUploadedFile()` for optional files:
|
|
116
259
|
|
|
@@ -140,7 +283,7 @@ A missing required file throws `UploadError` with code `FILE_REQUIRED` and statu
|
|
|
140
283
|
|
|
141
284
|
## File constraints
|
|
142
285
|
|
|
143
|
-
|
|
286
|
+
Buffered and streaming uploads can validate:
|
|
144
287
|
|
|
145
288
|
```ts
|
|
146
289
|
{
|
|
@@ -160,9 +303,9 @@ Supported checks are:
|
|
|
160
303
|
- MIME type allowlist,
|
|
161
304
|
- extension allowlist.
|
|
162
305
|
|
|
163
|
-
MIME type and file extension are metadata
|
|
306
|
+
MIME type and file extension are multipart metadata. They are useful validation signals but are not content-signature verification. Applications accepting security-sensitive formats should inspect the file content or magic bytes before trusting the file type.
|
|
164
307
|
|
|
165
|
-
## Saving files
|
|
308
|
+
## Saving buffered files
|
|
166
309
|
|
|
167
310
|
```ts
|
|
168
311
|
const saved =
|
|
@@ -192,25 +335,31 @@ The result contains:
|
|
|
192
335
|
|
|
193
336
|
Files are created with exclusive-write behavior by default. An existing destination produces `FILE_EXISTS` instead of silently overwriting data.
|
|
194
337
|
|
|
195
|
-
Use `overwrite: true` only when replacement is intentional
|
|
338
|
+
Use `overwrite: true` only when replacement is intentional.
|
|
339
|
+
|
|
340
|
+
## Streaming result
|
|
341
|
+
|
|
342
|
+
`storeMultipartFile()` returns storage metadata together with multipart identity:
|
|
196
343
|
|
|
197
344
|
```ts
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
345
|
+
{
|
|
346
|
+
key: string;
|
|
347
|
+
size: number;
|
|
348
|
+
contentType: string;
|
|
349
|
+
lastModified: Date;
|
|
350
|
+
etag: string;
|
|
351
|
+
checksumSha256?: string;
|
|
352
|
+
fieldName: string;
|
|
353
|
+
originalName: string;
|
|
354
|
+
encoding: string;
|
|
355
|
+
}
|
|
209
356
|
```
|
|
210
357
|
|
|
358
|
+
The checksum is optional because not every storage provider exposes a portable whole-object SHA-256 value for multipart uploads.
|
|
359
|
+
|
|
211
360
|
## File-name safety
|
|
212
361
|
|
|
213
|
-
`saveUploadedFile()` sanitizes the final file name and verifies that the resolved destination remains inside the configured directory.
|
|
362
|
+
`saveUploadedFile()` sanitizes the final local file name and verifies that the resolved destination remains inside the configured directory.
|
|
214
363
|
|
|
215
364
|
You can use the same sanitizer independently:
|
|
216
365
|
|
|
@@ -225,7 +374,7 @@ const safeName =
|
|
|
225
374
|
);
|
|
226
375
|
```
|
|
227
376
|
|
|
228
|
-
|
|
377
|
+
Streaming storage uses logical storage keys instead of direct operating-system paths. Storage adapters validate their own key rules; local/S3 BCP adapters reject traversal segments.
|
|
229
378
|
|
|
230
379
|
## Upload errors
|
|
231
380
|
|
|
@@ -273,8 +422,21 @@ INVALID_FILE_NAME
|
|
|
273
422
|
FILE_EXISTS
|
|
274
423
|
```
|
|
275
424
|
|
|
276
|
-
|
|
425
|
+
The streaming helper maps storage `STREAM_TOO_LARGE` and `OBJECT_EXISTS` failures into the corresponding upload-facing error codes.
|
|
426
|
+
|
|
427
|
+
## Choosing buffered vs streaming
|
|
428
|
+
|
|
429
|
+
Prefer the buffered API when:
|
|
430
|
+
|
|
431
|
+
- uploads are small,
|
|
432
|
+
- the route needs several regular form fields/files as `FormData`,
|
|
433
|
+
- application code needs random access to the parsed file before storage.
|
|
434
|
+
|
|
435
|
+
Prefer `storeMultipartFile()` when:
|
|
277
436
|
|
|
278
|
-
|
|
437
|
+
- files can be large,
|
|
438
|
+
- the file should be written directly to local/shared/object storage,
|
|
439
|
+
- memory usage should stay bounded,
|
|
440
|
+
- S3 multipart upload is required for streams with unknown final size.
|
|
279
441
|
|
|
280
|
-
|
|
442
|
+
The `0.1.26` streaming API intentionally targets one named file field per call. Multiple-file orchestration and richer streaming form-field APIs can be added later without changing the storage contract.
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# BCP Framework 0.1.26
|
|
2
|
+
|
|
3
|
+
BCP Framework `0.1.26` is the cloud-storage and production-streaming milestone.
|
|
4
|
+
|
|
5
|
+
> Release state: release candidate source complete; not published until the full RC checks pass and npm publication completes.
|
|
6
|
+
|
|
7
|
+
## Highlights
|
|
8
|
+
|
|
9
|
+
- Added additive storage streaming capabilities without breaking `0.1.25` adapters.
|
|
10
|
+
- Added native local filesystem streaming reads/writes.
|
|
11
|
+
- Added S3-compatible storage through `createS3Storage()`.
|
|
12
|
+
- Added direct multipart-to-storage streaming through `storeMultipartFile()`.
|
|
13
|
+
- Added streamed full and ranged file delivery.
|
|
14
|
+
- Added stream size limits and abort propagation.
|
|
15
|
+
- Added cleanup/abort behavior for incomplete local and S3 multipart writes.
|
|
16
|
+
- Added S3 integration coverage using a local S3-compatible test endpoint.
|
|
17
|
+
- Added package smoke coverage for cloud-storage dependencies and public exports.
|
|
18
|
+
|
|
19
|
+
## Storage streaming foundation
|
|
20
|
+
|
|
21
|
+
New public helpers:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
getStorageCapabilities,
|
|
26
|
+
putStorageStream,
|
|
27
|
+
readStorageStream,
|
|
28
|
+
} from "bcp/server";
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
New public types:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
StorageAdapterCapabilities
|
|
35
|
+
StoragePutStreamOptions
|
|
36
|
+
StorageReadableStream
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The streaming methods on `StorageAdapter` are optional:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
interface StorageAdapter {
|
|
43
|
+
put(...): Promise<StorageObjectMetadata>;
|
|
44
|
+
putStream?(...): Promise<StorageObjectMetadata>;
|
|
45
|
+
|
|
46
|
+
read(...): Promise<Uint8Array>;
|
|
47
|
+
readStream?(...): Promise<StorageReadableStream>;
|
|
48
|
+
|
|
49
|
+
stat(...): Promise<StorageObjectMetadata | null>;
|
|
50
|
+
exists(...): Promise<boolean>;
|
|
51
|
+
delete(...): Promise<boolean>;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`putStorageStream()` / `readStorageStream()` automatically use native streaming methods when available and preserve compatibility with legacy buffered adapters.
|
|
56
|
+
|
|
57
|
+
## Local storage streaming
|
|
58
|
+
|
|
59
|
+
`createLocalStorage()` now advertises:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
{
|
|
63
|
+
streamingRead: true,
|
|
64
|
+
streamingWrite: true,
|
|
65
|
+
ranges: true,
|
|
66
|
+
signedUrls: false,
|
|
67
|
+
listing: false,
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Streaming local writes:
|
|
72
|
+
|
|
73
|
+
- write chunks directly to disk,
|
|
74
|
+
- calculate SHA-256 while consuming the stream,
|
|
75
|
+
- enforce `maxBytes`,
|
|
76
|
+
- honor `AbortSignal`,
|
|
77
|
+
- remove incomplete objects when the operation fails.
|
|
78
|
+
|
|
79
|
+
Streaming local reads support full objects and inclusive byte ranges without first reading the complete file into memory.
|
|
80
|
+
|
|
81
|
+
## S3-compatible storage
|
|
82
|
+
|
|
83
|
+
New API:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import {
|
|
87
|
+
createS3Storage,
|
|
88
|
+
} from "bcp/server";
|
|
89
|
+
|
|
90
|
+
const storage =
|
|
91
|
+
createS3Storage({
|
|
92
|
+
bucket:
|
|
93
|
+
process.env.S3_BUCKET!,
|
|
94
|
+
region:
|
|
95
|
+
process.env.S3_REGION!,
|
|
96
|
+
endpoint:
|
|
97
|
+
process.env.S3_ENDPOINT,
|
|
98
|
+
accessKeyId:
|
|
99
|
+
process.env.S3_ACCESS_KEY_ID,
|
|
100
|
+
secretAccessKey:
|
|
101
|
+
process.env.S3_SECRET_ACCESS_KEY,
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The adapter supports:
|
|
106
|
+
|
|
107
|
+
- `put()` / conditional create-only writes,
|
|
108
|
+
- `putStream()` through AWS SDK multipart upload,
|
|
109
|
+
- `stat()` through `HeadObject`,
|
|
110
|
+
- `read()` / `readStream()` through `GetObject`,
|
|
111
|
+
- inclusive ranges,
|
|
112
|
+
- `exists()`,
|
|
113
|
+
- `delete()`,
|
|
114
|
+
- logical key prefixes,
|
|
115
|
+
- custom endpoints,
|
|
116
|
+
- path-style endpoints for MinIO/S3-compatible services,
|
|
117
|
+
- AWS SDK default credential provider behavior when explicit credentials are omitted,
|
|
118
|
+
- injected `S3Client` instances for advanced deployments.
|
|
119
|
+
|
|
120
|
+
The S3 adapter exposes `destroy()` for internally owned AWS SDK clients.
|
|
121
|
+
|
|
122
|
+
Buffered BCP writes persist SHA-256 as S3 user metadata. Whole-object SHA-256 remains optional for multipart streams because cloud providers do not expose one portable checksum format across all multipart implementations.
|
|
123
|
+
|
|
124
|
+
## Production multipart streaming
|
|
125
|
+
|
|
126
|
+
New API:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import {
|
|
130
|
+
storeMultipartFile,
|
|
131
|
+
} from "bcp/server";
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
const stored =
|
|
138
|
+
await storeMultipartFile(
|
|
139
|
+
request,
|
|
140
|
+
{
|
|
141
|
+
storage,
|
|
142
|
+
fieldName:
|
|
143
|
+
"file",
|
|
144
|
+
key:
|
|
145
|
+
"documents/report.pdf",
|
|
146
|
+
maxBytes:
|
|
147
|
+
100 * 1024 * 1024,
|
|
148
|
+
constraints: {
|
|
149
|
+
maxBytes:
|
|
150
|
+
80 * 1024 * 1024,
|
|
151
|
+
allowedTypes: [
|
|
152
|
+
"application/pdf",
|
|
153
|
+
],
|
|
154
|
+
allowedExtensions: [
|
|
155
|
+
".pdf",
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The multipart parser consumes `Request.body` incrementally and sends the selected file field directly into `putStorageStream()`.
|
|
163
|
+
|
|
164
|
+
This avoids calling `request.formData()` and avoids requiring the complete file to exist in application memory before storage begins.
|
|
165
|
+
|
|
166
|
+
The existing buffered APIs remain supported:
|
|
167
|
+
|
|
168
|
+
```text
|
|
169
|
+
parseMultipartFormData()
|
|
170
|
+
getUploadedFile()
|
|
171
|
+
requireUploadedFile()
|
|
172
|
+
saveUploadedFile()
|
|
173
|
+
storeUploadedFile()
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Upload validation and cleanup
|
|
177
|
+
|
|
178
|
+
`storeMultipartFile()` supports:
|
|
179
|
+
|
|
180
|
+
- total multipart request limit (`maxBytes`),
|
|
181
|
+
- per-file limit (`constraints.maxBytes`),
|
|
182
|
+
- MIME allowlists,
|
|
183
|
+
- extension allowlists,
|
|
184
|
+
- fixed or callback-generated storage keys,
|
|
185
|
+
- caller `AbortSignal`,
|
|
186
|
+
- one required named file field per operation.
|
|
187
|
+
|
|
188
|
+
Storage failures are translated where appropriate:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
STREAM_TOO_LARGE -> FILE_TOO_LARGE
|
|
192
|
+
OBJECT_EXISTS -> FILE_EXISTS
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Partial local objects are removed on failure. S3 multipart uploads use automatic part cleanup when upload completion fails.
|
|
196
|
+
|
|
197
|
+
## File delivery
|
|
198
|
+
|
|
199
|
+
`createStorageResponse()` uses `readStorageStream()` for response bodies.
|
|
200
|
+
|
|
201
|
+
Streaming-capable adapters therefore support:
|
|
202
|
+
|
|
203
|
+
```text
|
|
204
|
+
200 full object
|
|
205
|
+
206 single byte range
|
|
206
|
+
HEAD without body read
|
|
207
|
+
304 validators
|
|
208
|
+
416 invalid ranges
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The existing ETag, Last-Modified, If-Range, safe Content-Disposition and conservative private-cache behavior remain unchanged.
|
|
212
|
+
|
|
213
|
+
## Package/runtime dependencies
|
|
214
|
+
|
|
215
|
+
The published framework package now carries the runtime dependencies required by this milestone:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
@aws-sdk/client-s3
|
|
219
|
+
@aws-sdk/lib-storage
|
|
220
|
+
busboy
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Package smoke tests verify these dependencies are present in the staged npm artifact.
|
|
224
|
+
|
|
225
|
+
## Tests
|
|
226
|
+
|
|
227
|
+
`0.1.26` adds coverage for:
|
|
228
|
+
|
|
229
|
+
- local streaming writes and ranges,
|
|
230
|
+
- legacy adapter streaming fallbacks,
|
|
231
|
+
- stream size cleanup,
|
|
232
|
+
- streaming multipart uploads to local storage,
|
|
233
|
+
- multipart MIME and size failures,
|
|
234
|
+
- S3 buffered writes,
|
|
235
|
+
- S3 no-overwrite behavior,
|
|
236
|
+
- S3 streaming writes,
|
|
237
|
+
- S3 ranged reads,
|
|
238
|
+
- S3 streamed reads,
|
|
239
|
+
- S3 deletes,
|
|
240
|
+
- public `bcp/server` exports,
|
|
241
|
+
- packaged cloud-storage dependencies/artifacts.
|
|
242
|
+
|
|
243
|
+
The S3 integration test uses a local S3-compatible HTTP fixture; it does not require an AWS account.
|
|
244
|
+
|
|
245
|
+
## Backward compatibility
|
|
246
|
+
|
|
247
|
+
`0.1.26` is additive to the `0.1.25` storage contract.
|
|
248
|
+
|
|
249
|
+
Existing applications can continue using:
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
storage.put(...)
|
|
253
|
+
storage.read(...)
|
|
254
|
+
storeUploadedFile(...)
|
|
255
|
+
createStorageResponse(...)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Custom `StorageAdapter` implementations do not need to implement streaming methods immediately. Generic helpers fall back to the required buffer APIs.
|
|
259
|
+
|
|
260
|
+
## Intentional limitations
|
|
261
|
+
|
|
262
|
+
The following are not part of `0.1.26`:
|
|
263
|
+
|
|
264
|
+
- presigned URLs,
|
|
265
|
+
- object listing,
|
|
266
|
+
- copy/move helpers,
|
|
267
|
+
- multiple HTTP byte ranges,
|
|
268
|
+
- multi-file streaming multipart orchestration,
|
|
269
|
+
- generated cloud-provider-specific adapters beyond the S3 API.
|
|
270
|
+
|
|
271
|
+
These can be added in later milestones without expanding this release further.
|
|
272
|
+
|
|
273
|
+
## Documentation
|
|
274
|
+
|
|
275
|
+
- [File Upload](../file-upload.md)
|
|
276
|
+
- [Storage and File Delivery](../storage.md)
|
|
277
|
+
- [S3-Compatible Storage](../s3-storage.md)
|
|
278
|
+
|
|
279
|
+
## Next milestone
|
|
280
|
+
|
|
281
|
+
The recommended next storage/DX milestone is `0.1.27`, focusing on higher-level storage operations such as signed URLs/listing where needed, plus broader production hardening rather than changing the `0.1.26` core streaming contract.
|