@nuvix/storage 2.0.0 → 2.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/README.md +29 -9
- package/dist/index.js +1 -1
- package/dist/src/device/s3.d.ts +1 -4
- package/dist/src/device/s3.d.ts.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ bun add @nuvix/storage
|
|
|
28
28
|
```typescript
|
|
29
29
|
import { Storage, Local } from "@nuvix/storage";
|
|
30
30
|
|
|
31
|
-
const localStorage = new Local(
|
|
31
|
+
const localStorage = new Local("./uploads");
|
|
32
32
|
Storage.setDevice(Storage.DEVICE_LOCAL, localStorage);
|
|
33
33
|
|
|
34
34
|
const device = Storage.getDevice(Storage.DEVICE_LOCAL);
|
|
@@ -45,7 +45,7 @@ console.log(await device.read("hello.txt")); // Buffer("Hello, World!")
|
|
|
45
45
|
import { Local } from "@nuvix/storage";
|
|
46
46
|
|
|
47
47
|
// Root defaults to the current working directory
|
|
48
|
-
const device = new Local(
|
|
48
|
+
const device = new Local("./uploads");
|
|
49
49
|
|
|
50
50
|
await device.write("docs/report.pdf", buffer);
|
|
51
51
|
await device.exists("docs/report.pdf"); // true
|
|
@@ -199,18 +199,38 @@ v2.0.0 is a breaking release:
|
|
|
199
199
|
- **Fixed:** `S3.SA_EAST_1` now maps to `"sa-east-1"` (previously incorrectly mapped to `"eu-north-1"`).
|
|
200
200
|
- **New:** `stat()`, `presign()`, `Storage.removeDevice()`, `Storage.listDevices()`, `move()`, `abort()`.
|
|
201
201
|
|
|
202
|
+
See [MIGRATION.md](./MIGRATION.md) for the full step-by-step migration guide.
|
|
203
|
+
|
|
202
204
|
## Development
|
|
203
205
|
|
|
204
206
|
```bash
|
|
205
|
-
bun install
|
|
206
|
-
bun test
|
|
207
|
-
bun run test:watch
|
|
208
|
-
bun run lint
|
|
209
|
-
bun run typecheck
|
|
210
|
-
bun run build
|
|
207
|
+
bun install # install dev dependencies
|
|
208
|
+
bun test # run offline test suite
|
|
209
|
+
bun run test:watch # watch mode
|
|
210
|
+
bun run lint # oxlint
|
|
211
|
+
bun run typecheck # tsc --noEmit
|
|
212
|
+
bun run build # bundle to dist/ (ESM + declarations)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The default suite is fully offline — cloud devices are exercised through offline-presign URL checks and config validation, so no credentials are ever needed.
|
|
216
|
+
|
|
217
|
+
### Integration Tests
|
|
218
|
+
|
|
219
|
+
An opt-in integration suite (`__tests__/integration/`) runs against a real MinIO server and is skipped unless `MINIO_TEST_URL` is set:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# 1. Start MinIO and create the test bucket
|
|
223
|
+
docker run -d --name storage-test-minio -p 9000:9000 -p 9001:9001 \
|
|
224
|
+
-e MINIO_ROOT_USER=minioadmin -e MINIO_ROOT_PASSWORD=minioadmin-test \
|
|
225
|
+
minio/minio server /data --console-address ":9001"
|
|
226
|
+
docker exec storage-test-minio mc alias set local http://localhost:9000 minioadmin minioadmin-test
|
|
227
|
+
docker exec storage-test-minio mc mb --ignore-existing local/nuvix-storage-test
|
|
228
|
+
|
|
229
|
+
# 2. Run the integration tests
|
|
230
|
+
MINIO_TEST_URL=http://localhost:9000 bun run test:integration
|
|
211
231
|
```
|
|
212
232
|
|
|
213
|
-
|
|
233
|
+
These cover real S3 protocol behavior the offline suite can't: chunked upload assembly, abort cleanup, presigned GET/PUT round-trips, and cross-device transfers.
|
|
214
234
|
|
|
215
235
|
## Contributing
|
|
216
236
|
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var D=import.meta.require;class a extends Error{code;path;constructor(t,e,r){super(e);this.name="StorageError",this.code=t,this.path=r}}class y{transferChunkSize=20000000;setTransferChunkSize(t){this.transferChunkSize=t}getTransferChunkSize(){return this.transferChunkSize}async transfer(t,e,r){if(!await this.exists(t))throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);let i=await this.getFileSize(t),s=await this.getFileMimeType(t);if(i<=this.transferChunkSize)return r.write(e,await this.read(t),s);let n=Math.ceil(i/this.transferChunkSize),o={};for(let c=0;c<n;c++){let p=await this.read(t,c*this.transferChunkSize,this.transferChunkSize);await r.uploadData(p,e,s,c+1,n,o)}return!0}async move(t,e){if(t===e)return!1;if(!await this.exists(t))return!1;if(await this.transfer(t,e,this))return this.delete(t);return!1}presign(t,e){throw new a("UNSUPPORTED_OPERATION",`${this.getName()} does not support presigned URLs`)}getAbsolutePath(t){let r=t.replace(/[/\\]/g,"/").split("/").filter((s)=>s.length>0),i=[];for(let s of r){if(s===".")continue;if(s==="..")i.pop();else i.push(s)}return"/"+i.join("/")}}class d{static DEVICE_LOCAL="local";static DEVICE_S3="s3";static DEVICE_WASABI="wasabi";static DEVICE_MINIO="minio";static devices=new Map;static setDevice(t,e){this.devices.set(t,e)}static getDevice(t){let e=this.devices.get(t);if(!e)throw new a("DEVICE_NOT_FOUND",`The device "${t}" is not listed`,t);return e}static exists(t){return this.devices.has(t)}static removeDevice(t){return this.devices.delete(t)}static listDevices(){return[...this.devices.keys()]}static human(t,e=2,r="metric"){let i=r==="binary"?1024:1000,s={binary:["B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],metric:["B","kB","MB","GB","TB","PB","EB","ZB","YB"]},n=Math.floor((t.toString().length-1)/3);return`${(t/Math.pow(i,n)).toFixed(e)}${s[r][n]}`}}class u{static TYPE_STRING="string";static TYPE_ARRAY="array";static TYPE_INTEGER="integer";static TYPE_BOOLEAN="boolean"}class B extends u{getDescription(){return"File is not valid"}isValid(t){return!0}}class k extends u{static TYPE_JPEG="jpeg";static TYPE_JPG="jpg";static TYPE_GIF="gif";static TYPE_PNG="png";static TYPE_GZIP="gz";static TYPE_ZIP="zip";allowed;constructor(t){super();this.allowed=t}getDescription(){return"File extension is not valid"}isValid(t){let e=this.getFileExtension(t).toLowerCase();return this.allowed.includes(e)}getFileExtension(t){let e=t.lastIndexOf(".");return e!==-1?t.substring(e+1):""}}class C extends u{getDescription(){return"Filename is not valid"}isValid(t){if(!t)return!1;if(typeof t!=="string")return!1;if(!/^[a-zA-Z0-9.]+$/.test(t))return!1;return!0}}class U extends u{max;constructor(t){super();this.max=t}getDescription(){return`File size can't be bigger than ${this.max}`}isValid(t){if(typeof t!=="number"||!Number.isInteger(t))return!1;if(t>this.max||t<0)return!1;return!0}}class f extends u{static FILE_TYPE_JPEG="jpeg";static FILE_TYPE_GIF="gif";static FILE_TYPE_PNG="png";static FILE_TYPE_GZIP="gz";types={[f.FILE_TYPE_JPEG]:[255,216,255],[f.FILE_TYPE_GIF]:[71,73,70],[f.FILE_TYPE_PNG]:[137,80,78,71,13,10],[f.FILE_TYPE_GZIP]:[31,139]};allowed;constructor(t){super();for(let e of t)if(!(e in this.types))throw Error("Unknown file mime type");this.allowed=t}getDescription(){return"File mime-type is not allowed "}async isValid(t){try{let e=Math.max(...Object.values(this.types).map((i)=>i.length)),r=await Bun.file(t).slice(0,e).bytes();return this.allowed.some((i)=>{let s=this.types[i];return s!==void 0&&s.every((n,o)=>r[o]===n)})}catch{return!1}}}import{promises as z}from"fs";class M extends u{getDescription(){return"Not a valid upload file"}async isValid(t){if(typeof t!=="string")return!1;try{return(await z.stat(t)).isFile()}catch{return!1}}}import{mkdir as R,readdir as x,rm as E,statfs as F}from"fs/promises";import l from"path";var I=".part.";class V extends y{root;constructor(t=""){super();this.root=t}getName(){return"Local Storage"}getType(){return d.DEVICE_LOCAL}getDescription(){return"Adapter for local storage on the physical or virtual machine, or mounted to it."}getRoot(){return this.root}getPath(t,e){return this.getAbsolutePath(l.join(this.root,t))}async upload(t,e,r=1,i=1,s={}){if(i<=1){await this.createDirectory(l.dirname(e));try{return await this.rename(t,e),1}catch{throw new a("UPLOAD_FAILED",`Can't upload file ${e}`,e)}}let n=m(e);await this.createDirectory(n);let o=T(e,r);try{await this.rename(t,o)}catch{throw new a("UPLOAD_FAILED",`Failed to write chunk ${r}`,e)}let c=(await this.listParts(e)).length;if(c===i)await this.joinChunks(e,i);return c}async uploadData(t,e,r,i=1,s=1,n={}){if(s<=1)return await this.write(e,t)?1:0;let o=m(e);await this.createDirectory(o);try{await Bun.write(T(e,i),t)}catch{throw new a("UPLOAD_FAILED",`Failed to write chunk ${i}`,e)}let c=(await this.listParts(e)).length;if(c===s)await this.joinChunks(e,s);return c}async abort(t,e=""){let r=m(t);if(await Bun.file(t).exists())await Bun.file(t).delete();if(!await w(r))throw new a("FILE_NOT_FOUND",`No staged upload for ${t}`,t);return await E(r,{recursive:!0,force:!0}),!0}async read(t,e=0,r){let i=Bun.file(t);if(!await i.exists())throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);let s=r===void 0?e>0?i.slice(e):i:i.slice(e,e+r);return Buffer.from(await s.arrayBuffer())}async write(t,e,r=""){try{return await this.createDirectory(l.dirname(t)),await Bun.write(Bun.file(t),e),!0}catch{throw new a("WRITE_FAILED",`Can't write to path ${t}`,t)}}async move(t,e){if(t===e||!await G(t))return!1;try{return await this.createDirectory(l.dirname(e)),await this.rename(t,e),!0}catch{return!1}}async delete(t,e=!1){try{let r=Bun.file(t);if(await r.exists())return await r.delete(),!0;if(e&&await w(t))return await E(t,{recursive:!0}),!0;return!1}catch{return!1}}async deletePath(t){if(!await w(t))return!1;return await E(t,{recursive:!0,force:!0}),!0}async exists(t){return Bun.file(t).exists()}async getFileSize(t){let e=Bun.file(t).size;if(e===-1&&!await Bun.file(t).exists())throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);return e}async getFileMimeType(t){return A(Bun.file(t).type)}async getFileHash(t){let e=new Bun.CryptoHasher("md5");return e.update(await Bun.file(t).bytes()),e.digest("hex")}async stat(t){let e=Bun.file(t);if(!await e.exists())throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);return{size:e.size,mimeType:A(e.type)}}async createDirectory(t){try{return await R(t,{recursive:!0,mode:493}),!0}catch{return!1}}async getDirectorySize(t){let e=0,r;try{r=await this.getFiles(t)}catch{return-1}for(let i of r)if(await w(i.key))e+=await this.getDirectorySize(i.key);else e+=Bun.file(i.key).size;return e}async getPartitionFreeSpace(){let t=await F(this.root||".");return t.bavail*t.bsize}async getPartitionTotalSpace(){let t=await F(this.root||".");return t.blocks*t.bsize}async getFiles(t){try{return(await x(t,{withFileTypes:!0})).map((r)=>({key:l.join(t,r.name),size:r.isFile()?Bun.file(l.join(t,r.name)).size:void 0}))}catch{return[]}}async joinChunks(t,e){let i=Bun.file(t).writer();for(let s=1;s<=e;s++){let n=T(t,s);i.write(await Bun.file(n).bytes())}await i.end(),await E(m(t),{recursive:!0,force:!0})}async listParts(t){let e=m(t),r=`${l.parse(t).name}${I}`;try{return(await x(e)).filter((s)=>s.startsWith(r))}catch{return[]}}async rename(t,e){await import("fs").then((r)=>r.promises.rename(t,e))}}function m(t){return l.join(l.dirname(t),`tmp_${l.basename(t)}`)}function T(t,e){return l.join(m(t),`${l.parse(t).name}${I}${e}`)}async function G(t){return Bun.file(t).exists()}async function w(t){try{let{stat:e}=await import("fs/promises");return(await e(t)).isDirectory()}catch{return!1}}function A(t){return t.split(";")[0]?.trim()??""}var{S3Client:W}=globalThis.Bun;var b=".part-",P=(t)=>String(t).padStart(5,"0"),O=(t)=>t.replace(/^"|"$/g,""),v=(t)=>t.split(";")[0]?.trim()??"";class g extends y{static US_EAST_1="us-east-1";static US_EAST_2="us-east-2";static US_WEST_1="us-west-1";static US_WEST_2="us-west-2";static AF_SOUTH_1="af-south-1";static AP_EAST_1="ap-east-1";static AP_SOUTH_1="ap-south-1";static AP_NORTHEAST_1="ap-northeast-1";static AP_NORTHEAST_2="ap-northeast-2";static AP_NORTHEAST_3="ap-northeast-3";static AP_SOUTHEAST_1="ap-southeast-1";static AP_SOUTHEAST_2="ap-southeast-2";static CA_CENTRAL_1="ca-central-1";static EU_CENTRAL_1="eu-central-1";static EU_WEST_1="eu-west-1";static EU_WEST_2="eu-west-2";static EU_WEST_3="eu-west-3";static EU_SOUTH_1="eu-south-1";static EU_NORTH_1="eu-north-1";static SA_EAST_1="sa-east-1";static CN_NORTH_1="cn-north-1";static CN_NORTHWEST_1="cn-northwest-1";static ME_SOUTH_1="me-south-1";static US_GOV_EAST_1="us-gov-east-1";static US_GOV_WEST_1="us-gov-west-1";static ACL_PRIVATE="private";static ACL_PUBLIC_READ="public-read";static ACL_PUBLIC_READ_WRITE="public-read-write";static ACL_AUTHENTICATED_READ="authenticated-read";static MAX_PAGE_SIZE=1000;client;root;writerOptions;constructor(t){super();let{accessKeyId:e,secretAccessKey:r,bucket:i}=t;if(!e||!r||!i)throw new a("INVALID_CONFIG","S3 requires `accessKeyId`, `secretAccessKey` and `bucket`");this.root=t.root??"",this.writerOptions={partSize:t.partSize,queueSize:t.queueSize,retry:t.retry},this.client=new W({accessKeyId:e,secretAccessKey:r,bucket:i,region:t.region,acl:t.acl,endpoint:t.endpoint,sessionToken:t.sessionToken})}getName(){return"S3 Storage"}getType(){return d.DEVICE_S3}getDescription(){return"S3 Bucket Storage drive for AWS or on-premise solutions, powered by Bun's native S3 client"}getRoot(){return this.root}getPath(t,e){return this.root?`${this.root}/${t}`:t}key(t){return this.getPath(t)}async write(t,e,r=""){try{return await this.client.write(this.key(t),e,r?{type:r}:{}),!0}catch(i){throw new a("WRITE_FAILED",`Failed to write ${t}: ${h(i)}`,t)}}async read(t,e=0,r){let i=this.client.file(this.key(t)),s=r===void 0?e>0?i.slice(e):i:i.slice(e,e+r);try{return Buffer.from(await s.bytes())}catch(n){if(N(n))throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);throw new a("READ_FAILED",`Failed to read ${t}: ${h(n)}`,t)}}async upload(t,e,r=1,i=1,s={}){let n=Bun.file(t);if(!await n.exists())throw new a("FILE_NOT_FOUND",`Source file not found: ${t}`,t);let o=Buffer.from(await n.arrayBuffer()),c=typeof s.contentType==="string"&&s.contentType.length>0?s.contentType:v(n.type)||"application/octet-stream";return this.uploadData(o,e,c,r,i,s)}async uploadData(t,e,r,i=1,s=1,n={}){if(s<=1)return await this.write(e,t,r),1;let o=this.key(e),c=`${o}${b}${P(i)}`;await this.client.write(c,t,{type:r});let p=Number(n.receivedChunks??0)+1;if(n.receivedChunks=p,p<s)return p;try{let _=this.client.file(o,{type:r}).writer({...this.writerOptions});for(let S=1;S<=s;S++)_.write(await this.client.file(`${o}${b}${P(S)}`).bytes());await _.end()}catch(_){throw new a("UPLOAD_FAILED",`Failed to assemble chunks for ${e}: ${h(_)}`,e)}finally{await this.deleteStagedParts(o,s)}return p}async abort(t,e=""){return await this.deleteStagedPartsByPrefix(`${this.key(t)}${b}`),!0}async delete(t,e=!1){try{return await this.client.delete(this.key(t)),!0}catch(r){throw new a("DELETE_FAILED",`Failed to delete ${t}: ${h(r)}`,t)}}async deletePath(t){let e=`${this.key(t)}/`,r;do{let i=await this.client.list({prefix:e,maxKeys:g.MAX_PAGE_SIZE,continuationToken:r}),s=(i.contents??[]).map((n)=>n.key);await Promise.all(s.map((n)=>this.client.delete(n))),r=i.isTruncated?i.nextContinuationToken:void 0}while(r);return!0}async exists(t){return this.client.exists(this.key(t))}async stat(t){try{let e=await this.client.stat(this.key(t));return{size:e.size,mimeType:v(e.type),etag:e.etag?O(e.etag):void 0,lastModified:e.lastModified}}catch(e){if(N(e))throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);throw e}}async getFileSize(t){return(await this.stat(t)).size}async getFileMimeType(t){return(await this.stat(t)).mimeType}async getFileHash(t){return(await this.stat(t)).etag??""}presign(t,e={}){return this.client.presign(this.key(t),e)}async createDirectory(t){return!0}async getDirectorySize(t){let e=0,r;do{let i=await this.client.list({prefix:`${this.key(t)}/`,maxKeys:g.MAX_PAGE_SIZE,continuationToken:r});for(let s of i.contents??[])e+=s.size??0;r=i.isTruncated?i.nextContinuationToken:void 0}while(r);return e}async getPartitionFreeSpace(){return-1}async getPartitionTotalSpace(){return-1}async getFiles(t,e=g.MAX_PAGE_SIZE,r=""){return((await this.client.list({prefix:t?`${this.key(t)}/`:`${this.root}/`,maxKeys:Math.min(e,g.MAX_PAGE_SIZE),continuationToken:r||void 0})).contents??[]).map((s)=>({key:s.key,size:s.size,lastModified:s.lastModified?new Date(s.lastModified):void 0,etag:s.eTag?O(s.eTag):void 0}))}async deleteStagedParts(t,e){await Promise.all(Array.from({length:e},(r,i)=>this.client.delete(`${t}${b}${P(i+1)}`).catch(()=>{return})))}async deleteStagedPartsByPrefix(t){let e=await this.client.list({prefix:t,maxKeys:g.MAX_PAGE_SIZE});await Promise.all((e.contents??[]).map((r)=>this.client.delete(r.key).catch(()=>{return})))}}function h(t){return t instanceof Error?t.message:String(t)}function N(t){let e=h(t).toLowerCase();return e.includes("404")||e.includes("nosuchkey")}class L extends g{static US_WEST_1="us-west-1";static US_CENTRAL_1="us-central-1";static US_EAST_1="us-east-1";static US_EAST_2="us-east-2";static AP_NORTHEAST_1="ap-northeast-1";static AP_NORTHEAST_2="ap-northeast-2";static EU_CENTRAL_1="eu-central-1";static EU_CENTRAL_2="eu-central-2";static EU_WEST_1="eu-west-1";static EU_WEST_2="eu-west-2";constructor(t){let e=t.region??L.EU_CENTRAL_1;super({...t,region:e,endpoint:`https://s3.${e}.wasabisys.com`})}getName(){return"Wasabi Storage"}getDescription(){return"Wasabi hot cloud storage drive, powered by Bun's native S3 client"}getType(){return d.DEVICE_WASABI}}class Y extends g{constructor(t){let{endpoint:e="localhost:9000",useSSL:r=!1,...i}=t,s=e.replace(/^https?:\/\//,"");super({...i,region:i.region??g.US_EAST_1,endpoint:`${r?"https":"http"}://${s}`})}getName(){return"MinIO Storage"}getDescription(){return"MinIO S3-compatible object storage server, powered by Bun's native S3 client"}getType(){return d.DEVICE_MINIO}}export{y as Device,B as File,k as FileExt,C as FileName,U as FileSize,f as FileType,V as Local,Y as MinIO,g as S3,d as Storage,a as StorageError,M as Upload,L as Wasabi};
|
|
2
|
+
var D=import.meta.require;class a extends Error{code;path;constructor(t,e,r){super(e);this.name="StorageError",this.code=t,this.path=r}}class y{transferChunkSize=20000000;setTransferChunkSize(t){this.transferChunkSize=t}getTransferChunkSize(){return this.transferChunkSize}async transfer(t,e,r){if(!await this.exists(t))throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);let i=await this.getFileSize(t),n=await this.getFileMimeType(t);if(i<=this.transferChunkSize)return r.write(e,await this.read(t),n);let s=Math.ceil(i/this.transferChunkSize),o={};for(let c=0;c<s;c++){let p=await this.read(t,c*this.transferChunkSize,this.transferChunkSize);await r.uploadData(p,e,n,c+1,s,o)}return!0}async move(t,e){if(t===e)return!1;if(!await this.exists(t))return!1;if(await this.transfer(t,e,this))return this.delete(t);return!1}presign(t,e){throw new a("UNSUPPORTED_OPERATION",`${this.getName()} does not support presigned URLs`)}getAbsolutePath(t){let r=t.replace(/[/\\]/g,"/").split("/").filter((n)=>n.length>0),i=[];for(let n of r){if(n===".")continue;if(n==="..")i.pop();else i.push(n)}return"/"+i.join("/")}}class d{static DEVICE_LOCAL="local";static DEVICE_S3="s3";static DEVICE_WASABI="wasabi";static DEVICE_MINIO="minio";static devices=new Map;static setDevice(t,e){this.devices.set(t,e)}static getDevice(t){let e=this.devices.get(t);if(!e)throw new a("DEVICE_NOT_FOUND",`The device "${t}" is not listed`,t);return e}static exists(t){return this.devices.has(t)}static removeDevice(t){return this.devices.delete(t)}static listDevices(){return[...this.devices.keys()]}static human(t,e=2,r="metric"){let i=r==="binary"?1024:1000,n={binary:["B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],metric:["B","kB","MB","GB","TB","PB","EB","ZB","YB"]},s=Math.floor((t.toString().length-1)/3);return`${(t/Math.pow(i,s)).toFixed(e)}${n[r][s]}`}}class u{static TYPE_STRING="string";static TYPE_ARRAY="array";static TYPE_INTEGER="integer";static TYPE_BOOLEAN="boolean"}class k extends u{getDescription(){return"File is not valid"}isValid(t){return!0}}class B extends u{static TYPE_JPEG="jpeg";static TYPE_JPG="jpg";static TYPE_GIF="gif";static TYPE_PNG="png";static TYPE_GZIP="gz";static TYPE_ZIP="zip";allowed;constructor(t){super();this.allowed=t}getDescription(){return"File extension is not valid"}isValid(t){let e=this.getFileExtension(t).toLowerCase();return this.allowed.includes(e)}getFileExtension(t){let e=t.lastIndexOf(".");return e!==-1?t.substring(e+1):""}}class C extends u{getDescription(){return"Filename is not valid"}isValid(t){if(!t)return!1;if(typeof t!=="string")return!1;if(!/^[a-zA-Z0-9.]+$/.test(t))return!1;return!0}}class U extends u{max;constructor(t){super();this.max=t}getDescription(){return`File size can't be bigger than ${this.max}`}isValid(t){if(typeof t!=="number"||!Number.isInteger(t))return!1;if(t>this.max||t<0)return!1;return!0}}class f extends u{static FILE_TYPE_JPEG="jpeg";static FILE_TYPE_GIF="gif";static FILE_TYPE_PNG="png";static FILE_TYPE_GZIP="gz";types={[f.FILE_TYPE_JPEG]:[255,216,255],[f.FILE_TYPE_GIF]:[71,73,70],[f.FILE_TYPE_PNG]:[137,80,78,71,13,10],[f.FILE_TYPE_GZIP]:[31,139]};allowed;constructor(t){super();for(let e of t)if(!(e in this.types))throw Error("Unknown file mime type");this.allowed=t}getDescription(){return"File mime-type is not allowed "}async isValid(t){try{let e=Math.max(...Object.values(this.types).map((i)=>i.length)),r=await Bun.file(t).slice(0,e).bytes();return this.allowed.some((i)=>{let n=this.types[i];return n!==void 0&&n.every((s,o)=>r[o]===s)})}catch{return!1}}}import{promises as z}from"fs";class M extends u{getDescription(){return"Not a valid upload file"}async isValid(t){if(typeof t!=="string")return!1;try{return(await z.stat(t)).isFile()}catch{return!1}}}import{mkdir as R,readdir as x,rm as E,statfs as F}from"fs/promises";import l from"path";var I=".part.";class V extends y{root;constructor(t=""){super();this.root=t}getName(){return"Local Storage"}getType(){return d.DEVICE_LOCAL}getDescription(){return"Adapter for local storage on the physical or virtual machine, or mounted to it."}getRoot(){return this.root}getPath(t,e){return this.getAbsolutePath(l.join(this.root,t))}async upload(t,e,r=1,i=1,n={}){if(i<=1){await this.createDirectory(l.dirname(e));try{return await this.rename(t,e),1}catch{throw new a("UPLOAD_FAILED",`Can't upload file ${e}`,e)}}let s=m(e);await this.createDirectory(s);let o=T(e,r);try{await this.rename(t,o)}catch{throw new a("UPLOAD_FAILED",`Failed to write chunk ${r}`,e)}let c=(await this.listParts(e)).length;if(c===i)await this.joinChunks(e,i);return c}async uploadData(t,e,r,i=1,n=1,s={}){if(n<=1)return await this.write(e,t)?1:0;let o=m(e);await this.createDirectory(o);try{await Bun.write(T(e,i),t)}catch{throw new a("UPLOAD_FAILED",`Failed to write chunk ${i}`,e)}let c=(await this.listParts(e)).length;if(c===n)await this.joinChunks(e,n);return c}async abort(t,e=""){let r=m(t);if(await Bun.file(t).exists())await Bun.file(t).delete();if(!await w(r))throw new a("FILE_NOT_FOUND",`No staged upload for ${t}`,t);return await E(r,{recursive:!0,force:!0}),!0}async read(t,e=0,r){let i=Bun.file(t);if(!await i.exists())throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);let n=r===void 0?e>0?i.slice(e):i:i.slice(e,e+r);return Buffer.from(await n.arrayBuffer())}async write(t,e,r=""){try{return await this.createDirectory(l.dirname(t)),await Bun.write(Bun.file(t),e),!0}catch{throw new a("WRITE_FAILED",`Can't write to path ${t}`,t)}}async move(t,e){if(t===e||!await G(t))return!1;try{return await this.createDirectory(l.dirname(e)),await this.rename(t,e),!0}catch{return!1}}async delete(t,e=!1){try{let r=Bun.file(t);if(await r.exists())return await r.delete(),!0;if(e&&await w(t))return await E(t,{recursive:!0}),!0;return!1}catch{return!1}}async deletePath(t){if(!await w(t))return!1;return await E(t,{recursive:!0,force:!0}),!0}async exists(t){return Bun.file(t).exists()}async getFileSize(t){let e=Bun.file(t).size;if(e===-1&&!await Bun.file(t).exists())throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);return e}async getFileMimeType(t){return A(Bun.file(t).type)}async getFileHash(t){let e=new Bun.CryptoHasher("md5");return e.update(await Bun.file(t).bytes()),e.digest("hex")}async stat(t){let e=Bun.file(t);if(!await e.exists())throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);return{size:e.size,mimeType:A(e.type)}}async createDirectory(t){try{return await R(t,{recursive:!0,mode:493}),!0}catch{return!1}}async getDirectorySize(t){let e=0,r;try{r=await this.getFiles(t)}catch{return-1}for(let i of r)if(await w(i.key))e+=await this.getDirectorySize(i.key);else e+=Bun.file(i.key).size;return e}async getPartitionFreeSpace(){let t=await F(this.root||".");return t.bavail*t.bsize}async getPartitionTotalSpace(){let t=await F(this.root||".");return t.blocks*t.bsize}async getFiles(t){try{return(await x(t,{withFileTypes:!0})).map((r)=>({key:l.join(t,r.name),size:r.isFile()?Bun.file(l.join(t,r.name)).size:void 0}))}catch{return[]}}async joinChunks(t,e){let i=Bun.file(t).writer();for(let n=1;n<=e;n++){let s=T(t,n);i.write(await Bun.file(s).bytes())}await i.end(),await E(m(t),{recursive:!0,force:!0})}async listParts(t){let e=m(t),r=`${l.parse(t).name}${I}`;try{return(await x(e)).filter((n)=>n.startsWith(r))}catch{return[]}}async rename(t,e){await import("fs").then((r)=>r.promises.rename(t,e))}}function m(t){return l.join(l.dirname(t),`tmp_${l.basename(t)}`)}function T(t,e){return l.join(m(t),`${l.parse(t).name}${I}${e}`)}async function G(t){return Bun.file(t).exists()}async function w(t){try{let{stat:e}=await import("fs/promises");return(await e(t)).isDirectory()}catch{return!1}}function A(t){return t.split(";")[0]?.trim()??""}var{S3Client:W}=globalThis.Bun;var b=".part-",P=(t)=>String(t).padStart(5,"0"),O=(t)=>t.replace(/^"|"$/g,""),v=(t)=>t.split(";")[0]?.trim()??"";class g extends y{static US_EAST_1="us-east-1";static US_EAST_2="us-east-2";static US_WEST_1="us-west-1";static US_WEST_2="us-west-2";static AF_SOUTH_1="af-south-1";static AP_EAST_1="ap-east-1";static AP_SOUTH_1="ap-south-1";static AP_NORTHEAST_1="ap-northeast-1";static AP_NORTHEAST_2="ap-northeast-2";static AP_NORTHEAST_3="ap-northeast-3";static AP_SOUTHEAST_1="ap-southeast-1";static AP_SOUTHEAST_2="ap-southeast-2";static CA_CENTRAL_1="ca-central-1";static EU_CENTRAL_1="eu-central-1";static EU_WEST_1="eu-west-1";static EU_WEST_2="eu-west-2";static EU_WEST_3="eu-west-3";static EU_SOUTH_1="eu-south-1";static EU_NORTH_1="eu-north-1";static SA_EAST_1="sa-east-1";static CN_NORTH_1="cn-north-1";static CN_NORTHWEST_1="cn-northwest-1";static ME_SOUTH_1="me-south-1";static US_GOV_EAST_1="us-gov-east-1";static US_GOV_WEST_1="us-gov-west-1";static ACL_PRIVATE="private";static ACL_PUBLIC_READ="public-read";static ACL_PUBLIC_READ_WRITE="public-read-write";static ACL_AUTHENTICATED_READ="authenticated-read";static MAX_PAGE_SIZE=1000;client;root;writerOptions;constructor(t){super();let{accessKeyId:e,secretAccessKey:r,bucket:i}=t;if(!e||!r||!i)throw new a("INVALID_CONFIG","S3 requires `accessKeyId`, `secretAccessKey` and `bucket`");this.root=t.root??"",this.writerOptions={partSize:t.partSize,queueSize:t.queueSize,retry:t.retry},this.client=new W({accessKeyId:e,secretAccessKey:r,bucket:i,region:t.region,acl:t.acl,endpoint:t.endpoint,sessionToken:t.sessionToken})}getName(){return"S3 Storage"}getType(){return d.DEVICE_S3}getDescription(){return"S3 Bucket Storage drive for AWS or on-premise solutions, powered by Bun's native S3 client"}getRoot(){return this.root}getPath(t,e){return this.root?`${this.root}/${t}`:t}key(t){return this.getPath(t)}async write(t,e,r=""){try{return await this.client.write(this.key(t),e,r?{type:r}:{}),!0}catch(i){throw new a("WRITE_FAILED",`Failed to write ${t}: ${_(i)}`,t)}}async read(t,e=0,r){let i=this.client.file(this.key(t)),n=r===void 0?e>0?i.slice(e):i:i.slice(e,e+r);try{return Buffer.from(await n.bytes())}catch(s){if(N(s))throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);throw new a("READ_FAILED",`Failed to read ${t}: ${_(s)}`,t)}}async upload(t,e,r=1,i=1,n={}){let s=Bun.file(t);if(!await s.exists())throw new a("FILE_NOT_FOUND",`Source file not found: ${t}`,t);let o=Buffer.from(await s.arrayBuffer()),c=typeof n.contentType==="string"&&n.contentType.length>0?n.contentType:v(s.type)||"application/octet-stream";return this.uploadData(o,e,c,r,i,n)}async uploadData(t,e,r,i=1,n=1,s={}){if(n<=1)return await this.write(e,t,r),1;let o=this.key(e),c=`${o}${b}${P(i)}`;await this.client.write(c,t,{type:r});let p=Number(s.receivedChunks??0)+1;if(s.receivedChunks=p,p<n)return p;try{let h=this.client.file(o,{type:r}).writer({...this.writerOptions});for(let S=1;S<=n;S++)h.write(await this.client.file(`${o}${b}${P(S)}`).bytes());await h.end()}catch(h){throw new a("UPLOAD_FAILED",`Failed to assemble chunks for ${e}: ${_(h)}`,e)}finally{await this.deleteStagedParts(o,n)}return p}async abort(t,e=""){let r=`${this.key(t)}${b}`,n=(await this.client.list({prefix:r,maxKeys:g.MAX_PAGE_SIZE})).contents??[];if(n.length===0)throw new a("FILE_NOT_FOUND",`No staged upload found for ${t}`,t);return await Promise.all(n.map((s)=>this.client.delete(s.key).catch(()=>{return}))),!0}async delete(t,e=!1){try{return await this.client.delete(this.key(t)),!0}catch(r){throw new a("DELETE_FAILED",`Failed to delete ${t}: ${_(r)}`,t)}}async deletePath(t){let e=`${this.key(t)}/`,r;do{let i=await this.client.list({prefix:e,maxKeys:g.MAX_PAGE_SIZE,continuationToken:r}),n=(i.contents??[]).map((s)=>s.key);await Promise.all(n.map((s)=>this.client.delete(s))),r=i.isTruncated?i.nextContinuationToken:void 0}while(r);return!0}async exists(t){return this.client.exists(this.key(t))}async stat(t){try{let e=await this.client.stat(this.key(t));return{size:e.size,mimeType:v(e.type),etag:e.etag?O(e.etag):void 0,lastModified:e.lastModified}}catch(e){if(N(e))throw new a("FILE_NOT_FOUND",`File not found: ${t}`,t);throw e}}async getFileSize(t){return(await this.stat(t)).size}async getFileMimeType(t){return(await this.stat(t)).mimeType}async getFileHash(t){return(await this.stat(t)).etag??""}presign(t,e={}){return this.client.presign(this.key(t),e)}async createDirectory(t){return!0}async getDirectorySize(t){let e=0,r;do{let i=await this.client.list({prefix:`${this.key(t)}/`,maxKeys:g.MAX_PAGE_SIZE,continuationToken:r});for(let n of i.contents??[])e+=n.size??0;r=i.isTruncated?i.nextContinuationToken:void 0}while(r);return e}async getPartitionFreeSpace(){return-1}async getPartitionTotalSpace(){return-1}async getFiles(t,e=g.MAX_PAGE_SIZE,r=""){return((await this.client.list({prefix:t?`${this.key(t)}/`:`${this.root}/`,maxKeys:Math.min(e,g.MAX_PAGE_SIZE),continuationToken:r||void 0})).contents??[]).map((n)=>({key:n.key,size:n.size,lastModified:n.lastModified?new Date(n.lastModified):void 0,etag:n.eTag?O(n.eTag):void 0}))}async deleteStagedParts(t,e){await Promise.all(Array.from({length:e},(r,i)=>this.client.delete(`${t}${b}${P(i+1)}`).catch(()=>{return})))}}function _(t){return t instanceof Error?t.message:String(t)}function N(t){let e=t?.code;if(typeof e==="string"&&e.toLowerCase().includes("nosuchkey"))return!0;let r=_(t).toLowerCase();return r.includes("404")||r.includes("nosuchkey")||r.includes("does not exist")}class L extends g{static US_WEST_1="us-west-1";static US_CENTRAL_1="us-central-1";static US_EAST_1="us-east-1";static US_EAST_2="us-east-2";static AP_NORTHEAST_1="ap-northeast-1";static AP_NORTHEAST_2="ap-northeast-2";static EU_CENTRAL_1="eu-central-1";static EU_CENTRAL_2="eu-central-2";static EU_WEST_1="eu-west-1";static EU_WEST_2="eu-west-2";constructor(t){let e=t.region??L.EU_CENTRAL_1;super({...t,region:e,endpoint:`https://s3.${e}.wasabisys.com`})}getName(){return"Wasabi Storage"}getDescription(){return"Wasabi hot cloud storage drive, powered by Bun's native S3 client"}getType(){return d.DEVICE_WASABI}}class Y extends g{constructor(t){let{endpoint:e="localhost:9000",useSSL:r=!1,...i}=t,n=e.replace(/^https?:\/\//,"");super({...i,region:i.region??g.US_EAST_1,endpoint:`${r?"https":"http"}://${n}`})}getName(){return"MinIO Storage"}getDescription(){return"MinIO S3-compatible object storage server, powered by Bun's native S3 client"}getType(){return d.DEVICE_MINIO}}export{y as Device,k as File,B as FileExt,C as FileName,U as FileSize,f as FileType,V as Local,Y as MinIO,g as S3,d as Storage,a as StorageError,M as Upload,L as Wasabi};
|
package/dist/src/device/s3.d.ts
CHANGED
|
@@ -77,6 +77,7 @@ export declare class S3 extends Device {
|
|
|
77
77
|
uploadData(data: string | Buffer, path: string, contentType: string, chunk?: number, chunks?: number, metadata?: DeviceMetadata): Promise<number>;
|
|
78
78
|
/**
|
|
79
79
|
* Abort a chunked upload: remove any staged part objects.
|
|
80
|
+
* Throws FILE_NOT_FOUND when no staged parts exist.
|
|
80
81
|
*/
|
|
81
82
|
abort(path: string, _extra?: string): Promise<boolean>;
|
|
82
83
|
delete(path: string, _recursive?: boolean): Promise<boolean>;
|
|
@@ -96,9 +97,5 @@ export declare class S3 extends Device {
|
|
|
96
97
|
* Remove staged part objects after assembly or failure.
|
|
97
98
|
*/
|
|
98
99
|
private deleteStagedParts;
|
|
99
|
-
/**
|
|
100
|
-
* List and delete all staged parts matching a prefix (used by `abort`).
|
|
101
|
-
*/
|
|
102
|
-
private deleteStagedPartsByPrefix;
|
|
103
100
|
}
|
|
104
101
|
//# sourceMappingURL=s3.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../src/device/s3.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../src/device/s3.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuvix/storage",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "S3-compatible storage library for the Nuvix BaaS platform, built on Bun's native APIs. Supports AWS S3, Wasabi, MinIO, and local storage.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"clean": "rm -rf dist",
|
|
25
25
|
"prepublishOnly": "bun run clean && bun run build",
|
|
26
26
|
"test": "bun test",
|
|
27
|
+
"test:integration": "bun test __tests__/integration",
|
|
27
28
|
"test:watch": "bun test --watch",
|
|
28
29
|
"test:coverage": "bun test --coverage",
|
|
29
30
|
"lint": "oxlint src __tests__ index.ts",
|