@ooneex/storage 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) 2025 Ooneex
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,591 @@
1
+ # @ooneex/storage
2
+
3
+ A comprehensive TypeScript/JavaScript storage library providing unified interfaces for file storage operations. This package supports both local filesystem and Cloudflare R2 storage with a consistent API, making it easy to switch between storage backends or use multiple storage providers in your applications.
4
+
5
+ ![Bun](https://img.shields.io/badge/Bun-Compatible-orange?style=flat-square&logo=bun)
6
+ ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)
7
+ ![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)
8
+
9
+ ## Features
10
+
11
+ ✅ **Unified Storage Interface** - Single API for multiple storage providers
12
+
13
+ ✅ **Local Filesystem Storage** - Fast local file operations with automatic directory management
14
+
15
+ ✅ **Cloudflare R2 Support** - Full integration with Cloudflare's S3-compatible object storage
16
+
17
+ ✅ **Type-Safe** - Full TypeScript support with proper type definitions
18
+
19
+ ✅ **Bucket Management** - Create, clear, and manage storage buckets/directories
20
+
21
+ ✅ **Multiple Data Formats** - Support for strings, JSON, ArrayBuffers, Blobs, and streams
22
+
23
+ ✅ **File Operations** - Put, get, delete, exists, and list operations
24
+
25
+ ✅ **Stream Support** - Efficient streaming for large files
26
+
27
+ ✅ **Exception Handling** - Comprehensive error handling with custom exceptions
28
+
29
+ ✅ **Environment Configuration** - Support for environment variables and constructor options
30
+
31
+ ✅ **Zero External Dependencies** - Uses only Bun's built-in APIs
32
+
33
+ ## Installation
34
+
35
+ ### Bun
36
+ ```bash
37
+ bun add @ooneex/storage
38
+ ```
39
+
40
+ ### pnpm
41
+ ```bash
42
+ pnpm add @ooneex/storage
43
+ ```
44
+
45
+ ### Yarn
46
+ ```bash
47
+ yarn add @ooneex/storage
48
+ ```
49
+
50
+ ### npm
51
+ ```bash
52
+ npm install @ooneex/storage
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ ### Filesystem Storage
58
+
59
+ #### Basic Setup
60
+
61
+ ```typescript
62
+ import { FilesystemStorage } from '@ooneex/storage';
63
+
64
+ // Using constructor options
65
+ const storage = new FilesystemStorage({
66
+ storagePath: './my-storage'
67
+ });
68
+
69
+ // Or using environment variable FILESYSTEM_STORAGE_PATH
70
+ const storage = new FilesystemStorage();
71
+
72
+ // Set bucket/directory
73
+ storage.setBucket('my-bucket');
74
+ ```
75
+
76
+ #### File Operations
77
+
78
+ ```typescript
79
+ import { FilesystemStorage } from '@ooneex/storage';
80
+
81
+ const storage = new FilesystemStorage({ storagePath: './storage' })
82
+ .setBucket('documents');
83
+
84
+ // Store string content
85
+ await storage.put('hello.txt', 'Hello, World!');
86
+
87
+ // Store JSON data
88
+ await storage.put('config.json', JSON.stringify({ theme: 'dark', version: '1.0' }));
89
+
90
+ // Store from local file
91
+ await storage.putFile('backup.zip', '/path/to/local/file.zip');
92
+
93
+ // Check if file exists
94
+ const exists = await storage.exists('hello.txt'); // true
95
+
96
+ // Retrieve as string
97
+ const content = await storage.getAsArrayBuffer('hello.txt');
98
+ const text = new TextDecoder().decode(content);
99
+
100
+ // Retrieve as JSON
101
+ const config = await storage.getAsJson<{ theme: string; version: string }>('config.json');
102
+
103
+ // Get as stream for large files
104
+ const stream = storage.getAsStream('backup.zip');
105
+
106
+ // List all files
107
+ const files = await storage.list(); // ['hello.txt', 'config.json', 'backup.zip']
108
+
109
+ // Delete file
110
+ await storage.delete('hello.txt');
111
+
112
+ // Clear entire bucket
113
+ await storage.clearBucket();
114
+ ```
115
+
116
+ ### Cloudflare R2 Storage
117
+
118
+ #### Basic Setup
119
+
120
+ ```typescript
121
+ import { CloudflareStorageAdapter } from '@ooneex/storage';
122
+
123
+ // Using constructor options
124
+ const storage = new CloudflareStorageAdapter({
125
+ accessKey: 'your-access-key',
126
+ secretKey: 'your-secret-key',
127
+ endpoint: 'https://your-account.r2.cloudflarestorage.com',
128
+ region: 'EEUR' // EEUR, WEUR, APAC, NAM
129
+ });
130
+
131
+ // Or using environment variables:
132
+ // STORAGE_CLOUDFLARE_ACCESS_KEY, STORAGE_CLOUDFLARE_SECRET_KEY, STORAGE_CLOUDFLARE_ENDPOINT, STORAGE_CLOUDFLARE_REGION
133
+ const storage = new CloudflareStorageAdapter();
134
+
135
+ storage.setBucket('my-bucket');
136
+ ```
137
+
138
+ #### Advanced Usage
139
+
140
+ ```typescript
141
+ import { CloudflareStorageAdapter } from '@ooneex/storage';
142
+
143
+ const storage = new CloudflareStorageAdapter({
144
+ accessKey: process.env.STORAGE_CLOUDFLARE_ACCESS_KEY!,
145
+ secretKey: process.env.STORAGE_CLOUDFLARE_SECRET_KEY!,
146
+ endpoint: process.env.STORAGE_CLOUDFLARE_ENDPOINT!,
147
+ region: 'EEUR'
148
+ }).setBucket('media-files');
149
+
150
+ // Store different types of content
151
+ await storage.put('image.png', new Blob([imageData], { type: 'image/png' }));
152
+ await storage.put('data.json', JSON.stringify({ users: [], posts: [] }));
153
+ await storage.put('binary-data', new ArrayBuffer(1024));
154
+
155
+ // Stream large files
156
+ const largeFileStream = storage.getAsStream('large-video.mp4');
157
+ const response = new Response(largeFileStream);
158
+ ```
159
+
160
+ ### Working with Different Data Types
161
+
162
+ ```typescript
163
+ import { FilesystemStorage } from '@ooneex/storage';
164
+
165
+ const storage = new FilesystemStorage({ storagePath: './data' })
166
+ .setBucket('examples');
167
+
168
+ // String content
169
+ await storage.put('text.txt', 'Plain text content');
170
+
171
+ // JSON objects
172
+ const userData = { name: 'John', age: 30, active: true };
173
+ await storage.put('user.json', JSON.stringify(userData));
174
+
175
+ // Binary data (ArrayBuffer)
176
+ const binaryData = new ArrayBuffer(256);
177
+ await storage.put('binary.dat', binaryData);
178
+
179
+ // Blobs
180
+ const blob = new Blob(['CSV data,here'], { type: 'text/csv' });
181
+ await storage.put('data.csv', blob);
182
+
183
+ // Files from disk
184
+ await storage.putFile('document.pdf', './local/document.pdf');
185
+
186
+ // Retrieve data in different formats
187
+ const textBuffer = await storage.getAsArrayBuffer('text.txt');
188
+ const text = new TextDecoder().decode(textBuffer);
189
+
190
+ const user = await storage.getAsJson<{ name: string; age: number; active: boolean }>('user.json');
191
+
192
+ const csvStream = storage.getAsStream('data.csv');
193
+ ```
194
+
195
+ ### Error Handling
196
+
197
+ ```typescript
198
+ import { FilesystemStorage, StorageException } from '@ooneex/storage';
199
+
200
+ const storage = new FilesystemStorage({ storagePath: './storage' });
201
+
202
+ try {
203
+ await storage.getAsJson('nonexistent.json');
204
+ } catch (error) {
205
+ if (error instanceof StorageException) {
206
+ console.error('Storage error:', error.message);
207
+ console.error('Status:', error.getStatus()); // HTTP status code
208
+ }
209
+ }
210
+ ```
211
+
212
+ ### Using Abstract Interface
213
+
214
+ ```typescript
215
+ import { IStorage, FilesystemStorage, CloudflareStorageAdapter } from '@ooneex/storage';
216
+
217
+ function createStorage(type: 'filesystem' | 'cloudflare'): IStorage {
218
+ switch (type) {
219
+ case 'filesystem':
220
+ return new FilesystemStorage({ storagePath: './storage' });
221
+ case 'cloudflare':
222
+ return new CloudflareStorageAdapter();
223
+ default:
224
+ throw new Error('Unknown storage type');
225
+ }
226
+ }
227
+
228
+ // Use the same interface regardless of storage type
229
+ const storage = createStorage('filesystem');
230
+ await storage.setBucket('shared-bucket');
231
+ await storage.put('shared-file.txt', 'This works with any storage provider');
232
+ ```
233
+
234
+ ## API Reference
235
+
236
+ ### `IStorage` Interface
237
+
238
+ The main interface that all storage adapters implement.
239
+
240
+ ```typescript
241
+ interface IStorage {
242
+ setBucket(name: string): this;
243
+ list(): Promise<string[]>;
244
+ clearBucket(): Promise<this>;
245
+ exists(key: string): Promise<boolean>;
246
+ delete(key: string): Promise<void>;
247
+ putFile(key: string, localPath: string): Promise<number>;
248
+ put(key: string, content: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob): Promise<number>;
249
+ getAsJson<T = unknown>(key: string): Promise<T>;
250
+ getAsArrayBuffer(key: string): Promise<ArrayBuffer>;
251
+ getAsStream(key: string): ReadableStream;
252
+ }
253
+ ```
254
+
255
+ ### `FilesystemStorage` Class
256
+
257
+ Local filesystem storage implementation.
258
+
259
+ #### Constructor
260
+
261
+ ```typescript
262
+ constructor(options?: {
263
+ storagePath?: string;
264
+ })
265
+ ```
266
+
267
+ **Parameters:**
268
+ - `options.storagePath` - Base path for storage (optional, can use `FILESYSTEM_STORAGE_PATH` env var)
269
+
270
+ **Example:**
271
+ ```typescript
272
+ const storage = new FilesystemStorage({
273
+ storagePath: '/var/app/storage'
274
+ });
275
+ ```
276
+
277
+ #### Methods
278
+
279
+ ##### `setBucket(name: string): this`
280
+ Sets the bucket (directory) name for operations.
281
+
282
+ **Parameters:**
283
+ - `name` - Bucket/directory name
284
+
285
+ **Returns:** `this` for method chaining
286
+
287
+ **Example:**
288
+ ```typescript
289
+ storage.setBucket('uploads');
290
+ ```
291
+
292
+ ##### `put(key: string, content: ContentType): Promise<number>`
293
+ Stores content at the specified key.
294
+
295
+ **Parameters:**
296
+ - `key` - File key/path within the bucket
297
+ - `content` - Content to store (string, ArrayBuffer, Blob, etc.)
298
+
299
+ **Returns:** Number of bytes written
300
+
301
+ **Example:**
302
+ ```typescript
303
+ const bytesWritten = await storage.put('file.txt', 'Hello World');
304
+ ```
305
+
306
+ ##### `putFile(key: string, localPath: string): Promise<number>`
307
+ Stores a local file at the specified key.
308
+
309
+ **Parameters:**
310
+ - `key` - Destination key/path within the bucket
311
+ - `localPath` - Local file system path
312
+
313
+ **Returns:** Number of bytes written
314
+
315
+ **Example:**
316
+ ```typescript
317
+ await storage.putFile('backup.zip', './local-backup.zip');
318
+ ```
319
+
320
+ ##### `get*` Methods
321
+
322
+ ```typescript
323
+ // Get as JSON with type safety
324
+ getAsJson<T = unknown>(key: string): Promise<T>
325
+
326
+ // Get as ArrayBuffer
327
+ getAsArrayBuffer(key: string): Promise<ArrayBuffer>
328
+
329
+ // Get as ReadableStream
330
+ getAsStream(key: string): ReadableStream
331
+ ```
332
+
333
+ ##### `exists(key: string): Promise<boolean>`
334
+ Checks if a file exists at the specified key.
335
+
336
+ **Example:**
337
+ ```typescript
338
+ const fileExists = await storage.exists('important.txt');
339
+ ```
340
+
341
+ ##### `delete(key: string): Promise<void>`
342
+ Deletes the file at the specified key.
343
+
344
+ **Example:**
345
+ ```typescript
346
+ await storage.delete('old-file.txt');
347
+ ```
348
+
349
+ ##### `list(): Promise<string[]>`
350
+ Lists all files in the current bucket.
351
+
352
+ **Returns:** Array of file keys
353
+
354
+ **Example:**
355
+ ```typescript
356
+ const files = await storage.list();
357
+ console.log('Files:', files); // ['file1.txt', 'folder/file2.json']
358
+ ```
359
+
360
+ ##### `clearBucket(): Promise<this>`
361
+ Removes all files from the current bucket.
362
+
363
+ **Returns:** `this` for method chaining
364
+
365
+ **Example:**
366
+ ```typescript
367
+ await storage.clearBucket();
368
+ ```
369
+
370
+ ### `CloudflareStorageAdapter` Class
371
+
372
+ Cloudflare R2 storage implementation.
373
+
374
+ #### Constructor
375
+
376
+ ```typescript
377
+ constructor(options?: {
378
+ accessKey?: string;
379
+ secretKey?: string;
380
+ endpoint?: string;
381
+ region?: "EEUR" | "WEUR" | "APAC" | "NAM";
382
+ })
383
+ ```
384
+
385
+ **Parameters:**
386
+ - `options.accessKey` - Cloudflare R2 access key (or use `STORAGE_CLOUDFLARE_ACCESS_KEY` env var)
387
+ - `options.secretKey` - Cloudflare R2 secret key (or use `STORAGE_CLOUDFLARE_SECRET_KEY` env var)
388
+ - `options.endpoint` - Cloudflare R2 endpoint URL (or use `STORAGE_CLOUDFLARE_ENDPOINT` env var)
389
+ - `options.region` - Cloudflare R2 region (or use `STORAGE_CLOUDFLARE_REGION` env var)
390
+
391
+ **Example:**
392
+ ```typescript
393
+ const storage = new CloudflareStorageAdapter({
394
+ accessKey: 'your-access-key',
395
+ secretKey: 'your-secret-key',
396
+ endpoint: 'https://account-id.r2.cloudflarestorage.com',
397
+ region: 'EEUR'
398
+ });
399
+ ```
400
+
401
+ The `CloudflareStorageAdapter` inherits all methods from `AbstractStorage` and implements the same interface as `FilesystemStorage`.
402
+
403
+ ### `StorageException` Class
404
+
405
+ Custom exception class for storage-related errors.
406
+
407
+ ```typescript
408
+ class StorageException extends Exception {
409
+ constructor(message: string, data?: T)
410
+ }
411
+ ```
412
+
413
+ **Example:**
414
+ ```typescript
415
+ try {
416
+ await storage.getAsJson('missing.json');
417
+ } catch (error) {
418
+ if (error instanceof StorageException) {
419
+ console.error('Storage operation failed:', error.message);
420
+ }
421
+ }
422
+ ```
423
+
424
+ ### `AbstractStorage` Class
425
+
426
+ Base class providing common functionality for storage adapters.
427
+
428
+ ```typescript
429
+ abstract class AbstractStorage implements IStorage {
430
+ protected abstract bucket: string;
431
+ public abstract getOptions(): S3Options;
432
+
433
+ // All IStorage methods implemented
434
+ }
435
+ ```
436
+
437
+ ## Environment Variables
438
+
439
+ ### Filesystem Storage
440
+
441
+ - `FILESYSTEM_STORAGE_PATH` - Base path for filesystem storage
442
+
443
+ ### Cloudflare R2 Storage
444
+
445
+ - `STORAGE_CLOUDFLARE_ACCESS_KEY` - R2 access key
446
+ - `STORAGE_CLOUDFLARE_SECRET_KEY` - R2 secret key
447
+ - `STORAGE_CLOUDFLARE_ENDPOINT` - R2 endpoint URL
448
+ - `STORAGE_CLOUDFLARE_REGION` - R2 region (EEUR, WEUR, APAC, NAM)
449
+
450
+ ## Error Handling
451
+
452
+ The library uses custom `StorageException` for all storage-related errors:
453
+
454
+ ```typescript
455
+ import { StorageException } from '@ooneex/storage';
456
+
457
+ try {
458
+ const storage = new FilesystemStorage(); // No path provided
459
+ } catch (error) {
460
+ if (error instanceof StorageException) {
461
+ // Handle storage-specific errors
462
+ console.error('Storage setup failed:', error.message);
463
+ }
464
+ }
465
+ ```
466
+
467
+ Common error scenarios:
468
+ - Missing configuration (paths, credentials)
469
+ - File not found operations
470
+ - Permission errors
471
+ - Network errors (for cloud storage)
472
+ - Invalid JSON parsing
473
+
474
+ ## Best Practices
475
+
476
+ ### 1. Use Environment Variables for Configuration
477
+
478
+ ```typescript
479
+ // .env file
480
+ FILESYSTEM_STORAGE_PATH=./storage
481
+ STORAGE_CLOUDFLARE_ACCESS_KEY=your-access-key
482
+ STORAGE_CLOUDFLARE_SECRET_KEY=your-secret-key
483
+ STORAGE_CLOUDFLARE_ENDPOINT=https://your-account.r2.cloudflarestorage.com
484
+ STORAGE_CLOUDFLARE_REGION=EEUR
485
+
486
+ // Application code
487
+ const storage = new FilesystemStorage(); // Uses env vars
488
+ ```
489
+
490
+ ### 2. Implement Proper Error Handling
491
+
492
+ ```typescript
493
+ async function safeStorageOperation() {
494
+ try {
495
+ const result = await storage.getAsJson('config.json');
496
+ return result;
497
+ } catch (error) {
498
+ if (error instanceof StorageException) {
499
+ // Log error and provide fallback
500
+ console.warn('Failed to load config:', error.message);
501
+ return getDefaultConfig();
502
+ }
503
+ throw error; // Re-throw unexpected errors
504
+ }
505
+ }
506
+ ```
507
+
508
+ ### 3. Use Streams for Large Files
509
+
510
+ ```typescript
511
+ // Good for large files
512
+ const stream = storage.getAsStream('large-video.mp4');
513
+ const response = new Response(stream, {
514
+ headers: { 'Content-Type': 'video/mp4' }
515
+ });
516
+
517
+ // Avoid for large files - loads entire file into memory
518
+ const buffer = await storage.getAsArrayBuffer('large-video.mp4');
519
+ ```
520
+
521
+ ### 4. Organize Files with Proper Keys
522
+
523
+ ```typescript
524
+ // Good - organized structure
525
+ await storage.put('users/profile/123.json', userData);
526
+ await storage.put('uploads/images/2024/01/photo.jpg', imageData);
527
+ await storage.put('logs/2024-01-15.log', logData);
528
+
529
+ // Less organized
530
+ await storage.put('user123.json', userData);
531
+ await storage.put('photo.jpg', imageData);
532
+ await storage.put('log.txt', logData);
533
+ ```
534
+
535
+ ### 5. Use Type Safety with JSON Operations
536
+
537
+ ```typescript
538
+ interface UserProfile {
539
+ id: number;
540
+ name: string;
541
+ email: string;
542
+ preferences: {
543
+ theme: 'light' | 'dark';
544
+ notifications: boolean;
545
+ };
546
+ }
547
+
548
+ // Type-safe JSON operations
549
+ const profile = await storage.getAsJson<UserProfile>('user/profile.json');
550
+ console.log(profile.preferences.theme); // TypeScript knows this exists
551
+ ```
552
+
553
+ ## License
554
+
555
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
556
+
557
+ ## Contributing
558
+
559
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
560
+
561
+ ### Development Setup
562
+
563
+ 1. Clone the repository
564
+ 2. Install dependencies: `bun install`
565
+ 3. Run tests: `bun run test`
566
+ 4. Build the project: `bun run build`
567
+
568
+ ### Guidelines
569
+
570
+ - Write tests for new features
571
+ - Follow the existing code style
572
+ - Update documentation for API changes
573
+ - Ensure all tests pass before submitting PR
574
+ - Add TypeScript type definitions for new APIs
575
+
576
+ ### Running Tests
577
+
578
+ ```bash
579
+ # Run all tests
580
+ bun run test
581
+
582
+ # Run tests in watch mode
583
+ bun run test:watch
584
+
585
+ # Run specific test file
586
+ bun test tests/FilesystemStorage.spec.ts
587
+ ```
588
+
589
+ ---
590
+
591
+ Made with ❤️ by the Ooneex team
@@ -0,0 +1,75 @@
1
+ import { BunFile as BunFile2, S3File as S3File2, S3Options } from "bun";
2
+ import { BunFile, S3File } from "bun";
3
+ type StorageClassType = new (...args: any[]) => IStorage;
4
+ interface IStorage {
5
+ setBucket(name: string): IStorage;
6
+ list(): Promise<string[]>;
7
+ clearBucket(): Promise<this>;
8
+ exists(key: string): Promise<boolean>;
9
+ delete(key: string): Promise<void>;
10
+ putFile(key: string, localPath: string): Promise<number>;
11
+ put(key: string, content: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob): Promise<number>;
12
+ getAsJson<T = unknown>(key: string): Promise<T>;
13
+ getAsArrayBuffer(key: string): Promise<ArrayBuffer>;
14
+ getAsStream(key: string): ReadableStream;
15
+ }
16
+ declare abstract class AbstractStorage implements IStorage {
17
+ protected client: Bun.S3Client | null;
18
+ abstract getOptions(): S3Options;
19
+ protected abstract bucket: string;
20
+ setBucket(name: string): this;
21
+ list(): Promise<string[]>;
22
+ clearBucket(): Promise<this>;
23
+ exists(key: string): Promise<boolean>;
24
+ delete(key: string): Promise<void>;
25
+ putFile(key: string, localPath: string): Promise<number>;
26
+ put(key: string, content: string | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile2 | S3File2 | Blob): Promise<number>;
27
+ getAsJson<T>(key: string): Promise<T>;
28
+ getAsArrayBuffer(key: string): Promise<ArrayBuffer>;
29
+ getAsStream(key: string): ReadableStream;
30
+ protected getClient(): Bun.S3Client;
31
+ protected getS3File(path: string): S3File2;
32
+ }
33
+ import { S3Options as S3Options2 } from "bun";
34
+ declare class CloudflareStorageAdapter extends AbstractStorage {
35
+ protected bucket: string;
36
+ private readonly accessKey;
37
+ private readonly secretKey;
38
+ private readonly endpoint;
39
+ private readonly region;
40
+ constructor(options?: {
41
+ accessKey?: string;
42
+ secretKey?: string;
43
+ endpoint?: string;
44
+ region?: "EEUR" | "WEUR" | "APAC" | "NAM";
45
+ });
46
+ getOptions(): S3Options2;
47
+ }
48
+ import { BunFile as BunFile3, S3File as S3File3, S3Options as S3Options3 } from "bun";
49
+ declare class FilesystemStorage extends AbstractStorage {
50
+ protected bucket: string;
51
+ private readonly storagePath;
52
+ constructor(options?: {
53
+ storagePath?: string;
54
+ });
55
+ getOptions(): S3Options3;
56
+ private getBucketPath;
57
+ private getFilePath;
58
+ setBucket(name: string): this;
59
+ list(): Promise<string[]>;
60
+ private listFilesRecursive;
61
+ clearBucket(): Promise<this>;
62
+ private removeDirectoryRecursive;
63
+ exists(key: string): Promise<boolean>;
64
+ delete(key: string): Promise<void>;
65
+ putFile(key: string, localPath: string): Promise<number>;
66
+ put(key: string, content: string | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile3 | S3File3 | Blob): Promise<number>;
67
+ getAsJson<T>(key: string): Promise<T>;
68
+ getAsArrayBuffer(key: string): Promise<ArrayBuffer>;
69
+ getAsStream(key: string): ReadableStream;
70
+ }
71
+ import { Exception } from "@ooneex/exception";
72
+ declare class StorageException extends Exception {
73
+ constructor(message: string, data?: Record<string, unknown>);
74
+ }
75
+ export { StorageException, StorageClassType, IStorage, FilesystemStorage, CloudflareStorageAdapter, AbstractStorage };
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ class c{client=null;setBucket(t){return this.bucket=t,this.client=new Bun.S3Client(this.getOptions()),this}async list(){return(await this.getClient().list()).contents?.map((e)=>e.key)||[]}async clearBucket(){let t=this.getClient(),e=await this.list();for(let r of e)await t.delete(r);return this}async exists(t){return await this.getClient().exists(t)}async delete(t){await this.getClient().delete(t)}async putFile(t,e){let r=Bun.file(e);return await this.put(t,r)}async put(t,e){return await this.getS3File(t).write(e)}async getAsJson(t){return await this.getS3File(t).json()}async getAsArrayBuffer(t){return await this.getS3File(t).arrayBuffer()}getAsStream(t){return this.getS3File(t).stream()}getClient(){if(!this.client)this.client=new Bun.S3Client(this.getOptions());return this.client}getS3File(t){return this.getClient().file(t)}}import{Exception as d}from"@ooneex/exception";import{HttpStatus as m}from"@ooneex/http-status";class n extends d{constructor(t,e={}){super(t,{status:m.Code.InternalServerError,data:e});this.name="StorageException"}}class S extends c{bucket;accessKey;secretKey;endpoint;region;constructor(t){super();let e=t?.accessKey||Bun.env.STORAGE_CLOUDFLARE_ACCESS_KEY,r=t?.secretKey||Bun.env.STORAGE_CLOUDFLARE_SECRET_KEY,i=t?.endpoint||Bun.env.STORAGE_CLOUDFLARE_ENDPOINT;if(!e)throw new n("Cloudflare access key is required. Please provide an access key either through the constructor options or set the STORAGE_CLOUDFLARE_ACCESS_KEY environment variable.");if(!r)throw new n("Cloudflare secret key is required. Please provide a secret key either through the constructor options or set the STORAGE_CLOUDFLARE_SECRET_KEY environment variable.");if(!i)throw new n("Cloudflare endpoint is required. Please provide an endpoint either through the constructor options or set the STORAGE_CLOUDFLARE_ENDPOINT environment variable.");this.accessKey=e,this.secretKey=r,this.endpoint=i,this.region=t?.region||Bun.env.STORAGE_CLOUDFLARE_REGION||"EEUR"}getOptions(){return{accessKeyId:this.accessKey,secretAccessKey:this.secretKey,endpoint:this.endpoint,bucket:this.bucket,region:this.region}}}import{existsSync as o,mkdirSync as g}from"fs";import{mkdir as p,readdir as f,rmdir as y,stat as w}from"fs/promises";import{dirname as h,join as l}from"path";class b extends c{bucket;storagePath;constructor(t){super();let e=t?.storagePath||Bun.env.FILESYSTEM_STORAGE_PATH;if(!e)throw new n("Base path is required. Please provide a base path either through the constructor options or set the FILESYSTEM_STORAGE_PATH environment variable.");this.storagePath=e;try{if(!o(e))g(e,{recursive:!0})}catch(r){throw new n(`Failed to create base storage directory at ${e}: ${r instanceof Error?r.message:String(r)}`)}}getOptions(){return{accessKeyId:"filesystem",secretAccessKey:"filesystem",endpoint:this.storagePath,bucket:this.bucket,region:"local"}}getBucketPath(){if(!this.bucket)throw new n("Bucket name is required. Please call setBucket() first.");return l(this.storagePath,this.bucket)}getFilePath(t){return l(this.getBucketPath(),t)}setBucket(t){this.bucket=t;let e=this.getBucketPath();try{if(!o(e))g(e,{recursive:!0})}catch(r){throw new n(`Failed to create bucket directory at ${e}: ${r instanceof Error?r.message:String(r)}`)}return this}async list(){let t=this.getBucketPath();if(!o(t))return[];try{return await this.listFilesRecursive(t,t)}catch(e){throw new n(`Failed to list files in bucket: ${e instanceof Error?e.message:String(e)}`)}}async listFilesRecursive(t,e){let r=[],i=await f(t);for(let s of i){let a=l(t,s);if((await w(a)).isDirectory()){let u=await this.listFilesRecursive(a,e);r.push(...u)}else{let u=a.substring(e.length+1);r.push(u)}}return r}async clearBucket(){let t=this.getBucketPath();if(!o(t))return this;try{await this.removeDirectoryRecursive(t),await p(t,{recursive:!0})}catch(e){throw new n(`Failed to clear bucket: ${e instanceof Error?e.message:String(e)}`)}return this}async removeDirectoryRecursive(t){let e=await f(t);for(let r of e){let i=l(t,r);if((await w(i)).isDirectory())await this.removeDirectoryRecursive(i),await y(i);else await Bun.file(i).delete()}}async exists(t){let e=this.getFilePath(t);return await Bun.file(e).exists()}async delete(t){let e=this.getFilePath(t),r=Bun.file(e);if(!await r.exists())return;try{await r.delete();let i=h(e),s=this.getBucketPath();while(i!==s&&i!==this.storagePath)try{if((await f(i)).length===0)await y(i),i=h(i);else break}catch{break}}catch(i){throw new n(`Failed to delete file ${t}: ${i instanceof Error?i.message:String(i)}`)}}async putFile(t,e){let r=Bun.file(e);return await this.put(t,r)}async put(t,e){let r=this.getFilePath(t),i=h(r);try{if(!o(i))await p(i,{recursive:!0})}catch(s){throw new n(`Failed to create directory ${i}: ${s instanceof Error?s.message:String(s)}`)}try{let s;if(typeof e==="string")s=await Bun.write(r,e);else if(e instanceof ArrayBuffer)s=await Bun.write(r,e);else if(e instanceof SharedArrayBuffer){let a=new ArrayBuffer(e.byteLength);new Uint8Array(a).set(new Uint8Array(e)),s=await Bun.write(r,a)}else if(e instanceof Request){let a=await e.arrayBuffer();s=await Bun.write(r,a)}else if(e instanceof Response){let a=await e.arrayBuffer();s=await Bun.write(r,a)}else if(e instanceof Blob)s=await Bun.write(r,e);else{let a=await e.arrayBuffer();s=await Bun.write(r,a)}return s}catch(s){throw new n(`Failed to write file ${t}: ${s instanceof Error?s.message:String(s)}`)}}async getAsJson(t){let e=this.getFilePath(t),r=Bun.file(e);if(!await r.exists())throw new n(`File ${t} does not exist`);try{return await r.json()}catch(i){throw new n(`Failed to read file ${t} as JSON: ${i instanceof Error?i.message:String(i)}`)}}async getAsArrayBuffer(t){let e=this.getFilePath(t),r=Bun.file(e);if(!await r.exists())throw new n(`File ${t} does not exist`);try{return await r.arrayBuffer()}catch(i){throw new n(`Failed to read file ${t} as ArrayBuffer: ${i instanceof Error?i.message:String(i)}`)}}getAsStream(t){let e=this.getFilePath(t);if(!o(e))throw new n(`File ${t} does not exist`);let r=Bun.file(e);try{return r.stream()}catch(i){throw new n(`Failed to read file ${t} as stream: ${i instanceof Error?i.message:String(i)}`)}}}export{n as StorageException,b as FilesystemStorage,S as CloudflareStorageAdapter,c as AbstractStorage};
3
+
4
+ //# debugId=C81D99E08CC7CE6B64756E2164756E21
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/AbstractStorage.ts", "src/StorageException.ts", "src/CloudflareStorageAdapter.ts", "src/FilesystemStorage.ts"],
4
+ "sourcesContent": [
5
+ "import type { BunFile, S3File, S3Options } from \"bun\";\nimport type { IStorage } from \"./types\";\n\nexport abstract class AbstractStorage implements IStorage {\n protected client: Bun.S3Client | null = null;\n public abstract getOptions(): S3Options;\n protected abstract bucket: string;\n\n public setBucket(name: string): this {\n this.bucket = name;\n this.client = new Bun.S3Client(this.getOptions());\n\n return this;\n }\n\n public async list(): Promise<string[]> {\n const client = this.getClient();\n\n return (await client.list()).contents?.map((content) => content.key) || [];\n }\n\n public async clearBucket(): Promise<this> {\n const client = this.getClient();\n const keys = await this.list();\n\n for (const key of keys) {\n await client.delete(key);\n }\n\n return this;\n }\n\n public async exists(key: string): Promise<boolean> {\n const client = this.getClient();\n\n return await client.exists(key);\n }\n\n public async delete(key: string): Promise<void> {\n const client = this.getClient();\n\n await client.delete(key);\n }\n\n public async putFile(key: string, localPath: string): Promise<number> {\n const file = Bun.file(localPath);\n\n return await this.put(key, file);\n }\n\n public async put(\n key: string,\n content: string | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob,\n ): Promise<number> {\n const s3file: S3File = this.getS3File(key);\n\n return await s3file.write(content);\n }\n\n public async getAsJson<T>(key: string): Promise<T> {\n const s3file: S3File = this.getS3File(key);\n\n return await s3file.json();\n }\n\n public async getAsArrayBuffer(key: string): Promise<ArrayBuffer> {\n const s3file: S3File = this.getS3File(key);\n\n return await s3file.arrayBuffer();\n }\n\n public getAsStream(key: string): ReadableStream {\n const s3file: S3File = this.getS3File(key);\n\n return s3file.stream();\n }\n\n protected getClient(): Bun.S3Client {\n if (!this.client) {\n this.client = new Bun.S3Client(this.getOptions());\n }\n\n return this.client;\n }\n\n protected getS3File(path: string): S3File {\n const client = this.getClient();\n\n return client.file(path);\n }\n}\n",
6
+ "import { Exception } from \"@ooneex/exception\";\nimport { HttpStatus } from \"@ooneex/http-status\";\n\nexport class StorageException extends Exception {\n constructor(message: string, data: Record<string, unknown> = {}) {\n super(message, {\n status: HttpStatus.Code.InternalServerError,\n data,\n });\n this.name = \"StorageException\";\n }\n}\n",
7
+ "import type { S3Options } from \"bun\";\nimport { AbstractStorage } from \"./AbstractStorage\";\nimport { StorageException } from \"./StorageException\";\n\nexport class CloudflareStorageAdapter extends AbstractStorage {\n protected bucket: string;\n private readonly accessKey: string;\n private readonly secretKey: string;\n private readonly endpoint: string;\n private readonly region: string;\n\n constructor(options?: {\n accessKey?: string;\n secretKey?: string;\n endpoint?: string;\n region?: \"EEUR\" | \"WEUR\" | \"APAC\" | \"NAM\";\n }) {\n super();\n\n const accessKey = options?.accessKey || Bun.env.STORAGE_CLOUDFLARE_ACCESS_KEY;\n const secretKey = options?.secretKey || Bun.env.STORAGE_CLOUDFLARE_SECRET_KEY;\n const endpoint = options?.endpoint || Bun.env.STORAGE_CLOUDFLARE_ENDPOINT;\n\n if (!accessKey) {\n throw new StorageException(\n \"Cloudflare access key is required. Please provide an access key either through the constructor options or set the STORAGE_CLOUDFLARE_ACCESS_KEY environment variable.\",\n );\n }\n if (!secretKey) {\n throw new StorageException(\n \"Cloudflare secret key is required. Please provide a secret key either through the constructor options or set the STORAGE_CLOUDFLARE_SECRET_KEY environment variable.\",\n );\n }\n if (!endpoint) {\n throw new StorageException(\n \"Cloudflare endpoint is required. Please provide an endpoint either through the constructor options or set the STORAGE_CLOUDFLARE_ENDPOINT environment variable.\",\n );\n }\n\n this.accessKey = accessKey;\n this.secretKey = secretKey;\n this.endpoint = endpoint;\n this.region = options?.region || Bun.env.STORAGE_CLOUDFLARE_REGION || \"EEUR\";\n }\n\n public getOptions(): S3Options {\n return {\n accessKeyId: this.accessKey,\n secretAccessKey: this.secretKey,\n endpoint: this.endpoint,\n bucket: this.bucket,\n region: this.region,\n };\n }\n}\n",
8
+ "import { existsSync, mkdirSync } from \"node:fs\";\nimport { mkdir, readdir, rmdir, stat } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport type { BunFile, S3File, S3Options } from \"bun\";\nimport { AbstractStorage } from \"./AbstractStorage\";\nimport { StorageException } from \"./StorageException\";\n\nexport class FilesystemStorage extends AbstractStorage {\n protected bucket: string;\n private readonly storagePath: string;\n\n constructor(options?: {\n storagePath?: string;\n }) {\n super();\n\n const basePath = options?.storagePath || Bun.env.FILESYSTEM_STORAGE_PATH;\n\n if (!basePath) {\n throw new StorageException(\n \"Base path is required. Please provide a base path either through the constructor options or set the FILESYSTEM_STORAGE_PATH environment variable.\",\n );\n }\n\n this.storagePath = basePath;\n\n try {\n if (!existsSync(basePath)) {\n mkdirSync(basePath, { recursive: true });\n }\n } catch (error) {\n throw new StorageException(\n `Failed to create base storage directory at ${basePath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n public getOptions(): S3Options {\n return {\n accessKeyId: \"filesystem\",\n secretAccessKey: \"filesystem\",\n endpoint: this.storagePath,\n bucket: this.bucket,\n region: \"local\",\n };\n }\n\n private getBucketPath(): string {\n if (!this.bucket) {\n throw new StorageException(\"Bucket name is required. Please call setBucket() first.\");\n }\n return join(this.storagePath, this.bucket);\n }\n\n private getFilePath(key: string): string {\n return join(this.getBucketPath(), key);\n }\n\n public override setBucket(name: string): this {\n this.bucket = name;\n\n const bucketPath = this.getBucketPath();\n try {\n if (!existsSync(bucketPath)) {\n mkdirSync(bucketPath, { recursive: true });\n }\n } catch (error) {\n throw new StorageException(\n `Failed to create bucket directory at ${bucketPath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n return this;\n }\n\n public override async list(): Promise<string[]> {\n const bucketPath = this.getBucketPath();\n\n if (!existsSync(bucketPath)) {\n return [];\n }\n\n try {\n const files = await this.listFilesRecursive(bucketPath, bucketPath);\n return files;\n } catch (error) {\n throw new StorageException(\n `Failed to list files in bucket: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private async listFilesRecursive(dir: string, baseDir: string): Promise<string[]> {\n const files: string[] = [];\n const entries = await readdir(dir);\n\n for (const entry of entries) {\n const fullPath = join(dir, entry);\n const stats = await stat(fullPath);\n\n if (stats.isDirectory()) {\n const subFiles = await this.listFilesRecursive(fullPath, baseDir);\n files.push(...subFiles);\n } else {\n const relativePath = fullPath.substring(baseDir.length + 1);\n files.push(relativePath);\n }\n }\n\n return files;\n }\n\n public override async clearBucket(): Promise<this> {\n const bucketPath = this.getBucketPath();\n\n if (!existsSync(bucketPath)) {\n return this;\n }\n\n try {\n await this.removeDirectoryRecursive(bucketPath);\n await mkdir(bucketPath, { recursive: true });\n } catch (error) {\n throw new StorageException(`Failed to clear bucket: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n return this;\n }\n\n private async removeDirectoryRecursive(dir: string): Promise<void> {\n const entries = await readdir(dir);\n\n for (const entry of entries) {\n const fullPath = join(dir, entry);\n const stats = await stat(fullPath);\n\n if (stats.isDirectory()) {\n await this.removeDirectoryRecursive(fullPath);\n await rmdir(fullPath);\n } else {\n const file = Bun.file(fullPath);\n await file.delete();\n }\n }\n }\n\n public override async exists(key: string): Promise<boolean> {\n const filePath = this.getFilePath(key);\n const file = Bun.file(filePath);\n return await file.exists();\n }\n\n public override async delete(key: string): Promise<void> {\n const filePath = this.getFilePath(key);\n const file = Bun.file(filePath);\n\n if (!(await file.exists())) {\n return;\n }\n\n try {\n await file.delete();\n\n let parentDir = dirname(filePath);\n const bucketPath = this.getBucketPath();\n\n while (parentDir !== bucketPath && parentDir !== this.storagePath) {\n try {\n const entries = await readdir(parentDir);\n if (entries.length === 0) {\n await rmdir(parentDir);\n parentDir = dirname(parentDir);\n } else {\n break;\n }\n } catch {\n break;\n }\n }\n } catch (error) {\n throw new StorageException(\n `Failed to delete file ${key}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n public override async putFile(key: string, localPath: string): Promise<number> {\n const file = Bun.file(localPath);\n return await this.put(key, file);\n }\n\n public override async put(\n key: string,\n content: string | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob,\n ): Promise<number> {\n const filePath = this.getFilePath(key);\n const dir = dirname(filePath);\n\n try {\n if (!existsSync(dir)) {\n await mkdir(dir, { recursive: true });\n }\n } catch (error) {\n throw new StorageException(\n `Failed to create directory ${dir}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n try {\n let bytesWritten: number;\n\n if (typeof content === \"string\") {\n bytesWritten = await Bun.write(filePath, content);\n } else if (content instanceof ArrayBuffer) {\n bytesWritten = await Bun.write(filePath, content);\n } else if (content instanceof SharedArrayBuffer) {\n const arrayBuffer = new ArrayBuffer(content.byteLength);\n new Uint8Array(arrayBuffer).set(new Uint8Array(content));\n bytesWritten = await Bun.write(filePath, arrayBuffer);\n } else if (content instanceof Request) {\n const arrayBuffer = await content.arrayBuffer();\n bytesWritten = await Bun.write(filePath, arrayBuffer);\n } else if (content instanceof Response) {\n const arrayBuffer = await content.arrayBuffer();\n bytesWritten = await Bun.write(filePath, arrayBuffer);\n } else if (content instanceof Blob) {\n bytesWritten = await Bun.write(filePath, content);\n } else {\n const arrayBuffer = await (content as BunFile | S3File).arrayBuffer();\n bytesWritten = await Bun.write(filePath, arrayBuffer);\n }\n\n return bytesWritten;\n } catch (error) {\n throw new StorageException(\n `Failed to write file ${key}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n public override async getAsJson<T>(key: string): Promise<T> {\n const filePath = this.getFilePath(key);\n const file = Bun.file(filePath);\n\n if (!(await file.exists())) {\n throw new StorageException(`File ${key} does not exist`);\n }\n\n try {\n return await file.json();\n } catch (error) {\n throw new StorageException(\n `Failed to read file ${key} as JSON: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n public override async getAsArrayBuffer(key: string): Promise<ArrayBuffer> {\n const filePath = this.getFilePath(key);\n const file = Bun.file(filePath);\n\n if (!(await file.exists())) {\n throw new StorageException(`File ${key} does not exist`);\n }\n\n try {\n return await file.arrayBuffer();\n } catch (error) {\n throw new StorageException(\n `Failed to read file ${key} as ArrayBuffer: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n public override getAsStream(key: string): ReadableStream {\n const filePath = this.getFilePath(key);\n\n if (!existsSync(filePath)) {\n throw new StorageException(`File ${key} does not exist`);\n }\n\n const file = Bun.file(filePath);\n\n try {\n return file.stream();\n } catch (error) {\n throw new StorageException(\n `Failed to read file ${key} as stream: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n}\n"
9
+ ],
10
+ "mappings": "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",
11
+ "debugId": "C81D99E08CC7CE6B64756E2164756E21",
12
+ "names": []
13
+ }
Binary file
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@ooneex/storage",
3
+ "description": "",
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "files": [
7
+ "dist",
8
+ "LICENSE",
9
+ "README.md",
10
+ "package.json"
11
+ ],
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "license": "MIT",
24
+ "scripts": {
25
+ "test": "bun test tests",
26
+ "build": "bunup",
27
+ "lint": "tsgo --noEmit && bunx biome lint",
28
+ "publish:prod": "bun publish --tolerate-republish --access public",
29
+ "publish:pack": "bun pm pack --destination ./dist",
30
+ "publish:dry": "bun publish --dry-run"
31
+ },
32
+ "dependencies": {
33
+ "@ooneex/exception": "0.0.1",
34
+ "@ooneex/http-status": "0.0.1"
35
+ }
36
+ }