@ustorage/sdk 0.1.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/LICENSE +21 -0
- package/README.md +343 -0
- package/dist/browser/index.cjs +483 -0
- package/dist/browser/index.cjs.map +1 -0
- package/dist/browser/index.d.cts +37 -0
- package/dist/browser/index.d.ts +37 -0
- package/dist/browser/index.js +445 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/core/index.cjs +397 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +273 -0
- package/dist/core/index.d.ts +273 -0
- package/dist/core/index.js +363 -0
- package/dist/core/index.js.map +1 -0
- package/dist/node/index.cjs +555 -0
- package/dist/node/index.cjs.map +1 -0
- package/dist/node/index.d.cts +59 -0
- package/dist/node/index.d.ts +59 -0
- package/dist/node/index.js +514 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) UStorage
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# @ustorage/sdk
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for [UStorage](https://ustorage.app) — chunked uploads, resume, and file URL resolution against the UStorage upload API.
|
|
4
|
+
|
|
5
|
+
Supports **Node.js** (≥20) and **browser** runtimes with separate entry points. Ships ESM and CommonJS with TypeScript declarations.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @ustorage/sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @ustorage/sdk
|
|
13
|
+
yarn add @ustorage/sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Entry points
|
|
17
|
+
|
|
18
|
+
| Import | Use when |
|
|
19
|
+
|--------|----------|
|
|
20
|
+
| `@ustorage/sdk` | Shared types, `UploadApi`, `Uploader`, errors — no runtime client |
|
|
21
|
+
| `@ustorage/sdk/node` | `UStorageNodeClient` — servers, scripts, workers |
|
|
22
|
+
| `@ustorage/sdk/browser` | `UStorageBrowserClient` — web apps (no `secretKey`) |
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import type { CreateUploadSessionRequest, UploadObjectResult } from '@ustorage/sdk';
|
|
26
|
+
import { UStorageNodeClient } from '@ustorage/sdk/node';
|
|
27
|
+
import { UStorageBrowserClient } from '@ustorage/sdk/browser';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
|
|
32
|
+
- **Node.js** ≥ 20 (for `@ustorage/sdk/node`)
|
|
33
|
+
- A running UStorage upload service (`uploadBaseUrl`, e.g. `http://localhost:6900` in development)
|
|
34
|
+
- Credentials scoped to a workspace (see [Authentication](#authentication))
|
|
35
|
+
|
|
36
|
+
## Quick start (Node)
|
|
37
|
+
|
|
38
|
+
Node clients can authenticate with **access key + secret key** or a **Bearer token**. Credentials are already bound to a workspace, so uploads use `bucketName` and `key` (not workspace/bucket UUIDs).
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { UStorageNodeClient } from '@ustorage/sdk/node';
|
|
42
|
+
|
|
43
|
+
const client = new UStorageNodeClient({
|
|
44
|
+
uploadBaseUrl: process.env.USTORAGE_UPLOAD_URL!,
|
|
45
|
+
auth: {
|
|
46
|
+
accessKey: process.env.USTORAGE_ACCESS_KEY!,
|
|
47
|
+
secretKey: process.env.USTORAGE_SECRET_KEY!,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const result = await client.putObject({
|
|
52
|
+
bucketName: 'videos',
|
|
53
|
+
key: 'campaigns/intro.mp4',
|
|
54
|
+
body: './video.mp4',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
console.log(result.url, result.fileId);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Upload from a file path (key defaults to the basename of the path):
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
await client.uploadFile('./avatar.png', {
|
|
64
|
+
bucketName: 'assets',
|
|
65
|
+
key: 'users/u_123/avatar.png',
|
|
66
|
+
resume: true,
|
|
67
|
+
onProgress: (e) => console.log(`${e.progress}%`),
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Buffer upload:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
await client.putObject({
|
|
75
|
+
bucketName: 'assets',
|
|
76
|
+
key: 'data/report.json',
|
|
77
|
+
body: Buffer.from('{"ok":true}'),
|
|
78
|
+
contentType: 'application/json',
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Bearer token (Node)
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
const client = new UStorageNodeClient({
|
|
86
|
+
uploadBaseUrl: process.env.USTORAGE_UPLOAD_URL!,
|
|
87
|
+
auth: {
|
|
88
|
+
bearerToken: () => fetchNewToken(), // string or async factory
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Quick start (Browser)
|
|
94
|
+
|
|
95
|
+
**Never pass `secretKey` in the browser.** Mint a short-lived upload token on your backend, then pass it to the client.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { UStorageBrowserClient } from '@ustorage/sdk/browser';
|
|
99
|
+
|
|
100
|
+
const uploadToken = await fetch('/api/upload-token', {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: { 'content-type': 'application/json' },
|
|
103
|
+
body: JSON.stringify({ bucketName: 'assets', key: `uploads/${file.name}` }),
|
|
104
|
+
}).then((r) => r.text());
|
|
105
|
+
|
|
106
|
+
const client = new UStorageBrowserClient({
|
|
107
|
+
uploadBaseUrl: import.meta.env.VITE_USTORAGE_UPLOAD_URL,
|
|
108
|
+
uploadToken,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await client.uploadFile(file, {
|
|
112
|
+
bucketName: 'assets',
|
|
113
|
+
key: `uploads/${file.name}`,
|
|
114
|
+
onProgress: (event) => console.log(event.progress),
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`File` / `Blob` via `putObject`:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
await client.putObject({
|
|
122
|
+
bucketName: 'assets',
|
|
123
|
+
key: 'uploads/photo.jpg',
|
|
124
|
+
body: file,
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Browser auth also supports `bearerToken` (same shape as Node) if your app already uses Bearer auth end-to-end.
|
|
129
|
+
|
|
130
|
+
## Authentication
|
|
131
|
+
|
|
132
|
+
| Runtime | Method | Headers |
|
|
133
|
+
|---------|--------|---------|
|
|
134
|
+
| Node | `accessKey` + `secretKey` | `x-access-key`, `x-secret-key` |
|
|
135
|
+
| Node / Browser | `bearerToken` | `Authorization: Bearer …` |
|
|
136
|
+
| Browser | `uploadToken` | `x-upload-token` |
|
|
137
|
+
|
|
138
|
+
`bearerToken` and `uploadToken` accept a static string or `() => string | Promise<string>` for refresh.
|
|
139
|
+
|
|
140
|
+
Node credential auth uses raw access/secret headers because the API stores only a hash of `secretKey`; signed `x-ustorage-*` request auth is not available yet (`nodeSigner.supported === false`).
|
|
141
|
+
|
|
142
|
+
### Minting upload tokens (backend)
|
|
143
|
+
|
|
144
|
+
Use `client.uploads.createUploadToken` on a Node client (or call `POST /uploads/tokens` directly):
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
const { token, expires_at } = await serverClient.uploads.createUploadToken({
|
|
148
|
+
bucket_name: 'assets',
|
|
149
|
+
key: 'uploads/demo.png',
|
|
150
|
+
mime_type: 'image/png',
|
|
151
|
+
max_file_size: 10 * 1024 * 1024,
|
|
152
|
+
expires_in: 900,
|
|
153
|
+
});
|
|
154
|
+
// Return `token` to the browser as `uploadToken`
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Object keys and buckets
|
|
158
|
+
|
|
159
|
+
- `bucketName` — bucket name in the credential’s workspace.
|
|
160
|
+
- `key` — S3-style object key: path segments are folders; the last segment is the filename. Missing folders are created by the backend.
|
|
161
|
+
- `workspaceName` — optional override when the API allows it.
|
|
162
|
+
|
|
163
|
+
## Resolving URLs
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
// Public or private URL by bucket + key
|
|
167
|
+
const file = await client.getObjectUrl({
|
|
168
|
+
bucket: 'videos',
|
|
169
|
+
key: 'movies/demo.mp4',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Signed / time-limited URL by file ID
|
|
173
|
+
const signed = await client.getSignedUrl({
|
|
174
|
+
fileId: 'file_uuid',
|
|
175
|
+
expiresIn: 3600,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
console.log(file.url, signed.expiresAt);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`getSignedUrl` is an alias of `getObjectUrl`; both call `POST /files/url`.
|
|
182
|
+
|
|
183
|
+
## Upload options
|
|
184
|
+
|
|
185
|
+
| Option | Default | Description |
|
|
186
|
+
|--------|---------|-------------|
|
|
187
|
+
| `bucketName` | — | Target bucket (required) |
|
|
188
|
+
| `key` | — | Object key (required for `putObject`; optional for `uploadFile`, derived from path/name) |
|
|
189
|
+
| `workspaceName` | — | Optional workspace name |
|
|
190
|
+
| `contentType` | inferred | MIME type; inferred from extension / file metadata when omitted |
|
|
191
|
+
| `metadata` | — | Arbitrary JSON metadata on the session |
|
|
192
|
+
| `overwrite` | `true` | Replace existing object at the same key (backend soft-deletes the old object) |
|
|
193
|
+
| `visibility` | — | `'public'` or `'private'` |
|
|
194
|
+
| `checksum` | `false` | `false`, `'sha256'` (per-chunk hash), or `{ file?, chunks? }` |
|
|
195
|
+
| `chunkSize` | `8388608` (8 MiB) | Bytes per chunk |
|
|
196
|
+
| `resume` | `false` | Enable resume via `resumeStore` |
|
|
197
|
+
| `resumeKey` | fingerprint | Custom resume key; implies resume when set |
|
|
198
|
+
| `signal` | — | `AbortSignal` to cancel in-flight chunk uploads |
|
|
199
|
+
| `onProgress` | — | Callback after each chunk / status poll |
|
|
200
|
+
|
|
201
|
+
### Upload result
|
|
202
|
+
|
|
203
|
+
`putObject` / `uploadFile` resolve to `UploadObjectResult`:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
{
|
|
207
|
+
uploadId: string;
|
|
208
|
+
fileId: string;
|
|
209
|
+
publicId?: string;
|
|
210
|
+
visibility?: 'public' | 'private' | string;
|
|
211
|
+
url?: string;
|
|
212
|
+
expiresAt?: string | null;
|
|
213
|
+
status: string;
|
|
214
|
+
// …plus snake_case fields from the API response
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Resume
|
|
219
|
+
|
|
220
|
+
Resume is **opt-in** (`resume: true` or `resumeKey`). The SDK stores `upload_id` under a fingerprint of the source + key, polls missing chunks on retry, and clears the entry after completion.
|
|
221
|
+
|
|
222
|
+
**Node** — pass `resumeStore` on the client:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { UStorageNodeClient, NodeFileResumeStore } from '@ustorage/sdk/node';
|
|
226
|
+
|
|
227
|
+
const client = new UStorageNodeClient({
|
|
228
|
+
uploadBaseUrl: '…',
|
|
229
|
+
auth: { … },
|
|
230
|
+
resumeStore: new NodeFileResumeStore('.ustorage-resume.json'),
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Also available: `NodeMemoryResumeStore` (in-memory).
|
|
235
|
+
|
|
236
|
+
**Browser** — defaults to `BrowserResumeStore` (`localStorage` with prefix `ustorage:upload:`, falls back to memory). Override via `resumeStore` in client options.
|
|
237
|
+
|
|
238
|
+
Implement `ResumeStore` for custom persistence:
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
interface ResumeStore {
|
|
242
|
+
get(key: string): Promise<string | undefined> | string | undefined;
|
|
243
|
+
set(key: string, uploadId: string): Promise<void> | void;
|
|
244
|
+
delete(key: string): Promise<void> | void;
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Low-level upload API
|
|
249
|
+
|
|
250
|
+
Both clients expose `client.uploads` (`UploadApi`) for direct control:
|
|
251
|
+
|
|
252
|
+
| Method | HTTP | Description |
|
|
253
|
+
|--------|------|-------------|
|
|
254
|
+
| `createUploadToken` | `POST /uploads/tokens` | Mint browser upload token |
|
|
255
|
+
| `createSession` | `POST /uploads/sessions` | Start chunked session |
|
|
256
|
+
| `uploadChunk` | `PUT /uploads/:id/chunks/:index` | Upload one chunk |
|
|
257
|
+
| `getStatus` | `GET /uploads/:id/status` | Session status / missing chunks |
|
|
258
|
+
| `complete` | `POST /uploads/:id/complete` | Finalize upload |
|
|
259
|
+
| `cancel` | `DELETE /uploads/:id` | Cancel session |
|
|
260
|
+
| `createFileUrl` | `POST /files/url` | Resolve CDN URL |
|
|
261
|
+
|
|
262
|
+
The high-level `Uploader` (used by `putObject` / `uploadFile`) orchestrates session creation, chunk loop, checksum headers (`x-chunk-checksum`), and completion.
|
|
263
|
+
|
|
264
|
+
## Error handling
|
|
265
|
+
|
|
266
|
+
Failed API responses throw `UStorageError`:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import { UStorageError } from '@ustorage/sdk';
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
await client.putObject({ … });
|
|
273
|
+
} catch (err) {
|
|
274
|
+
if (err instanceof UStorageError) {
|
|
275
|
+
console.error(err.code, err.status, err.requestId, err.details);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## TypeScript
|
|
281
|
+
|
|
282
|
+
Types are exported from `@ustorage/sdk` (and re-exported from `/node` and `/browser`). Request/response DTOs use snake_case matching the HTTP API; client helpers like `getObjectUrl` accept camelCase (`fileId`, `expiresIn`, …).
|
|
283
|
+
|
|
284
|
+
## Development
|
|
285
|
+
|
|
286
|
+
From the `sdk` package directory:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
yarn install
|
|
290
|
+
yarn build # tsup → dist/
|
|
291
|
+
yarn typecheck
|
|
292
|
+
yarn test:upload # node test.js (requires env vars)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Environment variables used by `examples/node-upload.ts` and `test.js`:
|
|
296
|
+
|
|
297
|
+
- `USTORAGE_UPLOAD_URL` or hardcoded base URL
|
|
298
|
+
- `USTORAGE_ACCESS_KEY`, `USTORAGE_SECRET_KEY`
|
|
299
|
+
- `USTORAGE_BUCKET_NAME`, `USTORAGE_OBJECT_KEY` (for tests)
|
|
300
|
+
|
|
301
|
+
## Package exports
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{
|
|
305
|
+
".": "./dist/core/index.js",
|
|
306
|
+
"./browser": "./dist/browser/index.js",
|
|
307
|
+
"./node": "./dist/node/index.js"
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
`"type": "module"` with dual ESM/CJS builds. Default `main`/`module` point at the Node build; import the subpath you need in application code.
|
|
312
|
+
|
|
313
|
+
## Publish to npm
|
|
314
|
+
|
|
315
|
+
From the `sdk` directory (scoped package `@ustorage` must be published as **public**):
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# 1. Login (once)
|
|
319
|
+
npm login
|
|
320
|
+
|
|
321
|
+
# 2. Build + dry-run — inspect tarball contents
|
|
322
|
+
npm run build
|
|
323
|
+
npm pack --dry-run
|
|
324
|
+
|
|
325
|
+
# 3. Publish (prepack rebuilds dist; prepublishOnly runs typecheck)
|
|
326
|
+
npm publish --access public
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
What gets published is controlled by `package.json` → `"files": ["dist", "README.md", "LICENSE"]`. Source, tests, and examples are excluded (see also `.npmignore`).
|
|
330
|
+
|
|
331
|
+
Before the first publish, set `repository` in `package.json` if you have a public Git URL:
|
|
332
|
+
|
|
333
|
+
```json
|
|
334
|
+
"repository": {
|
|
335
|
+
"type": "git",
|
|
336
|
+
"url": "https://github.com/your-org/ustorage.git",
|
|
337
|
+
"directory": "sdk"
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## License
|
|
342
|
+
|
|
343
|
+
[MIT](./LICENSE)
|