@cubis/vfsclient 0.0.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) 2026 Cubis
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,315 @@
1
+ # @cubis/vfsclient
2
+
3
+ The official, universal TypeScript client SDK for **vFS Server** (Virtual File System). Works seamlessly across **Node.js**, **Bun**, and the **Browser**.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@cubis/vfsclient.svg)](https://www.npmjs.com/package/@cubis/vfsclient)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+
8
+ ---
9
+
10
+ ## Features
11
+
12
+ - 🌐 **Isomorphic & Universal**: Runs in Node.js (18+), Bun, Deno, and modern Browsers.
13
+ - 🔐 **Dual Auth Support**: Authenticate using `x-api-key` or `x-api-hash`.
14
+ - ⚡ **Smart Preflight Deduplication**: Checks quota and SHA-256 before transferring bytes. Skips re-uploading if content already exists in the bucket.
15
+ - 📦 **Resumable Chunked Session Uploads**: Supports VFS edge durable storage chunking (`edge-chunks-v1`) with 1 MiB chunk slicing, per-chunk checksums, and auto-resume.
16
+ - 🚀 **Background Multi-File Queue**: Concurrent batch uploads with progress tracking, pause, resume, and cancellation.
17
+ - 💾 **Multi-Tier Caching**:
18
+ - **Browser**: Native `CacheStorage` (`window.caches`) with memory fallback.
19
+ - **Server-Side**: Local persistent disk cache (`.vfs-cache`) with TTL and atomic writes.
20
+ - **In-Memory**: Universal LRU cache with TTL expiration.
21
+ - 📄 **Metadata & Manifest Lookups**: Retrieve full file metadata JSON (`ObjectManifestResponse`) without downloading file bytes.
22
+ - 📊 **Real-time Bucket Analytics**: File counts, byte usage, quota headroom, read/write counters, and daily growth metrics.
23
+ - 🛡️ **RFC 7807 Problem Details**: Rich, structured error handling with trace IDs and status checking.
24
+
25
+ ---
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ # Bun
31
+ bun add @cubis/vfsclient
32
+
33
+ # npm
34
+ npm install @cubis/vfsclient
35
+
36
+ # pnpm
37
+ pnpm add @cubis/vfsclient
38
+
39
+ # yarn
40
+ yarn add @cubis/vfsclient
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Quickstart
46
+
47
+ ```typescript
48
+ import { VFSClient } from '@cubis/vfsclient';
49
+
50
+ // Initialize client
51
+ const vfs = new VFSClient({
52
+ endpoint: 'https://vfs.example.com',
53
+ apiKey: 'vfs_your_api_key', // Or apiHash: 'your_hash_here'
54
+ defaultBucket: 'default',
55
+ cache: true, // Enables automatic browser/server caching
56
+ });
57
+
58
+ // 1. Upload a file
59
+ const file = await vfs.upload({
60
+ file: new Blob(['Hello World!'], { type: 'text/plain' }),
61
+ name: 'hello.txt',
62
+ onProgress: ({ percent, phase }) => {
63
+ console.log(`Upload ${phase}: ${percent}%`);
64
+ },
65
+ });
66
+
67
+ console.log('Uploaded File ID:', file.file_id);
68
+ console.log('Public URL:', file.url);
69
+
70
+ // 2. Get file metadata (JSON manifest)
71
+ const meta = await vfs.getFileMetadata('default', file.file_id);
72
+ console.log(`File size: ${meta.size} bytes, Hash: ${meta.file_hash}`);
73
+
74
+ // 3. Download the file (cached automatically)
75
+ const downloadedBlob = await vfs.getFile('default', file.file_id);
76
+
77
+ // 4. Delete the file (invalidates cache)
78
+ await vfs.deleteFile('default', file.file_id);
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Authentication
84
+
85
+ Configure authentication in `VFSClientOptions`:
86
+
87
+ ```typescript
88
+ // Plain API Key (x-api-key)
89
+ const clientWithKey = new VFSClient({
90
+ endpoint: 'https://vfs.example.com',
91
+ apiKey: 'vfs_live_xxxxxxxxxxxx',
92
+ });
93
+
94
+ // HMAC / Hash Key (x-api-hash)
95
+ const clientWithHash = new VFSClient({
96
+ endpoint: 'https://vfs.example.com',
97
+ apiHash: 'd3b07384d113edec49eaa6238ad5ff00',
98
+ });
99
+ ```
100
+
101
+ ---
102
+
103
+ ## File Uploads
104
+
105
+ ### 1. Single File Upload with Smart Preflight
106
+
107
+ Accepts `File`, `Blob`, `Buffer`, `Uint8Array`, `ArrayBuffer`, `ReadableStream`, or file path string in Node.js:
108
+
109
+ ```typescript
110
+ const result = await vfs.upload({
111
+ // In Node: '/path/to/document.pdf' or Buffer.from(...)
112
+ // In Browser: input.files[0] or new Blob(...)
113
+ file: documentFile,
114
+ name: 'document.pdf',
115
+ bucketId: 'invoices',
116
+ metadata: { customerId: 'cust_123', month: 'September' },
117
+ onProgress: (progress) => {
118
+ console.log(`${progress.phase}: ${progress.percent}%`);
119
+ },
120
+ });
121
+ ```
122
+
123
+ > **Deduplication:** When `preflight: true` (default), VFS Client computes the SHA-256 hash. If the bucket already holds an identical file, the server signals a duplicate hit. The SDK returns the existing attachment record immediately without transferring the bytes again.
124
+
125
+ ### 2. Resumable Chunked Session Upload
126
+
127
+ For large files or edge storage, enable session chunking:
128
+
129
+ ```typescript
130
+ const result = await vfs.upload({
131
+ file: largeVideoFile,
132
+ name: 'movie.mp4',
133
+ resumable: true, // Uses edge-chunks-v1 protocol
134
+ chunkSize: 1048576, // 1 MiB chunks
135
+ resumeId: 'optional_previous_session_id', // Resume interrupted transfer
136
+ onProgress: (p) => {
137
+ console.log(`Uploading chunk: ${p.percent}%`);
138
+ },
139
+ });
140
+ ```
141
+
142
+ ### 3. Multiple Files & Background Queue
143
+
144
+ Upload batches with concurrency limits and progress tracking:
145
+
146
+ ```typescript
147
+ // Simple multi-file upload
148
+ const results = await vfs.uploadMultiple({
149
+ files: [
150
+ { file: fileA, name: 'fileA.png' },
151
+ { file: fileB, name: 'fileB.png' },
152
+ { file: fileC, name: 'fileC.png' },
153
+ ],
154
+ concurrency: 3, // Upload up to 3 files in parallel
155
+ onProgress: (progress) => {
156
+ console.log(`Overall: ${progress.completedFiles}/${progress.totalFiles} (${progress.overallPercent}%)`);
157
+ },
158
+ onFileSuccess: (file, response) => {
159
+ console.log(`Uploaded ${file.name} -> ${response.file_id}`);
160
+ },
161
+ });
162
+
163
+ // Or use the interactive background queue
164
+ const queue = vfs.createBackgroundQueue({ concurrency: 2 });
165
+
166
+ queue.onProgress((progress, item) => {
167
+ console.log(`Queue ${progress.overallPercent}% complete. Current: ${progress.currentFile}`);
168
+ });
169
+
170
+ queue.add({ file: file1, name: 'f1.jpg' });
171
+ queue.add({ file: file2, name: 'f2.jpg' });
172
+
173
+ // Control queue
174
+ queue.pause();
175
+ queue.resume();
176
+ queue.cancel();
177
+
178
+ // Wait for all to complete
179
+ const uploaded = await queue.wait();
180
+ ```
181
+
182
+ ---
183
+
184
+ ## File Retrieval & URL Generation
185
+
186
+ ### Download with Cache
187
+
188
+ ```typescript
189
+ // Returns Blob (default)
190
+ const blob = await vfs.getFile('default', 'file-id');
191
+
192
+ // Desired format: 'arrayBuffer' | 'stream' | 'text' | 'json'
193
+ const text = await vfs.getFile('default', 'file-id', { responseType: 'text' });
194
+ const json = await vfs.getFile('default', 'config.json', { responseType: 'json' });
195
+ const stream = await vfs.getFile('default', 'video.mp4', { responseType: 'stream' });
196
+
197
+ // Download by SHA-256 Hash (/h/:hash - immutable cache)
198
+ const hashedBlob = await vfs.getFileByHash('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
199
+ ```
200
+
201
+ ### Get File Metadata (JSON Manifest)
202
+
203
+ ```typescript
204
+ const meta = await vfs.getFileMetadata('default', 'file-id');
205
+ console.log({
206
+ id: meta.id,
207
+ fileId: meta.file_id,
208
+ name: meta.name,
209
+ size: meta.size,
210
+ hash: meta.file_hash,
211
+ type: meta.type,
212
+ metadata: meta.metadata,
213
+ url: meta.url,
214
+ });
215
+ ```
216
+
217
+ ### URL Generation & Image Proxy
218
+
219
+ ```typescript
220
+ // Direct public URL
221
+ const url = vfs.getFileUrl('photos', 'pic.jpg', {
222
+ download: true, // Forces Content-Disposition: attachment
223
+ imgproxy: {
224
+ width: 600,
225
+ height: 400,
226
+ format: 'webp',
227
+ quality: 80,
228
+ },
229
+ });
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Caching
235
+
236
+ Enable caching by passing `cache: true` in `VFSClientOptions`:
237
+
238
+ - In **Browsers**, caching is stored in `window.caches` (CacheStorage API).
239
+ - In **Node.js / Bun**, caching is written to the local filesystem (`./.vfs-cache`) with TTL and sidecar metadata.
240
+ - Automatically handles cache invalidation when `deleteFile(bucketId, fileId)` is called.
241
+
242
+ Custom cache adapter:
243
+
244
+ ```typescript
245
+ import { VFSClient, VFSCacheAdapter } from '@cubis/vfsclient';
246
+
247
+ class RedisCacheAdapter implements VFSCacheAdapter {
248
+ async get(key: string) { /* ... */ }
249
+ async set(key: string, entry: VFSCacheEntry, ttl?: number) { /* ... */ }
250
+ async delete(key: string) { /* ... */ }
251
+ async clear() { /* ... */ }
252
+ async has(key: string) { /* ... */ }
253
+ }
254
+
255
+ const vfs = new VFSClient({
256
+ endpoint: 'https://vfs.example.com',
257
+ cache: new RedisCacheAdapter(),
258
+ });
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Bucket Statistics & Management
264
+
265
+ ```typescript
266
+ // 1. Single Bucket Statistics
267
+ const stats = await vfs.getBucketStats('my-bucket');
268
+ console.log(`Files: ${stats.total_file}, Used: ${stats.total_size} bytes`);
269
+ console.log(`Quota: ${stats.max_size_bytes} bytes, Max Files: ${stats.max_files}`);
270
+
271
+ // 2. System-wide Summary
272
+ const summary = await vfs.getBucketSummary();
273
+ console.log(`Total Buckets: ${summary.total_buckets}, Total Files: ${summary.total_files}`);
274
+
275
+ // 3. Bucket Growth Metrics by Date
276
+ const metrics = await vfs.getBucketMetrics('my-bucket');
277
+ metrics.forEach(m => console.log(`${m.date}: ${m.total_files} files, ${m.total_size} bytes`));
278
+
279
+ // 4. List Files in Bucket
280
+ const files = await vfs.listFiles({ bucketId: 'my-bucket' });
281
+
282
+ // 5. Delete Operations
283
+ await vfs.deleteFile('my-bucket', 'file-id'); // Delete specific file
284
+ await vfs.deleteFileById('mongo-object-id'); // Delete by catalog ID
285
+ await vfs.deleteAllInBucket('temp-bucket'); // Purge all files in bucket
286
+ ```
287
+
288
+ ---
289
+
290
+ ## Error Handling
291
+
292
+ Errors are instances of `VFSError` with RFC 7807 Problem Details:
293
+
294
+ ```typescript
295
+ import { VFSError } from '@cubis/vfsclient';
296
+
297
+ try {
298
+ await vfs.upload({ file, name: 'large.zip', bucketId: 'limited-bucket' });
299
+ } catch (err) {
300
+ if (err instanceof VFSError) {
301
+ console.error(`Status: ${err.status}`);
302
+ console.error(`Message: ${err.message}`);
303
+ console.error(`Trace ID: ${err.problem?.extensions?.trace_id}`);
304
+ if (err.is(400)) {
305
+ console.error('Quota or metadata validation error');
306
+ }
307
+ }
308
+ }
309
+ ```
310
+
311
+ ---
312
+
313
+ ## License
314
+
315
+ MIT © [Cubis](https://cubis.tech)