@vertracloud/blob 0.0.0-stage → 0.1.1

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