@vertracloud/blob 0.0.0-stage → 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 +169 -2
- package/dist/index.cjs +1097 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +293 -0
- package/dist/index.d.ts +293 -0
- package/dist/index.js +1063 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Vertra Cloud
|
|
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
CHANGED
|
@@ -1,3 +1,170 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @vertracloud/blob
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official JavaScript/TypeScript SDK for Vertra Cloud Blob Storage. Works in Node.js 18+, browsers and edge runtimes, using the native `fetch`.
|
|
4
|
+
|
|
5
|
+
> **Status: under development, not published yet.** The API surface below may still change before the first release.
|
|
6
|
+
|
|
7
|
+
Full documentation: <https://docs.vertracloud.app>
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @vertracloud/blob
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { BlobClient } from "@vertracloud/blob";
|
|
19
|
+
|
|
20
|
+
const blob = new BlobClient({ apiKey: process.env.VERTRA_API_KEY });
|
|
21
|
+
|
|
22
|
+
const object = await blob.put("images/logo.png", fileBytes, { visibility: "public" });
|
|
23
|
+
console.log(object.id, object.url);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Client options
|
|
27
|
+
|
|
28
|
+
| Option | Default | Notes |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `apiKey` | | API key with `blob:read` / `blob:write`. Sent as `Authorization: Bearer <key>`. |
|
|
31
|
+
| `uploadToken` | | An upload token (`vcup_…`) instead of an API key. Only valid on the upload routes. |
|
|
32
|
+
| `baseUrl` | `https://blob.vertracloud.app/v1` | Includes the version prefix; every route is appended to it (`${baseUrl}/objects`). |
|
|
33
|
+
| `fetch` | global `fetch` | Custom implementation. When set, upload progress is reported per finished part instead of per byte. |
|
|
34
|
+
| `credentials` | | Passed to every API call (for example `"include"` with a cookie session; no `Authorization` header is needed then). Never sent to signed upload URLs. |
|
|
35
|
+
| `workspaceId` | | Added as `?workspace_id=` to every call, to act on the blob of a workspace owner. |
|
|
36
|
+
| `headers` | | Extra headers for every API call. |
|
|
37
|
+
| `partConcurrency` | `4` | Parts uploaded at the same time. |
|
|
38
|
+
| `maxRetries` | `3` | Retries for `GET` and for the PUT of a part or single upload, on 5xx and network errors. Never on writes to the API or on `429`. |
|
|
39
|
+
| `retryDelayMs` | `300` | First retry delay; doubles each attempt, with jitter. |
|
|
40
|
+
| `timeout` | `30000` | Per API call, in ms. Every method also accepts `{ timeout, signal }`. |
|
|
41
|
+
| `uploadTimeout` | `300000` | Per request that sends file bytes. |
|
|
42
|
+
|
|
43
|
+
### Naming: camelCase in, snake_case out
|
|
44
|
+
|
|
45
|
+
Options you pass use camelCase (`expiresIn`, `maxDownloads`, `randomSuffix`) and are converted to the API's snake_case internally.
|
|
46
|
+
The objects the SDK returns are the API payloads **as they are on the wire**, in snake_case (`object.content_type`, `upload.part_size`, `stats.used_bytes`). They are typed by `@vertracloud/api-types`, which this package re-exports.
|
|
47
|
+
|
|
48
|
+
## Uploading
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const object = await blob.put("videos/intro.mp4", file, {
|
|
52
|
+
visibility: "public",
|
|
53
|
+
metadata: { owner: "alice" },
|
|
54
|
+
signal: controller.signal, // AbortSignal
|
|
55
|
+
onProgress: ({ loaded, total }) => bar.set(loaded / total),
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`data` can be a `Blob`, `File`, `ArrayBuffer`, `Uint8Array`, `string` or `ReadableStream` (a stream also needs `size`).
|
|
60
|
+
|
|
61
|
+
- Up to 5 MB the file goes in one `POST /objects` request.
|
|
62
|
+
- Above that the SDK starts a direct upload and follows the mode the API returns: a single signed PUT, or a multipart upload whose parts are sent in parallel (`partConcurrency`), each retried on its own.
|
|
63
|
+
- Progress is by byte in browsers (the SDK uses `XMLHttpRequest` when `onProgress` is set and you did not pass a custom `fetch`) and by finished part in Node.js and edge runtimes.
|
|
64
|
+
- Aborting `signal` during a direct upload cancels it on the server (best effort) and throws a `BlobError` with code `ABORTED`.
|
|
65
|
+
- Any other failure leaves the upload open on the server so you can resume it.
|
|
66
|
+
|
|
67
|
+
### Resume
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
let uploadId: string;
|
|
71
|
+
try {
|
|
72
|
+
await blob.put("backup.tar", file, { onUpload: (upload) => (uploadId = upload.id) });
|
|
73
|
+
} catch {
|
|
74
|
+
// later, with the same file
|
|
75
|
+
await blob.resume(uploadId, file, { onProgress }); // sends only the missing parts
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const open = await blob.listUploads(); // direct uploads still in progress
|
|
79
|
+
await blob.abortUpload(uploadId);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Browser with an upload token
|
|
83
|
+
|
|
84
|
+
Create a short-lived token on your server and hand it to the browser:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// server
|
|
88
|
+
const { token } = await new BlobClient({ apiKey }).createUploadToken({ prefix: "avatars/", maxSize: 5_000_000, allowedExtensions: ["png", "jpg"], expiresIn: 900 });
|
|
89
|
+
|
|
90
|
+
// browser
|
|
91
|
+
const blob = new BlobClient({ uploadToken: token });
|
|
92
|
+
await blob.put("avatars/me.png", file, { onProgress });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Edge runtimes
|
|
96
|
+
|
|
97
|
+
Nothing special is needed: the SDK only uses `fetch`, `Blob`, `FormData` and `AbortController`.
|
|
98
|
+
|
|
99
|
+
## Objects
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
for await (const obj of blob.list({ prefix: "images/" })) {} // follows the cursor for you
|
|
103
|
+
const page = await blob.listPage({ prefix: "images/", delimiter: "/", limit: 100 });
|
|
104
|
+
page.objects; page.folders; page.cursor;
|
|
105
|
+
|
|
106
|
+
await blob.get(id);
|
|
107
|
+
await blob.update(id, { metadata: { owner: "alice" }, visibility: "private" }); // null restores a default
|
|
108
|
+
await blob.updateMany(ids, { visibility: "private" }); // any number of ids (sent in batches of 100)
|
|
109
|
+
await blob.copy(id, "backup/logo.png");
|
|
110
|
+
await blob.move(id, "img/logo.png"); // keeps the id
|
|
111
|
+
await blob.delete(id);
|
|
112
|
+
await blob.deleteMany({ prefix: "tmp/" }); // repeats while the API reports `remaining`
|
|
113
|
+
await blob.deleteMany({ ids });
|
|
114
|
+
|
|
115
|
+
const { url, expires_at } = await blob.downloadUrl(id, { expiresIn: 600, disposition: "attachment", filename: "logo.png" });
|
|
116
|
+
blob.imageUrl(obj, { width: 400, format: "webp" }); // pure: builds ?w=400&format=webp on the public URL
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`imageUrl` throws for a private object. `fit`, `height`, `quality` are also accepted.
|
|
120
|
+
|
|
121
|
+
## Shares, folder rules, domains, account
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
await blob.shares.create(id, { expiresIn: 3600, maxDownloads: 10, password });
|
|
125
|
+
await blob.shares.list({ objectId: id });
|
|
126
|
+
await blob.shares.revoke(shareId);
|
|
127
|
+
|
|
128
|
+
const { rules, etag } = await blob.rules.get();
|
|
129
|
+
await blob.rules.set([{ prefix: "tmp/", deleteAfterDays: 30 }], etag); // sends If-Match; a stale etag fails with BLOB_RULES_CONFLICT
|
|
130
|
+
await blob.rules.put("tmp/", { deleteAfterDays: 7 });
|
|
131
|
+
await blob.rules.delete("tmp/");
|
|
132
|
+
|
|
133
|
+
await blob.domains.add("cdn.example.com");
|
|
134
|
+
await blob.domains.verify(domainId);
|
|
135
|
+
await blob.domains.list();
|
|
136
|
+
await blob.domains.remove(domainId);
|
|
137
|
+
|
|
138
|
+
await blob.stats(); // usage, quota and plan limits
|
|
139
|
+
const s3 = await blob.s3Credentials(); // for aws-cli, rclone, boto3
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Errors
|
|
143
|
+
|
|
144
|
+
Every failure is a `BlobError`:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { BlobError } from "@vertracloud/blob";
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
await blob.put("a.png", data);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
if (error instanceof BlobError) {
|
|
153
|
+
error.status; // HTTP status, 0 when no response was received
|
|
154
|
+
error.code; // for example "BLOB_OBJECT_ALREADY_EXISTS", "STORAGE_QUOTA_EXCEEDED"
|
|
155
|
+
error.message;
|
|
156
|
+
error.details; // for example { path: "..." } on VALIDATION_ERROR
|
|
157
|
+
error.retryAfter; // seconds, on 429
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The SDK itself raises `NETWORK_ERROR`, `TIMEOUT`, `ABORTED`, `INVALID_ARGUMENT`, `UPLOAD_FAILED` and `INVALID_RESPONSE`. Errors never include request headers, credentials or signed upload URLs. A `429` is never retried: wait `retryAfter` seconds yourself.
|
|
163
|
+
|
|
164
|
+
## Types
|
|
165
|
+
|
|
166
|
+
Payload types and enums (`BlobVisibility`, `BlobUploadMode`, `APIBlobObject`, `BlobObject`, the `REST…` request types, ...) are re-exported from [`@vertracloud/api-types`](https://www.npmjs.com/package/@vertracloud/api-types), installed with this package, so you can import everything from `@vertracloud/blob`.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|