@ooneex/storage 0.0.18 → 1.0.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/README.md CHANGED
@@ -1,53 +1,37 @@
1
1
  # @ooneex/storage
2
2
 
3
- A file and object storage abstraction layer for TypeScript applications with support for local filesystem, Cloudflare R2, and Bunny CDN. This package provides a unified interface for storing, retrieving, and managing files across different storage backends.
3
+ File storage abstraction supporting local filesystem and cloud providers -- upload, download, list, and manage files with a unified bucket-based API.
4
4
 
5
5
  ![Bun](https://img.shields.io/badge/Bun-Compatible-orange?style=flat-square&logo=bun)
6
- ![Deno](https://img.shields.io/badge/Deno-Compatible-blue?style=flat-square&logo=deno)
7
- ![Node.js](https://img.shields.io/badge/Node.js-Compatible-green?style=flat-square&logo=node.js)
8
6
  ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)
9
7
  ![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)
10
8
 
11
9
  ## Features
12
10
 
13
- ✅ **Multiple Backends** - Support for filesystem, Cloudflare R2, and Bunny CDN
11
+ ✅ **Multiple Backends** - Support for local filesystem, Cloudflare R2, and Bunny CDN via a shared abstract `Storage` base class
14
12
 
15
- ✅ **Unified Interface** - Consistent API across all storage providers
13
+ ✅ **Unified Interface** - Consistent `IStorage` API across all storage providers
16
14
 
17
- ✅ **Bucket Management** - Organize files into buckets/directories
15
+ ✅ **Bucket Management** - Organize files into buckets with `getBucket`/`setBucket` methods
18
16
 
19
- ✅ **Streaming Support** - Read files as streams for memory-efficient processing
17
+ ✅ **Directory Upload** - Upload entire directories recursively with optional regex filtering via `putDir`
18
+
19
+ ✅ **File Download** - Download files from storage to local filesystem with `getFile`
20
20
 
21
- ✅ **JSON Support** - Store and retrieve JSON data directly
21
+ ✅ **Streaming Support** - Read files as streams for memory-efficient processing
22
22
 
23
- ✅ **File Upload** - Upload files from local paths or binary data
23
+ ✅ **JSON Support** - Store and retrieve JSON data directly with `getAsJson`
24
24
 
25
- ✅ **Container Integration** - Works seamlessly with dependency injection
25
+ ✅ **Container Integration** - Built-in decorator for dependency injection registration
26
26
 
27
27
  ✅ **Type-Safe** - Full TypeScript support with proper type definitions
28
28
 
29
29
  ## Installation
30
30
 
31
- ### Bun
32
31
  ```bash
33
32
  bun add @ooneex/storage
34
33
  ```
35
34
 
36
- ### pnpm
37
- ```bash
38
- pnpm add @ooneex/storage
39
- ```
40
-
41
- ### Yarn
42
- ```bash
43
- yarn add @ooneex/storage
44
- ```
45
-
46
- ### npm
47
- ```bash
48
- npm install @ooneex/storage
49
- ```
50
-
51
35
  ## Usage
52
36
 
53
37
  ### Filesystem Storage
package/dist/index.d.ts CHANGED
@@ -1,57 +1,74 @@
1
- import { BunFile as BunFile2, S3File as S3File2, S3Options } from "bun";
1
+ import { BunFile as BunFile2, S3File as S3File2 } from "bun";
2
2
  import { BunFile, S3File } from "bun";
3
3
  type StorageClassType = new (...args: any[]) => IStorage;
4
+ type PutDirOptionsType = {
5
+ path: string;
6
+ filter?: RegExp;
7
+ };
8
+ type GetFileOptionsType = {
9
+ outputDir: string;
10
+ filename?: string;
11
+ };
4
12
  interface IStorage {
13
+ getBucket(): string;
5
14
  setBucket(name: string): IStorage;
6
15
  list(): Promise<string[]>;
7
16
  clearBucket(): Promise<this>;
8
17
  exists(key: string): Promise<boolean>;
9
18
  delete(key: string): Promise<void>;
10
19
  putFile(key: string, localPath: string): Promise<number>;
20
+ putDir(bucket: string, options: PutDirOptionsType): Promise<number>;
11
21
  put(key: string, content: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob): Promise<number>;
22
+ getFile(key: string, options: GetFileOptionsType): Promise<number>;
12
23
  getAsJson<T = unknown>(key: string): Promise<T>;
13
24
  getAsArrayBuffer(key: string): Promise<ArrayBuffer>;
14
25
  getAsStream(key: string): ReadableStream;
15
26
  }
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 { BunFile as BunFile3, S3File as S3File3 } from "bun";
34
27
  declare class BunnyStorage implements IStorage {
35
28
  private bucket;
36
29
  private readonly accessKey;
37
30
  private readonly storageZone;
38
31
  private readonly region;
32
+ private readonly baseUrl;
39
33
  constructor();
34
+ getBucket(): string;
40
35
  setBucket(name: string): this;
41
36
  list(): Promise<string[]>;
42
37
  clearBucket(): Promise<this>;
43
38
  exists(key: string): Promise<boolean>;
44
39
  delete(key: string): Promise<void>;
45
40
  putFile(key: string, localPath: string): Promise<number>;
46
- put(key: string, content: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile3 | S3File3 | Blob): Promise<number>;
41
+ putDir(bucket: string, options: PutDirOptionsType): Promise<number>;
42
+ put(key: string, content: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile2 | S3File2 | Blob): Promise<number>;
43
+ getFile(key: string, options: GetFileOptionsType): Promise<number>;
47
44
  getAsJson<T = unknown>(key: string): Promise<T>;
48
45
  getAsArrayBuffer(key: string): Promise<ArrayBuffer>;
49
46
  getAsStream(key: string): ReadableStream;
50
- private getBaseUrl;
51
47
  private buildFileUrl;
52
48
  }
53
49
  import { S3Options as S3Options2 } from "bun";
54
- declare class CloudflareStorage extends AbstractStorage {
50
+ import { BunFile as BunFile3, S3File as S3File3, S3Options } from "bun";
51
+ declare abstract class Storage implements IStorage {
52
+ protected client: Bun.S3Client | null;
53
+ abstract getOptions(): S3Options;
54
+ protected abstract bucket: string;
55
+ getBucket(): string;
56
+ setBucket(name: string): this;
57
+ list(): Promise<string[]>;
58
+ clearBucket(): Promise<this>;
59
+ exists(key: string): Promise<boolean>;
60
+ delete(key: string): Promise<void>;
61
+ putFile(key: string, localPath: string): Promise<number>;
62
+ putDir(bucket: string, options: PutDirOptionsType): Promise<number>;
63
+ put(key: string, content: string | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile3 | S3File3 | Blob): Promise<number>;
64
+ getFile(key: string, options: GetFileOptionsType): Promise<number>;
65
+ getAsJson<T>(key: string): Promise<T>;
66
+ getAsArrayBuffer(key: string): Promise<ArrayBuffer>;
67
+ getAsStream(key: string): ReadableStream;
68
+ protected getClient(): Bun.S3Client;
69
+ protected getS3File(path: string): S3File3;
70
+ }
71
+ declare class CloudflareStorage extends Storage {
55
72
  protected bucket: string;
56
73
  private readonly accessKey;
57
74
  private readonly secretKey;
@@ -65,7 +82,7 @@ declare const decorator: {
65
82
  storage: (scope?: EContainerScope) => (target: StorageClassType) => void;
66
83
  };
67
84
  import { BunFile as BunFile4, S3File as S3File4, S3Options as S3Options3 } from "bun";
68
- declare class FilesystemStorage extends AbstractStorage {
85
+ declare class FilesystemStorage extends Storage {
69
86
  protected bucket: string;
70
87
  private readonly storagePath;
71
88
  constructor();
@@ -76,11 +93,11 @@ declare class FilesystemStorage extends AbstractStorage {
76
93
  list(): Promise<string[]>;
77
94
  private listFilesRecursive;
78
95
  clearBucket(): Promise<this>;
79
- private removeDirectoryRecursive;
80
96
  exists(key: string): Promise<boolean>;
81
97
  delete(key: string): Promise<void>;
82
98
  putFile(key: string, localPath: string): Promise<number>;
83
99
  put(key: string, content: string | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile4 | S3File4 | Blob): Promise<number>;
100
+ getFile(key: string, options: GetFileOptionsType): Promise<number>;
84
101
  getAsJson<T>(key: string): Promise<T>;
85
102
  getAsArrayBuffer(key: string): Promise<ArrayBuffer>;
86
103
  getAsStream(key: string): ReadableStream;
@@ -89,4 +106,4 @@ import { Exception } from "@ooneex/exception";
89
106
  declare class StorageException extends Exception {
90
107
  constructor(message: string, data?: Record<string, unknown>);
91
108
  }
92
- export { decorator, StorageException, StorageClassType, IStorage, FilesystemStorage, CloudflareStorage, BunnyStorage, AbstractStorage };
109
+ export { decorator, StorageException, StorageClassType, Storage, PutDirOptionsType, IStorage, GetFileOptionsType, FilesystemStorage, CloudflareStorage, BunnyStorage };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- var l=function(t,e,r,s){var i=arguments.length,a=i<3?e:s===null?s=Object.getOwnPropertyDescriptor(e,r):s,o;if(typeof Reflect==="object"&&typeof Reflect.decorate==="function")a=Reflect.decorate(t,e,r,s);else for(var c=t.length-1;c>=0;c--)if(o=t[c])a=(i<3?o(a):i>3?o(e,r,a):o(e,r))||a;return i>3&&a&&Object.defineProperty(e,r,a),a};var f=(t,e)=>{if(typeof Reflect==="object"&&typeof Reflect.metadata==="function")return Reflect.metadata(t,e)};class h{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{container as v,EContainerScope as P}from"@ooneex/container";var u={storage:(t=P.Singleton)=>{return(e)=>{v.add(e,t)}}};import{Exception as R}from"@ooneex/exception";import{HttpStatus as k}from"@ooneex/http-status";class n extends R{constructor(t,e={}){super(t,{status:k.Code.InternalServerError,data:e});this.name="StorageException"}}class p{bucket="";accessKey;storageZone;region;constructor(){let t=Bun.env.STORAGE_BUNNY_ACCESS_KEY,e=Bun.env.STORAGE_BUNNY_STORAGE_ZONE,r=Bun.env.STORAGE_BUNNY_REGION;if(!t)throw new n("Bunny access key is required. Please provide an access key either through the constructor options or set the STORAGE_BUNNY_ACCESS_KEY environment variable.");if(!e)throw new n("Bunny storage zone is required. Please provide a storage zone either through the constructor options or set the STORAGE_BUNNY_STORAGE_ZONE environment variable.");this.accessKey=t,this.storageZone=e,this.region=r??"de"}setBucket(t){return this.bucket=t,this}async list(){let t=this.bucket?`${this.bucket}/`:"",e=`${this.getBaseUrl()}/${this.storageZone}/${t}`,r=await fetch(e,{method:"GET",headers:{AccessKey:this.accessKey,accept:"application/json"}});if(!r.ok)throw new n(`Failed to list files: ${r.status} ${r.statusText}`,{status:r.status,path:t});return(await r.json()).filter((i)=>!i.IsDirectory).map((i)=>i.ObjectName)}async clearBucket(){let t=await this.list();for(let e of t)await this.delete(e);return this}async exists(t){let e=this.buildFileUrl(t);return(await fetch(e,{method:"GET",headers:{AccessKey:this.accessKey}})).ok}async delete(t){let e=this.buildFileUrl(t),r=await fetch(e,{method:"DELETE",headers:{AccessKey:this.accessKey}});if(!r.ok&&r.status!==404)throw new n(`Failed to delete file: ${r.status} ${r.statusText}`,{status:r.status,key:t})}async putFile(t,e){let r=Bun.file(e);return await this.put(t,r)}async put(t,e){let r=this.buildFileUrl(t),s,i;if(typeof e==="string")s=e,i=new TextEncoder().encode(e).length;else if(e instanceof ArrayBuffer)s=new Blob([e]),i=e.byteLength;else if(e instanceof SharedArrayBuffer){let o=new Uint8Array(e),c=new Uint8Array(o.length);c.set(o),s=new Blob([c]),i=e.byteLength}else if(ArrayBuffer.isView(e)){let o=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);s=new Blob([o]),i=e.byteLength}else if(e instanceof Blob)s=e,i=e.size;else if(e instanceof Request||e instanceof Response){let o=await e.arrayBuffer();s=new Blob([o]),i=o.byteLength}else if(typeof e==="object"&&e!==null&&"arrayBuffer"in e){let c=await e.arrayBuffer();s=new Blob([c]),i=c.byteLength}else throw new n("Unsupported content type for upload",{key:t});let a=await fetch(r,{method:"PUT",headers:{AccessKey:this.accessKey,"Content-Type":"application/octet-stream"},body:s});if(!a.ok)throw new n(`Failed to upload file: ${a.status} ${a.statusText}`,{status:a.status,key:t});return i}async getAsJson(t){let e=this.buildFileUrl(t),r=await fetch(e,{method:"GET",headers:{AccessKey:this.accessKey}});if(!r.ok)throw new n(`Failed to get file as JSON: ${r.status} ${r.statusText}`,{status:r.status,key:t});return await r.json()}async getAsArrayBuffer(t){let e=this.buildFileUrl(t),r=await fetch(e,{method:"GET",headers:{AccessKey:this.accessKey}});if(!r.ok)throw new n(`Failed to get file as ArrayBuffer: ${r.status} ${r.statusText}`,{status:r.status,key:t});return await r.arrayBuffer()}getAsStream(t){let e=this.buildFileUrl(t);return new ReadableStream({start:async(s)=>{let i=await fetch(e,{method:"GET",headers:{AccessKey:this.accessKey}});if(!i.ok){s.error(new n(`Failed to get file as stream: ${i.status} ${i.statusText}`,{status:i.status,key:t}));return}if(!i.body){s.error(new n("Response body is null",{key:t}));return}let a=i.body.getReader(),o=async()=>{let{done:c,value:F}=await a.read();if(c){s.close();return}s.enqueue(F),await o()};await o()}})}getBaseUrl(){return`https://${{de:"storage.bunnycdn.com",uk:"uk.storage.bunnycdn.com",ny:"ny.storage.bunnycdn.com",la:"la.storage.bunnycdn.com",sg:"sg.storage.bunnycdn.com",se:"se.storage.bunnycdn.com",br:"br.storage.bunnycdn.com",jh:"jh.storage.bunnycdn.com",syd:"syd.storage.bunnycdn.com"}[this.region]}`}buildFileUrl(t){let e=this.bucket?`${this.bucket}/${t}`:t;return`${this.getBaseUrl()}/${this.storageZone}/${e}`}}p=l([u.storage(),f("design:paramtypes",[])],p);class y extends h{bucket;accessKey;secretKey;endpoint;region;constructor(){super();let t=Bun.env.STORAGE_CLOUDFLARE_ACCESS_KEY,e=Bun.env.STORAGE_CLOUDFLARE_SECRET_KEY,r=Bun.env.STORAGE_CLOUDFLARE_ENDPOINT;if(!t)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(!e)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(!r)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=t,this.secretKey=e,this.endpoint=r,this.region=Bun.env.STORAGE_CLOUDFLARE_REGION||"EEUR"}getOptions(){return{accessKeyId:this.accessKey,secretAccessKey:this.secretKey,endpoint:this.endpoint,bucket:this.bucket,region:this.region}}}y=l([u.storage(),f("design:paramtypes",[])],y);import{existsSync as g,mkdirSync as B}from"fs";import{mkdir as S,readdir as b,rmdir as A,stat as E}from"fs/promises";import{dirname as w,join as d}from"path";class m extends h{bucket;storagePath;constructor(){super();let t=Bun.env.FILESYSTEM_STORAGE_PATH;if(!t)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=t;try{if(!g(t))B(t,{recursive:!0})}catch(e){throw new n(`Failed to create base storage directory at ${t}: ${e instanceof Error?e.message:String(e)}`)}}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 d(this.storagePath,this.bucket)}getFilePath(t){return d(this.getBucketPath(),t)}setBucket(t){this.bucket=t;let e=this.getBucketPath();try{if(!g(e))B(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(!g(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=[],s=await b(t);for(let i of s){let a=d(t,i);if((await E(a)).isDirectory()){let c=await this.listFilesRecursive(a,e);r.push(...c)}else{let c=a.substring(e.length+1);r.push(c)}}return r}async clearBucket(){let t=this.getBucketPath();if(!g(t))return this;try{await this.removeDirectoryRecursive(t),await S(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 b(t);for(let r of e){let s=d(t,r);if((await E(s)).isDirectory())await this.removeDirectoryRecursive(s),await A(s);else await Bun.file(s).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 s=w(e),i=this.getBucketPath();while(s!==i&&s!==this.storagePath)try{if((await b(s)).length===0)await A(s),s=w(s);else break}catch{break}}catch(s){throw new n(`Failed to delete file ${t}: ${s instanceof Error?s.message:String(s)}`)}}async putFile(t,e){let r=Bun.file(e);return await this.put(t,r)}async put(t,e){let r=this.getFilePath(t),s=w(r);try{if(!g(s))await S(s,{recursive:!0})}catch(i){throw new n(`Failed to create directory ${s}: ${i instanceof Error?i.message:String(i)}`)}try{let i;if(typeof e==="string")i=await Bun.write(r,e);else if(e instanceof ArrayBuffer)i=await Bun.write(r,e);else if(e instanceof SharedArrayBuffer){let a=new ArrayBuffer(e.byteLength);new Uint8Array(a).set(new Uint8Array(e)),i=await Bun.write(r,a)}else if(e instanceof Request){let a=await e.arrayBuffer();i=await Bun.write(r,a)}else if(e instanceof Response){let a=await e.arrayBuffer();i=await Bun.write(r,a)}else if(e instanceof Blob)i=await Bun.write(r,e);else{let a=await e.arrayBuffer();i=await Bun.write(r,a)}return i}catch(i){throw new n(`Failed to write file ${t}: ${i instanceof Error?i.message:String(i)}`)}}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(s){throw new n(`Failed to read file ${t} as JSON: ${s instanceof Error?s.message:String(s)}`)}}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(s){throw new n(`Failed to read file ${t} as ArrayBuffer: ${s instanceof Error?s.message:String(s)}`)}}getAsStream(t){let e=this.getFilePath(t);if(!g(e))throw new n(`File ${t} does not exist`);let r=Bun.file(e);try{return r.stream()}catch(s){throw new n(`Failed to read file ${t} as stream: ${s instanceof Error?s.message:String(s)}`)}}}m=l([u.storage(),f("design:paramtypes",[])],m);export{u as decorator,n as StorageException,m as FilesystemStorage,y as CloudflareStorage,p as BunnyStorage,h as AbstractStorage};
2
+ var h=function(t,e,r,i){var s=arguments.length,n=s<3?e:i===null?i=Object.getOwnPropertyDescriptor(e,r):i,c;if(typeof Reflect==="object"&&typeof Reflect.decorate==="function")n=Reflect.decorate(t,e,r,i);else for(var o=t.length-1;o>=0;o--)if(c=t[o])n=(s<3?c(n):s>3?c(e,r,n):c(e,r))||n;return s>3&&n&&Object.defineProperty(e,r,n),n};var p=(t,e)=>{if(typeof Reflect==="object"&&typeof Reflect.metadata==="function")return Reflect.metadata(t,e)};import{readdir as k}from"fs/promises";import{basename as x,join as F}from"path";import{container as v,EContainerScope as O}from"@ooneex/container";var l={storage:(t=O.Singleton)=>{return(e)=>{v.add(e,t)}}};import{Exception as R}from"@ooneex/exception";import{HttpStatus as $}from"@ooneex/http-status";class a extends R{constructor(t,e={}){super(t,{status:$.Code.InternalServerError,data:e});this.name="StorageException"}}var C={de:"storage.bunnycdn.com",uk:"uk.storage.bunnycdn.com",ny:"ny.storage.bunnycdn.com",la:"la.storage.bunnycdn.com",sg:"sg.storage.bunnycdn.com",se:"se.storage.bunnycdn.com",br:"br.storage.bunnycdn.com",jh:"jh.storage.bunnycdn.com",syd:"syd.storage.bunnycdn.com"};class d{bucket="";accessKey;storageZone;region;baseUrl;constructor(){let t=Bun.env.STORAGE_BUNNY_ACCESS_KEY,e=Bun.env.STORAGE_BUNNY_STORAGE_ZONE,r=Bun.env.STORAGE_BUNNY_REGION;if(!t)throw new a("Bunny access key is required. Please provide an access key either through the constructor options or set the STORAGE_BUNNY_ACCESS_KEY environment variable.");if(!e)throw new a("Bunny storage zone is required. Please provide a storage zone either through the constructor options or set the STORAGE_BUNNY_STORAGE_ZONE environment variable.");this.accessKey=t,this.storageZone=e,this.region=r??"de",this.baseUrl=`https://${C[this.region]}`}getBucket(){return this.bucket}setBucket(t){return this.bucket=t,this}async list(){let t=this.bucket?`${this.bucket}/`:"",e=`${this.baseUrl}/${this.storageZone}/${t}`,r=await fetch(e,{method:"GET",headers:{AccessKey:this.accessKey,accept:"application/json"}});if(!r.ok)throw new a(`Failed to list files: ${r.status} ${r.statusText}`,{status:r.status,path:t});return(await r.json()).filter((s)=>!s.IsDirectory).map((s)=>s.ObjectName)}async clearBucket(){let t=await this.list();return await Promise.all(t.map((e)=>this.delete(e))),this}async exists(t){let e=this.buildFileUrl(t);return(await fetch(e,{method:"HEAD",headers:{AccessKey:this.accessKey}})).ok}async delete(t){let e=this.buildFileUrl(t),r=await fetch(e,{method:"DELETE",headers:{AccessKey:this.accessKey}});if(!r.ok&&r.status!==404)throw new a(`Failed to delete file: ${r.status} ${r.statusText}`,{status:r.status,key:t})}async putFile(t,e){let r=Bun.file(e);return await this.put(t,r)}async putDir(t,e){let{path:r,filter:i}=e,s=await k(r,{withFileTypes:!0}),n=[];for(let o of s){let u=F(r,o.name),y=t?`${t}/${o.name}`:o.name;if(i&&!i.test(u))continue;if(o.isDirectory()){let m={path:u};if(i)m.filter=i;n.push(this.putDir(y,m))}else n.push(this.putFile(y,u))}return(await Promise.all(n)).reduce((o,u)=>o+u,0)}async put(t,e){let r=this.buildFileUrl(t),i,s;if(typeof e==="string")i=e,s=new TextEncoder().encode(e).length;else if(e instanceof ArrayBuffer)i=new Blob([e]),s=e.byteLength;else if(e instanceof SharedArrayBuffer){let c=new Uint8Array(e),o=new Uint8Array(c.length);o.set(c),i=new Blob([o]),s=e.byteLength}else if(ArrayBuffer.isView(e))i=new Blob([new Uint8Array(e.buffer,e.byteOffset,e.byteLength)]),s=e.byteLength;else if(e instanceof Blob)i=e,s=e.size;else if(e instanceof Request||e instanceof Response){let c=await e.arrayBuffer();i=new Blob([c]),s=c.byteLength}else if(typeof e==="object"&&e!==null&&"arrayBuffer"in e){let o=await e.arrayBuffer();i=new Blob([o]),s=o.byteLength}else throw new a("Unsupported content type for upload",{key:t});let n=await fetch(r,{method:"PUT",headers:{AccessKey:this.accessKey,"Content-Type":"application/octet-stream"},body:i});if(!n.ok)throw new a(`Failed to upload file: ${n.status} ${n.statusText}`,{status:n.status,key:t});return s}async getFile(t,e){let r=await this.getAsArrayBuffer(t),i=e.filename??x(t),s=F(e.outputDir,i);return await Bun.write(s,r)}async getAsJson(t){let e=this.buildFileUrl(t),r=await fetch(e,{method:"GET",headers:{AccessKey:this.accessKey}});if(!r.ok)throw new a(`Failed to get file as JSON: ${r.status} ${r.statusText}`,{status:r.status,key:t});return await r.json()}async getAsArrayBuffer(t){let e=this.buildFileUrl(t),r=await fetch(e,{method:"GET",headers:{AccessKey:this.accessKey}});if(!r.ok)throw new a(`Failed to get file as ArrayBuffer: ${r.status} ${r.statusText}`,{status:r.status,key:t});return await r.arrayBuffer()}getAsStream(t){let e=this.buildFileUrl(t),r=this.accessKey,{readable:i,writable:s}=new TransformStream;return fetch(e,{method:"GET",headers:{AccessKey:r}}).then((n)=>{if(!n.ok){s.abort(new a(`Failed to get file as stream: ${n.status} ${n.statusText}`,{status:n.status,key:t}));return}if(!n.body){s.abort(new a("Response body is null",{key:t}));return}n.body.pipeTo(s)}),i}buildFileUrl(t){let e=this.bucket?`${this.bucket}/${t}`:t;return`${this.baseUrl}/${this.storageZone}/${e}`}}d=h([l.storage(),p("design:paramtypes",[])],d);import{readdir as K}from"fs/promises";import{basename as _,join as A}from"path";class g{client=null;getBucket(){return this.bucket}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();return await Promise.all(e.map((r)=>t.delete(r))),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 putDir(t,e){let{path:r,filter:i}=e,s=await K(r,{withFileTypes:!0}),n=[];for(let o of s){let u=A(r,o.name),y=t?`${t}/${o.name}`:o.name;if(i&&!i.test(u))continue;if(o.isDirectory()){let m={path:u};if(i)m.filter=i;n.push(this.putDir(y,m))}else n.push(this.putFile(y,u))}return(await Promise.all(n)).reduce((o,u)=>o+u,0)}async put(t,e){return await this.getS3File(t).write(e)}async getFile(t,e){let r=await this.getAsArrayBuffer(t),i=e.filename??_(t),s=A(e.outputDir,i);return await Bun.write(s,r)}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)}}class b extends g{bucket;accessKey;secretKey;endpoint;region;constructor(){super();let t=Bun.env.STORAGE_CLOUDFLARE_ACCESS_KEY,e=Bun.env.STORAGE_CLOUDFLARE_SECRET_KEY,r=Bun.env.STORAGE_CLOUDFLARE_ENDPOINT;if(!t)throw new a("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(!e)throw new a("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(!r)throw new a("Cloudflare endpoint is required. Please provide an endpoint either through the constructor options or set the STORAGE_CLOUDFLARE_ENDPOINT environment variable.");this.accessKey=t,this.secretKey=e,this.endpoint=r,this.region=Bun.env.STORAGE_CLOUDFLARE_REGION||"EEUR"}getOptions(){return{accessKeyId:this.accessKey,secretAccessKey:this.secretKey,endpoint:this.endpoint,bucket:this.bucket,region:this.region}}}b=h([l.storage(),p("design:paramtypes",[])],b);import{existsSync as f,mkdirSync as E}from"fs";import{mkdir as P,readdir as T,rm as U,rmdir as D}from"fs/promises";import{basename as G,dirname as w,join as B}from"path";class S extends g{bucket;storagePath;constructor(){super();let t=Bun.env.FILESYSTEM_STORAGE_PATH;if(!t)throw new a("Base path is required. Please provide a base path either through the constructor options or set the FILESYSTEM_STORAGE_PATH environment variable.");this.storagePath=t;try{if(!f(t))E(t,{recursive:!0})}catch(e){throw new a(`Failed to create base storage directory at ${t}: ${e instanceof Error?e.message:String(e)}`)}}getOptions(){return{accessKeyId:"filesystem",secretAccessKey:"filesystem",endpoint:this.storagePath,bucket:this.bucket,region:"local"}}getBucketPath(){if(!this.bucket)throw new a("Bucket name is required. Please call setBucket() first.");return B(this.storagePath,this.bucket)}getFilePath(t){return B(this.getBucketPath(),t)}setBucket(t){this.bucket=t;let e=this.getBucketPath();try{if(!f(e))E(e,{recursive:!0})}catch(r){throw new a(`Failed to create bucket directory at ${e}: ${r instanceof Error?r.message:String(r)}`)}return this}async list(){let t=this.getBucketPath();if(!f(t))return[];try{return await this.listFilesRecursive(t,t)}catch(e){throw new a(`Failed to list files in bucket: ${e instanceof Error?e.message:String(e)}`)}}async listFilesRecursive(t,e){let r=await T(t,{withFileTypes:!0});return(await Promise.all(r.map(async(s)=>{let n=B(t,s.name);if(s.isDirectory())return this.listFilesRecursive(n,e);return[n.substring(e.length+1)]}))).flat()}async clearBucket(){let t=this.getBucketPath();if(!f(t))return this;try{await U(t,{recursive:!0}),await P(t,{recursive:!0})}catch(e){throw new a(`Failed to clear bucket: ${e instanceof Error?e.message:String(e)}`)}return this}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=w(e),s=this.getBucketPath();while(i!==s&&i!==this.storagePath)try{if((await T(i)).length===0)await D(i),i=w(i);else break}catch{break}}catch(i){throw new a(`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=w(r);try{if(!f(i))await P(i,{recursive:!0})}catch(s){throw new a(`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 n=new ArrayBuffer(e.byteLength);new Uint8Array(n).set(new Uint8Array(e)),s=await Bun.write(r,n)}else if(e instanceof Request){let n=await e.arrayBuffer();s=await Bun.write(r,n)}else if(e instanceof Response){let n=await e.arrayBuffer();s=await Bun.write(r,n)}else if(e instanceof Blob)s=await Bun.write(r,e);else{let n=await e.arrayBuffer();s=await Bun.write(r,n)}return s}catch(s){throw new a(`Failed to write file ${t}: ${s instanceof Error?s.message:String(s)}`)}}async getFile(t,e){let r=this.getFilePath(t),i=Bun.file(r);if(!await i.exists())throw new a(`File ${t} does not exist`);let s=e.filename??G(t),n=B(e.outputDir,s),c=w(n);try{if(!f(c))await P(c,{recursive:!0})}catch(o){throw new a(`Failed to create directory ${c}: ${o instanceof Error?o.message:String(o)}`)}try{return await Bun.write(n,i)}catch(o){throw new a(`Failed to save file ${t} to ${n}: ${o instanceof Error?o.message:String(o)}`)}}async getAsJson(t){let e=this.getFilePath(t),r=Bun.file(e);if(!await r.exists())throw new a(`File ${t} does not exist`);try{return await r.json()}catch(i){throw new a(`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 a(`File ${t} does not exist`);try{return await r.arrayBuffer()}catch(i){throw new a(`Failed to read file ${t} as ArrayBuffer: ${i instanceof Error?i.message:String(i)}`)}}getAsStream(t){let e=this.getFilePath(t);if(!f(e))throw new a(`File ${t} does not exist`);let r=Bun.file(e);try{return r.stream()}catch(i){throw new a(`Failed to read file ${t} as stream: ${i instanceof Error?i.message:String(i)}`)}}}S=h([l.storage(),p("design:paramtypes",[])],S);export{l as decorator,a as StorageException,g as Storage,S as FilesystemStorage,b as CloudflareStorage,d as BunnyStorage};
3
3
 
4
- //# debugId=1F5330000B5D249864756E2164756E21
4
+ //# debugId=E4FCBBA2EAC6C3A664756E2164756E21
package/dist/index.js.map CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["src/AbstractStorage.ts", "src/decorators.ts", "src/StorageException.ts", "src/BunnyStorage.ts", "src/CloudflareStorage.ts", "src/FilesystemStorage.ts"],
3
+ "sources": ["src/BunnyStorage.ts", "src/decorators.ts", "src/StorageException.ts", "src/Storage.ts", "src/CloudflareStorage.ts", "src/FilesystemStorage.ts"],
4
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",
5
+ "import { readdir } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport type { BunFile, S3File } from \"bun\";\nimport { decorator } from \"./decorators\";\nimport { StorageException } from \"./StorageException\";\nimport type { GetFileOptionsType, IStorage, PutDirOptionsType } from \"./types\";\n\ntype BunnyRegionType = \"de\" | \"uk\" | \"ny\" | \"la\" | \"sg\" | \"se\" | \"br\" | \"jh\" | \"syd\";\n\ntype BunnyFileInfoType = {\n Guid: string;\n StorageZoneName: string;\n Path: string;\n ObjectName: string;\n Length: number;\n LastChanged: string;\n ServerId: number;\n ArrayNumber: number;\n IsDirectory: boolean;\n UserId: string;\n ContentType: string;\n DateCreated: string;\n StorageZoneId: number;\n Checksum: string | null;\n ReplicatedZones: string | null;\n};\n\nconst REGION_ENDPOINTS: Record<BunnyRegionType, string> = {\n de: \"storage.bunnycdn.com\",\n uk: \"uk.storage.bunnycdn.com\",\n ny: \"ny.storage.bunnycdn.com\",\n la: \"la.storage.bunnycdn.com\",\n sg: \"sg.storage.bunnycdn.com\",\n se: \"se.storage.bunnycdn.com\",\n br: \"br.storage.bunnycdn.com\",\n jh: \"jh.storage.bunnycdn.com\",\n syd: \"syd.storage.bunnycdn.com\",\n};\n\n@decorator.storage()\nexport class BunnyStorage implements IStorage {\n private bucket = \"\";\n private readonly accessKey: string;\n private readonly storageZone: string;\n private readonly region: BunnyRegionType;\n private readonly baseUrl: string;\n\n constructor() {\n const accessKey = Bun.env.STORAGE_BUNNY_ACCESS_KEY;\n const storageZone = Bun.env.STORAGE_BUNNY_STORAGE_ZONE;\n const region = Bun.env.STORAGE_BUNNY_REGION as BunnyRegionType | undefined;\n\n if (!accessKey) {\n throw new StorageException(\n \"Bunny access key is required. Please provide an access key either through the constructor options or set the STORAGE_BUNNY_ACCESS_KEY environment variable.\",\n );\n }\n if (!storageZone) {\n throw new StorageException(\n \"Bunny storage zone is required. Please provide a storage zone either through the constructor options or set the STORAGE_BUNNY_STORAGE_ZONE environment variable.\",\n );\n }\n\n this.accessKey = accessKey;\n this.storageZone = storageZone;\n this.region = region ?? \"de\";\n this.baseUrl = `https://${REGION_ENDPOINTS[this.region]}`;\n }\n\n public getBucket(): string {\n return this.bucket;\n }\n\n public setBucket(name: string): this {\n this.bucket = name;\n\n return this;\n }\n\n public async list(): Promise<string[]> {\n const path = this.bucket ? `${this.bucket}/` : \"\";\n const url = `${this.baseUrl}/${this.storageZone}/${path}`;\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n AccessKey: this.accessKey,\n accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw new StorageException(`Failed to list files: ${response.status} ${response.statusText}`, {\n status: response.status,\n path,\n });\n }\n\n const files: BunnyFileInfoType[] = await response.json();\n\n return files.filter((file) => !file.IsDirectory).map((file) => file.ObjectName);\n }\n\n public async clearBucket(): Promise<this> {\n const keys = await this.list();\n\n await Promise.all(keys.map((key) => this.delete(key)));\n\n return this;\n }\n\n public async exists(key: string): Promise<boolean> {\n const url = this.buildFileUrl(key);\n\n const response = await fetch(url, {\n method: \"HEAD\",\n headers: {\n AccessKey: this.accessKey,\n },\n });\n\n return response.ok;\n }\n\n public async delete(key: string): Promise<void> {\n const url = this.buildFileUrl(key);\n\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: {\n AccessKey: this.accessKey,\n },\n });\n\n if (!response.ok && response.status !== 404) {\n throw new StorageException(`Failed to delete file: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n });\n }\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 putDir(bucket: string, options: PutDirOptionsType): Promise<number> {\n const { path, filter } = options;\n const entries = await readdir(path, { withFileTypes: true });\n\n const tasks: Promise<number>[] = [];\n\n for (const entry of entries) {\n const entryLocalPath = join(path, entry.name);\n const entryKey = bucket ? `${bucket}/${entry.name}` : entry.name;\n\n if (filter && !filter.test(entryLocalPath)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n const subOptions: PutDirOptionsType = { path: entryLocalPath };\n if (filter) {\n subOptions.filter = filter;\n }\n tasks.push(this.putDir(entryKey, subOptions));\n } else {\n tasks.push(this.putFile(entryKey, entryLocalPath));\n }\n }\n\n const results = await Promise.all(tasks);\n\n return results.reduce((sum, bytes) => sum + bytes, 0);\n }\n\n public async put(\n key: string,\n content: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob,\n ): Promise<number> {\n const url = this.buildFileUrl(key);\n\n let body: BodyInit;\n let contentLength: number;\n\n if (typeof content === \"string\") {\n body = content;\n contentLength = new TextEncoder().encode(content).length;\n } else if (content instanceof ArrayBuffer) {\n body = new Blob([content]);\n contentLength = content.byteLength;\n } else if (content instanceof SharedArrayBuffer) {\n const uint8Array = new Uint8Array(content);\n const copiedArray = new Uint8Array(uint8Array.length);\n copiedArray.set(uint8Array);\n body = new Blob([copiedArray]);\n contentLength = content.byteLength;\n } else if (ArrayBuffer.isView(content)) {\n body = new Blob([new Uint8Array(content.buffer as ArrayBuffer, content.byteOffset, content.byteLength)]);\n contentLength = content.byteLength;\n } else if (content instanceof Blob) {\n body = content;\n contentLength = content.size;\n } else if (content instanceof Request || content instanceof Response) {\n const arrayBuffer = await content.arrayBuffer();\n body = new Blob([arrayBuffer]);\n contentLength = arrayBuffer.byteLength;\n } else if (typeof content === \"object\" && content !== null && \"arrayBuffer\" in content) {\n const fileContent = content as BunFile | S3File;\n const arrayBuffer = await fileContent.arrayBuffer();\n body = new Blob([arrayBuffer]);\n contentLength = arrayBuffer.byteLength;\n } else {\n throw new StorageException(\"Unsupported content type for upload\", { key });\n }\n\n const response = await fetch(url, {\n method: \"PUT\",\n headers: {\n AccessKey: this.accessKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body,\n });\n\n if (!response.ok) {\n throw new StorageException(`Failed to upload file: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n });\n }\n\n return contentLength;\n }\n\n public async getFile(key: string, options: GetFileOptionsType): Promise<number> {\n const arrayBuffer = await this.getAsArrayBuffer(key);\n const filename = options.filename ?? basename(key);\n const localPath = join(options.outputDir, filename);\n\n return await Bun.write(localPath, arrayBuffer);\n }\n\n public async getAsJson<T = unknown>(key: string): Promise<T> {\n const url = this.buildFileUrl(key);\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n AccessKey: this.accessKey,\n },\n });\n\n if (!response.ok) {\n throw new StorageException(`Failed to get file as JSON: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n });\n }\n\n return await response.json();\n }\n\n public async getAsArrayBuffer(key: string): Promise<ArrayBuffer> {\n const url = this.buildFileUrl(key);\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n AccessKey: this.accessKey,\n },\n });\n\n if (!response.ok) {\n throw new StorageException(`Failed to get file as ArrayBuffer: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n });\n }\n\n return await response.arrayBuffer();\n }\n\n public getAsStream(key: string): ReadableStream {\n const url = this.buildFileUrl(key);\n const accessKey = this.accessKey;\n\n const { readable, writable } = new TransformStream();\n\n fetch(url, {\n method: \"GET\",\n headers: { AccessKey: accessKey },\n }).then((response) => {\n if (!response.ok) {\n writable.abort(\n new StorageException(`Failed to get file as stream: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n }),\n );\n return;\n }\n\n if (!response.body) {\n writable.abort(new StorageException(\"Response body is null\", { key }));\n return;\n }\n\n response.body.pipeTo(writable);\n });\n\n return readable;\n }\n\n private buildFileUrl(key: string): string {\n const path = this.bucket ? `${this.bucket}/${key}` : key;\n\n return `${this.baseUrl}/${this.storageZone}/${path}`;\n }\n}\n",
6
6
  "import { container, EContainerScope } from \"@ooneex/container\";\nimport type { StorageClassType } from \"./types\";\n\nexport const decorator = {\n storage: (scope: EContainerScope = EContainerScope.Singleton) => {\n return (target: StorageClassType): void => {\n container.add(target, scope);\n };\n },\n};\n",
7
7
  "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",
8
- "import type { BunFile, S3File } from \"bun\";\nimport { decorator } from \"./decorators\";\nimport { StorageException } from \"./StorageException\";\nimport type { IStorage } from \"./types\";\n\ntype BunnyRegion = \"de\" | \"uk\" | \"ny\" | \"la\" | \"sg\" | \"se\" | \"br\" | \"jh\" | \"syd\";\n\ninterface BunnyFileInfo {\n Guid: string;\n StorageZoneName: string;\n Path: string;\n ObjectName: string;\n Length: number;\n LastChanged: string;\n ServerId: number;\n ArrayNumber: number;\n IsDirectory: boolean;\n UserId: string;\n ContentType: string;\n DateCreated: string;\n StorageZoneId: number;\n Checksum: string | null;\n ReplicatedZones: string | null;\n}\n\n@decorator.storage()\nexport class BunnyStorage implements IStorage {\n private bucket = \"\";\n private readonly accessKey: string;\n private readonly storageZone: string;\n private readonly region: BunnyRegion;\n\n constructor() {\n const accessKey = Bun.env.STORAGE_BUNNY_ACCESS_KEY;\n const storageZone = Bun.env.STORAGE_BUNNY_STORAGE_ZONE;\n const region = Bun.env.STORAGE_BUNNY_REGION as BunnyRegion | undefined;\n\n if (!accessKey) {\n throw new StorageException(\n \"Bunny access key is required. Please provide an access key either through the constructor options or set the STORAGE_BUNNY_ACCESS_KEY environment variable.\",\n );\n }\n if (!storageZone) {\n throw new StorageException(\n \"Bunny storage zone is required. Please provide a storage zone either through the constructor options or set the STORAGE_BUNNY_STORAGE_ZONE environment variable.\",\n );\n }\n\n this.accessKey = accessKey;\n this.storageZone = storageZone;\n this.region = region ?? \"de\";\n }\n\n public setBucket(name: string): this {\n this.bucket = name;\n\n return this;\n }\n\n public async list(): Promise<string[]> {\n const path = this.bucket ? `${this.bucket}/` : \"\";\n const url = `${this.getBaseUrl()}/${this.storageZone}/${path}`;\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n AccessKey: this.accessKey,\n accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw new StorageException(`Failed to list files: ${response.status} ${response.statusText}`, {\n status: response.status,\n path,\n });\n }\n\n const files: BunnyFileInfo[] = await response.json();\n\n return files.filter((file) => !file.IsDirectory).map((file) => file.ObjectName);\n }\n\n public async clearBucket(): Promise<this> {\n const keys = await this.list();\n\n for (const key of keys) {\n await this.delete(key);\n }\n\n return this;\n }\n\n public async exists(key: string): Promise<boolean> {\n const url = this.buildFileUrl(key);\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n AccessKey: this.accessKey,\n },\n });\n\n return response.ok;\n }\n\n public async delete(key: string): Promise<void> {\n const url = this.buildFileUrl(key);\n\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: {\n AccessKey: this.accessKey,\n },\n });\n\n if (!response.ok && response.status !== 404) {\n throw new StorageException(`Failed to delete file: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n });\n }\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 | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob,\n ): Promise<number> {\n const url = this.buildFileUrl(key);\n\n let body: BodyInit;\n let contentLength: number;\n\n if (typeof content === \"string\") {\n body = content;\n contentLength = new TextEncoder().encode(content).length;\n } else if (content instanceof ArrayBuffer) {\n body = new Blob([content]);\n contentLength = content.byteLength;\n } else if (content instanceof SharedArrayBuffer) {\n const uint8Array = new Uint8Array(content);\n const copiedArray = new Uint8Array(uint8Array.length);\n copiedArray.set(uint8Array);\n body = new Blob([copiedArray]);\n contentLength = content.byteLength;\n } else if (ArrayBuffer.isView(content)) {\n const arrayBuffer = content.buffer.slice(\n content.byteOffset,\n content.byteOffset + content.byteLength,\n ) as ArrayBuffer;\n body = new Blob([arrayBuffer]);\n contentLength = content.byteLength;\n } else if (content instanceof Blob) {\n body = content;\n contentLength = content.size;\n } else if (content instanceof Request || content instanceof Response) {\n const arrayBuffer = await content.arrayBuffer();\n body = new Blob([arrayBuffer]);\n contentLength = arrayBuffer.byteLength;\n } else if (typeof content === \"object\" && content !== null && \"arrayBuffer\" in content) {\n const fileContent = content as BunFile | S3File;\n const arrayBuffer = await fileContent.arrayBuffer();\n body = new Blob([arrayBuffer]);\n contentLength = arrayBuffer.byteLength;\n } else {\n throw new StorageException(\"Unsupported content type for upload\", { key });\n }\n\n const response = await fetch(url, {\n method: \"PUT\",\n headers: {\n AccessKey: this.accessKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body,\n });\n\n if (!response.ok) {\n throw new StorageException(`Failed to upload file: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n });\n }\n\n return contentLength;\n }\n\n public async getAsJson<T = unknown>(key: string): Promise<T> {\n const url = this.buildFileUrl(key);\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n AccessKey: this.accessKey,\n },\n });\n\n if (!response.ok) {\n throw new StorageException(`Failed to get file as JSON: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n });\n }\n\n return await response.json();\n }\n\n public async getAsArrayBuffer(key: string): Promise<ArrayBuffer> {\n const url = this.buildFileUrl(key);\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n AccessKey: this.accessKey,\n },\n });\n\n if (!response.ok) {\n throw new StorageException(`Failed to get file as ArrayBuffer: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n });\n }\n\n return await response.arrayBuffer();\n }\n\n public getAsStream(key: string): ReadableStream {\n const url = this.buildFileUrl(key);\n\n const stream = new ReadableStream({\n start: async (controller) => {\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n AccessKey: this.accessKey,\n },\n });\n\n if (!response.ok) {\n controller.error(\n new StorageException(`Failed to get file as stream: ${response.status} ${response.statusText}`, {\n status: response.status,\n key,\n }),\n );\n return;\n }\n\n if (!response.body) {\n controller.error(new StorageException(\"Response body is null\", { key }));\n return;\n }\n\n const reader = response.body.getReader();\n\n const pump = async (): Promise<void> => {\n const { done, value } = await reader.read();\n\n if (done) {\n controller.close();\n return;\n }\n\n controller.enqueue(value);\n await pump();\n };\n\n await pump();\n },\n });\n\n return stream;\n }\n\n private getBaseUrl(): string {\n const regionEndpoints: Record<BunnyRegion, string> = {\n de: \"storage.bunnycdn.com\",\n uk: \"uk.storage.bunnycdn.com\",\n ny: \"ny.storage.bunnycdn.com\",\n la: \"la.storage.bunnycdn.com\",\n sg: \"sg.storage.bunnycdn.com\",\n se: \"se.storage.bunnycdn.com\",\n br: \"br.storage.bunnycdn.com\",\n jh: \"jh.storage.bunnycdn.com\",\n syd: \"syd.storage.bunnycdn.com\",\n };\n\n return `https://${regionEndpoints[this.region]}`;\n }\n\n private buildFileUrl(key: string): string {\n const path = this.bucket ? `${this.bucket}/${key}` : key;\n\n return `${this.getBaseUrl()}/${this.storageZone}/${path}`;\n }\n}\n",
9
- "import type { S3Options } from \"bun\";\nimport { AbstractStorage } from \"./AbstractStorage\";\nimport { decorator } from \"./decorators\";\nimport { StorageException } from \"./StorageException\";\n\n@decorator.storage()\nexport class CloudflareStorage 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() {\n super();\n\n const accessKey = Bun.env.STORAGE_CLOUDFLARE_ACCESS_KEY;\n const secretKey = Bun.env.STORAGE_CLOUDFLARE_SECRET_KEY;\n const 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 = 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",
10
- "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 { decorator } from \"./decorators\";\nimport { StorageException } from \"./StorageException\";\n\n@decorator.storage()\nexport class FilesystemStorage extends AbstractStorage {\n protected bucket: string;\n private readonly storagePath: string;\n\n constructor() {\n super();\n\n const basePath = 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"
8
+ "import { readdir } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport type { BunFile, S3File, S3Options } from \"bun\";\nimport type { GetFileOptionsType, IStorage, PutDirOptionsType } from \"./types\";\n\nexport abstract class Storage implements IStorage {\n protected client: Bun.S3Client | null = null;\n public abstract getOptions(): S3Options;\n protected abstract bucket: string;\n\n public getBucket(): string {\n return this.bucket;\n }\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 await Promise.all(keys.map((key) => client.delete(key)));\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 putDir(bucket: string, options: PutDirOptionsType): Promise<number> {\n const { path, filter } = options;\n const entries = await readdir(path, { withFileTypes: true });\n\n const tasks: Promise<number>[] = [];\n\n for (const entry of entries) {\n const entryLocalPath = join(path, entry.name);\n const entryKey = bucket ? `${bucket}/${entry.name}` : entry.name;\n\n if (filter && !filter.test(entryLocalPath)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n const subOptions: PutDirOptionsType = { path: entryLocalPath };\n if (filter) {\n subOptions.filter = filter;\n }\n tasks.push(this.putDir(entryKey, subOptions));\n } else {\n tasks.push(this.putFile(entryKey, entryLocalPath));\n }\n }\n\n const results = await Promise.all(tasks);\n\n return results.reduce((sum, bytes) => sum + bytes, 0);\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 getFile(key: string, options: GetFileOptionsType): Promise<number> {\n const arrayBuffer = await this.getAsArrayBuffer(key);\n const filename = options.filename ?? basename(key);\n const localPath = join(options.outputDir, filename);\n\n return await Bun.write(localPath, arrayBuffer);\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",
9
+ "import type { S3Options } from \"bun\";\nimport { decorator } from \"./decorators\";\nimport { Storage } from \"./Storage\";\nimport { StorageException } from \"./StorageException\";\n\n@decorator.storage()\nexport class CloudflareStorage extends Storage {\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() {\n super();\n\n const accessKey = Bun.env.STORAGE_CLOUDFLARE_ACCESS_KEY;\n const secretKey = Bun.env.STORAGE_CLOUDFLARE_SECRET_KEY;\n const 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 = 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",
10
+ "import { existsSync, mkdirSync } from \"node:fs\";\nimport { mkdir, readdir, rm, rmdir } from \"node:fs/promises\";\nimport { basename, dirname, join } from \"node:path\";\nimport type { BunFile, S3File, S3Options } from \"bun\";\nimport { decorator } from \"./decorators\";\nimport { Storage } from \"./Storage\";\nimport { StorageException } from \"./StorageException\";\nimport type { GetFileOptionsType } from \"./types\";\n\n@decorator.storage()\nexport class FilesystemStorage extends Storage {\n protected bucket: string;\n private readonly storagePath: string;\n\n constructor() {\n super();\n\n const basePath = 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 entries = await readdir(dir, { withFileTypes: true });\n\n const results = await Promise.all(\n entries.map(async (entry) => {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n return this.listFilesRecursive(fullPath, baseDir);\n }\n\n return [fullPath.substring(baseDir.length + 1)];\n }),\n );\n\n return results.flat();\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 rm(bucketPath, { recursive: true });\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 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 getFile(key: string, options: GetFileOptionsType): Promise<number> {\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 const filename = options.filename ?? basename(key);\n const localPath = join(options.outputDir, filename);\n const dir = dirname(localPath);\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 return await Bun.write(localPath, file);\n } catch (error) {\n throw new StorageException(\n `Failed to save file ${key} to ${localPath}: ${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"
11
11
  ],
12
- "mappings": "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",
13
- "debugId": "1F5330000B5D249864756E2164756E21",
12
+ "mappings": "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",
13
+ "debugId": "E4FCBBA2EAC6C3A664756E2164756E21",
14
14
  "names": []
15
15
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ooneex/storage",
3
- "description": "File and object storage abstraction layer with support for local filesystem and cloud storage providers",
4
- "version": "0.0.18",
3
+ "description": "File storage abstraction supporting local filesystem and cloud providers — upload, download, list, and manage files with a unified bucket-based API",
4
+ "version": "1.0.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -28,9 +28,9 @@
28
28
  "npm:publish": "bun publish --tolerate-republish --access public"
29
29
  },
30
30
  "dependencies": {
31
- "@ooneex/container": "0.0.17",
32
- "@ooneex/exception": "0.0.16",
33
- "@ooneex/http-status": "0.0.16"
31
+ "@ooneex/container": "0.0.19",
32
+ "@ooneex/exception": "0.0.18",
33
+ "@ooneex/http-status": "0.0.18"
34
34
  },
35
35
  "keywords": [
36
36
  "blob",