@uploadista/data-store-gcs 0.1.4-beta.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e=require(`node:stream`),t=require(`@google-cloud/storage`),n=require(`@uploadista/core/errors`),r=require(`@uploadista/core/types`),i=require(`@uploadista/observability`),a=require(`effect`);function o(e){return{size:e.size??null,sizeIsDeferred:`${e.sizeIsDeferred}`,offset:e.offset,metadata:JSON.stringify(e.metadata),storage:JSON.stringify(e.storage)}}const s=(e,t,r)=>a.Effect.gen(function*(){try{let[n]=yield*a.Effect.promise(()=>e.file(t).getMetadata()),{size:i,metadata:o}=n,s=yield*r.get(t);return{id:t,size:i?Number.parseInt(`${i}`,10):void 0,offset:n.size?Number.parseInt(`${n.size}`,10):0,metadata:o||void 0,storage:{id:s.storage.id,type:s.storage.type,path:t,bucket:e.name}}}catch(e){if(e&&typeof e==`object`&&`code`in e&&e.code===404)return yield*a.Effect.fail(n.UploadistaError.fromCode(`FILE_NOT_FOUND`));throw e}});function c({keyFilename:e,credentials:t,bucketName:n}){return a.Effect.gen(function*(){return l({keyFilename:e,credentials:t,bucketName:n,kvStore:yield*r.UploadFileKVStore})})}function l({keyFilename:c,credentials:l,bucketName:u,kvStore:d}){let f=new t.Storage(c?{keyFilename:c}:l?{credentials:l}:{}).bucket(u),p=()=>({supportsParallelUploads:!1,supportsConcatenation:!0,supportsDeferredLength:!0,supportsResumableUploads:!0,supportsTransactionalUploads:!1,supportsStreamingRead:!0,supportsStreamingWrite:!0,maxConcurrentUploads:1,minChunkSize:void 0,maxChunkSize:void 0,maxParts:void 0,optimalChunkSize:8*1024*1024,requiresOrderedChunks:!0,requiresMimeTypeValidation:!0,maxValidationSize:void 0});return{bucket:f.name,create:t=>a.Effect.gen(function*(){if(yield*(0,i.gcsUploadRequestsTotal)(a.Effect.succeed(1)),yield*(0,i.gcsActiveUploadsGauge)(a.Effect.succeed(1)),yield*(0,i.gcsFileSizeHistogram)(a.Effect.succeed(t.size||0)),!t.id)return yield*(0,i.gcsUploadErrorsTotal)(a.Effect.succeed(1)),yield*a.Effect.fail(n.UploadistaError.fromCode(`FILE_NOT_FOUND`));let r=f.file(t.id);t.storage={id:t.storage.id,type:t.storage.type,path:t.id,bucket:f.name},console.log(`file`,r.id);let s={metadata:{metadata:{...o(t)}}};return t.metadata?.contentType&&(s.contentType=t.metadata.contentType.toString()),yield*a.Effect.tryPromise({try:()=>(console.log(`creating file`,r.id),new Promise((n,i)=>{let a=new e.PassThrough;a.end(),a.pipe(r.createWriteStream(s)).on(`error`,i).on(`finish`,()=>{n(t)})})),catch:e=>(console.error(`error creating file`,e),a.Effect.runSync((0,i.trackGCSError)(`create`,e,{upload_id:t.id,bucket:f.name})),n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e}))})}),read:e=>a.Effect.tryPromise({try:async()=>{let[t]=await f.file(e).download();return new Uint8Array(t)},catch:t=>(a.Effect.runSync((0,i.trackGCSError)(`read`,t,{upload_id:e,bucket:f.name})),t&&typeof t==`object`&&`code`in t&&t.code===404?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`FILE_READ_ERROR`,{cause:t}))}),readStream:(e,t)=>a.Effect.gen(function*(){let o={...r.DEFAULT_STREAMING_CONFIG,...t},s=f.file(e),[c]=yield*a.Effect.tryPromise({try:()=>s.exists(),catch:t=>(a.Effect.runSync((0,i.trackGCSError)(`readStream`,t,{upload_id:e,bucket:f.name})),n.UploadistaError.fromCode(`FILE_READ_ERROR`,{cause:t}))});if(!c)return yield*a.Effect.fail(n.UploadistaError.fromCode(`FILE_NOT_FOUND`));let l=s.createReadStream();return a.Stream.async(t=>{let r=o.chunkSize,s=new Uint8Array;return l.on(`data`,e=>{let n=new Uint8Array(s.length+e.length);for(n.set(s),n.set(new Uint8Array(e),s.length),s=n;s.length>=r;){let e=s.slice(0,r);s=s.slice(r),t.single(e)}}),l.on(`end`,()=>{s.length>0&&t.single(s),t.end()}),l.on(`error`,r=>{a.Effect.runSync((0,i.trackGCSError)(`readStream`,r,{upload_id:e,bucket:f.name})),t.fail(new n.UploadistaError({code:`FILE_READ_ERROR`,status:500,body:`Failed to read GCS file stream`,details:`GCS stream read failed: ${String(r)}`}))}),a.Effect.sync(()=>{l.destroy()})})}),remove:e=>a.Effect.gen(function*(){try{yield*a.Effect.promise(()=>f.file(e).delete()),yield*(0,i.gcsActiveUploadsGauge)(a.Effect.succeed(-1))}catch(t){throw a.Effect.runSync((0,i.trackGCSError)(`remove`,t,{upload_id:e,bucket:f.name})),t}}),write:(t,r)=>(0,i.withGCSUploadMetrics)(t.file_id,(0,i.withGCSTimingMetrics)(i.gcsUploadDurationHistogram,a.Effect.gen(function*(){let c=Date.now(),{file_id:l,offset:u,stream:p}=t;console.log(`write`,l,u);let{onProgress:m}=r,h=yield*s(f,l,d);return console.log(`upload`,h),yield*a.Effect.promise(()=>new Promise((t,r)=>{let s=f.file(l),d=h.offset===0?s:f.file(`${l}_patch`);h.offset=u;let g={metadata:{metadata:{...o(h)}}},_=d.createWriteStream(g);if(!_){a.Effect.runSync((0,i.gcsUploadErrorsTotal)(a.Effect.succeed(1))),r(n.UploadistaError.fromCode(`FILE_WRITE_ERROR`));return}let v=h.offset,y=a.Stream.toReadableStream(p),b=new e.Transform({transform(e,t,n){v+=e.length,m?.(v),n(null,e)}});(0,e.pipeline)(e.Readable.fromWeb(y),b,_,async e=>{if(e){console.error(`error writing file`,e),a.Effect.runSync((0,i.trackGCSError)(`write`,e,{upload_id:l,bucket:f.name,offset:u}));try{await d.delete({ignoreNotFound:!0})}finally{r(n.UploadistaError.fromCode(`FILE_WRITE_ERROR`))}}else try{s!==d&&(await f.combine([s,d],s),await Promise.all([s.setMetadata(g.metadata),d.delete({ignoreNotFound:!0})])),a.Effect.runSync((0,i.logGCSUploadCompletion)(l,{fileSize:h.size||0,totalDurationMs:Date.now()-c,partsCount:1,averagePartSize:h.size,throughputBps:(h.size||0)/(Date.now()-c),retryCount:0})),a.Effect.runSync((0,i.gcsUploadSuccessTotal)(a.Effect.succeed(1))),a.Effect.runSync((0,i.gcsActiveUploadsGauge)(a.Effect.succeed(-1))),t(v)}catch(e){console.error(e),a.Effect.runSync((0,i.trackGCSError)(`write`,e,{upload_id:l,bucket:f.name,operation:`combine`})),r(n.UploadistaError.fromCode(`FILE_WRITE_ERROR`))}})}))}))),writeStream:(t,r)=>(0,i.withGCSTimingMetrics)(i.gcsUploadDurationHistogram,a.Effect.gen(function*(){let o=Date.now();yield*a.Effect.logInfo(`Starting streaming write to GCS`).pipe(a.Effect.annotateLogs({upload_id:t,bucket:f.name,size_hint:r.sizeHint})),yield*(0,i.gcsUploadRequestsTotal)(a.Effect.succeed(1)),yield*(0,i.gcsActiveUploadsGauge)(a.Effect.succeed(1));let s=f.file(t),c={resumable:!0,metadata:r.metadata?{metadata:r.metadata}:void 0};r.contentType&&(c.contentType=r.contentType);let l=s.createWriteStream(c),u=yield*a.Effect.tryPromise({try:()=>new Promise((n,o)=>{let s=0,c=new e.PassThrough;c.on(`data`,e=>{s+=e.length}),c.pipe(l),l.on(`error`,e=>{a.Effect.runSync((0,i.trackGCSError)(`writeStream`,e,{upload_id:t,bucket:f.name})),o(e)}),l.on(`finish`,()=>{n(s)});let u=a.Stream.toReadableStream(r.stream),d=e.Readable.fromWeb(u);d.on(`error`,e=>{a.Effect.runSync((0,i.trackGCSError)(`writeStream`,e,{upload_id:t,bucket:f.name,phase:`read`})),c.destroy(e),o(e)}),(0,e.pipeline)(d,c,e=>{e&&(a.Effect.runSync((0,i.trackGCSError)(`writeStream`,e,{upload_id:t,bucket:f.name,phase:`pipeline`})),o(e))})}),catch:e=>(a.Effect.runSync((0,i.gcsUploadErrorsTotal)(a.Effect.succeed(1))),a.Effect.runSync((0,i.gcsActiveUploadsGauge)(a.Effect.succeed(-1))),new n.UploadistaError({code:`FILE_WRITE_ERROR`,status:500,body:`Failed to write stream to GCS`,details:`GCS streaming write failed: ${String(e)}`}))}),d=Date.now()-o;return yield*(0,i.logGCSUploadCompletion)(t,{fileSize:u,totalDurationMs:d,partsCount:1,averagePartSize:u,throughputBps:d>0?u*1e3/d:0,retryCount:0}),yield*(0,i.gcsUploadSuccessTotal)(a.Effect.succeed(1)),yield*(0,i.gcsActiveUploadsGauge)(a.Effect.succeed(-1)),yield*(0,i.gcsFileSizeHistogram)(a.Effect.succeed(u)),yield*a.Effect.logInfo(`Streaming write to GCS completed`).pipe(a.Effect.annotateLogs({upload_id:t,total_bytes:u,duration_ms:d})),{id:t,size:u,path:t,bucket:f.name}})),getCapabilities:p,validateUploadStrategy:e=>{let t=p(),n=(()=>{switch(e){case`parallel`:return t.supportsParallelUploads;case`single`:return!0;default:return!1}})();return a.Effect.succeed(n)}}}var u=class extends a.Context.Tag(`GCSClientService`)(){};function d(r){let i=new t.Storage({keyFilename:r.keyFilename,credentials:r.credentials,projectId:r.projectId}).bucket(r.bucket),o=e=>a.Effect.tryPromise({try:async()=>{let t=i.file(e).createReadStream();return new ReadableStream({start(e){t.on(`data`,t=>{e.enqueue(new Uint8Array(t))}),t.on(`end`,()=>{e.close()}),t.on(`error`,t=>{e.error(t)})}})},catch:e=>e&&typeof e==`object`&&`code`in e&&e.code===404?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),s=e=>a.Effect.tryPromise({try:async()=>{let[t]=await i.file(e).getMetadata();return{name:t.name,bucket:t.bucket,size:t.size?Number.parseInt(`${t.size}`,10):void 0,contentType:t.contentType,metadata:(e=>{if(!e)return{};if(typeof e.metadata==`string`)try{return JSON.parse(e.metadata)}catch{return e}return e})(t.metadata),generation:t.generation,timeCreated:t.timeCreated,updated:t.updated}},catch:e=>e&&typeof e==`object`&&`code`in e&&e.code===404?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),c=e=>a.Effect.tryPromise({try:async()=>{let[t]=await i.file(e).exists();return t},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),l=(e,t,r)=>a.Effect.tryPromise({try:async()=>new Promise((n,a)=>{let o=i.file(e),s={metadata:{contentType:r?.contentType||`application/octet-stream`,metadata:r?.metadata||{}}},c=o.createWriteStream(s);c.on(`error`,a),c.on(`finish`,()=>{n(o.name)}),c.end(Buffer.from(t))}),catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),u=(t,r,o,s,c)=>a.Effect.tryPromise({try:async()=>new Promise((a,l)=>{let u=i.file(t),d={metadata:{contentType:s?.contentType||`application/octet-stream`,metadata:s?.metadata||{}}},f=u.createWriteStream(d),p=r,m=new e.Transform({transform(e,t,n){p+=e.length,c?.(p),n(null,e)}});(0,e.pipeline)(e.Readable.fromWeb(o),m,f,e=>{e?l(n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})):a(p)})}),catch:e=>(console.error(`error putting object from stream`,e),n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e}))}),d=e=>a.Effect.tryPromise({try:async()=>{await i.file(e).delete({ignoreNotFound:!0})},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),f=e=>a.Effect.tryPromise({try:async()=>`resumable://nodejs/${e.bucket}/${e.key}`,catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),p=(e,t,r,o)=>a.Effect.tryPromise({try:async()=>{let n=e.split(`/`).pop();if(!n)throw Error(`Invalid upload URL`);let a=i.file(n);return new Promise((e,n)=>{let i=a.createWriteStream({resumable:!0,offset:r});i.on(`error`,n),i.on(`finish`,()=>{e({completed:o?r+t.length>=o:!1,bytesUploaded:r+t.length})}),i.end(Buffer.from(t))})},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),m=e=>a.Effect.promise(async()=>{try{let t=e.split(`/`).pop();if(!t)throw Error(`Invalid upload URL`);let[n]=await i.file(t).getMetadata();return{bytesUploaded:n.size?Number.parseInt(`${n.size}`,10):0,completed:!0}}catch{return{bytesUploaded:0,completed:!1}}}),h=e=>a.Effect.tryPromise({try:async()=>{let t=e.split(`/`).pop();if(!t)throw Error(`Invalid upload URL`);await i.file(t).delete({ignoreNotFound:!0})},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),g=(e,t,r)=>a.Effect.tryPromise({try:async()=>{let n=e.map(e=>i.file(e)),a=i.file(t);return await i.combine(n,a),r?.metadata&&await a.setMetadata({metadata:r.metadata}),t},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})});return{bucket:r.bucket,getObject:o,getObjectBuffer:e=>a.Effect.tryPromise({try:async()=>{let[t]=await i.file(e).download();return new Uint8Array(t)},catch:e=>e&&typeof e==`object`&&`code`in e&&e.code===404?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`FILE_READ_ERROR`,{cause:e})}),getObjectMetadata:s,objectExists:c,putObject:l,putObjectFromStream:u,putObjectFromStreamWithPatching:(e,t,n,r,i,o=!1)=>a.Effect.gen(function*(){if(!o)return yield*u(e,t,n,r,i);let a=`${e}_patch`,s=yield*u(a,t,n,r,i);return yield*g([e,a],e,r),yield*d(a),s}),deleteObject:d,createResumableUpload:f,uploadChunk:p,getUploadStatus:m,cancelUpload:h,composeObjects:g,putTemporaryObject:(e,t,n)=>l(`${e}_tmp`,t,n),getTemporaryObject:e=>a.Effect.gen(function*(){try{return yield*o(`${e}_tmp`)}catch{return}}),deleteTemporaryObject:e=>d(`${e}_tmp`)}}const f=e=>a.Layer.succeed(u,d(e));function p(e){if(!e.accessToken)throw Error(`accessToken is required for REST API implementation`);let t=`https://storage.googleapis.com/storage/v1/b/${e.bucket}`,r=`https://storage.googleapis.com/upload/storage/v1/b/${e.bucket}/o`,i=e.accessToken,o=()=>({Authorization:`Bearer ${i}`,"Content-Type":`application/json`}),s=e=>a.Effect.tryPromise({try:async()=>{let n=await fetch(`${t}/o/${encodeURIComponent(e)}?alt=media`,{headers:{Authorization:`Bearer ${i}`}});if(!n.ok)throw n.status===404?Error(`File not found`):Error(`HTTP ${n.status}: ${n.statusText}`);if(!n.body)throw Error(`body not found`);return n.body},catch:e=>e instanceof Error&&e.message.includes(`not found`)?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),c=e=>a.Effect.tryPromise({try:async()=>{let n=await fetch(`${t}/o/${encodeURIComponent(e)}`,{headers:o()});if(!n.ok)throw n.status===404?Error(`File not found`):Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();return{name:r.name,bucket:r.bucket,size:r.size?Number.parseInt(r.size,10):void 0,contentType:r.contentType,metadata:r.metadata||{},generation:r.generation,timeCreated:r.timeCreated,updated:r.updated}},catch:e=>e instanceof Error&&e.message.includes(`not found`)?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),l=e=>a.Effect.tryPromise({try:async()=>(await fetch(`${t}/o/${encodeURIComponent(e)}`,{method:`HEAD`,headers:{Authorization:`Bearer ${i}`}})).ok,catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),u=(e,t,o)=>a.Effect.tryPromise({try:async()=>{let n={name:e,contentType:o?.contentType||`application/octet-stream`,metadata:o?.metadata||{}},a=await fetch(`${r}?uploadType=media&name=${encodeURIComponent(e)}`,{method:`POST`,headers:{Authorization:`Bearer ${i}`,"Content-Type":n.contentType,"Content-Length":t.length.toString()},body:t});if(!a.ok)throw Error(`HTTP ${a.status}: ${a.statusText}`);return e},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),d=e=>a.Effect.tryPromise({try:async()=>{let n=await fetch(`${t}/o/${encodeURIComponent(e)}`,{method:`DELETE`,headers:{Authorization:`Bearer ${i}`}});if(!n.ok&&n.status!==404)throw Error(`HTTP ${n.status}: ${n.statusText}`)},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})});return{bucket:e.bucket,getObject:s,getObjectBuffer:e=>a.Effect.tryPromise({try:async()=>{let n=await fetch(`${t}/o/${encodeURIComponent(e)}?alt=media`,{headers:o()});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())},catch:e=>n.UploadistaError.fromCode(`FILE_READ_ERROR`,{cause:e})}),getObjectMetadata:c,objectExists:l,putObject:u,deleteObject:d,createResumableUpload:e=>a.Effect.tryPromise({try:async()=>{let t={name:e.key,contentType:e.contentType||`application/octet-stream`,metadata:e.metadata||{}},n=await fetch(`${r}?uploadType=resumable&name=${encodeURIComponent(e.key)}`,{method:`POST`,headers:{Authorization:`Bearer ${i}`,"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let a=n.headers.get(`Location`);if(!a)throw Error(`No upload URL returned`);return a},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),uploadChunk:(e,t,r,i)=>a.Effect.tryPromise({try:async()=>{let n=r+t.length-1,a=i?`bytes ${r}-${n}/${i}`:`bytes ${r}-${n}/*`,o=await fetch(e,{method:`PUT`,headers:{"Content-Length":t.length.toString(),"Content-Range":a},body:t}),s=o.status===200||o.status===201;if(!s&&o.status!==308)throw Error(`HTTP ${o.status}: ${o.statusText}`);return{completed:s,bytesUploaded:n+1}},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),getUploadStatus:e=>a.Effect.tryPromise({try:async()=>{let t=await fetch(e,{method:`PUT`,headers:{"Content-Range":`bytes */*`}});if(t.status===308){let e=t.headers.get(`Range`);return{bytesUploaded:e?Number.parseInt(e.split(`-`)[1],10)+1:0,completed:!1}}else if(t.status===200||t.status===201)return{bytesUploaded:0,completed:!0};else throw Error(`HTTP ${t.status}: ${t.statusText}`)},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),cancelUpload:e=>a.Effect.tryPromise({try:async()=>{await fetch(e,{method:`DELETE`})},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),composeObjects:(e,r,i)=>a.Effect.tryPromise({try:async()=>{let n={kind:`storage#composeRequest`,sourceObjects:e.map(e=>({name:e})),destination:{name:r,contentType:i?.contentType||`application/octet-stream`,metadata:i?.metadata||{}}},a=await fetch(`${t}/o/${encodeURIComponent(r)}/compose`,{method:`POST`,headers:o(),body:JSON.stringify(n)});if(!a.ok)throw Error(`HTTP ${a.status}: ${a.statusText}`);return r},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),putTemporaryObject:(e,t,n)=>u(`${e}_tmp`,t,n),getTemporaryObject:e=>a.Effect.tryPromise({try:async()=>{try{return await s(`${e}_tmp`).pipe(a.Effect.runPromise)}catch{return}},catch:()=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`)}),deleteTemporaryObject:e=>d(`${e}_tmp`)}}const m=e=>a.Layer.succeed(u,p(e));function h(e){return{size:e.size?.toString()??null,sizeIsDeferred:`${e.sizeIsDeferred}`,offset:e.offset?.toString()??`0`,metadata:JSON.stringify(e.metadata),storage:JSON.stringify(e.storage)}}const g=(e,t,r)=>a.Effect.gen(function*(){try{let n=yield*r.getObjectMetadata(e),i=yield*t.get(e);return{id:e,size:n.size,offset:n.size||0,metadata:n.metadata,storage:{id:i.storage.id,type:i.storage.type,path:e,bucket:r.bucket}}}catch(e){if(e instanceof n.UploadistaError&&e.code===`FILE_NOT_FOUND`)return yield*a.Effect.fail(e);throw e}});function _(){return a.Effect.gen(function*(){let e=yield*u,t=yield*r.UploadFileKVStore,i=()=>({supportsParallelUploads:!1,supportsConcatenation:!0,supportsDeferredLength:!0,supportsResumableUploads:!0,supportsTransactionalUploads:!1,maxConcurrentUploads:1,minChunkSize:void 0,maxChunkSize:void 0,maxParts:void 0,optimalChunkSize:8*1024*1024,requiresOrderedChunks:!0});return{bucket:e.bucket,create:t=>a.Effect.gen(function*(){if(!t.id)return yield*a.Effect.fail(n.UploadistaError.fromCode(`FILE_NOT_FOUND`));t.storage={id:t.storage.id,type:t.storage.type,path:t.id,bucket:e.bucket};let r={bucket:e.bucket,key:t.id,contentType:t.metadata?.contentType?.toString()||`application/octet-stream`,metadata:h(t)};return yield*e.putObject(t.id,new Uint8Array,r),t}),remove:t=>e.deleteObject(t),write:(n,r)=>a.Effect.gen(function*(){let{file_id:i,offset:o,stream:s}=n,{onProgress:c}=r,l=yield*g(i,t,e);l.offset=o,yield*t.set(i,l);let u={bucket:e.bucket,key:i,contentType:l.metadata?.contentType||`application/octet-stream`,metadata:h(l)},d=a.Stream.toReadableStream(s);if(e.putObjectFromStreamWithPatching){let t=l.offset>0;return yield*e.putObjectFromStreamWithPatching(i,l.offset,d,u,c,t)}else{let t=d.getReader(),n=[],r=0;for(;;){let{done:e,value:i}=yield*a.Effect.promise(()=>t.read());if(e)break;n.push(i);let o=i.byteLength;r+=o,c?.(r)}let o=new Uint8Array(r),s=0;for(let e of n)o.set(e,s),s+=e.byteLength;if(l.offset===0)yield*e.putObject(i,o,u);else{let t=`${i}_patch`;yield*e.putTemporaryObject(t,o,u),yield*e.composeObjects([i,t],i,u),yield*e.deleteTemporaryObject(t)}return r}}),getCapabilities:i,validateUploadStrategy:e=>{let t=i(),n=(()=>{switch(e){case`parallel`:return t.supportsParallelUploads;case`single`:return!0;default:return!1}})();return a.Effect.succeed(n)},read:t=>a.Effect.gen(function*(){return yield*e.getObjectBuffer(t)})}})}const v=e=>_().pipe(a.Effect.provide(m(e))),y=e=>_().pipe(a.Effect.provide(f(e)));exports.GCSClientNodeJSLayer=f,exports.GCSClientRESTLayer=m,exports.GCSClientService=u,exports.createGCSStore=c,exports.gcsStore=l,exports.gcsStoreNodejs=y,exports.gcsStoreRest=v;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`node:stream`),t=require(`@google-cloud/storage`),n=require(`@uploadista/core/errors`),r=require(`@uploadista/core/types`),i=require(`@uploadista/observability`),a=require(`effect`);function o(e){return{size:e.size??null,sizeIsDeferred:`${e.sizeIsDeferred}`,offset:e.offset,metadata:JSON.stringify(e.metadata),storage:JSON.stringify(e.storage)}}const s=(e,t,r)=>a.Effect.gen(function*(){try{let[n]=yield*a.Effect.promise(()=>e.file(t).getMetadata()),{size:i,metadata:o}=n,s=yield*r.get(t);return{id:t,size:i?Number.parseInt(`${i}`,10):void 0,offset:n.size?Number.parseInt(`${n.size}`,10):0,metadata:o||void 0,storage:{id:s.storage.id,type:s.storage.type,path:t,bucket:e.name}}}catch(e){if(e&&typeof e==`object`&&`code`in e&&e.code===404)return yield*a.Effect.fail(n.UploadistaError.fromCode(`FILE_NOT_FOUND`));throw e}});function c({keyFilename:e,credentials:t,bucketName:n}){return a.Effect.gen(function*(){return l({keyFilename:e,credentials:t,bucketName:n,kvStore:yield*r.UploadFileKVStore})})}function l({keyFilename:c,credentials:l,bucketName:u,kvStore:d}){let f=new t.Storage(c?{keyFilename:c}:l?{credentials:l}:{}).bucket(u),p=()=>({supportsParallelUploads:!1,supportsConcatenation:!0,supportsDeferredLength:!0,supportsResumableUploads:!0,supportsTransactionalUploads:!1,supportsStreamingRead:!0,supportsStreamingWrite:!0,maxConcurrentUploads:1,minChunkSize:void 0,maxChunkSize:void 0,maxParts:void 0,optimalChunkSize:8*1024*1024,requiresOrderedChunks:!0,requiresMimeTypeValidation:!0,maxValidationSize:void 0});return{bucket:f.name,create:t=>a.Effect.gen(function*(){if(yield*(0,i.gcsUploadRequestsTotal)(a.Effect.succeed(1)),yield*(0,i.gcsActiveUploadsGauge)(a.Effect.succeed(1)),yield*(0,i.gcsFileSizeHistogram)(a.Effect.succeed(t.size||0)),!t.id)return yield*(0,i.gcsUploadErrorsTotal)(a.Effect.succeed(1)),yield*a.Effect.fail(n.UploadistaError.fromCode(`FILE_NOT_FOUND`));let r=f.file(t.id);t.storage={id:t.storage.id,type:t.storage.type,path:t.id,bucket:f.name},console.log(`file`,r.id);let s={metadata:{metadata:{...o(t)}}};return t.metadata?.contentType&&(s.contentType=t.metadata.contentType.toString()),yield*a.Effect.tryPromise({try:()=>(console.log(`creating file`,r.id),new Promise((n,i)=>{let a=new e.PassThrough;a.end(),a.pipe(r.createWriteStream(s)).on(`error`,i).on(`finish`,()=>{n(t)})})),catch:e=>(console.error(`error creating file`,e),a.Effect.runSync((0,i.trackGCSError)(`create`,e,{upload_id:t.id,bucket:f.name})),n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e}))})}),read:e=>a.Effect.tryPromise({try:async()=>{let[t]=await f.file(e).download();return new Uint8Array(t)},catch:t=>(a.Effect.runSync((0,i.trackGCSError)(`read`,t,{upload_id:e,bucket:f.name})),t&&typeof t==`object`&&`code`in t&&t.code===404?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`FILE_READ_ERROR`,{cause:t}))}),readStream:(e,t)=>a.Effect.gen(function*(){let o={...r.DEFAULT_STREAMING_CONFIG,...t},s=f.file(e),[c]=yield*a.Effect.tryPromise({try:()=>s.exists(),catch:t=>(a.Effect.runSync((0,i.trackGCSError)(`readStream`,t,{upload_id:e,bucket:f.name})),n.UploadistaError.fromCode(`FILE_READ_ERROR`,{cause:t}))});if(!c)return yield*a.Effect.fail(n.UploadistaError.fromCode(`FILE_NOT_FOUND`));let l=s.createReadStream();return a.Stream.async(t=>{let r=o.chunkSize,s=new Uint8Array;return l.on(`data`,e=>{let n=new Uint8Array(s.length+e.length);for(n.set(s),n.set(new Uint8Array(e),s.length),s=n;s.length>=r;){let e=s.slice(0,r);s=s.slice(r),t.single(e)}}),l.on(`end`,()=>{s.length>0&&t.single(s),t.end()}),l.on(`error`,r=>{a.Effect.runSync((0,i.trackGCSError)(`readStream`,r,{upload_id:e,bucket:f.name})),t.fail(new n.UploadistaError({code:`FILE_READ_ERROR`,status:500,body:`Failed to read GCS file stream`,details:`GCS stream read failed: ${String(r)}`}))}),a.Effect.sync(()=>{l.destroy()})})}),remove:e=>a.Effect.gen(function*(){try{yield*a.Effect.promise(()=>f.file(e).delete()),yield*(0,i.gcsActiveUploadsGauge)(a.Effect.succeed(-1))}catch(t){throw a.Effect.runSync((0,i.trackGCSError)(`remove`,t,{upload_id:e,bucket:f.name})),t}}),write:(t,r)=>(0,i.withGCSUploadMetrics)(t.file_id,(0,i.withGCSTimingMetrics)(i.gcsUploadDurationHistogram,a.Effect.gen(function*(){let c=Date.now(),{file_id:l,offset:u,stream:p}=t;console.log(`write`,l,u);let{onProgress:m}=r,h=yield*s(f,l,d);return console.log(`upload`,h),yield*a.Effect.promise(()=>new Promise((t,r)=>{let s=f.file(l),d=h.offset===0?s:f.file(`${l}_patch`);h.offset=u;let g={metadata:{metadata:{...o(h)}}},_=d.createWriteStream(g);if(!_){a.Effect.runSync((0,i.gcsUploadErrorsTotal)(a.Effect.succeed(1))),r(n.UploadistaError.fromCode(`FILE_WRITE_ERROR`));return}let v=h.offset,y=a.Stream.toReadableStream(p),b=new e.Transform({transform(e,t,n){v+=e.length,m?.(v),n(null,e)}});(0,e.pipeline)(e.Readable.fromWeb(y),b,_,async e=>{if(e){console.error(`error writing file`,e),a.Effect.runSync((0,i.trackGCSError)(`write`,e,{upload_id:l,bucket:f.name,offset:u}));try{await d.delete({ignoreNotFound:!0})}finally{r(n.UploadistaError.fromCode(`FILE_WRITE_ERROR`))}}else try{s!==d&&(await f.combine([s,d],s),await Promise.all([s.setMetadata(g.metadata),d.delete({ignoreNotFound:!0})])),a.Effect.runSync((0,i.logGCSUploadCompletion)(l,{fileSize:h.size||0,totalDurationMs:Date.now()-c,partsCount:1,averagePartSize:h.size,throughputBps:(h.size||0)/(Date.now()-c),retryCount:0})),a.Effect.runSync((0,i.gcsUploadSuccessTotal)(a.Effect.succeed(1))),a.Effect.runSync((0,i.gcsActiveUploadsGauge)(a.Effect.succeed(-1))),t(v)}catch(e){console.error(e),a.Effect.runSync((0,i.trackGCSError)(`write`,e,{upload_id:l,bucket:f.name,operation:`combine`})),r(n.UploadistaError.fromCode(`FILE_WRITE_ERROR`))}})}))}))),writeStream:(t,r)=>(0,i.withGCSTimingMetrics)(i.gcsUploadDurationHistogram,a.Effect.gen(function*(){let o=Date.now();yield*a.Effect.logInfo(`Starting streaming write to GCS`).pipe(a.Effect.annotateLogs({upload_id:t,bucket:f.name,size_hint:r.sizeHint})),yield*(0,i.gcsUploadRequestsTotal)(a.Effect.succeed(1)),yield*(0,i.gcsActiveUploadsGauge)(a.Effect.succeed(1));let s=f.file(t),c={resumable:!0,metadata:r.metadata?{metadata:r.metadata}:void 0};r.contentType&&(c.contentType=r.contentType);let l=s.createWriteStream(c),u=yield*a.Effect.tryPromise({try:()=>new Promise((n,o)=>{let s=0,c=new e.PassThrough;c.on(`data`,e=>{s+=e.length}),c.pipe(l),l.on(`error`,e=>{a.Effect.runSync((0,i.trackGCSError)(`writeStream`,e,{upload_id:t,bucket:f.name})),o(e)}),l.on(`finish`,()=>{n(s)});let u=a.Stream.toReadableStream(r.stream),d=e.Readable.fromWeb(u);d.on(`error`,e=>{a.Effect.runSync((0,i.trackGCSError)(`writeStream`,e,{upload_id:t,bucket:f.name,phase:`read`})),c.destroy(e),o(e)}),(0,e.pipeline)(d,c,e=>{e&&(a.Effect.runSync((0,i.trackGCSError)(`writeStream`,e,{upload_id:t,bucket:f.name,phase:`pipeline`})),o(e))})}),catch:e=>(a.Effect.runSync((0,i.gcsUploadErrorsTotal)(a.Effect.succeed(1))),a.Effect.runSync((0,i.gcsActiveUploadsGauge)(a.Effect.succeed(-1))),new n.UploadistaError({code:`FILE_WRITE_ERROR`,status:500,body:`Failed to write stream to GCS`,details:`GCS streaming write failed: ${String(e)}`}))}),d=Date.now()-o;return yield*(0,i.logGCSUploadCompletion)(t,{fileSize:u,totalDurationMs:d,partsCount:1,averagePartSize:u,throughputBps:d>0?u*1e3/d:0,retryCount:0}),yield*(0,i.gcsUploadSuccessTotal)(a.Effect.succeed(1)),yield*(0,i.gcsActiveUploadsGauge)(a.Effect.succeed(-1)),yield*(0,i.gcsFileSizeHistogram)(a.Effect.succeed(u)),yield*a.Effect.logInfo(`Streaming write to GCS completed`).pipe(a.Effect.annotateLogs({upload_id:t,total_bytes:u,duration_ms:d})),{id:t,size:u,path:t,bucket:f.name}})),getCapabilities:p,validateUploadStrategy:e=>{let t=p(),n=(()=>{switch(e){case`parallel`:return t.supportsParallelUploads;case`single`:return!0;default:return!1}})();return a.Effect.succeed(n)}}}var u=class extends a.Context.Tag(`GCSClientService`)(){};function d(r){let i=new t.Storage({keyFilename:r.keyFilename,credentials:r.credentials,projectId:r.projectId}).bucket(r.bucket),o=e=>a.Effect.tryPromise({try:async()=>{let t=i.file(e).createReadStream();return new ReadableStream({start(e){t.on(`data`,t=>{e.enqueue(new Uint8Array(t))}),t.on(`end`,()=>{e.close()}),t.on(`error`,t=>{e.error(t)})}})},catch:e=>e&&typeof e==`object`&&`code`in e&&e.code===404?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),s=e=>a.Effect.tryPromise({try:async()=>{let[t]=await i.file(e).getMetadata();return{name:t.name,bucket:t.bucket,size:t.size?Number.parseInt(`${t.size}`,10):void 0,contentType:t.contentType,metadata:(e=>{if(!e)return{};if(typeof e.metadata==`string`)try{return JSON.parse(e.metadata)}catch{return e}return e})(t.metadata),generation:t.generation,timeCreated:t.timeCreated,updated:t.updated}},catch:e=>e&&typeof e==`object`&&`code`in e&&e.code===404?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),c=e=>a.Effect.tryPromise({try:async()=>{let[t]=await i.file(e).exists();return t},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),l=(e,t,r)=>a.Effect.tryPromise({try:async()=>new Promise((n,a)=>{let o=i.file(e),s={metadata:{contentType:r?.contentType||`application/octet-stream`,metadata:r?.metadata||{}}},c=o.createWriteStream(s);c.on(`error`,a),c.on(`finish`,()=>{n(o.name)}),c.end(Buffer.from(t))}),catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),u=(t,r,o,s,c)=>a.Effect.tryPromise({try:async()=>new Promise((a,l)=>{let u=i.file(t),d={metadata:{contentType:s?.contentType||`application/octet-stream`,metadata:s?.metadata||{}}},f=u.createWriteStream(d),p=r,m=new e.Transform({transform(e,t,n){p+=e.length,c?.(p),n(null,e)}});(0,e.pipeline)(e.Readable.fromWeb(o),m,f,e=>{e?l(n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})):a(p)})}),catch:e=>(console.error(`error putting object from stream`,e),n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e}))}),d=e=>a.Effect.tryPromise({try:async()=>{await i.file(e).delete({ignoreNotFound:!0})},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),f=e=>a.Effect.tryPromise({try:async()=>`resumable://nodejs/${e.bucket}/${e.key}`,catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),p=(e,t,r,o)=>a.Effect.tryPromise({try:async()=>{let n=e.split(`/`).pop();if(!n)throw Error(`Invalid upload URL`);let a=i.file(n);return new Promise((e,n)=>{let i=a.createWriteStream({resumable:!0,offset:r});i.on(`error`,n),i.on(`finish`,()=>{e({completed:o?r+t.length>=o:!1,bytesUploaded:r+t.length})}),i.end(Buffer.from(t))})},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),m=e=>a.Effect.promise(async()=>{try{let t=e.split(`/`).pop();if(!t)throw Error(`Invalid upload URL`);let[n]=await i.file(t).getMetadata();return{bytesUploaded:n.size?Number.parseInt(`${n.size}`,10):0,completed:!0}}catch{return{bytesUploaded:0,completed:!1}}}),h=e=>a.Effect.tryPromise({try:async()=>{let t=e.split(`/`).pop();if(!t)throw Error(`Invalid upload URL`);await i.file(t).delete({ignoreNotFound:!0})},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),g=(e,t,r)=>a.Effect.tryPromise({try:async()=>{let n=e.map(e=>i.file(e)),a=i.file(t);return await i.combine(n,a),r?.metadata&&await a.setMetadata({metadata:r.metadata}),t},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})});return{bucket:r.bucket,getObject:o,getObjectBuffer:e=>a.Effect.tryPromise({try:async()=>{let[t]=await i.file(e).download();return new Uint8Array(t)},catch:e=>e&&typeof e==`object`&&`code`in e&&e.code===404?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`FILE_READ_ERROR`,{cause:e})}),getObjectMetadata:s,objectExists:c,putObject:l,putObjectFromStream:u,putObjectFromStreamWithPatching:(e,t,n,r,i,o=!1)=>a.Effect.gen(function*(){if(!o)return yield*u(e,t,n,r,i);let a=`${e}_patch`,s=yield*u(a,t,n,r,i);return yield*g([e,a],e,r),yield*d(a),s}),deleteObject:d,createResumableUpload:f,uploadChunk:p,getUploadStatus:m,cancelUpload:h,composeObjects:g,putTemporaryObject:(e,t,n)=>l(`${e}_tmp`,t,n),getTemporaryObject:e=>a.Effect.gen(function*(){try{return yield*o(`${e}_tmp`)}catch{return}}),deleteTemporaryObject:e=>d(`${e}_tmp`)}}const f=e=>a.Layer.succeed(u,d(e));function p(e){if(!e.accessToken)throw Error(`accessToken is required for REST API implementation`);let t=`https://storage.googleapis.com/storage/v1/b/${e.bucket}`,r=`https://storage.googleapis.com/upload/storage/v1/b/${e.bucket}/o`,i=e.accessToken,o=()=>({Authorization:`Bearer ${i}`,"Content-Type":`application/json`}),s=e=>a.Effect.tryPromise({try:async()=>{let n=await fetch(`${t}/o/${encodeURIComponent(e)}?alt=media`,{headers:{Authorization:`Bearer ${i}`}});if(!n.ok)throw n.status===404?Error(`File not found`):Error(`HTTP ${n.status}: ${n.statusText}`);if(!n.body)throw Error(`body not found`);return n.body},catch:e=>e instanceof Error&&e.message.includes(`not found`)?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),c=e=>a.Effect.tryPromise({try:async()=>{let n=await fetch(`${t}/o/${encodeURIComponent(e)}`,{headers:o()});if(!n.ok)throw n.status===404?Error(`File not found`):Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();return{name:r.name,bucket:r.bucket,size:r.size?Number.parseInt(r.size,10):void 0,contentType:r.contentType,metadata:r.metadata||{},generation:r.generation,timeCreated:r.timeCreated,updated:r.updated}},catch:e=>e instanceof Error&&e.message.includes(`not found`)?n.UploadistaError.fromCode(`FILE_NOT_FOUND`):n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),l=e=>a.Effect.tryPromise({try:async()=>(await fetch(`${t}/o/${encodeURIComponent(e)}`,{method:`HEAD`,headers:{Authorization:`Bearer ${i}`}})).ok,catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),u=(e,t,o)=>a.Effect.tryPromise({try:async()=>{let n={name:e,contentType:o?.contentType||`application/octet-stream`,metadata:o?.metadata||{}},a=await fetch(`${r}?uploadType=media&name=${encodeURIComponent(e)}`,{method:`POST`,headers:{Authorization:`Bearer ${i}`,"Content-Type":n.contentType,"Content-Length":t.length.toString()},body:t});if(!a.ok)throw Error(`HTTP ${a.status}: ${a.statusText}`);return e},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),d=e=>a.Effect.tryPromise({try:async()=>{let n=await fetch(`${t}/o/${encodeURIComponent(e)}`,{method:`DELETE`,headers:{Authorization:`Bearer ${i}`}});if(!n.ok&&n.status!==404)throw Error(`HTTP ${n.status}: ${n.statusText}`)},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})});return{bucket:e.bucket,getObject:s,getObjectBuffer:e=>a.Effect.tryPromise({try:async()=>{let n=await fetch(`${t}/o/${encodeURIComponent(e)}?alt=media`,{headers:o()});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())},catch:e=>n.UploadistaError.fromCode(`FILE_READ_ERROR`,{cause:e})}),getObjectMetadata:c,objectExists:l,putObject:u,deleteObject:d,createResumableUpload:e=>a.Effect.tryPromise({try:async()=>{let t={name:e.key,contentType:e.contentType||`application/octet-stream`,metadata:e.metadata||{}},n=await fetch(`${r}?uploadType=resumable&name=${encodeURIComponent(e.key)}`,{method:`POST`,headers:{Authorization:`Bearer ${i}`,"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let a=n.headers.get(`Location`);if(!a)throw Error(`No upload URL returned`);return a},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),uploadChunk:(e,t,r,i)=>a.Effect.tryPromise({try:async()=>{let n=r+t.length-1,a=i?`bytes ${r}-${n}/${i}`:`bytes ${r}-${n}/*`,o=await fetch(e,{method:`PUT`,headers:{"Content-Length":t.length.toString(),"Content-Range":a},body:t}),s=o.status===200||o.status===201;if(!s&&o.status!==308)throw Error(`HTTP ${o.status}: ${o.statusText}`);return{completed:s,bytesUploaded:n+1}},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),getUploadStatus:e=>a.Effect.tryPromise({try:async()=>{let t=await fetch(e,{method:`PUT`,headers:{"Content-Range":`bytes */*`}});if(t.status===308){let e=t.headers.get(`Range`);return{bytesUploaded:e?Number.parseInt(e.split(`-`)[1],10)+1:0,completed:!1}}else if(t.status===200||t.status===201)return{bytesUploaded:0,completed:!0};else throw Error(`HTTP ${t.status}: ${t.statusText}`)},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),cancelUpload:e=>a.Effect.tryPromise({try:async()=>{await fetch(e,{method:`DELETE`})},catch:e=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`,{cause:e})}),composeObjects:(e,r,i)=>a.Effect.tryPromise({try:async()=>{let n={kind:`storage#composeRequest`,sourceObjects:e.map(e=>({name:e})),destination:{name:r,contentType:i?.contentType||`application/octet-stream`,metadata:i?.metadata||{}}},a=await fetch(`${t}/o/${encodeURIComponent(r)}/compose`,{method:`POST`,headers:o(),body:JSON.stringify(n)});if(!a.ok)throw Error(`HTTP ${a.status}: ${a.statusText}`);return r},catch:e=>n.UploadistaError.fromCode(`FILE_WRITE_ERROR`,{cause:e})}),putTemporaryObject:(e,t,n)=>u(`${e}_tmp`,t,n),getTemporaryObject:e=>a.Effect.tryPromise({try:async()=>{try{return await s(`${e}_tmp`).pipe(a.Effect.runPromise)}catch{return}},catch:()=>n.UploadistaError.fromCode(`UNKNOWN_ERROR`)}),deleteTemporaryObject:e=>d(`${e}_tmp`)}}const m=e=>a.Layer.succeed(u,p(e));function h(e){return{size:e.size?.toString()??null,sizeIsDeferred:`${e.sizeIsDeferred}`,offset:e.offset?.toString()??`0`,metadata:JSON.stringify(e.metadata),storage:JSON.stringify(e.storage)}}const g=(e,t,r)=>a.Effect.gen(function*(){try{let n=yield*r.getObjectMetadata(e),i=yield*t.get(e);return{id:e,size:n.size,offset:n.size||0,metadata:n.metadata,storage:{id:i.storage.id,type:i.storage.type,path:e,bucket:r.bucket}}}catch(e){if(e instanceof n.UploadistaError&&e.code===`FILE_NOT_FOUND`)return yield*a.Effect.fail(e);throw e}});function _(){return a.Effect.gen(function*(){let e=yield*u,t=yield*r.UploadFileKVStore,i=()=>({supportsParallelUploads:!1,supportsConcatenation:!0,supportsDeferredLength:!0,supportsResumableUploads:!0,supportsTransactionalUploads:!1,maxConcurrentUploads:1,minChunkSize:void 0,maxChunkSize:void 0,maxParts:void 0,optimalChunkSize:8*1024*1024,requiresOrderedChunks:!0});return{bucket:e.bucket,create:t=>a.Effect.gen(function*(){if(!t.id)return yield*a.Effect.fail(n.UploadistaError.fromCode(`FILE_NOT_FOUND`));t.storage={id:t.storage.id,type:t.storage.type,path:t.id,bucket:e.bucket};let r={bucket:e.bucket,key:t.id,contentType:t.metadata?.contentType?.toString()||`application/octet-stream`,metadata:h(t)};return yield*e.putObject(t.id,new Uint8Array,r),t}),remove:t=>e.deleteObject(t),write:(n,r)=>a.Effect.gen(function*(){let{file_id:i,offset:o,stream:s}=n,{onProgress:c}=r,l=yield*g(i,t,e);l.offset=o,yield*t.set(i,l);let u={bucket:e.bucket,key:i,contentType:l.metadata?.contentType||`application/octet-stream`,metadata:h(l)},d=a.Stream.toReadableStream(s);if(e.putObjectFromStreamWithPatching){let t=l.offset>0;return yield*e.putObjectFromStreamWithPatching(i,l.offset,d,u,c,t)}else{let t=d.getReader(),n=[],r=0;for(;;){let{done:e,value:i}=yield*a.Effect.promise(()=>t.read());if(e)break;n.push(i);let o=i.byteLength;r+=o,c?.(r)}let o=new Uint8Array(r),s=0;for(let e of n)o.set(e,s),s+=e.byteLength;if(l.offset===0)yield*e.putObject(i,o,u);else{let t=`${i}_patch`;yield*e.putTemporaryObject(t,o,u),yield*e.composeObjects([i,t],i,u),yield*e.deleteTemporaryObject(t)}return r}}),getCapabilities:i,validateUploadStrategy:e=>{let t=i(),n=(()=>{switch(e){case`parallel`:return t.supportsParallelUploads;case`single`:return!0;default:return!1}})();return a.Effect.succeed(n)},read:t=>a.Effect.gen(function*(){return yield*e.getObjectBuffer(t)})}})}const v=e=>_().pipe(a.Effect.provide(m(e))),y=e=>_().pipe(a.Effect.provide(f(e)));exports.GCSClientNodeJSLayer=f,exports.GCSClientRESTLayer=m,exports.GCSClientService=u,exports.createGCSStore=c,exports.gcsStore=l,exports.gcsStoreNodejs=y,exports.gcsStoreRest=v;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uploadista/data-store-gcs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "Google Cloud Storage data store for Uploadista",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Uploadista",
|
|
@@ -14,20 +14,20 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@google-cloud/storage": "7.
|
|
18
|
-
"@uploadista/core": "0.
|
|
19
|
-
"@uploadista/observability": "0.
|
|
17
|
+
"@google-cloud/storage": "7.19.0",
|
|
18
|
+
"@uploadista/core": "0.2.0",
|
|
19
|
+
"@uploadista/observability": "0.2.0"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"effect": "^3.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@effect/vitest": "0.27.0",
|
|
26
|
-
"effect": "3.19.
|
|
27
|
-
"tsdown": "0.20.
|
|
26
|
+
"effect": "3.19.17",
|
|
27
|
+
"tsdown": "0.20.3",
|
|
28
28
|
"vitest": "4.0.18",
|
|
29
|
-
"@uploadista/kv-store-memory": "0.
|
|
30
|
-
"@uploadista/typescript-config": "0.
|
|
29
|
+
"@uploadista/kv-store-memory": "0.2.0",
|
|
30
|
+
"@uploadista/typescript-config": "0.2.0"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsc --noEmit && tsdown",
|