@uploadbox/core 0.1.0 → 0.2.0
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 +228 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# @uploadbox/core
|
|
2
|
+
|
|
3
|
+
Core utilities for Uploadbox — S3 operations, file validation, and the file-router builder pattern.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@uploadbox/core)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i @uploadbox/core
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
Define upload routes using the `f()` builder pattern:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { f } from "@uploadbox/core";
|
|
20
|
+
|
|
21
|
+
const router = {
|
|
22
|
+
imageUploader: f({ image: { maxFileSize: "4MB", maxFileCount: 5 } })
|
|
23
|
+
.middleware(async ({ files, auth }) => {
|
|
24
|
+
return { userId: auth?.apiKeyId };
|
|
25
|
+
})
|
|
26
|
+
.onUploadComplete(async ({ file, metadata }) => {
|
|
27
|
+
console.log("Upload complete:", file.url);
|
|
28
|
+
return { fileId: file.key };
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
documentUploader: f({
|
|
32
|
+
pdf: { maxFileSize: "16MB" },
|
|
33
|
+
text: { maxFileSize: "1MB" },
|
|
34
|
+
})
|
|
35
|
+
.middleware(async ({ auth }) => {
|
|
36
|
+
if (!auth) throw new Error("Unauthorized");
|
|
37
|
+
return { userId: auth.apiKeyId };
|
|
38
|
+
})
|
|
39
|
+
.onUploadComplete(async ({ file }) => {
|
|
40
|
+
return { url: file.url };
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type AppRouter = typeof router;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### File Route Config
|
|
48
|
+
|
|
49
|
+
Each route accepts a map of file types with constraints:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
f({
|
|
53
|
+
image: {
|
|
54
|
+
maxFileSize: "4MB", // "B" | "KB" | "MB" | "GB"
|
|
55
|
+
maxFileCount: 10,
|
|
56
|
+
minFileCount: 1,
|
|
57
|
+
acl: "public-read", // "public-read" | "private"
|
|
58
|
+
contentDisposition: "inline",
|
|
59
|
+
presignedUrlExpiry: 3600, // seconds
|
|
60
|
+
defaultTtlSeconds: 86400,
|
|
61
|
+
maxTtlSeconds: 604800,
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Supported file types: `image`, `video`, `audio`, `pdf`, `text`, `blob` (any file).
|
|
67
|
+
|
|
68
|
+
## S3 Utilities
|
|
69
|
+
|
|
70
|
+
### Create an S3 Client
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { createS3Client } from "@uploadbox/core";
|
|
74
|
+
|
|
75
|
+
const s3 = createS3Client({
|
|
76
|
+
bucket: "my-bucket",
|
|
77
|
+
region: "us-east-1",
|
|
78
|
+
accessKeyId: "...",
|
|
79
|
+
secretAccessKey: "...",
|
|
80
|
+
// Optional — for MinIO, R2, Wasabi:
|
|
81
|
+
endpoint: "https://s3.example.com",
|
|
82
|
+
forcePathStyle: true,
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Or use environment variables (`UPLOADBOX_S3_BUCKET`, `UPLOADBOX_AWS_REGION`, etc.) and let `createUploadbox()` auto-configure.
|
|
87
|
+
|
|
88
|
+
### Presigned URLs
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { generatePresignedPutUrl, generatePresignedGetUrl } from "@uploadbox/core";
|
|
92
|
+
|
|
93
|
+
const putUrl = await generatePresignedPutUrl(s3, "bucket", "key.png", "image/png", 1024);
|
|
94
|
+
const getUrl = await generatePresignedGetUrl(s3, "bucket", "key.png", 3600);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Other S3 Operations
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { headObject, deleteObject, deleteObjects, listObjects } from "@uploadbox/core";
|
|
101
|
+
|
|
102
|
+
await headObject(s3, "bucket", "key.png"); // verify existence
|
|
103
|
+
await deleteObject(s3, "bucket", "key.png");
|
|
104
|
+
await deleteObjects(s3, "bucket", ["a.png", "b.png"]);
|
|
105
|
+
const { keys, nextToken } = await listObjects(s3, "bucket", "uploads/");
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Multipart Uploads
|
|
109
|
+
|
|
110
|
+
Files larger than 10 MB are automatically split into parts:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import {
|
|
114
|
+
createMultipartUpload,
|
|
115
|
+
generatePresignedPartUrls,
|
|
116
|
+
completeMultipartUpload,
|
|
117
|
+
abortMultipartUpload,
|
|
118
|
+
MULTIPART_THRESHOLD,
|
|
119
|
+
DEFAULT_PART_SIZE,
|
|
120
|
+
} from "@uploadbox/core";
|
|
121
|
+
|
|
122
|
+
const uploadId = await createMultipartUpload(s3, "bucket", "key.mp4", "video/mp4");
|
|
123
|
+
const parts = await generatePresignedPartUrls(s3, "bucket", "key.mp4", uploadId, [1, 2, 3]);
|
|
124
|
+
// ... upload parts ...
|
|
125
|
+
await completeMultipartUpload(s3, "bucket", "key.mp4", uploadId, [
|
|
126
|
+
{ partNumber: 1, etag: "..." },
|
|
127
|
+
{ partNumber: 2, etag: "..." },
|
|
128
|
+
]);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Processing Hooks
|
|
132
|
+
|
|
133
|
+
Optional post-upload processing via subpath imports. These run server-side after a file is uploaded.
|
|
134
|
+
|
|
135
|
+
### Image Resize
|
|
136
|
+
|
|
137
|
+
Requires `sharp` as a peer dependency.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { createImageResizeHook } from "@uploadbox/core/hooks/image-resize";
|
|
141
|
+
|
|
142
|
+
const resizeHook = createImageResizeHook({
|
|
143
|
+
maxWidth: 1920,
|
|
144
|
+
maxHeight: 1080,
|
|
145
|
+
quality: 80,
|
|
146
|
+
format: "webp", // "jpeg" | "png" | "webp"
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Virus Scan
|
|
151
|
+
|
|
152
|
+
Requires a running ClamAV REST service.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { createVirusScanHook } from "@uploadbox/core/hooks/virus-scan";
|
|
156
|
+
|
|
157
|
+
const scanHook = createVirusScanHook({
|
|
158
|
+
clamavUrl: "http://localhost:3310",
|
|
159
|
+
deleteOnDetection: true,
|
|
160
|
+
timeoutMs: 30000,
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Server-Side Upload
|
|
165
|
+
|
|
166
|
+
Upload files directly from your server (no presigned URL):
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { serverUpload, serverUploadMany } from "@uploadbox/core";
|
|
170
|
+
|
|
171
|
+
const result = await serverUpload(s3, config, {
|
|
172
|
+
name: "report.pdf",
|
|
173
|
+
body: buffer,
|
|
174
|
+
contentType: "application/pdf",
|
|
175
|
+
acl: "private",
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const results = await serverUploadMany(s3, config, files, 3); // concurrency = 3
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### UploadboxApi
|
|
182
|
+
|
|
183
|
+
High-level wrapper combining common operations:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import { UploadboxApi } from "@uploadbox/core";
|
|
187
|
+
|
|
188
|
+
const api = new UploadboxApi(s3, config);
|
|
189
|
+
|
|
190
|
+
await api.uploadFile({ name: "photo.jpg", body: buffer });
|
|
191
|
+
await api.deleteFile("uploads/photo.jpg");
|
|
192
|
+
const url = await api.getSignedUrl("uploads/photo.jpg");
|
|
193
|
+
const { keys } = await api.listFiles({ prefix: "uploads/" });
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Utilities
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
import { parseFileSize, formatFileSize, validateFiles, generateFileKey } from "@uploadbox/core";
|
|
200
|
+
|
|
201
|
+
parseFileSize("4MB"); // 4194304
|
|
202
|
+
formatFileSize(4194304); // "4 MB"
|
|
203
|
+
generateFileKey("my file.png"); // "my-file-a1b2c3d4e5f6.png"
|
|
204
|
+
|
|
205
|
+
// Throws UploadboxError if validation fails
|
|
206
|
+
validateFiles(files, { image: { maxFileSize: "4MB", maxFileCount: 5 } });
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Key Types
|
|
210
|
+
|
|
211
|
+
| Type | Description |
|
|
212
|
+
|------|-------------|
|
|
213
|
+
| `FileRouter` | `Record<string, FileRoute>` — your upload route definitions |
|
|
214
|
+
| `UploadboxConfig` | S3 config: bucket, region, credentials, endpoint, CDN URL |
|
|
215
|
+
| `UploadedFileData` | Completed upload: key, name, size, type, url, acl, metadata |
|
|
216
|
+
| `AuthContext` | `{ apiKeyId, apiKeyName, projectId? }` |
|
|
217
|
+
| `FileRouteConfig` | Per-type constraints: maxFileSize, maxFileCount, acl, etc. |
|
|
218
|
+
| `ProcessingHook` | `{ name, shouldRun, run, timeoutMs? }` |
|
|
219
|
+
| `UploadboxError` | Structured error with code, statusCode, and static factories |
|
|
220
|
+
|
|
221
|
+
## Related Packages
|
|
222
|
+
|
|
223
|
+
- [`@uploadbox/nextjs`](https://www.npmjs.com/package/@uploadbox/nextjs) — Next.js route handler adapter
|
|
224
|
+
- [`@uploadbox/react`](https://www.npmjs.com/package/@uploadbox/react) — React components and hooks
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT
|