@visulima/storage 2.0.2 → 2.0.4
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/CHANGELOG.md +14 -0
- package/dist/packem_shared/{GCSMetaStorage-C5WuNwdJ.js → GCSMetaStorage-CNdizA6j.js} +1 -1
- package/dist/packem_shared/{GCStorage-PTbHNnv0.js → GCStorage-CBPtwGpH.js} +1 -1
- package/dist/packem_shared/{gcs-meta-storage-B50U4jps.js → gcs-meta-storage-SmWBDZQp.js} +1 -1
- package/dist/storage/gcs/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## @visulima/storage [2.0.4](https://github.com/visulima/visulima/compare/%40visulima%2Fstorage%402.0.3...%40visulima%2Fstorage%402.0.4) (2026-08-19)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Dependencies
|
|
5
|
+
|
|
6
|
+
* **@visulima/fs:** upgraded to 6.0.3
|
|
7
|
+
|
|
8
|
+
## @visulima/storage [2.0.3](https://github.com/visulima/visulima/compare/%40visulima%2Fstorage%402.0.2...%40visulima%2Fstorage%402.0.3) (2026-08-18)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* **@visulima/fs:** upgraded to 6.0.2
|
|
14
|
+
|
|
1
15
|
## @visulima/storage [2.0.2](https://github.com/visulima/visulima/compare/%40visulima%2Fstorage%402.0.1...%40visulima%2Fstorage%402.0.2) (2026-08-17)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{G as i}from"./gcs-meta-storage-
|
|
1
|
+
import{G as i}from"./gcs-meta-storage-SmWBDZQp.js";import"./MetaStorage-DPEUyWgN.js";import"./Metadata-BeQVDFer.js";import"./GCSConfig-BQ0OpVhe.js";export{i as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import m from"./GCSConfig-BQ0OpVhe.js";import f from"./GCSFile-mFuZfcTW.js";import{r as y,G as w,b as C,g as R,p as d}from"./gcs-meta-storage-
|
|
1
|
+
import m from"./GCSConfig-BQ0OpVhe.js";import f from"./GCSFile-mFuZfcTW.js";import{r as y,G as w,b as C,g as R,p as d}from"./gcs-meta-storage-SmWBDZQp.js";import{randomUUID as b}from"node:crypto";import{request as E}from"gaxios";import{GoogleAuth as k}from"google-auth-library";import{a as U}from"./detect-file-type-go3r-MOD.js";import{throwErrorCode as S,ERRORS as I}from"./ERRORS-olUcTqo2.js";import{B as x,t as T}from"./storage-uypoNkqu.js";import{L as $}from"./local-meta-storage-Ca6WrUmN.js";import{p as v}from"./part-match-Cb6xL9eN.js";import{h as A}from"./has-content-96mmyLn2.js";import{g as O}from"./get-file-status-B8WmWzcM.js";class B extends Error{constructor(t,s,e){super(t),this.code=s,this.config=e}code;config;name="FetchError"}const P=h=>h>=200&&h<300||h===308||h===499;class g extends x{static name="gcs";checksumTypes=["md5","crc32c"];supportsDelimiter=!0;get raw(){return this.authClient}meta;bucket;authClient;storageBaseURI;uploadBaseURI;isCustomEndpoint=!1;retryOptions={};useAuthWithCustomEndpoint;userProject;constructor(t){super(t);const s=t.bucket||process.env.GCS_BUCKET;if(!s)throw new Error("GCS bucket is not defined");if(!t.projectId)throw new Error("Sorry, we cannot connect to Cloud Services without a project ID.");this.bucket=s,this.storageBaseURI=`${t.storageAPI||m.storageAPI}/${this.bucket}/o`,this.uploadBaseURI=`${t.uploadAPI||m.uploadAPI}/${this.bucket}/o`,this.isCustomEndpoint=new URL(this.storageBaseURI).hostname!=="storage.googleapis.com";const{retryOptions:e,useAuthWithCustomEndpoint:r,userProject:i}=t;this.userProject=i,this.useAuthWithCustomEndpoint=r||!1,this.retryOptions={...y,...e},t.scopes||=m.authScopes,this.authClient=new k(t);const{bucket:o,metaStorage:n,metaStorageConfig:c,projectId:a}=t;if(n)this.meta=n;else{let u={...t,...c,logger:this.logger};"directory"in u?(this.logger?.debug("Using local meta storage"),this.meta=new $(u)):(o===u.bucket&&a===u.projectId&&(u={...u,authClient:this.authClient}),this.meta=new w(u))}this.isReady=!1,this.accessCheck().then(()=>{this.isReady=!0}).catch(u=>{this.logger?.error("Storage access check failed: %O",u)})}normalizeError(t){if(t.config){const s=+t.code||500;return{code:`GCS${s}`,message:t.message,name:t.name,retryable:s>=499,statusCode:s}}return{code:"GenericUploadError",message:`[${this.constructor.name}] ${t.message}`,name:t.name,statusCode:500}}async create(t,s){return this.instrumentOperation("create",async()=>{const e={...t};if(t.ttl){const a=typeof t.ttl=="string"?T(t.ttl):t.ttl;a!==void 0&&(e.expiredAt=Date.now()+a)}const r=new f(e);r.name=this.namingFunction(r),await this.validate(r);try{const a=await this.getMeta(r.id);return a.bytesWritten=await this.internalWrite(a,s),a}catch{}const i={"Content-Type":"application/json; charset=utf-8","X-Goog-Upload-Command":"start","X-Goog-Upload-Protocol":"resumable","X-Upload-Content-Length":r.size.toString(),"X-Upload-Content-Type":r.contentType},o={body:JSON.stringify({metadata:r.metadata}),headers:i,method:"POST",params:{name:r.name,size:r.size,uploadType:"resumable"},url:this.uploadBaseURI},n=await this.makeRequest(o,s);if(n.status!==200)throw new Error("Expected 200 response from GCS");const c=n.headers.get("X-Goog-Upload-Status")||n.headers.get("x-goog-upload-status");if(c!=="active")throw new Error(`X-Goog-Upload-Status response header expected 'active' got: ${c}`);return r.uri=n.headers.get("location"),this.config.clientDirectUpload?(r.GCSUploadURI=r.uri,this.logger?.debug("send uploadURI to client: %s",r.GCSUploadURI),r.status="created",r):(r.bytesWritten=0,await this.saveMeta(r),r.status="created",await this.onCreate(r),r)})}async write(t,s){return this.instrumentOperation("write",async()=>{let e;if("contentType"in t&&"metadata"in t&&!("body"in t)&&!("start"in t)?e=t:(e=await this.getMeta(t.id),await this.checkIfExpired(e)),e.status==="completed")return e;if(!v(t,e))return S(I.FILE_CONFLICT);const r=await this.lock(t.id);try{if(A(t)&&(e.bytesWritten===0||Number.isNaN(e.bytesWritten))&&(!e.contentType||e.contentType==="application/octet-stream"))try{const{fileType:i,stream:o}=await U(t.body);i?.mime&&(e.contentType=i.mime),t.body=o}catch{}e.bytesWritten=await this.internalWrite({...e,...t},s),e.status=O(e),e.status==="completed"&&(e.uri=`${this.storageBaseURI}/${e.name}`,await this.internalOnComplete(e))}finally{await this.unlock(t.id,r)}return e})}async delete({id:t},s){return this.instrumentOperation("delete",async()=>{const e=await this.getMeta(t);if(!e.uri)throw new Error(`File ${t} does not have a valid URI`);e.status="deleted",await this.makeRequest({method:"DELETE",url:e.uri,validateStatus:P},s),await this.deleteMeta(e.id);const r={...e};return await this.onDelete(r),r})}async copy(t,s,e){return this.instrumentOperation("copy",async()=>{const r=new URL(`/${this.bucket}/${t}`,"file://"),o=new URL(encodeURI(s),r).pathname,[,n,...c]=o.split("/"),a=c.join("/"),u=`${this.storageBaseURI}/${t}/rewriteTo/b/${n}/o/${a}`;let l={};const p={body:"",headers:{"Content-Type":"application/json"},method:"POST",url:u};do p.body=l.rewriteToken?JSON.stringify({rewriteToken:l.rewriteToken}):"",l=(await this.makeRequest(p,e)).data||{};while(l.rewriteToken);return await this.getMeta(s)})}async move(t,s,e){return this.instrumentOperation("move",async()=>{const r=await this.copy(t,s,e),i=`${this.storageBaseURI}/${t}`;return await this.makeRequest({method:"DELETE",url:i},e),r})}async get({id:t},s){return this.instrumentOperation("get",async()=>{const{data:e}=await this.makeRequest({params:{alt:"json"},url:`${this.storageBaseURI}/${t}`},s);if(await this.checkIfExpired({expiredAt:e.timeDeleted}),!e.uri)throw new Error("File URI not found");const i=(await this.makeRequest({params:{alt:"media"},url:e.uri},s)).data;let o;if(typeof i=="string")o=Buffer.from(i,"utf8");else if(i&&typeof i=="object"&&"data"in i){const n=i.data;o=Buffer.from(n)}else o=Buffer.from(i);return{...e,content:o}})}async exists({id:t},s){return this.instrumentOperation("exists",async()=>{try{return await this.getMeta(t),await this.makeRequest({method:"HEAD",url:`${this.storageBaseURI}/${t}`},s),!0}catch(e){const r=e;return(r.status||r.response?.status)===404,!1}})}async list(t=1e3,s){return this.instrumentOperation("list",async()=>{const e=[],r=Math.min(t,1e3);let i=!0,o={params:{maxResults:r},url:this.storageBaseURI};for(;i&&e.length<t;)try{const{data:n}=await this.makeRequest(o,s);for(const{name:c,timeCreated:a,updated:u}of n?.items||[]){if(e.length>=t)break;e.push({createdAt:a,id:c,modifiedAt:u})}i=n?.nextPageToken!==void 0,i&&e.length<t&&(o={...o,params:{...o.params,maxResults:Math.min(r,t-e.length),pageToken:n.nextPageToken}})}catch(n){const c=this.normalizeError(n instanceof Error?n:new Error(String(n)));throw await this.onError(c),n}return e},{limit:t})}async listDirectory(t){return this.instrumentOperation("listDirectory",async()=>{const s=t?.limit??1e3,e=Math.min(s,1e3),r=[],i=new Set;let o=!0,n={params:{delimiter:t?.delimiter,maxResults:e,...t?.prefix!==void 0&&{prefix:t.prefix}},url:this.storageBaseURI};for(;o&&r.length<s;)try{const{data:c}=await this.makeRequest(n,t);for(const a of c?.prefixes||[])i.add(a);for(const{name:a,timeCreated:u,updated:l}of c?.items||[]){if(r.length>=s)break;r.push({createdAt:u,id:a,modifiedAt:l})}o=c?.nextPageToken!==void 0,o&&r.length<s&&(n={...n,params:{...n.params,maxResults:Math.min(e,s-r.length),pageToken:c.nextPageToken}})}catch(c){const a=this.normalizeError(c instanceof Error?c:new Error(String(c)));throw await this.onError(a),c}return{files:r,prefixes:[...i]}},{limit:t?.limit??1e3})}async internalWrite(t,s){const{body:e,bytesWritten:r,size:i,uri:o=""}=t,n=C(t),c={method:"PUT"};if(e?.on){const a=new AbortController;e.on("aborted",()=>{a.abort()}),c.body=e,c.signal=a.signal,c.retry=!1}c.headers={Accept:"application/json","Content-Range":n,...i===r?{"X-Goog-Upload-Command":"upload, finalize"}:{}};try{const a=await this.makeRequest({url:o,...c},s);if(a.status===308){const u=a.headers.get("range");return u?R(u):0}if(a.status===200)return this.logger?.debug("uploaded %O",a.data),i;throw new B(String(a.data),`GCS${a.status}`,{uri:o})}catch(a){this.logger?.error(o,a);const u=this.normalizeError(a instanceof Error?a:new Error(String(a)));throw await this.onError(u),a}}internalOnComplete=t=>this.deleteMeta(t.id);static buildCallSignal(t,s){const e=[];if(s&&e.push(s),t?.signal&&e.push(t.signal),e.length!==0)return e.length===1?e[0]:AbortSignal.any(e)}async makeRequest(t,s){typeof t.url=="string"&&(t.url=t.url.replaceAll("/:",":"));const e=g.buildCallSignal(s,t.signal);let r=this.retryOptions;if(s?.retries!==void 0){const o=typeof s.retries=="number"?s.retries:s.retries.maxRetries;typeof o=="number"&&(r={...this.retryOptions,retry:o})}const i=typeof s?.timeout=="number"&&s.timeout>0?s.timeout:6e4;return t={...t,headers:{"User-Agent":`${d.name}/${d.version}`,"x-goog-api-client":`gl-node/${process.versions.node} gccl/${d.version} gccl-invocation-id/${b()}`,...t.headers},params:{...this.userProject===void 0?{}:{userProject:this.userProject},...t.params},retry:t.retry===!1?!1:s?.retries!==0,retryConfig:r,timeout:i,...e?{signal:e}:{}},this.isCustomEndpoint&&!this.useAuthWithCustomEndpoint?E(t):this.authClient.request(t)}async accessCheck(){return this.makeRequest({url:this.storageBaseURI.replace("/o","")})}}export{g as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as p}from"node:crypto";import{request as l}from"gaxios";import{GoogleAuth as g}from"google-auth-library";import f from"./MetaStorage-DPEUyWgN.js";import{stringifyMetadata as C,parseMetadata as y}from"./Metadata-BeQVDFer.js";import a from"./GCSConfig-BQ0OpVhe.js";import{h as R}from"./has-content-96mmyLn2.js";var E="@visulima/storage",I="2.0.
|
|
1
|
+
import{randomUUID as p}from"node:crypto";import{request as l}from"gaxios";import{GoogleAuth as g}from"google-auth-library";import f from"./MetaStorage-DPEUyWgN.js";import{stringifyMetadata as C,parseMetadata as y}from"./Metadata-BeQVDFer.js";import a from"./GCSConfig-BQ0OpVhe.js";import{h as R}from"./has-content-96mmyLn2.js";var E="@visulima/storage",I="2.0.3";const n={name:E,version:I},M=o=>{const t=o.match(/(\d+)-(\d+)/),s=t?.[2]?+t[2]:0;return s>0?s+1:0},j=o=>{if(R(o)){const t=o.contentLength?o.start+o.contentLength-1:"*";return`bytes ${o.start}-${t}/${o.size??"*"}`}return`bytes */${o.size??"*"}`},v=o=>{if(o.response!==void 0){const{response:t}=o;return t.data?.error?.errors?.map(({reason:e=""})=>e==="rateLimitExceeded"||e==="userRateLimitExceeded"?!0:!!(e&&e.includes("EAI_AGAIN"))).includes(!0)??!1}return!1},$={retry:3,shouldRetry:v,statusCodesToRetry:[[408,429,500,502,503,504],[100,199],[429],[500,599]]};class b extends f{constructor(t){super(t),this.config=t;const{authClient:s,...e}=t,r=e.bucket||process.env.GCS_BUCKET;if(s===void 0){if(!r)throw new Error("GCS bucket is not defined");if(!e.projectId)throw new Error("Sorry, we cannot connect to Cloud Services without a project ID.");e.scopes||=a.authScopes,this.authClient=new g(e)}else this.authClient=s;this.storageBaseURI=`${e.storageAPI||a.storageAPI}/${r}/o`,this.uploadBaseURI=`${e.uploadAPI||a.uploadAPI}/${r}/o`;const u=["storage.googleapis.com"],c=new URL(this.storageBaseURI).hostname;this.isCustomEndpoint=!u.includes(c);const{retryOptions:h,useAuthWithCustomEndpoint:d,userProject:m}=t;this.userProject=m,this.useAuthWithCustomEndpoint=d||!1,this.retryOptions={...$,...h},s===void 0&&this.accessCheck().catch(i=>{throw i.code==="404"?new Error(`Bucket ${r} does not exist`):i})}config;authClient;storageBaseURI;uploadBaseURI;isCustomEndpoint=!1;retryOptions={};useAuthWithCustomEndpoint;userProject;async save(t,s){const e={...s};return e.metadata&&(e.metadata=C(s.metadata)),await this.makeRequest({body:JSON.stringify(e),headers:{"Content-Type":"application/json; charset=utf-8"},method:"POST",params:{name:encodeURIComponent(this.getMetaName(t)),uploadType:"media"},url:this.uploadBaseURI}),s}async delete(t){const s=this.getMetaPath(t);await this.makeRequest({method:"DELETE",url:s})}async get(t){const s=this.getMetaPath(t),{data:e}=await this.makeRequest({params:{alt:"media"},url:s});return e.metadata&&typeof e.metadata=="string"&&(e.metadata=y(e.metadata)),e}async touch(t,s){return this.save(t,s)}async accessCheck(){return this.makeRequest({url:this.storageBaseURI.replace("/o","")})}getMetaPath(t){return`${this.storageBaseURI}/${this.getMetaName(t)}`}async makeRequest(t){return typeof t.url=="string"&&(t.url=t.url.replaceAll("/:",":")),t={...t,headers:{"User-Agent":`${n.name}/${n.version}`,"x-goog-api-client":`gl-node/${process.versions.node} gccl/${n.version} gccl-invocation-id/${p()}`},params:{...this.userProject===void 0?{}:{userProject:this.userProject}},retry:!0,retryConfig:this.retryOptions,timeout:6e4},this.isCustomEndpoint&&!this.useAuthWithCustomEndpoint?l(t):this.authClient.request(t)}}export{b as G,j as b,M as g,n as p,$ as r};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as a}from"../../packem_shared/GCSConfig-BQ0OpVhe.js";import{default as t}from"../../packem_shared/GCSFile-mFuZfcTW.js";import{G as C}from"../../packem_shared/gcs-meta-storage-
|
|
1
|
+
import{default as a}from"../../packem_shared/GCSConfig-BQ0OpVhe.js";import{default as t}from"../../packem_shared/GCSFile-mFuZfcTW.js";import{G as C}from"../../packem_shared/gcs-meta-storage-SmWBDZQp.js";import{default as S}from"../../packem_shared/GCStorage-CBPtwGpH.js";export{a as GCSConfig,t as GCSFile,C as GCSMetaStorage,S as GCStorage};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@visulima/storage",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Server-side file-storage abstraction - store files in a web-accessible location via a simplified API. Includes S3, Azure, GCS, local filesystem and 20+ other backends with TUS/multipart/REST upload handlers and the most convenient features of each.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"anolilab",
|