@telestack/storage 1.2.0 → 1.3.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 +83 -84
- package/dist/index.d.mts +597 -116
- package/dist/index.d.ts +597 -116
- package/dist/index.global.js +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +5 -4
package/dist/index.global.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var TelestackStorageSetup=(()=>{var U=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var S=(l,t)=>{for(var e in t)U(l,e,{get:t[e],enumerable:!0})},M=(l,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of x(t))!C.call(l,r)&&r!==e&&U(l,r,{get:()=>t[r],enumerable:!(s=B(t,r))||s.enumerable});return l};var E=l=>M(U({},"__esModule",{value:!0}),l);var O={};S(O,{BatchBuilder:()=>b,CryptoHelper:()=>d,DirRef:()=>y,FileRef:()=>f,HttpClient:()=>_,ImageHelper:()=>g,QueryBuilder:()=>v,StorageRef:()=>w,TelestackError:()=>u,TelestackStorage:()=>T,UploadTask:()=>m});var _=class{baseUrl;tenantId;headers;constructor(t){this.baseUrl=t.baseUrl.replace(/\/$/,""),this.tenantId=t.tenantId,this.headers={"Content-Type":"application/json","X-Tenant-ID":t.tenantId},t.apiKey?this.headers["X-API-Key"]=t.apiKey:t.token&&(this.headers.Authorization=`Bearer ${t.token}`)}async _fetchWithRetry(t,e,s=3){for(let r=0;r<=s;r++)try{let i=await fetch(t,e);if((i.status===429||i.status>=500)&&r<s){let a=Math.min(1e3*Math.pow(2,r),1e4);console.warn(`[TelestackStorage] Network error ${i.status}. Retrying in ${a}ms...`),await new Promise(n=>setTimeout(n,a));continue}return i}catch(i){if(r<s){let a=Math.min(1e3*Math.pow(2,r),1e4);console.warn(`[TelestackStorage] Network un-reachable. Retrying in ${a}ms...`),await new Promise(n=>setTimeout(n,a));continue}throw i}throw new Error("Unreachable code in retry logic")}async get(t,e){let s=new URL(`${this.baseUrl}${t}`);e&&Object.entries(e).forEach(([i,a])=>{a!==void 0&&s.searchParams.set(i,a)});let r=await this._fetchWithRetry(s.toString(),{headers:this.headers});return this._handle(r)}async post(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"POST",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async patch(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PATCH",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async put(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PUT",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async delete(t){let e=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"DELETE",headers:this.headers});return this._handle(e)}async uploadToPresignedUrl(t,e,s,r){for(let a=0;a<=3;a++)try{await new Promise((n,h)=>{let o=new XMLHttpRequest;o.open("PUT",t),o.setRequestHeader("Content-Type",s),r&&(o.upload.onprogress=p=>{p.lengthComputable&&r(Math.round(p.loaded/p.total*100))}),o.onload=()=>{o.status>=200&&o.status<300?n():o.status===429||o.status>=500?h(new Error(`Retryable network error: ${o.status}`)):h(new u(`Upload failed: ${o.status}`,o.status))},o.onerror=()=>h(new Error("Retryable network error")),o.send(e)});return}catch(n){if(n instanceof u||a>=3)throw n;let h=Math.min(1e3*Math.pow(2,a),1e4);console.warn(`[TelestackStorage] Upload chunk failed. Retrying in ${h}ms...`),await new Promise(o=>setTimeout(o,h))}}async _handle(t){let e=await t.json();if(!t.ok)throw new u(e.error||t.statusText,t.status,e.code);return e}},u=class extends Error{constructor(e,s,r){super(e);this.status=s;this.code=r;this.name="TelestackError"}};var d=class{static async generateKey(){let t=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"]),e=await crypto.subtle.exportKey("raw",t);return btoa(String.fromCharCode(...new Uint8Array(e)))}static async encrypt(t,e){let s=await this._importKey(e),r=crypto.getRandomValues(new Uint8Array(12)),i=await t.arrayBuffer(),a=await crypto.subtle.encrypt({name:"AES-GCM",iv:r},s,i);return new Blob([r,a],{type:"application/octet-stream"})}static async decrypt(t,e,s="application/octet-stream"){let r=await this._importKey(e),i=await t.arrayBuffer(),a=i.slice(0,12),n=i.slice(12),h=await crypto.subtle.decrypt({name:"AES-GCM",iv:new Uint8Array(a)},r,n);return new Blob([h],{type:s})}static async _importKey(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0));return crypto.subtle.importKey("raw",e,{name:"AES-GCM"},!1,["encrypt","decrypt"])}};var g=class{static async compress(t,e={}){if(!t.type.startsWith("image/"))return t;let{maxWidth:s=1920,maxHeight:r=1080,quality:i=.8}=e;return new Promise((a,n)=>{let h=new Image,o=URL.createObjectURL(t);h.onload=()=>{URL.revokeObjectURL(o);let p=h.width,c=h.height;p>s&&(c=Math.round(c*s/p),p=s),c>r&&(p=Math.round(p*r/c),c=r);let R=document.createElement("canvas");R.width=p,R.height=c;let P=R.getContext("2d");if(!P)return n(new Error("Failed to get canvas 2d context for image compression"));P.drawImage(h,0,0,p,c);let k=t.type==="image/png"?"image/png":"image/webp";R.toBlob(I=>{I?a(I):n(new Error("Canvas toBlob failed"))},k,i)},h.onerror=()=>{URL.revokeObjectURL(o),n(new Error("Failed to load image for compression"))},h.src=o})}};var m=class{constructor(t,e,s,r,i,a){this.client=t;this.path=e;this.name=r;this.contentType=i;this.options=a;this._data=s,this._totalBytes=s.size,this._promise=new Promise((n,h)=>{this._resolve=n,this._reject=h}),this._start()}_state="processing";_bytesTransferred=0;_totalBytes=0;_data;_observers=[];_promise;_resolve;_reject;_uploadId;_parts=[];_currentPartIndex=0;_activeXhr;_isResumable=!1;_simpleFileMetadata;on(t,e,s,r){let i=typeof e=="function"?{next:e,error:s,complete:r}:e||{error:s,complete:r};return this._observers.push(i),i.next&&i.next(this.snapshot),()=>{this._observers=this._observers.filter(a=>a!==i)}}pause(){return this._state!=="running"?!1:(this._state="paused",this._activeXhr&&(this._activeXhr.abort(),this._activeXhr=void 0),this._notifyObservers(),!0)}resume(){return this._state!=="paused"?!1:(this._state="running",this._notifyObservers(),this._isResumable?this._continueResumable().catch(this._handleError):(this._bytesTransferred=0,this._notifyObservers(),this._startSimple().catch(this._handleError)),!0)}cancel(){if(this._state==="success"||this._state==="error"||this._state==="canceled")return!1;this._state="canceled",this._activeXhr&&(this._activeXhr.abort(),this._activeXhr=void 0),this._isResumable&&this._uploadId?this.client.post("/files/resumable/abort",{path:this.path,uploadId:this._uploadId}).catch(()=>{}):!this._isResumable&&this._simpleFileMetadata&&this.client.delete(`/files/${encodeURIComponent(this.path)}`).catch(()=>{}),this._notifyObservers();let t=new Error("Upload canceled by user");return t.name="UploadCanceled",this._handleError(t),!0}get snapshot(){return{bytesTransferred:this._bytesTransferred,totalBytes:this._totalBytes,state:this._state,task:this}}then(t,e){return this._promise.then(t,e)}catch(t){return this._promise.catch(t)}finally(t){return this._promise.finally(t)}[Symbol.toStringTag]="UploadTask";async _start(){try{if(this.options.compressImage&&this._data.type.startsWith("image/")&&(this._data=await g.compress(this._data,this.options.compressImage)),this.options.encryptionKey&&(this._data=await d.encrypt(this._data,this.options.encryptionKey)),this._totalBytes=this._data.size,this._state!=="processing")return;this._state="running",this._notifyObservers();let t=50*1024*1024;this._isResumable=this._totalBytes>=t,this._isResumable?await this._startResumable():await this._startSimple()}catch(t){this._handleError(t)}}async _startSimple(){let t=await this.client.post("/files/upload-url",{path:this.path,name:this.name,size:this._totalBytes,contentType:this.contentType,userId:this.options.userId,metadata:this.options.metadata});if(this._simpleFileMetadata=t.file,this._state!=="running")return;let e=new XMLHttpRequest;this._activeXhr=e,await new Promise((s,r)=>{e.open("PUT",t.uploadUrl),e.setRequestHeader("Content-Type",this.contentType),e.upload.onprogress=i=>{i.lengthComputable&&this._state==="running"&&(this._bytesTransferred=i.loaded,this._notifyObservers())},e.onload=()=>{e.status>=200&&e.status<300?s():r(new Error(`Upload failed: ${e.status} ${e.responseText}`))},e.onerror=()=>r(new Error("Network error during upload")),e.onabort=()=>r(new Error("Upload aborted")),e.send(this._data)}),this._activeXhr=void 0,this._state==="running"&&(await this.client.post("/files/complete-upload",{path:this.path}),this._bytesTransferred=this._totalBytes,this._state="success",this._notifyObservers(),this._resolve({file:t.file,resumable:!1}))}async _startResumable(){let t=await this.client.post("/files/resumable/init",{path:this.path,name:this.name,size:this._totalBytes,contentType:this.contentType,userId:this.options.userId,metadata:this.options.metadata});this._uploadId=t.uploadId,this._simpleFileMetadata=t.file,await this._continueResumable()}async _continueResumable(){if(!this._uploadId||!this._simpleFileMetadata)return;let t=this.options.chunkSize??10*1024*1024,e=Math.ceil(this._totalBytes/t);try{for(;this._currentPartIndex<e;){if(this._state!=="running")return;let s=this._currentPartIndex*t,r=Math.min(s+t,this._totalBytes),i=this._data.slice(s,r),a=await this.client.post("/files/resumable/part-url",{path:this.path,uploadId:this._uploadId,partNumber:this._currentPartIndex+1});if(this._state!=="running")return;let n=await this._uploadChunkWithProgress(a.uploadUrl,i,s);this._parts.push({PartNumber:this._currentPartIndex+1,ETag:n}),this._currentPartIndex++,this._bytesTransferred=Math.min(this._currentPartIndex*t,this._totalBytes),this._notifyObservers()}this._state==="running"&&(await this.client.post("/files/resumable/complete",{path:this.path,uploadId:this._uploadId,parts:this._parts}),this._state="success",this._notifyObservers(),this._resolve({file:this._simpleFileMetadata,resumable:!0}))}catch(s){if(s.message==="Upload aborted")return;this._handleError(s)}}_uploadChunkWithProgress(t,e,s){return new Promise((r,i)=>{let a=new XMLHttpRequest;this._activeXhr=a,a.open("PUT",t),a.setRequestHeader("Content-Type",this.contentType),a.upload.onprogress=n=>{n.lengthComputable&&this._state==="running"&&(this._bytesTransferred=s+n.loaded,this._notifyObservers())},a.onload=()=>{if(a.status>=200&&a.status<300){let n=a.getResponseHeader("ETag")||"";r(n.replace(/"/g,""))}else i(new Error(`Part upload failed: ${a.status}`))},a.onerror=()=>i(new Error("Network error during part upload")),a.onabort=()=>i(new Error("Upload aborted")),a.send(e)})}_notifyObservers(){let t=this.snapshot;this._observers.forEach(e=>{t.state==="success"&&e.complete?e.complete():e.next&&e.next(t)})}_handleError=t=>{this._state!=="canceled"&&(this._state="error",this._activeXhr=void 0,this._notifyObservers(),this._observers.forEach(e=>e.error?.(t)),this._reject(t))}};var w=class{constructor(t,e,s){this.client=t;this.path=e;this.tenantId=s}child(t){let s=(this.path?this.path.endsWith("/")?this.path:this.path+"/":"")+t.replace(/^\//,"");return s.endsWith("/")?new y(this.client,s,this.tenantId):new f(this.client,s,this.tenantId)}},y=class extends w{async listAll(t=100){return(await this.client.get("/files",{prefix:this.path,limit:String(t)})).files}},f=class extends w{put(t,e){let s=(t instanceof File,t.size),r=t instanceof File?t.name:this.path.split("/").pop()||"file",i=t.type||"application/octet-stream";return new m(this.client,this.path,t,r,i,e)}putBytes(t,e,s){let r=this.path.split("/").pop()||"file",i=new Blob([t],{type:e});return new m(this.client,this.path,i,r,e,s)}async getDownloadUrl(t){return t?.versionId?(await this.client.get(`/files/version-url/${encodeURIComponent(this.path)}`,{versionId:t.versionId})).downloadUrl:(await this.client.get(`/files/download-url/${encodeURIComponent(this.path)}`)).downloadUrl}async getDecryptedBlob(t){let e=await this.getDownloadUrl(),s=await fetch(e);if(!s.ok)throw new Error(`Failed to download file from S3: ${s.status}`);let r=await s.blob(),i=await this.getMetadata();return await d.decrypt(r,t,i.content_type)}async getMetadata(){return(await this.client.get(`/files/metadata/${encodeURIComponent(this.path)}`)).file}async updateMetadata(t){return(await this.client.patch(`/files/metadata/${encodeURIComponent(this.path)}`,{metadata:t})).file}async delete(){await this.client.delete(`/files/${encodeURIComponent(this.path)}`)}async listVersions(){return this.client.get(`/files/versions/${encodeURIComponent(this.path)}`)}async getVersionUrl(t){return(await this.client.get(`/files/version-url/${encodeURIComponent(this.path)}`,{versionId:t})).downloadUrl}async getTags(){return(await this.client.get(`/files/tags/${encodeURIComponent(this.path)}`)).tags}async setTags(t){await this.client.put(`/files/tags/${encodeURIComponent(this.path)}`,{tags:t})}async setLegalHold(t){await this.client.post(`/files/legal-hold/${encodeURIComponent(this.path)}`,{status:t})}};var b=class{constructor(t){this.client=t}_op=null;_paths=[];_dest="";delete(t){return this._op="delete",this._paths=t,this}copy(t,e){return this._op="copy",this._paths=t,this._dest=e,this}move(t,e){return this._op="move",this._paths=t,this._dest=e,this}async run(){if(!this._op)throw new Error("No batch operation specified");if(this._op==="delete")return this.client.post("/files/batch/delete",{paths:this._paths});let t={sourcePaths:this._paths,destinationPrefix:this._dest};return this.client.post(`/files/batch/${this._op}`,t)}};var v=class{constructor(t){this.client=t}_query="";_metadata={};_limit=20;nameContains(t){return this._query=t,this}where(t,e){return this._metadata[t]=e,this}limit(t){return this._limit=t,this}async get(){let t={q:this._query||void 0,limit:String(this._limit),metadata:Object.keys(this._metadata).length>0?JSON.stringify(this._metadata):void 0};return(await this.client.get("/files/search",t)).files}};var T=class{constructor(t){this.config=t;this.http=new _(t),this.tenantId=t.tenantId}http;tenantId;ref(t){if(t.endsWith("/"))throw new Error("Use .dir() for directory references");return new f(this.http,t,this.tenantId)}dir(t){let e=t.endsWith("/")?t:t+"/";return new y(this.http,e,this.tenantId)}async list(t){let e={prefix:t?.prefix||"",limit:String(t?.limit||100)};return(await this.http.get("/files",e)).files}async rename(t,e){return this.http.post("/files/rename",{oldPath:t,newName:e})}batch(){return new b(this.http)}query(){return new v(this.http)}async generateApiKey(t){return this.http.post("/internal/keys/generate",{name:t})}async revokeApiKey(t){await this.http.post("/internal/keys/revoke",{keyId:t})}async getBucketInfo(){return this.http.get("/internal/bucket-info")}};return E(O);})();
|
|
1
|
+
"use strict";var TelestackStorageSetup=(()=>{var R=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var x=(h,t)=>{for(var e in t)R(h,e,{get:t[e],enumerable:!0})},E=(h,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of B(t))!M.call(h,i)&&i!==e&&R(h,i,{get:()=>t[i],enumerable:!(s=S(t,i))||s.enumerable});return h};var O=h=>E(R({},"__esModule",{value:!0}),h);var $={};x($,{BatchBuilder:()=>w,CryptoHelper:()=>p,DirRef:()=>y,FileRef:()=>m,HttpClient:()=>g,ImageHelper:()=>_,QueryBuilder:()=>v,StorageRef:()=>b,TelestackError:()=>c,TelestackStorage:()=>P,UploadTask:()=>f});var c=class extends Error{constructor(e,s,i){super(e);this.status=s;this.code=i;this.name="TelestackError"}};var g=class{baseUrl;tenantId;headers;defaultUploadOptions;constructor(t){this.baseUrl=t.baseUrl.replace(/\/$/,""),this.tenantId=t.tenantId,this.defaultUploadOptions=t.defaultUploadOptions,this.headers={"Content-Type":"application/json","X-Tenant-ID":t.tenantId,"X-SDK-Platform":"Web","X-SDK-Version":"1.3.0"},t.apiKey?this.headers["X-API-Key"]=t.apiKey:t.token&&(this.headers.Authorization=`Bearer ${t.token}`)}async _fetchWithRetry(t,e,s=3){let i=null;for(let n=0;n<=s;n++)try{let r=await fetch(t,e);if(r.status>=200&&r.status<300)return r;if((r.status===429||r.status>=500)&&n<s){let a=Math.min(1e3*Math.pow(2,n),15e3),l=Math.random()*1e3,o=a+l;console.warn(`[Telestack] ${r.status} on ${t}. Retrying in ${Math.round(o)}ms (Attempt ${n+1}/${s})...`),await new Promise(d=>setTimeout(d,o));continue}return r}catch(r){if(i=r,n<s){let a=Math.min(1e3*Math.pow(2,n),15e3),l=Math.random()*1e3,o=a+l;console.warn(`[Telestack] Network unreachable. Retrying in ${Math.round(o)}ms...`),await new Promise(d=>setTimeout(d,o));continue}throw r}throw i||new Error("Unreachable code in retry logic")}async get(t,e){let s=new URL(`${this.baseUrl}${t}`);e&&Object.entries(e).forEach(([n,r])=>{r!==void 0&&s.searchParams.set(n,r)});let i=await this._fetchWithRetry(s.toString(),{headers:this.headers});return this._handle(i)}async post(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"POST",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async patch(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PATCH",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async put(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PUT",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async delete(t){let e=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"DELETE",headers:this.headers});return this._handle(e)}async uploadToPresignedUrl(t,e,s,i){for(let r=0;r<=3;r++)try{await new Promise((a,l)=>{let o=new XMLHttpRequest;o.open("PUT",t),o.setRequestHeader("Content-Type",s),i&&(o.upload.onprogress=d=>{d.lengthComputable&&i(Math.round(d.loaded/d.total*100))}),o.onload=()=>{o.status>=200&&o.status<300?a():o.status===429||o.status>=500?l(new Error(`Retryable network error: ${o.status}`)):l(new c(`Upload failed: ${o.status}`,o.status))},o.onerror=()=>l(new Error("Retryable network error")),o.send(e)});return}catch(a){if(a instanceof c||r>=3)throw a;let l=Math.min(1e3*Math.pow(2,r),1e4);console.warn(`[TelestackStorage] Upload chunk failed. Retrying in ${l}ms...`),await new Promise(o=>setTimeout(o,l))}}async _handle(t){if(!(t.headers.get("content-type")||"").includes("application/json")){let i=await t.text();if(!t.ok)throw new c(i||t.statusText,t.status);return i}let s=await t.json();if(!t.ok)throw new c(s.error||t.statusText,t.status,s.code);return s}};var p=class{static async generateKey(){let t=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"]),e=await crypto.subtle.exportKey("raw",t);return btoa(String.fromCharCode(...new Uint8Array(e)))}static async encrypt(t,e){let s=await this._importKey(e),i=crypto.getRandomValues(new Uint8Array(12)),n=await t.arrayBuffer(),r=await crypto.subtle.encrypt({name:"AES-GCM",iv:i},s,n);return new Blob([i,r],{type:"application/octet-stream"})}static async decrypt(t,e,s="application/octet-stream"){let i=await this._importKey(e),n=await t.arrayBuffer(),r=n.slice(0,12),a=n.slice(12),l=await crypto.subtle.decrypt({name:"AES-GCM",iv:new Uint8Array(r)},i,a);return new Blob([l],{type:s})}static async _importKey(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0));return crypto.subtle.importKey("raw",e,{name:"AES-GCM"},!1,["encrypt","decrypt"])}};var _=class{static async compress(t,e={}){if(!t.type.startsWith("image/"))return t;let{maxWidth:s=1920,maxHeight:i=1080,quality:n=.8}=e;return new Promise((r,a)=>{let l=new Image,o=URL.createObjectURL(t);l.onload=()=>{URL.revokeObjectURL(o);let d=l.width,u=l.height;d>s&&(u=Math.round(u*s/d),d=s),u>i&&(d=Math.round(d*i/u),u=i);let I=document.createElement("canvas");I.width=d,I.height=u;let T=I.getContext("2d");if(!T)return a(new Error("Failed to get canvas 2d context for image compression"));T.drawImage(l,0,0,d,u);let U=t.type==="image/png"?"image/png":"image/webp";I.toBlob(k=>{k?r(k):a(new Error("Canvas toBlob failed"))},U,n)},l.onerror=()=>{URL.revokeObjectURL(o),a(new Error("Failed to load image for compression"))},l.src=o})}};var C=50*1024*1024,f=class{constructor(t,e,s,i,n,r,a){this.client=t;this.path=e;this.name=i;this.contentType=n;this.options=r;this.tenantId=a;this._data=s,this._totalBytes=s.size,this._promise=new Promise((l,o)=>{this._resolve=l,this._reject=o}),this._start()}_state="processing";_bytesTransferred=0;_totalBytes=0;_data;_observers=[];_promise;_resolve;_reject;_uploadId;_fileId;_versionId;_storageKey;_parts=[];_currentPartIndex=0;_isMultipart=!1;_tier;_activeXhr;on(t,e,s,i){let n=typeof e=="function"?{next:e,error:s,complete:i}:e||{error:s,complete:i};return this._observers.push(n),n.next&&n.next(this.snapshot),()=>{this._observers=this._observers.filter(r=>r!==n)}}pause(){return this._state!=="running"?!1:(this._state="paused",this._activeXhr?.abort(),this._activeXhr=void 0,this._notifyObservers(),!0)}resume(){return this._state!=="paused"?!1:(this._state="running",this._notifyObservers(),this._isMultipart?this._continueMultipart().catch(this._handleError):(this._bytesTransferred=0,this._startSimple().catch(this._handleError)),!0)}cancel(){if(this._state==="success"||this._state==="error"||this._state==="canceled")return!1;this._state="canceled",this._activeXhr?.abort(),this._activeXhr=void 0,this._isMultipart&&this._storageKey&&this._uploadId&&this.client.post("/files/resumable/abort",{storage_key:this._storageKey,uploadId:this._uploadId}).catch(()=>{}),this._notifyObservers();let t=Object.assign(new Error("Upload canceled by user"),{name:"UploadCanceled"});return this._handleError(t),!0}get snapshot(){return{bytesTransferred:this._bytesTransferred,totalBytes:this._totalBytes,state:this._state,task:this}}then(t,e){return this._promise.then(t,e)}catch(t){return this._promise.catch(t)}finally(t){return this._promise.finally(t)}[Symbol.toStringTag]="UploadTask";async _start(){try{if(this.options.compressImage&&this._data.type.startsWith("image/")&&(this._data=await _.compress(this._data,this.options.compressImage)),this.options.encryptionKey&&(this._data=await p.encrypt(this._data,this.options.encryptionKey)),this._totalBytes=this._data.size,this._state!=="processing")return;this._state="running",this._notifyObservers(),this._isMultipart=this._totalBytes>=C,this._isMultipart?await this._startMultipart():await this._startSimple()}catch(t){this._handleError(t)}}async _startSimple(){let t=await this.client.post("/files/upload-url",{tenant_id:this.tenantId,directory_id:this.options.directoryId||"root",name:this.name,content_type:this.contentType});if(this._fileId=t.file_id,this._versionId=t.version_id,this._tier=t.tier,this._state!=="running"||(await this._putToPresignedUrl(t.upload_url,this._data),this._state!=="running"))return;let e=await this.client.post(`/files/${t.file_id}/confirm`,{version_id:t.version_id,size:this._totalBytes,storage_key:this._storageKey});this._bytesTransferred=this._totalBytes,this._state="success",this._notifyObservers(),this._resolve({success:!0,file_id:t.file_id,version_id:t.version_id,resumable:!1})}async _startMultipart(){let t=await this.client.post("/files/resumable/init",{tenant_id:this.tenantId,name:this.name,content_type:this.contentType});this._uploadId=t.uploadId,this._fileId=t.fileId,this._storageKey=t.storage_key,await this._continueMultipart()}async _continueMultipart(){if(!this._uploadId||!this._storageKey)return;let t=this.options.chunkSize??10*1024*1024,e=Math.ceil(this._totalBytes/t);try{for(;this._currentPartIndex<e;){if(this._state!=="running")return;let s=this._currentPartIndex*t,i=Math.min(s+t,this._totalBytes),n=this._data.slice(s,i),r=await this.client.post("/files/resumable/part-url",{storage_key:this._storageKey,uploadId:this._uploadId,partNumber:this._currentPartIndex+1});if(this._state!=="running")return;let a=await this._uploadChunkXhr(r.upload_url,n,s);this._parts.push({PartNumber:this._currentPartIndex+1,ETag:a}),this._currentPartIndex++,this._bytesTransferred=Math.min(this._currentPartIndex*t,this._totalBytes),this._notifyObservers()}if(this._state==="running"){let s=await this.client.post("/files/resumable/complete",{fileId:this._fileId,storage_key:this._storageKey,uploadId:this._uploadId,parts:this._parts});this._bytesTransferred=this._totalBytes,this._state="success",this._notifyObservers(),this._resolve({success:!0,file_id:this._fileId,version_id:"",resumable:!0})}}catch(s){if(s.message==="Upload aborted")return;this._handleError(s)}}async _putToPresignedUrl(t,e){let s=await e.arrayBuffer();return new Promise((i,n)=>{let r=new XMLHttpRequest;this._activeXhr=r,r.open("PUT",t),r.setRequestHeader("Content-Type",this.contentType),r.upload.onprogress=a=>{a.lengthComputable&&this._state==="running"&&(this._bytesTransferred=a.loaded,this._notifyObservers())},r.onload=()=>r.status>=200&&r.status<300?i():n(new Error(`Storage upload failed: ${r.status}`)),r.onerror=()=>n(new Error("Network error during upload")),r.onabort=()=>n(new Error("Upload aborted")),r.send(s)})}async _uploadChunkXhr(t,e,s){let i=await e.arrayBuffer();return new Promise((n,r)=>{let a=new XMLHttpRequest;this._activeXhr=a,a.open("PUT",t),a.setRequestHeader("Content-Type",this.contentType),a.upload.onprogress=l=>{l.lengthComputable&&this._state==="running"&&(this._bytesTransferred=s+l.loaded,this._notifyObservers())},a.onload=()=>{a.status>=200&&a.status<300?n(a.getResponseHeader("ETag")?.replace(/"/g,"")||""):r(new Error(`Part upload failed: ${a.status}`))},a.onerror=()=>r(new Error("Network error during part upload")),a.onabort=()=>r(new Error("Upload aborted")),a.send(i)})}_notifyObservers(){let t=this.snapshot;this._observers.forEach(e=>{t.state==="success"&&e.complete?e.complete():e.next&&e.next(t)})}_handleError=t=>{this._state!=="canceled"&&(this._state="error",this._activeXhr=void 0,this._notifyObservers(),this._observers.forEach(e=>e.error?.(t)),this._reject(t))}};var b=class{constructor(t,e,s){this.client=t;this.path=e;this.tenantId=s}child(t){let s=(this.path?this.path.endsWith("/")?this.path:this.path+"/":"")+t.replace(/^\//,"");return s.endsWith("/")?new y(this.client,s,this.tenantId):new m(this.client,s,this.tenantId)}},y=class extends b{async listAll(t){return this.client.get("/files",{tenant_id:this.tenantId,directory_id:this.path||"root",limit:String(t?.limit||100),offset:String(t?.offset||0)})}async create(t){let s=this.path.replace(/\/$/,"").split("/").pop()||this.path;return this.client.post("/directories",{tenant_id:this.tenantId,parent_id:t||null,name:s})}async delete(t){return this.client.delete(`/directories/${t}?tenant_id=${this.tenantId}`)}async search(t){return this.client.get("/files/search",{tenant_id:this.tenantId,q:t.query,ext:t.extension,minSize:t.minSize?String(t.minSize):void 0,limit:String(t.limit||25),offset:String(t.offset||0)})}},m=class extends b{async resolveFileRecord(t){if(t){let i=(await this.client.get("/files",{tenant_id:this.tenantId})).files?.find(n=>n.$id===t);if(!i)throw new Error("File not found");return i}return(await this.client.get("/files/resolve",{tenant_id:this.tenantId,path:this.path})).file}put(t,e={}){let s=typeof File<"u"&&t instanceof File?t.name:this.path.split("/").pop()||"file",i=t.type||"application/octet-stream",n={...this.client.defaultUploadOptions,...e};return new f(this.client,this.path,t,s,i,n,this.tenantId)}putBytes(t,e,s={}){let i=this.path.split("/").pop()||"file",n=new Blob([t],{type:e});return new f(this.client,this.path,n,i,e,s,this.tenantId)}async getDownloadUrl(t,e){let s=e?.mode||"download";return t?(await this.client.get(`/files/${t}/download-url`,{tenant_id:this.tenantId,mode:s})).download_url:(await this.client.get("/files/download-url/by-path",{tenant_id:this.tenantId,path:this.path,mode:s})).download_url}getCdnUrl(t){return`${this.client.baseUrl}/f/${this.tenantId}/${t}`}async getDecryptedBlob(t,e,s){let i=await this.getDownloadUrl(s),n=await fetch(i);if(!n.ok)throw new Error(`Download failed: ${n.status}`);let r=await n.blob();return p.decrypt(r,t,e)}async getMetadata(t){return this.resolveFileRecord(t)}async getVersions(t){let e=await this.resolveFileRecord(t);return(await this.client.get(`/files/${e.$id}/versions`,{tenant_id:this.tenantId})).versions||[]}async delete(t){let e=await this.resolveFileRecord(t);await this.client.delete(`/files/${e.$id}?tenant_id=${this.tenantId}`)}};var w=class{constructor(t,e){this.client=t;this.tenantId=e}_op=null;_fileIds=[];_mappings=[];delete(t){return this._op="delete",this._fileIds=t,this}copy(t){return this._op="copy",this._mappings=t,this}move(t){return this._op="move",this._mappings=t,this}async run(){if(!this._op)throw new Error("No batch operation specified. Call .delete(), .copy(), or .move() first.");return this._op==="delete"?this.client.post("/files/batch/delete",{fileIds:this._fileIds,tenant_id:this.tenantId}):this.client.post(`/files/batch/${this._op}`,{mappings:this._mappings,tenant_id:this.tenantId})}};var v=class{constructor(t,e){this.client=t;this.tenantId=e}_query="";_extension="";_minSize=0;_limit=20;_offset=0;search(t){return this._query=t,this}withExtension(t){return this._extension=t.replace(/^\./,""),this}minSize(t){return this._minSize=t,this}limit(t){return this._limit=t,this}offset(t){return this._offset=t,this}async get(){let t={tenant_id:this.tenantId,q:this._query||void 0,ext:this._extension||void 0,minSize:this._minSize>0?String(this._minSize):void 0,limit:String(this._limit),offset:String(this._offset)};return(await this.client.get("/files/search",t)).files||[]}async run(){return this.get()}};var P=class h{constructor(t){this.config=t;this.http=new g(t),this.tenantId=t.tenantId}http;tenantId;withContext(t){return new h({...this.config,tenantId:t.tenantId??this.config.tenantId,projectId:t.projectId??this.config.projectId,orgId:t.orgId??this.config.orgId})}withAuth(t){let e={...this.config};return t.apiKey!==void 0&&(t.apiKey?e.apiKey=t.apiKey:delete e.apiKey),t.token!==void 0&&(t.token?e.token=t.token:delete e.token),new h(e)}ref(t){return new m(this.http,t,this.tenantId)}dir(t){let e=t.endsWith("/")?t:t+"/";return new y(this.http,e,this.tenantId)}async getBucket(){return(await this.http.get("/buckets",{tenant_id:this.tenantId})).bucket}async list(t){return this.http.get("/files",{tenant_id:this.tenantId,directory_id:t?.directoryId??void 0,limit:String(t?.limit||100),offset:String(t?.offset||0)})}async search(t){return this.http.get("/files/search",{tenant_id:this.tenantId,q:t.query,ext:t.extension,minSize:t.minSize?String(t.minSize):void 0,limit:String(t.limit||25),offset:String(t.offset||0)})}query(){return new v(this.http,this.tenantId)}batch(){return new w(this.http,this.tenantId)}async batchDelete(t){return this.http.post("/files/batch/delete",{fileIds:t,tenant_id:this.tenantId})}async batchCopy(t){return this.http.post("/files/batch/copy",{tenant_id:this.tenantId,mappings:t})}async batchMove(t){return this.http.post("/files/batch/move",{tenant_id:this.tenantId,mappings:t})}async getUsageStats(t=7){return(await this.http.get("/stats/usage",{tenant_id:this.tenantId,days:String(t)})).stats}onEvent(t){let e=new URL(`${this.config.baseUrl}/files/events`);e.searchParams.set("tenant_id",this.tenantId),this.config.apiKey&&e.searchParams.set("apiKey",this.config.apiKey),this.config.token&&e.searchParams.set("jwt",this.config.token);let s=new EventSource(e.toString());return s.addEventListener("file_change",i=>{try{t(JSON.parse(i.data))}catch(n){console.error("[Telestack] SSE parse error:",n)}}),s.onerror=()=>{console.warn("[Telestack] SSE connection lost. Browser will auto-reconnect.")},()=>s.close()}async initDatabase(){return this.http.post("/db/init")}async generateApiKey(t){return this.http.post("/internal/keys/generate",{name:t})}async revokeApiKey(t){await this.http.post("/internal/keys/revoke",{keyId:t})}async getInternalAnalytics(t=this.tenantId){return(await this.http.get(`/internal/analytics/${t}`)).analytics||[]}async getInternalBucketInfo(t=this.tenantId){return this.http.get("/internal/bucket-info",{tenantId:t})}async updateBucketVisibility(t,e=this.tenantId){return this.http.patch("/internal/buckets/settings?tenantId="+encodeURIComponent(e),{isPublic:t})}async listPublicPathRules(t=this.tenantId){return(await this.http.get("/internal/buckets/rules",{tenantId:t})).rules||[]}async addPublicPathRule(t,e=this.tenantId){return(await this.http.post(`/internal/buckets/rules?tenantId=${encodeURIComponent(e)}`,{pathPrefix:t,isPublic:!0})).rule}async deletePublicPathRule(t,e=this.tenantId){await this.http.delete(`/internal/buckets/rules/${encodeURIComponent(t)}?tenantId=${encodeURIComponent(e)}`)}};return O($);})();
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var U=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var S=(l,t)=>{for(var e in t)U(l,e,{get:t[e],enumerable:!0})},M=(l,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of x(t))!C.call(l,r)&&r!==e&&U(l,r,{get:()=>t[r],enumerable:!(s=B(t,r))||s.enumerable});return l};var E=l=>M(U({},"__esModule",{value:!0}),l);var O={};S(O,{BatchBuilder:()=>b,CryptoHelper:()=>d,DirRef:()=>y,FileRef:()=>f,HttpClient:()=>_,ImageHelper:()=>g,QueryBuilder:()=>v,StorageRef:()=>w,TelestackError:()=>u,TelestackStorage:()=>T,UploadTask:()=>m});module.exports=E(O);var _=class{baseUrl;tenantId;headers;constructor(t){this.baseUrl=t.baseUrl.replace(/\/$/,""),this.tenantId=t.tenantId,this.headers={"Content-Type":"application/json","X-Tenant-ID":t.tenantId},t.apiKey?this.headers["X-API-Key"]=t.apiKey:t.token&&(this.headers.Authorization=`Bearer ${t.token}`)}async _fetchWithRetry(t,e,s=3){for(let r=0;r<=s;r++)try{let i=await fetch(t,e);if((i.status===429||i.status>=500)&&r<s){let a=Math.min(1e3*Math.pow(2,r),1e4);console.warn(`[TelestackStorage] Network error ${i.status}. Retrying in ${a}ms...`),await new Promise(n=>setTimeout(n,a));continue}return i}catch(i){if(r<s){let a=Math.min(1e3*Math.pow(2,r),1e4);console.warn(`[TelestackStorage] Network un-reachable. Retrying in ${a}ms...`),await new Promise(n=>setTimeout(n,a));continue}throw i}throw new Error("Unreachable code in retry logic")}async get(t,e){let s=new URL(`${this.baseUrl}${t}`);e&&Object.entries(e).forEach(([i,a])=>{a!==void 0&&s.searchParams.set(i,a)});let r=await this._fetchWithRetry(s.toString(),{headers:this.headers});return this._handle(r)}async post(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"POST",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async patch(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PATCH",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async put(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PUT",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async delete(t){let e=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"DELETE",headers:this.headers});return this._handle(e)}async uploadToPresignedUrl(t,e,s,r){for(let a=0;a<=3;a++)try{await new Promise((n,h)=>{let o=new XMLHttpRequest;o.open("PUT",t),o.setRequestHeader("Content-Type",s),r&&(o.upload.onprogress=p=>{p.lengthComputable&&r(Math.round(p.loaded/p.total*100))}),o.onload=()=>{o.status>=200&&o.status<300?n():o.status===429||o.status>=500?h(new Error(`Retryable network error: ${o.status}`)):h(new u(`Upload failed: ${o.status}`,o.status))},o.onerror=()=>h(new Error("Retryable network error")),o.send(e)});return}catch(n){if(n instanceof u||a>=3)throw n;let h=Math.min(1e3*Math.pow(2,a),1e4);console.warn(`[TelestackStorage] Upload chunk failed. Retrying in ${h}ms...`),await new Promise(o=>setTimeout(o,h))}}async _handle(t){let e=await t.json();if(!t.ok)throw new u(e.error||t.statusText,t.status,e.code);return e}},u=class extends Error{constructor(e,s,r){super(e);this.status=s;this.code=r;this.name="TelestackError"}};var d=class{static async generateKey(){let t=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"]),e=await crypto.subtle.exportKey("raw",t);return btoa(String.fromCharCode(...new Uint8Array(e)))}static async encrypt(t,e){let s=await this._importKey(e),r=crypto.getRandomValues(new Uint8Array(12)),i=await t.arrayBuffer(),a=await crypto.subtle.encrypt({name:"AES-GCM",iv:r},s,i);return new Blob([r,a],{type:"application/octet-stream"})}static async decrypt(t,e,s="application/octet-stream"){let r=await this._importKey(e),i=await t.arrayBuffer(),a=i.slice(0,12),n=i.slice(12),h=await crypto.subtle.decrypt({name:"AES-GCM",iv:new Uint8Array(a)},r,n);return new Blob([h],{type:s})}static async _importKey(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0));return crypto.subtle.importKey("raw",e,{name:"AES-GCM"},!1,["encrypt","decrypt"])}};var g=class{static async compress(t,e={}){if(!t.type.startsWith("image/"))return t;let{maxWidth:s=1920,maxHeight:r=1080,quality:i=.8}=e;return new Promise((a,n)=>{let h=new Image,o=URL.createObjectURL(t);h.onload=()=>{URL.revokeObjectURL(o);let p=h.width,c=h.height;p>s&&(c=Math.round(c*s/p),p=s),c>r&&(p=Math.round(p*r/c),c=r);let R=document.createElement("canvas");R.width=p,R.height=c;let P=R.getContext("2d");if(!P)return n(new Error("Failed to get canvas 2d context for image compression"));P.drawImage(h,0,0,p,c);let k=t.type==="image/png"?"image/png":"image/webp";R.toBlob(I=>{I?a(I):n(new Error("Canvas toBlob failed"))},k,i)},h.onerror=()=>{URL.revokeObjectURL(o),n(new Error("Failed to load image for compression"))},h.src=o})}};var m=class{constructor(t,e,s,r,i,a){this.client=t;this.path=e;this.name=r;this.contentType=i;this.options=a;this._data=s,this._totalBytes=s.size,this._promise=new Promise((n,h)=>{this._resolve=n,this._reject=h}),this._start()}_state="processing";_bytesTransferred=0;_totalBytes=0;_data;_observers=[];_promise;_resolve;_reject;_uploadId;_parts=[];_currentPartIndex=0;_activeXhr;_isResumable=!1;_simpleFileMetadata;on(t,e,s,r){let i=typeof e=="function"?{next:e,error:s,complete:r}:e||{error:s,complete:r};return this._observers.push(i),i.next&&i.next(this.snapshot),()=>{this._observers=this._observers.filter(a=>a!==i)}}pause(){return this._state!=="running"?!1:(this._state="paused",this._activeXhr&&(this._activeXhr.abort(),this._activeXhr=void 0),this._notifyObservers(),!0)}resume(){return this._state!=="paused"?!1:(this._state="running",this._notifyObservers(),this._isResumable?this._continueResumable().catch(this._handleError):(this._bytesTransferred=0,this._notifyObservers(),this._startSimple().catch(this._handleError)),!0)}cancel(){if(this._state==="success"||this._state==="error"||this._state==="canceled")return!1;this._state="canceled",this._activeXhr&&(this._activeXhr.abort(),this._activeXhr=void 0),this._isResumable&&this._uploadId?this.client.post("/files/resumable/abort",{path:this.path,uploadId:this._uploadId}).catch(()=>{}):!this._isResumable&&this._simpleFileMetadata&&this.client.delete(`/files/${encodeURIComponent(this.path)}`).catch(()=>{}),this._notifyObservers();let t=new Error("Upload canceled by user");return t.name="UploadCanceled",this._handleError(t),!0}get snapshot(){return{bytesTransferred:this._bytesTransferred,totalBytes:this._totalBytes,state:this._state,task:this}}then(t,e){return this._promise.then(t,e)}catch(t){return this._promise.catch(t)}finally(t){return this._promise.finally(t)}[Symbol.toStringTag]="UploadTask";async _start(){try{if(this.options.compressImage&&this._data.type.startsWith("image/")&&(this._data=await g.compress(this._data,this.options.compressImage)),this.options.encryptionKey&&(this._data=await d.encrypt(this._data,this.options.encryptionKey)),this._totalBytes=this._data.size,this._state!=="processing")return;this._state="running",this._notifyObservers();let t=50*1024*1024;this._isResumable=this._totalBytes>=t,this._isResumable?await this._startResumable():await this._startSimple()}catch(t){this._handleError(t)}}async _startSimple(){let t=await this.client.post("/files/upload-url",{path:this.path,name:this.name,size:this._totalBytes,contentType:this.contentType,userId:this.options.userId,metadata:this.options.metadata});if(this._simpleFileMetadata=t.file,this._state!=="running")return;let e=new XMLHttpRequest;this._activeXhr=e,await new Promise((s,r)=>{e.open("PUT",t.uploadUrl),e.setRequestHeader("Content-Type",this.contentType),e.upload.onprogress=i=>{i.lengthComputable&&this._state==="running"&&(this._bytesTransferred=i.loaded,this._notifyObservers())},e.onload=()=>{e.status>=200&&e.status<300?s():r(new Error(`Upload failed: ${e.status} ${e.responseText}`))},e.onerror=()=>r(new Error("Network error during upload")),e.onabort=()=>r(new Error("Upload aborted")),e.send(this._data)}),this._activeXhr=void 0,this._state==="running"&&(await this.client.post("/files/complete-upload",{path:this.path}),this._bytesTransferred=this._totalBytes,this._state="success",this._notifyObservers(),this._resolve({file:t.file,resumable:!1}))}async _startResumable(){let t=await this.client.post("/files/resumable/init",{path:this.path,name:this.name,size:this._totalBytes,contentType:this.contentType,userId:this.options.userId,metadata:this.options.metadata});this._uploadId=t.uploadId,this._simpleFileMetadata=t.file,await this._continueResumable()}async _continueResumable(){if(!this._uploadId||!this._simpleFileMetadata)return;let t=this.options.chunkSize??10*1024*1024,e=Math.ceil(this._totalBytes/t);try{for(;this._currentPartIndex<e;){if(this._state!=="running")return;let s=this._currentPartIndex*t,r=Math.min(s+t,this._totalBytes),i=this._data.slice(s,r),a=await this.client.post("/files/resumable/part-url",{path:this.path,uploadId:this._uploadId,partNumber:this._currentPartIndex+1});if(this._state!=="running")return;let n=await this._uploadChunkWithProgress(a.uploadUrl,i,s);this._parts.push({PartNumber:this._currentPartIndex+1,ETag:n}),this._currentPartIndex++,this._bytesTransferred=Math.min(this._currentPartIndex*t,this._totalBytes),this._notifyObservers()}this._state==="running"&&(await this.client.post("/files/resumable/complete",{path:this.path,uploadId:this._uploadId,parts:this._parts}),this._state="success",this._notifyObservers(),this._resolve({file:this._simpleFileMetadata,resumable:!0}))}catch(s){if(s.message==="Upload aborted")return;this._handleError(s)}}_uploadChunkWithProgress(t,e,s){return new Promise((r,i)=>{let a=new XMLHttpRequest;this._activeXhr=a,a.open("PUT",t),a.setRequestHeader("Content-Type",this.contentType),a.upload.onprogress=n=>{n.lengthComputable&&this._state==="running"&&(this._bytesTransferred=s+n.loaded,this._notifyObservers())},a.onload=()=>{if(a.status>=200&&a.status<300){let n=a.getResponseHeader("ETag")||"";r(n.replace(/"/g,""))}else i(new Error(`Part upload failed: ${a.status}`))},a.onerror=()=>i(new Error("Network error during part upload")),a.onabort=()=>i(new Error("Upload aborted")),a.send(e)})}_notifyObservers(){let t=this.snapshot;this._observers.forEach(e=>{t.state==="success"&&e.complete?e.complete():e.next&&e.next(t)})}_handleError=t=>{this._state!=="canceled"&&(this._state="error",this._activeXhr=void 0,this._notifyObservers(),this._observers.forEach(e=>e.error?.(t)),this._reject(t))}};var w=class{constructor(t,e,s){this.client=t;this.path=e;this.tenantId=s}child(t){let s=(this.path?this.path.endsWith("/")?this.path:this.path+"/":"")+t.replace(/^\//,"");return s.endsWith("/")?new y(this.client,s,this.tenantId):new f(this.client,s,this.tenantId)}},y=class extends w{async listAll(t=100){return(await this.client.get("/files",{prefix:this.path,limit:String(t)})).files}},f=class extends w{put(t,e){let s=(t instanceof File,t.size),r=t instanceof File?t.name:this.path.split("/").pop()||"file",i=t.type||"application/octet-stream";return new m(this.client,this.path,t,r,i,e)}putBytes(t,e,s){let r=this.path.split("/").pop()||"file",i=new Blob([t],{type:e});return new m(this.client,this.path,i,r,e,s)}async getDownloadUrl(t){return t?.versionId?(await this.client.get(`/files/version-url/${encodeURIComponent(this.path)}`,{versionId:t.versionId})).downloadUrl:(await this.client.get(`/files/download-url/${encodeURIComponent(this.path)}`)).downloadUrl}async getDecryptedBlob(t){let e=await this.getDownloadUrl(),s=await fetch(e);if(!s.ok)throw new Error(`Failed to download file from S3: ${s.status}`);let r=await s.blob(),i=await this.getMetadata();return await d.decrypt(r,t,i.content_type)}async getMetadata(){return(await this.client.get(`/files/metadata/${encodeURIComponent(this.path)}`)).file}async updateMetadata(t){return(await this.client.patch(`/files/metadata/${encodeURIComponent(this.path)}`,{metadata:t})).file}async delete(){await this.client.delete(`/files/${encodeURIComponent(this.path)}`)}async listVersions(){return this.client.get(`/files/versions/${encodeURIComponent(this.path)}`)}async getVersionUrl(t){return(await this.client.get(`/files/version-url/${encodeURIComponent(this.path)}`,{versionId:t})).downloadUrl}async getTags(){return(await this.client.get(`/files/tags/${encodeURIComponent(this.path)}`)).tags}async setTags(t){await this.client.put(`/files/tags/${encodeURIComponent(this.path)}`,{tags:t})}async setLegalHold(t){await this.client.post(`/files/legal-hold/${encodeURIComponent(this.path)}`,{status:t})}};var b=class{constructor(t){this.client=t}_op=null;_paths=[];_dest="";delete(t){return this._op="delete",this._paths=t,this}copy(t,e){return this._op="copy",this._paths=t,this._dest=e,this}move(t,e){return this._op="move",this._paths=t,this._dest=e,this}async run(){if(!this._op)throw new Error("No batch operation specified");if(this._op==="delete")return this.client.post("/files/batch/delete",{paths:this._paths});let t={sourcePaths:this._paths,destinationPrefix:this._dest};return this.client.post(`/files/batch/${this._op}`,t)}};var v=class{constructor(t){this.client=t}_query="";_metadata={};_limit=20;nameContains(t){return this._query=t,this}where(t,e){return this._metadata[t]=e,this}limit(t){return this._limit=t,this}async get(){let t={q:this._query||void 0,limit:String(this._limit),metadata:Object.keys(this._metadata).length>0?JSON.stringify(this._metadata):void 0};return(await this.client.get("/files/search",t)).files}};var T=class{constructor(t){this.config=t;this.http=new _(t),this.tenantId=t.tenantId}http;tenantId;ref(t){if(t.endsWith("/"))throw new Error("Use .dir() for directory references");return new f(this.http,t,this.tenantId)}dir(t){let e=t.endsWith("/")?t:t+"/";return new y(this.http,e,this.tenantId)}async list(t){let e={prefix:t?.prefix||"",limit:String(t?.limit||100)};return(await this.http.get("/files",e)).files}async rename(t,e){return this.http.post("/files/rename",{oldPath:t,newName:e})}batch(){return new b(this.http)}query(){return new v(this.http)}async generateApiKey(t){return this.http.post("/internal/keys/generate",{name:t})}async revokeApiKey(t){await this.http.post("/internal/keys/revoke",{keyId:t})}async getBucketInfo(){return this.http.get("/internal/bucket-info")}};0&&(module.exports={BatchBuilder,CryptoHelper,DirRef,FileRef,HttpClient,ImageHelper,QueryBuilder,StorageRef,TelestackError,TelestackStorage,UploadTask});
|
|
1
|
+
"use strict";var R=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var x=(h,t)=>{for(var e in t)R(h,e,{get:t[e],enumerable:!0})},E=(h,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of B(t))!M.call(h,i)&&i!==e&&R(h,i,{get:()=>t[i],enumerable:!(s=S(t,i))||s.enumerable});return h};var O=h=>E(R({},"__esModule",{value:!0}),h);var $={};x($,{BatchBuilder:()=>w,CryptoHelper:()=>p,DirRef:()=>y,FileRef:()=>m,HttpClient:()=>g,ImageHelper:()=>_,QueryBuilder:()=>v,StorageRef:()=>b,TelestackError:()=>c,TelestackStorage:()=>P,UploadTask:()=>f});module.exports=O($);var c=class extends Error{constructor(e,s,i){super(e);this.status=s;this.code=i;this.name="TelestackError"}};var g=class{baseUrl;tenantId;headers;defaultUploadOptions;constructor(t){this.baseUrl=t.baseUrl.replace(/\/$/,""),this.tenantId=t.tenantId,this.defaultUploadOptions=t.defaultUploadOptions,this.headers={"Content-Type":"application/json","X-Tenant-ID":t.tenantId,"X-SDK-Platform":"Web","X-SDK-Version":"1.3.0"},t.apiKey?this.headers["X-API-Key"]=t.apiKey:t.token&&(this.headers.Authorization=`Bearer ${t.token}`)}async _fetchWithRetry(t,e,s=3){let i=null;for(let n=0;n<=s;n++)try{let r=await fetch(t,e);if(r.status>=200&&r.status<300)return r;if((r.status===429||r.status>=500)&&n<s){let a=Math.min(1e3*Math.pow(2,n),15e3),l=Math.random()*1e3,o=a+l;console.warn(`[Telestack] ${r.status} on ${t}. Retrying in ${Math.round(o)}ms (Attempt ${n+1}/${s})...`),await new Promise(d=>setTimeout(d,o));continue}return r}catch(r){if(i=r,n<s){let a=Math.min(1e3*Math.pow(2,n),15e3),l=Math.random()*1e3,o=a+l;console.warn(`[Telestack] Network unreachable. Retrying in ${Math.round(o)}ms...`),await new Promise(d=>setTimeout(d,o));continue}throw r}throw i||new Error("Unreachable code in retry logic")}async get(t,e){let s=new URL(`${this.baseUrl}${t}`);e&&Object.entries(e).forEach(([n,r])=>{r!==void 0&&s.searchParams.set(n,r)});let i=await this._fetchWithRetry(s.toString(),{headers:this.headers});return this._handle(i)}async post(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"POST",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async patch(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PATCH",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async put(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PUT",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async delete(t){let e=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"DELETE",headers:this.headers});return this._handle(e)}async uploadToPresignedUrl(t,e,s,i){for(let r=0;r<=3;r++)try{await new Promise((a,l)=>{let o=new XMLHttpRequest;o.open("PUT",t),o.setRequestHeader("Content-Type",s),i&&(o.upload.onprogress=d=>{d.lengthComputable&&i(Math.round(d.loaded/d.total*100))}),o.onload=()=>{o.status>=200&&o.status<300?a():o.status===429||o.status>=500?l(new Error(`Retryable network error: ${o.status}`)):l(new c(`Upload failed: ${o.status}`,o.status))},o.onerror=()=>l(new Error("Retryable network error")),o.send(e)});return}catch(a){if(a instanceof c||r>=3)throw a;let l=Math.min(1e3*Math.pow(2,r),1e4);console.warn(`[TelestackStorage] Upload chunk failed. Retrying in ${l}ms...`),await new Promise(o=>setTimeout(o,l))}}async _handle(t){if(!(t.headers.get("content-type")||"").includes("application/json")){let i=await t.text();if(!t.ok)throw new c(i||t.statusText,t.status);return i}let s=await t.json();if(!t.ok)throw new c(s.error||t.statusText,t.status,s.code);return s}};var p=class{static async generateKey(){let t=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"]),e=await crypto.subtle.exportKey("raw",t);return btoa(String.fromCharCode(...new Uint8Array(e)))}static async encrypt(t,e){let s=await this._importKey(e),i=crypto.getRandomValues(new Uint8Array(12)),n=await t.arrayBuffer(),r=await crypto.subtle.encrypt({name:"AES-GCM",iv:i},s,n);return new Blob([i,r],{type:"application/octet-stream"})}static async decrypt(t,e,s="application/octet-stream"){let i=await this._importKey(e),n=await t.arrayBuffer(),r=n.slice(0,12),a=n.slice(12),l=await crypto.subtle.decrypt({name:"AES-GCM",iv:new Uint8Array(r)},i,a);return new Blob([l],{type:s})}static async _importKey(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0));return crypto.subtle.importKey("raw",e,{name:"AES-GCM"},!1,["encrypt","decrypt"])}};var _=class{static async compress(t,e={}){if(!t.type.startsWith("image/"))return t;let{maxWidth:s=1920,maxHeight:i=1080,quality:n=.8}=e;return new Promise((r,a)=>{let l=new Image,o=URL.createObjectURL(t);l.onload=()=>{URL.revokeObjectURL(o);let d=l.width,u=l.height;d>s&&(u=Math.round(u*s/d),d=s),u>i&&(d=Math.round(d*i/u),u=i);let I=document.createElement("canvas");I.width=d,I.height=u;let T=I.getContext("2d");if(!T)return a(new Error("Failed to get canvas 2d context for image compression"));T.drawImage(l,0,0,d,u);let U=t.type==="image/png"?"image/png":"image/webp";I.toBlob(k=>{k?r(k):a(new Error("Canvas toBlob failed"))},U,n)},l.onerror=()=>{URL.revokeObjectURL(o),a(new Error("Failed to load image for compression"))},l.src=o})}};var C=50*1024*1024,f=class{constructor(t,e,s,i,n,r,a){this.client=t;this.path=e;this.name=i;this.contentType=n;this.options=r;this.tenantId=a;this._data=s,this._totalBytes=s.size,this._promise=new Promise((l,o)=>{this._resolve=l,this._reject=o}),this._start()}_state="processing";_bytesTransferred=0;_totalBytes=0;_data;_observers=[];_promise;_resolve;_reject;_uploadId;_fileId;_versionId;_storageKey;_parts=[];_currentPartIndex=0;_isMultipart=!1;_tier;_activeXhr;on(t,e,s,i){let n=typeof e=="function"?{next:e,error:s,complete:i}:e||{error:s,complete:i};return this._observers.push(n),n.next&&n.next(this.snapshot),()=>{this._observers=this._observers.filter(r=>r!==n)}}pause(){return this._state!=="running"?!1:(this._state="paused",this._activeXhr?.abort(),this._activeXhr=void 0,this._notifyObservers(),!0)}resume(){return this._state!=="paused"?!1:(this._state="running",this._notifyObservers(),this._isMultipart?this._continueMultipart().catch(this._handleError):(this._bytesTransferred=0,this._startSimple().catch(this._handleError)),!0)}cancel(){if(this._state==="success"||this._state==="error"||this._state==="canceled")return!1;this._state="canceled",this._activeXhr?.abort(),this._activeXhr=void 0,this._isMultipart&&this._storageKey&&this._uploadId&&this.client.post("/files/resumable/abort",{storage_key:this._storageKey,uploadId:this._uploadId}).catch(()=>{}),this._notifyObservers();let t=Object.assign(new Error("Upload canceled by user"),{name:"UploadCanceled"});return this._handleError(t),!0}get snapshot(){return{bytesTransferred:this._bytesTransferred,totalBytes:this._totalBytes,state:this._state,task:this}}then(t,e){return this._promise.then(t,e)}catch(t){return this._promise.catch(t)}finally(t){return this._promise.finally(t)}[Symbol.toStringTag]="UploadTask";async _start(){try{if(this.options.compressImage&&this._data.type.startsWith("image/")&&(this._data=await _.compress(this._data,this.options.compressImage)),this.options.encryptionKey&&(this._data=await p.encrypt(this._data,this.options.encryptionKey)),this._totalBytes=this._data.size,this._state!=="processing")return;this._state="running",this._notifyObservers(),this._isMultipart=this._totalBytes>=C,this._isMultipart?await this._startMultipart():await this._startSimple()}catch(t){this._handleError(t)}}async _startSimple(){let t=await this.client.post("/files/upload-url",{tenant_id:this.tenantId,directory_id:this.options.directoryId||"root",name:this.name,content_type:this.contentType});if(this._fileId=t.file_id,this._versionId=t.version_id,this._tier=t.tier,this._state!=="running"||(await this._putToPresignedUrl(t.upload_url,this._data),this._state!=="running"))return;let e=await this.client.post(`/files/${t.file_id}/confirm`,{version_id:t.version_id,size:this._totalBytes,storage_key:this._storageKey});this._bytesTransferred=this._totalBytes,this._state="success",this._notifyObservers(),this._resolve({success:!0,file_id:t.file_id,version_id:t.version_id,resumable:!1})}async _startMultipart(){let t=await this.client.post("/files/resumable/init",{tenant_id:this.tenantId,name:this.name,content_type:this.contentType});this._uploadId=t.uploadId,this._fileId=t.fileId,this._storageKey=t.storage_key,await this._continueMultipart()}async _continueMultipart(){if(!this._uploadId||!this._storageKey)return;let t=this.options.chunkSize??10*1024*1024,e=Math.ceil(this._totalBytes/t);try{for(;this._currentPartIndex<e;){if(this._state!=="running")return;let s=this._currentPartIndex*t,i=Math.min(s+t,this._totalBytes),n=this._data.slice(s,i),r=await this.client.post("/files/resumable/part-url",{storage_key:this._storageKey,uploadId:this._uploadId,partNumber:this._currentPartIndex+1});if(this._state!=="running")return;let a=await this._uploadChunkXhr(r.upload_url,n,s);this._parts.push({PartNumber:this._currentPartIndex+1,ETag:a}),this._currentPartIndex++,this._bytesTransferred=Math.min(this._currentPartIndex*t,this._totalBytes),this._notifyObservers()}if(this._state==="running"){let s=await this.client.post("/files/resumable/complete",{fileId:this._fileId,storage_key:this._storageKey,uploadId:this._uploadId,parts:this._parts});this._bytesTransferred=this._totalBytes,this._state="success",this._notifyObservers(),this._resolve({success:!0,file_id:this._fileId,version_id:"",resumable:!0})}}catch(s){if(s.message==="Upload aborted")return;this._handleError(s)}}async _putToPresignedUrl(t,e){let s=await e.arrayBuffer();return new Promise((i,n)=>{let r=new XMLHttpRequest;this._activeXhr=r,r.open("PUT",t),r.setRequestHeader("Content-Type",this.contentType),r.upload.onprogress=a=>{a.lengthComputable&&this._state==="running"&&(this._bytesTransferred=a.loaded,this._notifyObservers())},r.onload=()=>r.status>=200&&r.status<300?i():n(new Error(`Storage upload failed: ${r.status}`)),r.onerror=()=>n(new Error("Network error during upload")),r.onabort=()=>n(new Error("Upload aborted")),r.send(s)})}async _uploadChunkXhr(t,e,s){let i=await e.arrayBuffer();return new Promise((n,r)=>{let a=new XMLHttpRequest;this._activeXhr=a,a.open("PUT",t),a.setRequestHeader("Content-Type",this.contentType),a.upload.onprogress=l=>{l.lengthComputable&&this._state==="running"&&(this._bytesTransferred=s+l.loaded,this._notifyObservers())},a.onload=()=>{a.status>=200&&a.status<300?n(a.getResponseHeader("ETag")?.replace(/"/g,"")||""):r(new Error(`Part upload failed: ${a.status}`))},a.onerror=()=>r(new Error("Network error during part upload")),a.onabort=()=>r(new Error("Upload aborted")),a.send(i)})}_notifyObservers(){let t=this.snapshot;this._observers.forEach(e=>{t.state==="success"&&e.complete?e.complete():e.next&&e.next(t)})}_handleError=t=>{this._state!=="canceled"&&(this._state="error",this._activeXhr=void 0,this._notifyObservers(),this._observers.forEach(e=>e.error?.(t)),this._reject(t))}};var b=class{constructor(t,e,s){this.client=t;this.path=e;this.tenantId=s}child(t){let s=(this.path?this.path.endsWith("/")?this.path:this.path+"/":"")+t.replace(/^\//,"");return s.endsWith("/")?new y(this.client,s,this.tenantId):new m(this.client,s,this.tenantId)}},y=class extends b{async listAll(t){return this.client.get("/files",{tenant_id:this.tenantId,directory_id:this.path||"root",limit:String(t?.limit||100),offset:String(t?.offset||0)})}async create(t){let s=this.path.replace(/\/$/,"").split("/").pop()||this.path;return this.client.post("/directories",{tenant_id:this.tenantId,parent_id:t||null,name:s})}async delete(t){return this.client.delete(`/directories/${t}?tenant_id=${this.tenantId}`)}async search(t){return this.client.get("/files/search",{tenant_id:this.tenantId,q:t.query,ext:t.extension,minSize:t.minSize?String(t.minSize):void 0,limit:String(t.limit||25),offset:String(t.offset||0)})}},m=class extends b{async resolveFileRecord(t){if(t){let i=(await this.client.get("/files",{tenant_id:this.tenantId})).files?.find(n=>n.$id===t);if(!i)throw new Error("File not found");return i}return(await this.client.get("/files/resolve",{tenant_id:this.tenantId,path:this.path})).file}put(t,e={}){let s=typeof File<"u"&&t instanceof File?t.name:this.path.split("/").pop()||"file",i=t.type||"application/octet-stream",n={...this.client.defaultUploadOptions,...e};return new f(this.client,this.path,t,s,i,n,this.tenantId)}putBytes(t,e,s={}){let i=this.path.split("/").pop()||"file",n=new Blob([t],{type:e});return new f(this.client,this.path,n,i,e,s,this.tenantId)}async getDownloadUrl(t,e){let s=e?.mode||"download";return t?(await this.client.get(`/files/${t}/download-url`,{tenant_id:this.tenantId,mode:s})).download_url:(await this.client.get("/files/download-url/by-path",{tenant_id:this.tenantId,path:this.path,mode:s})).download_url}getCdnUrl(t){return`${this.client.baseUrl}/f/${this.tenantId}/${t}`}async getDecryptedBlob(t,e,s){let i=await this.getDownloadUrl(s),n=await fetch(i);if(!n.ok)throw new Error(`Download failed: ${n.status}`);let r=await n.blob();return p.decrypt(r,t,e)}async getMetadata(t){return this.resolveFileRecord(t)}async getVersions(t){let e=await this.resolveFileRecord(t);return(await this.client.get(`/files/${e.$id}/versions`,{tenant_id:this.tenantId})).versions||[]}async delete(t){let e=await this.resolveFileRecord(t);await this.client.delete(`/files/${e.$id}?tenant_id=${this.tenantId}`)}};var w=class{constructor(t,e){this.client=t;this.tenantId=e}_op=null;_fileIds=[];_mappings=[];delete(t){return this._op="delete",this._fileIds=t,this}copy(t){return this._op="copy",this._mappings=t,this}move(t){return this._op="move",this._mappings=t,this}async run(){if(!this._op)throw new Error("No batch operation specified. Call .delete(), .copy(), or .move() first.");return this._op==="delete"?this.client.post("/files/batch/delete",{fileIds:this._fileIds,tenant_id:this.tenantId}):this.client.post(`/files/batch/${this._op}`,{mappings:this._mappings,tenant_id:this.tenantId})}};var v=class{constructor(t,e){this.client=t;this.tenantId=e}_query="";_extension="";_minSize=0;_limit=20;_offset=0;search(t){return this._query=t,this}withExtension(t){return this._extension=t.replace(/^\./,""),this}minSize(t){return this._minSize=t,this}limit(t){return this._limit=t,this}offset(t){return this._offset=t,this}async get(){let t={tenant_id:this.tenantId,q:this._query||void 0,ext:this._extension||void 0,minSize:this._minSize>0?String(this._minSize):void 0,limit:String(this._limit),offset:String(this._offset)};return(await this.client.get("/files/search",t)).files||[]}async run(){return this.get()}};var P=class h{constructor(t){this.config=t;this.http=new g(t),this.tenantId=t.tenantId}http;tenantId;withContext(t){return new h({...this.config,tenantId:t.tenantId??this.config.tenantId,projectId:t.projectId??this.config.projectId,orgId:t.orgId??this.config.orgId})}withAuth(t){let e={...this.config};return t.apiKey!==void 0&&(t.apiKey?e.apiKey=t.apiKey:delete e.apiKey),t.token!==void 0&&(t.token?e.token=t.token:delete e.token),new h(e)}ref(t){return new m(this.http,t,this.tenantId)}dir(t){let e=t.endsWith("/")?t:t+"/";return new y(this.http,e,this.tenantId)}async getBucket(){return(await this.http.get("/buckets",{tenant_id:this.tenantId})).bucket}async list(t){return this.http.get("/files",{tenant_id:this.tenantId,directory_id:t?.directoryId??void 0,limit:String(t?.limit||100),offset:String(t?.offset||0)})}async search(t){return this.http.get("/files/search",{tenant_id:this.tenantId,q:t.query,ext:t.extension,minSize:t.minSize?String(t.minSize):void 0,limit:String(t.limit||25),offset:String(t.offset||0)})}query(){return new v(this.http,this.tenantId)}batch(){return new w(this.http,this.tenantId)}async batchDelete(t){return this.http.post("/files/batch/delete",{fileIds:t,tenant_id:this.tenantId})}async batchCopy(t){return this.http.post("/files/batch/copy",{tenant_id:this.tenantId,mappings:t})}async batchMove(t){return this.http.post("/files/batch/move",{tenant_id:this.tenantId,mappings:t})}async getUsageStats(t=7){return(await this.http.get("/stats/usage",{tenant_id:this.tenantId,days:String(t)})).stats}onEvent(t){let e=new URL(`${this.config.baseUrl}/files/events`);e.searchParams.set("tenant_id",this.tenantId),this.config.apiKey&&e.searchParams.set("apiKey",this.config.apiKey),this.config.token&&e.searchParams.set("jwt",this.config.token);let s=new EventSource(e.toString());return s.addEventListener("file_change",i=>{try{t(JSON.parse(i.data))}catch(n){console.error("[Telestack] SSE parse error:",n)}}),s.onerror=()=>{console.warn("[Telestack] SSE connection lost. Browser will auto-reconnect.")},()=>s.close()}async initDatabase(){return this.http.post("/db/init")}async generateApiKey(t){return this.http.post("/internal/keys/generate",{name:t})}async revokeApiKey(t){await this.http.post("/internal/keys/revoke",{keyId:t})}async getInternalAnalytics(t=this.tenantId){return(await this.http.get(`/internal/analytics/${t}`)).analytics||[]}async getInternalBucketInfo(t=this.tenantId){return this.http.get("/internal/bucket-info",{tenantId:t})}async updateBucketVisibility(t,e=this.tenantId){return this.http.patch("/internal/buckets/settings?tenantId="+encodeURIComponent(e),{isPublic:t})}async listPublicPathRules(t=this.tenantId){return(await this.http.get("/internal/buckets/rules",{tenantId:t})).rules||[]}async addPublicPathRule(t,e=this.tenantId){return(await this.http.post(`/internal/buckets/rules?tenantId=${encodeURIComponent(e)}`,{pathPrefix:t,isPublic:!0})).rule}async deletePublicPathRule(t,e=this.tenantId){await this.http.delete(`/internal/buckets/rules/${encodeURIComponent(t)}?tenantId=${encodeURIComponent(e)}`)}};0&&(module.exports={BatchBuilder,CryptoHelper,DirRef,FileRef,HttpClient,ImageHelper,QueryBuilder,StorageRef,TelestackError,TelestackStorage,UploadTask});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var _=class{baseUrl;tenantId;headers;constructor(t){this.baseUrl=t.baseUrl.replace(/\/$/,""),this.tenantId=t.tenantId,this.headers={"Content-Type":"application/json","X-Tenant-ID":t.tenantId},t.apiKey?this.headers["X-API-Key"]=t.apiKey:t.token&&(this.headers.Authorization=`Bearer ${t.token}`)}async _fetchWithRetry(t,e,s=3){for(let r=0;r<=s;r++)try{let i=await fetch(t,e);if((i.status===429||i.status>=500)&&r<s){let a=Math.min(1e3*Math.pow(2,r),1e4);console.warn(`[TelestackStorage] Network error ${i.status}. Retrying in ${a}ms...`),await new Promise(n=>setTimeout(n,a));continue}return i}catch(i){if(r<s){let a=Math.min(1e3*Math.pow(2,r),1e4);console.warn(`[TelestackStorage] Network un-reachable. Retrying in ${a}ms...`),await new Promise(n=>setTimeout(n,a));continue}throw i}throw new Error("Unreachable code in retry logic")}async get(t,e){let s=new URL(`${this.baseUrl}${t}`);e&&Object.entries(e).forEach(([i,a])=>{a!==void 0&&s.searchParams.set(i,a)});let r=await this._fetchWithRetry(s.toString(),{headers:this.headers});return this._handle(r)}async post(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"POST",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async patch(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PATCH",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async put(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PUT",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async delete(t){let e=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"DELETE",headers:this.headers});return this._handle(e)}async uploadToPresignedUrl(t,e,s,r){for(let a=0;a<=3;a++)try{await new Promise((n,l)=>{let o=new XMLHttpRequest;o.open("PUT",t),o.setRequestHeader("Content-Type",s),r&&(o.upload.onprogress=h=>{h.lengthComputable&&r(Math.round(h.loaded/h.total*100))}),o.onload=()=>{o.status>=200&&o.status<300?n():o.status===429||o.status>=500?l(new Error(`Retryable network error: ${o.status}`)):l(new u(`Upload failed: ${o.status}`,o.status))},o.onerror=()=>l(new Error("Retryable network error")),o.send(e)});return}catch(n){if(n instanceof u||a>=3)throw n;let l=Math.min(1e3*Math.pow(2,a),1e4);console.warn(`[TelestackStorage] Upload chunk failed. Retrying in ${l}ms...`),await new Promise(o=>setTimeout(o,l))}}async _handle(t){let e=await t.json();if(!t.ok)throw new u(e.error||t.statusText,t.status,e.code);return e}},u=class extends Error{constructor(e,s,r){super(e);this.status=s;this.code=r;this.name="TelestackError"}};var c=class{static async generateKey(){let t=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"]),e=await crypto.subtle.exportKey("raw",t);return btoa(String.fromCharCode(...new Uint8Array(e)))}static async encrypt(t,e){let s=await this._importKey(e),r=crypto.getRandomValues(new Uint8Array(12)),i=await t.arrayBuffer(),a=await crypto.subtle.encrypt({name:"AES-GCM",iv:r},s,i);return new Blob([r,a],{type:"application/octet-stream"})}static async decrypt(t,e,s="application/octet-stream"){let r=await this._importKey(e),i=await t.arrayBuffer(),a=i.slice(0,12),n=i.slice(12),l=await crypto.subtle.decrypt({name:"AES-GCM",iv:new Uint8Array(a)},r,n);return new Blob([l],{type:s})}static async _importKey(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0));return crypto.subtle.importKey("raw",e,{name:"AES-GCM"},!1,["encrypt","decrypt"])}};var g=class{static async compress(t,e={}){if(!t.type.startsWith("image/"))return t;let{maxWidth:s=1920,maxHeight:r=1080,quality:i=.8}=e;return new Promise((a,n)=>{let l=new Image,o=URL.createObjectURL(t);l.onload=()=>{URL.revokeObjectURL(o);let h=l.width,d=l.height;h>s&&(d=Math.round(d*s/h),h=s),d>r&&(h=Math.round(h*r/d),d=r);let R=document.createElement("canvas");R.width=h,R.height=d;let U=R.getContext("2d");if(!U)return n(new Error("Failed to get canvas 2d context for image compression"));U.drawImage(l,0,0,h,d);let I=t.type==="image/png"?"image/png":"image/webp";R.toBlob(P=>{P?a(P):n(new Error("Canvas toBlob failed"))},I,i)},l.onerror=()=>{URL.revokeObjectURL(o),n(new Error("Failed to load image for compression"))},l.src=o})}};var m=class{constructor(t,e,s,r,i,a){this.client=t;this.path=e;this.name=r;this.contentType=i;this.options=a;this._data=s,this._totalBytes=s.size,this._promise=new Promise((n,l)=>{this._resolve=n,this._reject=l}),this._start()}_state="processing";_bytesTransferred=0;_totalBytes=0;_data;_observers=[];_promise;_resolve;_reject;_uploadId;_parts=[];_currentPartIndex=0;_activeXhr;_isResumable=!1;_simpleFileMetadata;on(t,e,s,r){let i=typeof e=="function"?{next:e,error:s,complete:r}:e||{error:s,complete:r};return this._observers.push(i),i.next&&i.next(this.snapshot),()=>{this._observers=this._observers.filter(a=>a!==i)}}pause(){return this._state!=="running"?!1:(this._state="paused",this._activeXhr&&(this._activeXhr.abort(),this._activeXhr=void 0),this._notifyObservers(),!0)}resume(){return this._state!=="paused"?!1:(this._state="running",this._notifyObservers(),this._isResumable?this._continueResumable().catch(this._handleError):(this._bytesTransferred=0,this._notifyObservers(),this._startSimple().catch(this._handleError)),!0)}cancel(){if(this._state==="success"||this._state==="error"||this._state==="canceled")return!1;this._state="canceled",this._activeXhr&&(this._activeXhr.abort(),this._activeXhr=void 0),this._isResumable&&this._uploadId?this.client.post("/files/resumable/abort",{path:this.path,uploadId:this._uploadId}).catch(()=>{}):!this._isResumable&&this._simpleFileMetadata&&this.client.delete(`/files/${encodeURIComponent(this.path)}`).catch(()=>{}),this._notifyObservers();let t=new Error("Upload canceled by user");return t.name="UploadCanceled",this._handleError(t),!0}get snapshot(){return{bytesTransferred:this._bytesTransferred,totalBytes:this._totalBytes,state:this._state,task:this}}then(t,e){return this._promise.then(t,e)}catch(t){return this._promise.catch(t)}finally(t){return this._promise.finally(t)}[Symbol.toStringTag]="UploadTask";async _start(){try{if(this.options.compressImage&&this._data.type.startsWith("image/")&&(this._data=await g.compress(this._data,this.options.compressImage)),this.options.encryptionKey&&(this._data=await c.encrypt(this._data,this.options.encryptionKey)),this._totalBytes=this._data.size,this._state!=="processing")return;this._state="running",this._notifyObservers();let t=50*1024*1024;this._isResumable=this._totalBytes>=t,this._isResumable?await this._startResumable():await this._startSimple()}catch(t){this._handleError(t)}}async _startSimple(){let t=await this.client.post("/files/upload-url",{path:this.path,name:this.name,size:this._totalBytes,contentType:this.contentType,userId:this.options.userId,metadata:this.options.metadata});if(this._simpleFileMetadata=t.file,this._state!=="running")return;let e=new XMLHttpRequest;this._activeXhr=e,await new Promise((s,r)=>{e.open("PUT",t.uploadUrl),e.setRequestHeader("Content-Type",this.contentType),e.upload.onprogress=i=>{i.lengthComputable&&this._state==="running"&&(this._bytesTransferred=i.loaded,this._notifyObservers())},e.onload=()=>{e.status>=200&&e.status<300?s():r(new Error(`Upload failed: ${e.status} ${e.responseText}`))},e.onerror=()=>r(new Error("Network error during upload")),e.onabort=()=>r(new Error("Upload aborted")),e.send(this._data)}),this._activeXhr=void 0,this._state==="running"&&(await this.client.post("/files/complete-upload",{path:this.path}),this._bytesTransferred=this._totalBytes,this._state="success",this._notifyObservers(),this._resolve({file:t.file,resumable:!1}))}async _startResumable(){let t=await this.client.post("/files/resumable/init",{path:this.path,name:this.name,size:this._totalBytes,contentType:this.contentType,userId:this.options.userId,metadata:this.options.metadata});this._uploadId=t.uploadId,this._simpleFileMetadata=t.file,await this._continueResumable()}async _continueResumable(){if(!this._uploadId||!this._simpleFileMetadata)return;let t=this.options.chunkSize??10*1024*1024,e=Math.ceil(this._totalBytes/t);try{for(;this._currentPartIndex<e;){if(this._state!=="running")return;let s=this._currentPartIndex*t,r=Math.min(s+t,this._totalBytes),i=this._data.slice(s,r),a=await this.client.post("/files/resumable/part-url",{path:this.path,uploadId:this._uploadId,partNumber:this._currentPartIndex+1});if(this._state!=="running")return;let n=await this._uploadChunkWithProgress(a.uploadUrl,i,s);this._parts.push({PartNumber:this._currentPartIndex+1,ETag:n}),this._currentPartIndex++,this._bytesTransferred=Math.min(this._currentPartIndex*t,this._totalBytes),this._notifyObservers()}this._state==="running"&&(await this.client.post("/files/resumable/complete",{path:this.path,uploadId:this._uploadId,parts:this._parts}),this._state="success",this._notifyObservers(),this._resolve({file:this._simpleFileMetadata,resumable:!0}))}catch(s){if(s.message==="Upload aborted")return;this._handleError(s)}}_uploadChunkWithProgress(t,e,s){return new Promise((r,i)=>{let a=new XMLHttpRequest;this._activeXhr=a,a.open("PUT",t),a.setRequestHeader("Content-Type",this.contentType),a.upload.onprogress=n=>{n.lengthComputable&&this._state==="running"&&(this._bytesTransferred=s+n.loaded,this._notifyObservers())},a.onload=()=>{if(a.status>=200&&a.status<300){let n=a.getResponseHeader("ETag")||"";r(n.replace(/"/g,""))}else i(new Error(`Part upload failed: ${a.status}`))},a.onerror=()=>i(new Error("Network error during part upload")),a.onabort=()=>i(new Error("Upload aborted")),a.send(e)})}_notifyObservers(){let t=this.snapshot;this._observers.forEach(e=>{t.state==="success"&&e.complete?e.complete():e.next&&e.next(t)})}_handleError=t=>{this._state!=="canceled"&&(this._state="error",this._activeXhr=void 0,this._notifyObservers(),this._observers.forEach(e=>e.error?.(t)),this._reject(t))}};var w=class{constructor(t,e,s){this.client=t;this.path=e;this.tenantId=s}child(t){let s=(this.path?this.path.endsWith("/")?this.path:this.path+"/":"")+t.replace(/^\//,"");return s.endsWith("/")?new y(this.client,s,this.tenantId):new f(this.client,s,this.tenantId)}},y=class extends w{async listAll(t=100){return(await this.client.get("/files",{prefix:this.path,limit:String(t)})).files}},f=class extends w{put(t,e){let s=(t instanceof File,t.size),r=t instanceof File?t.name:this.path.split("/").pop()||"file",i=t.type||"application/octet-stream";return new m(this.client,this.path,t,r,i,e)}putBytes(t,e,s){let r=this.path.split("/").pop()||"file",i=new Blob([t],{type:e});return new m(this.client,this.path,i,r,e,s)}async getDownloadUrl(t){return t?.versionId?(await this.client.get(`/files/version-url/${encodeURIComponent(this.path)}`,{versionId:t.versionId})).downloadUrl:(await this.client.get(`/files/download-url/${encodeURIComponent(this.path)}`)).downloadUrl}async getDecryptedBlob(t){let e=await this.getDownloadUrl(),s=await fetch(e);if(!s.ok)throw new Error(`Failed to download file from S3: ${s.status}`);let r=await s.blob(),i=await this.getMetadata();return await c.decrypt(r,t,i.content_type)}async getMetadata(){return(await this.client.get(`/files/metadata/${encodeURIComponent(this.path)}`)).file}async updateMetadata(t){return(await this.client.patch(`/files/metadata/${encodeURIComponent(this.path)}`,{metadata:t})).file}async delete(){await this.client.delete(`/files/${encodeURIComponent(this.path)}`)}async listVersions(){return this.client.get(`/files/versions/${encodeURIComponent(this.path)}`)}async getVersionUrl(t){return(await this.client.get(`/files/version-url/${encodeURIComponent(this.path)}`,{versionId:t})).downloadUrl}async getTags(){return(await this.client.get(`/files/tags/${encodeURIComponent(this.path)}`)).tags}async setTags(t){await this.client.put(`/files/tags/${encodeURIComponent(this.path)}`,{tags:t})}async setLegalHold(t){await this.client.post(`/files/legal-hold/${encodeURIComponent(this.path)}`,{status:t})}};var b=class{constructor(t){this.client=t}_op=null;_paths=[];_dest="";delete(t){return this._op="delete",this._paths=t,this}copy(t,e){return this._op="copy",this._paths=t,this._dest=e,this}move(t,e){return this._op="move",this._paths=t,this._dest=e,this}async run(){if(!this._op)throw new Error("No batch operation specified");if(this._op==="delete")return this.client.post("/files/batch/delete",{paths:this._paths});let t={sourcePaths:this._paths,destinationPrefix:this._dest};return this.client.post(`/files/batch/${this._op}`,t)}};var v=class{constructor(t){this.client=t}_query="";_metadata={};_limit=20;nameContains(t){return this._query=t,this}where(t,e){return this._metadata[t]=e,this}limit(t){return this._limit=t,this}async get(){let t={q:this._query||void 0,limit:String(this._limit),metadata:Object.keys(this._metadata).length>0?JSON.stringify(this._metadata):void 0};return(await this.client.get("/files/search",t)).files}};var T=class{constructor(t){this.config=t;this.http=new _(t),this.tenantId=t.tenantId}http;tenantId;ref(t){if(t.endsWith("/"))throw new Error("Use .dir() for directory references");return new f(this.http,t,this.tenantId)}dir(t){let e=t.endsWith("/")?t:t+"/";return new y(this.http,e,this.tenantId)}async list(t){let e={prefix:t?.prefix||"",limit:String(t?.limit||100)};return(await this.http.get("/files",e)).files}async rename(t,e){return this.http.post("/files/rename",{oldPath:t,newName:e})}batch(){return new b(this.http)}query(){return new v(this.http)}async generateApiKey(t){return this.http.post("/internal/keys/generate",{name:t})}async revokeApiKey(t){await this.http.post("/internal/keys/revoke",{keyId:t})}async getBucketInfo(){return this.http.get("/internal/bucket-info")}};export{b as BatchBuilder,c as CryptoHelper,y as DirRef,f as FileRef,_ as HttpClient,g as ImageHelper,v as QueryBuilder,w as StorageRef,u as TelestackError,T as TelestackStorage,m as UploadTask};
|
|
1
|
+
var c=class extends Error{constructor(e,s,n){super(e);this.status=s;this.code=n;this.name="TelestackError"}};var g=class{baseUrl;tenantId;headers;defaultUploadOptions;constructor(t){this.baseUrl=t.baseUrl.replace(/\/$/,""),this.tenantId=t.tenantId,this.defaultUploadOptions=t.defaultUploadOptions,this.headers={"Content-Type":"application/json","X-Tenant-ID":t.tenantId,"X-SDK-Platform":"Web","X-SDK-Version":"1.3.0"},t.apiKey?this.headers["X-API-Key"]=t.apiKey:t.token&&(this.headers.Authorization=`Bearer ${t.token}`)}async _fetchWithRetry(t,e,s=3){let n=null;for(let r=0;r<=s;r++)try{let i=await fetch(t,e);if(i.status>=200&&i.status<300)return i;if((i.status===429||i.status>=500)&&r<s){let a=Math.min(1e3*Math.pow(2,r),15e3),l=Math.random()*1e3,o=a+l;console.warn(`[Telestack] ${i.status} on ${t}. Retrying in ${Math.round(o)}ms (Attempt ${r+1}/${s})...`),await new Promise(h=>setTimeout(h,o));continue}return i}catch(i){if(n=i,r<s){let a=Math.min(1e3*Math.pow(2,r),15e3),l=Math.random()*1e3,o=a+l;console.warn(`[Telestack] Network unreachable. Retrying in ${Math.round(o)}ms...`),await new Promise(h=>setTimeout(h,o));continue}throw i}throw n||new Error("Unreachable code in retry logic")}async get(t,e){let s=new URL(`${this.baseUrl}${t}`);e&&Object.entries(e).forEach(([r,i])=>{i!==void 0&&s.searchParams.set(r,i)});let n=await this._fetchWithRetry(s.toString(),{headers:this.headers});return this._handle(n)}async post(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"POST",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async patch(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PATCH",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async put(t,e){let s=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"PUT",headers:this.headers,body:JSON.stringify(e)});return this._handle(s)}async delete(t){let e=await this._fetchWithRetry(`${this.baseUrl}${t}`,{method:"DELETE",headers:this.headers});return this._handle(e)}async uploadToPresignedUrl(t,e,s,n){for(let i=0;i<=3;i++)try{await new Promise((a,l)=>{let o=new XMLHttpRequest;o.open("PUT",t),o.setRequestHeader("Content-Type",s),n&&(o.upload.onprogress=h=>{h.lengthComputable&&n(Math.round(h.loaded/h.total*100))}),o.onload=()=>{o.status>=200&&o.status<300?a():o.status===429||o.status>=500?l(new Error(`Retryable network error: ${o.status}`)):l(new c(`Upload failed: ${o.status}`,o.status))},o.onerror=()=>l(new Error("Retryable network error")),o.send(e)});return}catch(a){if(a instanceof c||i>=3)throw a;let l=Math.min(1e3*Math.pow(2,i),1e4);console.warn(`[TelestackStorage] Upload chunk failed. Retrying in ${l}ms...`),await new Promise(o=>setTimeout(o,l))}}async _handle(t){if(!(t.headers.get("content-type")||"").includes("application/json")){let n=await t.text();if(!t.ok)throw new c(n||t.statusText,t.status);return n}let s=await t.json();if(!t.ok)throw new c(s.error||t.statusText,t.status,s.code);return s}};var u=class{static async generateKey(){let t=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"]),e=await crypto.subtle.exportKey("raw",t);return btoa(String.fromCharCode(...new Uint8Array(e)))}static async encrypt(t,e){let s=await this._importKey(e),n=crypto.getRandomValues(new Uint8Array(12)),r=await t.arrayBuffer(),i=await crypto.subtle.encrypt({name:"AES-GCM",iv:n},s,r);return new Blob([n,i],{type:"application/octet-stream"})}static async decrypt(t,e,s="application/octet-stream"){let n=await this._importKey(e),r=await t.arrayBuffer(),i=r.slice(0,12),a=r.slice(12),l=await crypto.subtle.decrypt({name:"AES-GCM",iv:new Uint8Array(i)},n,a);return new Blob([l],{type:s})}static async _importKey(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0));return crypto.subtle.importKey("raw",e,{name:"AES-GCM"},!1,["encrypt","decrypt"])}};var _=class{static async compress(t,e={}){if(!t.type.startsWith("image/"))return t;let{maxWidth:s=1920,maxHeight:n=1080,quality:r=.8}=e;return new Promise((i,a)=>{let l=new Image,o=URL.createObjectURL(t);l.onload=()=>{URL.revokeObjectURL(o);let h=l.width,p=l.height;h>s&&(p=Math.round(p*s/h),h=s),p>n&&(h=Math.round(h*n/p),p=n);let I=document.createElement("canvas");I.width=h,I.height=p;let R=I.getContext("2d");if(!R)return a(new Error("Failed to get canvas 2d context for image compression"));R.drawImage(l,0,0,h,p);let k=t.type==="image/png"?"image/png":"image/webp";I.toBlob(T=>{T?i(T):a(new Error("Canvas toBlob failed"))},k,r)},l.onerror=()=>{URL.revokeObjectURL(o),a(new Error("Failed to load image for compression"))},l.src=o})}};var U=50*1024*1024,f=class{constructor(t,e,s,n,r,i,a){this.client=t;this.path=e;this.name=n;this.contentType=r;this.options=i;this.tenantId=a;this._data=s,this._totalBytes=s.size,this._promise=new Promise((l,o)=>{this._resolve=l,this._reject=o}),this._start()}_state="processing";_bytesTransferred=0;_totalBytes=0;_data;_observers=[];_promise;_resolve;_reject;_uploadId;_fileId;_versionId;_storageKey;_parts=[];_currentPartIndex=0;_isMultipart=!1;_tier;_activeXhr;on(t,e,s,n){let r=typeof e=="function"?{next:e,error:s,complete:n}:e||{error:s,complete:n};return this._observers.push(r),r.next&&r.next(this.snapshot),()=>{this._observers=this._observers.filter(i=>i!==r)}}pause(){return this._state!=="running"?!1:(this._state="paused",this._activeXhr?.abort(),this._activeXhr=void 0,this._notifyObservers(),!0)}resume(){return this._state!=="paused"?!1:(this._state="running",this._notifyObservers(),this._isMultipart?this._continueMultipart().catch(this._handleError):(this._bytesTransferred=0,this._startSimple().catch(this._handleError)),!0)}cancel(){if(this._state==="success"||this._state==="error"||this._state==="canceled")return!1;this._state="canceled",this._activeXhr?.abort(),this._activeXhr=void 0,this._isMultipart&&this._storageKey&&this._uploadId&&this.client.post("/files/resumable/abort",{storage_key:this._storageKey,uploadId:this._uploadId}).catch(()=>{}),this._notifyObservers();let t=Object.assign(new Error("Upload canceled by user"),{name:"UploadCanceled"});return this._handleError(t),!0}get snapshot(){return{bytesTransferred:this._bytesTransferred,totalBytes:this._totalBytes,state:this._state,task:this}}then(t,e){return this._promise.then(t,e)}catch(t){return this._promise.catch(t)}finally(t){return this._promise.finally(t)}[Symbol.toStringTag]="UploadTask";async _start(){try{if(this.options.compressImage&&this._data.type.startsWith("image/")&&(this._data=await _.compress(this._data,this.options.compressImage)),this.options.encryptionKey&&(this._data=await u.encrypt(this._data,this.options.encryptionKey)),this._totalBytes=this._data.size,this._state!=="processing")return;this._state="running",this._notifyObservers(),this._isMultipart=this._totalBytes>=U,this._isMultipart?await this._startMultipart():await this._startSimple()}catch(t){this._handleError(t)}}async _startSimple(){let t=await this.client.post("/files/upload-url",{tenant_id:this.tenantId,directory_id:this.options.directoryId||"root",name:this.name,content_type:this.contentType});if(this._fileId=t.file_id,this._versionId=t.version_id,this._tier=t.tier,this._state!=="running"||(await this._putToPresignedUrl(t.upload_url,this._data),this._state!=="running"))return;let e=await this.client.post(`/files/${t.file_id}/confirm`,{version_id:t.version_id,size:this._totalBytes,storage_key:this._storageKey});this._bytesTransferred=this._totalBytes,this._state="success",this._notifyObservers(),this._resolve({success:!0,file_id:t.file_id,version_id:t.version_id,resumable:!1})}async _startMultipart(){let t=await this.client.post("/files/resumable/init",{tenant_id:this.tenantId,name:this.name,content_type:this.contentType});this._uploadId=t.uploadId,this._fileId=t.fileId,this._storageKey=t.storage_key,await this._continueMultipart()}async _continueMultipart(){if(!this._uploadId||!this._storageKey)return;let t=this.options.chunkSize??10*1024*1024,e=Math.ceil(this._totalBytes/t);try{for(;this._currentPartIndex<e;){if(this._state!=="running")return;let s=this._currentPartIndex*t,n=Math.min(s+t,this._totalBytes),r=this._data.slice(s,n),i=await this.client.post("/files/resumable/part-url",{storage_key:this._storageKey,uploadId:this._uploadId,partNumber:this._currentPartIndex+1});if(this._state!=="running")return;let a=await this._uploadChunkXhr(i.upload_url,r,s);this._parts.push({PartNumber:this._currentPartIndex+1,ETag:a}),this._currentPartIndex++,this._bytesTransferred=Math.min(this._currentPartIndex*t,this._totalBytes),this._notifyObservers()}if(this._state==="running"){let s=await this.client.post("/files/resumable/complete",{fileId:this._fileId,storage_key:this._storageKey,uploadId:this._uploadId,parts:this._parts});this._bytesTransferred=this._totalBytes,this._state="success",this._notifyObservers(),this._resolve({success:!0,file_id:this._fileId,version_id:"",resumable:!0})}}catch(s){if(s.message==="Upload aborted")return;this._handleError(s)}}async _putToPresignedUrl(t,e){let s=await e.arrayBuffer();return new Promise((n,r)=>{let i=new XMLHttpRequest;this._activeXhr=i,i.open("PUT",t),i.setRequestHeader("Content-Type",this.contentType),i.upload.onprogress=a=>{a.lengthComputable&&this._state==="running"&&(this._bytesTransferred=a.loaded,this._notifyObservers())},i.onload=()=>i.status>=200&&i.status<300?n():r(new Error(`Storage upload failed: ${i.status}`)),i.onerror=()=>r(new Error("Network error during upload")),i.onabort=()=>r(new Error("Upload aborted")),i.send(s)})}async _uploadChunkXhr(t,e,s){let n=await e.arrayBuffer();return new Promise((r,i)=>{let a=new XMLHttpRequest;this._activeXhr=a,a.open("PUT",t),a.setRequestHeader("Content-Type",this.contentType),a.upload.onprogress=l=>{l.lengthComputable&&this._state==="running"&&(this._bytesTransferred=s+l.loaded,this._notifyObservers())},a.onload=()=>{a.status>=200&&a.status<300?r(a.getResponseHeader("ETag")?.replace(/"/g,"")||""):i(new Error(`Part upload failed: ${a.status}`))},a.onerror=()=>i(new Error("Network error during part upload")),a.onabort=()=>i(new Error("Upload aborted")),a.send(n)})}_notifyObservers(){let t=this.snapshot;this._observers.forEach(e=>{t.state==="success"&&e.complete?e.complete():e.next&&e.next(t)})}_handleError=t=>{this._state!=="canceled"&&(this._state="error",this._activeXhr=void 0,this._notifyObservers(),this._observers.forEach(e=>e.error?.(t)),this._reject(t))}};var b=class{constructor(t,e,s){this.client=t;this.path=e;this.tenantId=s}child(t){let s=(this.path?this.path.endsWith("/")?this.path:this.path+"/":"")+t.replace(/^\//,"");return s.endsWith("/")?new y(this.client,s,this.tenantId):new m(this.client,s,this.tenantId)}},y=class extends b{async listAll(t){return this.client.get("/files",{tenant_id:this.tenantId,directory_id:this.path||"root",limit:String(t?.limit||100),offset:String(t?.offset||0)})}async create(t){let s=this.path.replace(/\/$/,"").split("/").pop()||this.path;return this.client.post("/directories",{tenant_id:this.tenantId,parent_id:t||null,name:s})}async delete(t){return this.client.delete(`/directories/${t}?tenant_id=${this.tenantId}`)}async search(t){return this.client.get("/files/search",{tenant_id:this.tenantId,q:t.query,ext:t.extension,minSize:t.minSize?String(t.minSize):void 0,limit:String(t.limit||25),offset:String(t.offset||0)})}},m=class extends b{async resolveFileRecord(t){if(t){let n=(await this.client.get("/files",{tenant_id:this.tenantId})).files?.find(r=>r.$id===t);if(!n)throw new Error("File not found");return n}return(await this.client.get("/files/resolve",{tenant_id:this.tenantId,path:this.path})).file}put(t,e={}){let s=typeof File<"u"&&t instanceof File?t.name:this.path.split("/").pop()||"file",n=t.type||"application/octet-stream",r={...this.client.defaultUploadOptions,...e};return new f(this.client,this.path,t,s,n,r,this.tenantId)}putBytes(t,e,s={}){let n=this.path.split("/").pop()||"file",r=new Blob([t],{type:e});return new f(this.client,this.path,r,n,e,s,this.tenantId)}async getDownloadUrl(t,e){let s=e?.mode||"download";return t?(await this.client.get(`/files/${t}/download-url`,{tenant_id:this.tenantId,mode:s})).download_url:(await this.client.get("/files/download-url/by-path",{tenant_id:this.tenantId,path:this.path,mode:s})).download_url}getCdnUrl(t){return`${this.client.baseUrl}/f/${this.tenantId}/${t}`}async getDecryptedBlob(t,e,s){let n=await this.getDownloadUrl(s),r=await fetch(n);if(!r.ok)throw new Error(`Download failed: ${r.status}`);let i=await r.blob();return u.decrypt(i,t,e)}async getMetadata(t){return this.resolveFileRecord(t)}async getVersions(t){let e=await this.resolveFileRecord(t);return(await this.client.get(`/files/${e.$id}/versions`,{tenant_id:this.tenantId})).versions||[]}async delete(t){let e=await this.resolveFileRecord(t);await this.client.delete(`/files/${e.$id}?tenant_id=${this.tenantId}`)}};var w=class{constructor(t,e){this.client=t;this.tenantId=e}_op=null;_fileIds=[];_mappings=[];delete(t){return this._op="delete",this._fileIds=t,this}copy(t){return this._op="copy",this._mappings=t,this}move(t){return this._op="move",this._mappings=t,this}async run(){if(!this._op)throw new Error("No batch operation specified. Call .delete(), .copy(), or .move() first.");return this._op==="delete"?this.client.post("/files/batch/delete",{fileIds:this._fileIds,tenant_id:this.tenantId}):this.client.post(`/files/batch/${this._op}`,{mappings:this._mappings,tenant_id:this.tenantId})}};var v=class{constructor(t,e){this.client=t;this.tenantId=e}_query="";_extension="";_minSize=0;_limit=20;_offset=0;search(t){return this._query=t,this}withExtension(t){return this._extension=t.replace(/^\./,""),this}minSize(t){return this._minSize=t,this}limit(t){return this._limit=t,this}offset(t){return this._offset=t,this}async get(){let t={tenant_id:this.tenantId,q:this._query||void 0,ext:this._extension||void 0,minSize:this._minSize>0?String(this._minSize):void 0,limit:String(this._limit),offset:String(this._offset)};return(await this.client.get("/files/search",t)).files||[]}async run(){return this.get()}};var P=class d{constructor(t){this.config=t;this.http=new g(t),this.tenantId=t.tenantId}http;tenantId;withContext(t){return new d({...this.config,tenantId:t.tenantId??this.config.tenantId,projectId:t.projectId??this.config.projectId,orgId:t.orgId??this.config.orgId})}withAuth(t){let e={...this.config};return t.apiKey!==void 0&&(t.apiKey?e.apiKey=t.apiKey:delete e.apiKey),t.token!==void 0&&(t.token?e.token=t.token:delete e.token),new d(e)}ref(t){return new m(this.http,t,this.tenantId)}dir(t){let e=t.endsWith("/")?t:t+"/";return new y(this.http,e,this.tenantId)}async getBucket(){return(await this.http.get("/buckets",{tenant_id:this.tenantId})).bucket}async list(t){return this.http.get("/files",{tenant_id:this.tenantId,directory_id:t?.directoryId??void 0,limit:String(t?.limit||100),offset:String(t?.offset||0)})}async search(t){return this.http.get("/files/search",{tenant_id:this.tenantId,q:t.query,ext:t.extension,minSize:t.minSize?String(t.minSize):void 0,limit:String(t.limit||25),offset:String(t.offset||0)})}query(){return new v(this.http,this.tenantId)}batch(){return new w(this.http,this.tenantId)}async batchDelete(t){return this.http.post("/files/batch/delete",{fileIds:t,tenant_id:this.tenantId})}async batchCopy(t){return this.http.post("/files/batch/copy",{tenant_id:this.tenantId,mappings:t})}async batchMove(t){return this.http.post("/files/batch/move",{tenant_id:this.tenantId,mappings:t})}async getUsageStats(t=7){return(await this.http.get("/stats/usage",{tenant_id:this.tenantId,days:String(t)})).stats}onEvent(t){let e=new URL(`${this.config.baseUrl}/files/events`);e.searchParams.set("tenant_id",this.tenantId),this.config.apiKey&&e.searchParams.set("apiKey",this.config.apiKey),this.config.token&&e.searchParams.set("jwt",this.config.token);let s=new EventSource(e.toString());return s.addEventListener("file_change",n=>{try{t(JSON.parse(n.data))}catch(r){console.error("[Telestack] SSE parse error:",r)}}),s.onerror=()=>{console.warn("[Telestack] SSE connection lost. Browser will auto-reconnect.")},()=>s.close()}async initDatabase(){return this.http.post("/db/init")}async generateApiKey(t){return this.http.post("/internal/keys/generate",{name:t})}async revokeApiKey(t){await this.http.post("/internal/keys/revoke",{keyId:t})}async getInternalAnalytics(t=this.tenantId){return(await this.http.get(`/internal/analytics/${t}`)).analytics||[]}async getInternalBucketInfo(t=this.tenantId){return this.http.get("/internal/bucket-info",{tenantId:t})}async updateBucketVisibility(t,e=this.tenantId){return this.http.patch("/internal/buckets/settings?tenantId="+encodeURIComponent(e),{isPublic:t})}async listPublicPathRules(t=this.tenantId){return(await this.http.get("/internal/buckets/rules",{tenantId:t})).rules||[]}async addPublicPathRule(t,e=this.tenantId){return(await this.http.post(`/internal/buckets/rules?tenantId=${encodeURIComponent(e)}`,{pathPrefix:t,isPublic:!0})).rule}async deletePublicPathRule(t,e=this.tenantId){await this.http.delete(`/internal/buckets/rules/${encodeURIComponent(t)}?tenantId=${encodeURIComponent(e)}`)}};export{w as BatchBuilder,u as CryptoHelper,y as DirRef,m as FileRef,g as HttpClient,_ as ImageHelper,v as QueryBuilder,b as StorageRef,c as TelestackError,P as TelestackStorage,f as UploadTask};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telestack/storage",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Official Telestack Storage Web SDK — fluent,
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "Official Telestack Storage Web SDK — fluent, real-time cloud storage for web apps.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"LICENSE"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
|
-
"build": "tsup src/index.ts --format cjs,esm,iife --global-name TelestackStorageSetup --minify --dts --clean"
|
|
21
|
+
"build": "tsup src/index.ts --format cjs,esm,iife --global-name TelestackStorageSetup --minify --dts --clean",
|
|
22
|
+
"test:e2e": "npm run build && node test-e2e.js"
|
|
22
23
|
},
|
|
23
24
|
"keywords": [
|
|
24
25
|
"telestack",
|
|
@@ -42,4 +43,4 @@
|
|
|
42
43
|
"typescript": "^5.0.0",
|
|
43
44
|
"xhr2": "^0.2.1"
|
|
44
45
|
}
|
|
45
|
-
}
|
|
46
|
+
}
|