pushduck
Version:
The fastest way to add file uploads to any web application. Enterprise security, edge-ready. Works with 16+ frameworks and 5+ storage providers. No heavy AWS SDK required.
22 lines (19 loc) • 22.1 kB
JavaScript
import{_defineProperty as e,checkFileExists as t,deleteFile as n,deleteFiles as r,deleteFilesByPrefix as i,fileExistsWithInfo as a,generateFileKey as o,generatePresignedDownloadUrl as s,generatePresignedUploadUrl as c,generatePresignedUploadUrls as l,getFileContentType as u,getFileInfo as d,getFileLastModified as f,getFileMetadata as p,getFileSize as m,getFileUrl as h,getFilesInfo as g,listDirectories as _,listFiles as v,listFilesByDate as y,listFilesByExtension as b,listFilesBySize as x,listFilesPaginated as S,listFilesPaginatedGenerator as C,logger as w,setFileMetadata as T,uploadFileToS3 as E,validateFile as D,validateFiles as O,validateS3Connection as k}from"./client-BQgbehVh.mjs";function A(e){return(t={})=>{let n={provider:e.provider};for(let[r]of Object.entries(e.configKeys))n[r]=t[r]||e.defaults[r]||``;e.customLogic&&Object.assign(n,e.customLogic(t,n));let r=N(n);if(!r.valid)throw Error(`Provider validation failed: ${r.errors.join(`, `)}`);return n}}const j={aws:{provider:`aws`,configKeys:{region:[`AWS_REGION`,`S3_REGION`],bucket:[`AWS_S3_BUCKET`,`S3_BUCKET`,`S3_BUCKET_NAME`],accessKeyId:[`AWS_ACCESS_KEY_ID`,`S3_ACCESS_KEY_ID`],secretAccessKey:[`AWS_SECRET_ACCESS_KEY`,`S3_SECRET_ACCESS_KEY`],sessionToken:[`AWS_SESSION_TOKEN`],acl:[`S3_ACL`],customDomain:[`S3_CUSTOM_DOMAIN`]},defaults:{region:`us-east-1`,acl:`private`}},cloudflareR2:{provider:`cloudflare-r2`,configKeys:{accountId:[`CLOUDFLARE_ACCOUNT_ID`,`R2_ACCOUNT_ID`],bucket:[`CLOUDFLARE_R2_BUCKET`,`R2_BUCKET`],accessKeyId:[`CLOUDFLARE_R2_ACCESS_KEY_ID`,`R2_ACCESS_KEY_ID`],secretAccessKey:[`CLOUDFLARE_R2_SECRET_ACCESS_KEY`,`R2_SECRET_ACCESS_KEY`],endpoint:[`CLOUDFLARE_R2_ENDPOINT`,`R2_ENDPOINT`],customDomain:[`R2_CUSTOM_DOMAIN`],acl:[]},defaults:{region:`auto`,acl:`private`},customLogic:(e,t)=>({endpoint:t.endpoint||(t.accountId?`https://${t.accountId}.r2.cloudflarestorage.com`:``)})},digitalOceanSpaces:{provider:`digitalocean-spaces`,configKeys:{region:[`DO_SPACES_REGION`,`DIGITALOCEAN_SPACES_REGION`],bucket:[`DO_SPACES_BUCKET`,`DIGITALOCEAN_SPACES_BUCKET`],accessKeyId:[`DO_SPACES_ACCESS_KEY_ID`,`DIGITALOCEAN_SPACES_ACCESS_KEY_ID`],secretAccessKey:[`DO_SPACES_SECRET_ACCESS_KEY`,`DIGITALOCEAN_SPACES_SECRET_ACCESS_KEY`],endpoint:[`DO_SPACES_ENDPOINT`,`DIGITALOCEAN_SPACES_ENDPOINT`],customDomain:[`DO_SPACES_CUSTOM_DOMAIN`],acl:[]},defaults:{region:`nyc3`,acl:`private`},customLogic:(e,t)=>({endpoint:t.endpoint||`https://${t.region}.digitaloceanspaces.com`})},minio:{provider:`minio`,configKeys:{endpoint:[`MINIO_ENDPOINT`],bucket:[`MINIO_BUCKET`],accessKeyId:[`MINIO_ACCESS_KEY_ID`,`MINIO_ACCESS_KEY`],secretAccessKey:[`MINIO_SECRET_ACCESS_KEY`,`MINIO_SECRET_KEY`],region:[`MINIO_REGION`],customDomain:[`MINIO_CUSTOM_DOMAIN`],acl:[]},defaults:{endpoint:`localhost:9000`,region:`us-east-1`,acl:`private`},customLogic:(e,t)=>({useSSL:e.useSSL??!1,port:e.port?Number(e.port):void 0})},gcs:{provider:`gcs`,configKeys:{projectId:[`GOOGLE_CLOUD_PROJECT_ID`,`GCS_PROJECT_ID`],bucket:[`GCS_BUCKET`,`GOOGLE_CLOUD_STORAGE_BUCKET`],keyFilename:[`GOOGLE_APPLICATION_CREDENTIALS`,`GCS_KEY_FILE`],region:[`GCS_REGION`],customDomain:[`GCS_CUSTOM_DOMAIN`],acl:[]},defaults:{region:`us-central1`,acl:`private`},customLogic:e=>({credentials:e.credentials})},s3Compatible:{provider:`s3-compatible`,configKeys:{endpoint:[`S3_ENDPOINT`,`S3_COMPATIBLE_ENDPOINT`],bucket:[`S3_BUCKET`,`S3_BUCKET_NAME`],accessKeyId:[`S3_ACCESS_KEY_ID`,`ACCESS_KEY_ID`],secretAccessKey:[`S3_SECRET_ACCESS_KEY`,`SECRET_ACCESS_KEY`],region:[`S3_REGION`,`REGION`],customDomain:[`S3_CUSTOM_DOMAIN`],acl:[`S3_ACL`]},defaults:{region:`us-east-1`,acl:`private`,forcePathStyle:!0}}};function M(e,t={}){let n=j[e];if(!n)throw Error(`Unknown provider type: ${e}`);return A(n)(t)}function N(e){let t=[];switch(e.bucket||t.push(`Bucket name is required`),e.provider){case`aws`:e.accessKeyId||t.push(`AWS Access Key ID is required`),e.secretAccessKey||t.push(`AWS Secret Access Key is required`),e.region||t.push(`AWS Region is required`);break;case`cloudflare-r2`:e.accountId||t.push(`Cloudflare Account ID is required`),e.accessKeyId||t.push(`R2 Access Key ID is required`),e.secretAccessKey||t.push(`R2 Secret Access Key is required`);break;case`digitalocean-spaces`:e.accessKeyId||t.push(`DigitalOcean Spaces Access Key ID is required`),e.secretAccessKey||t.push(`DigitalOcean Spaces Secret Access Key is required`),e.region||t.push(`DigitalOcean Spaces Region is required`);break;case`minio`:e.endpoint||t.push(`MinIO Endpoint is required`),e.accessKeyId||t.push(`MinIO Access Key ID is required`),e.secretAccessKey||t.push(`MinIO Secret Access Key is required`);break;case`gcs`:e.projectId||t.push(`Google Cloud Project ID is required`),!e.keyFilename&&!e.credentials&&t.push(`Google Cloud credentials (keyFilename or credentials object) are required`);break;case`s3-compatible`:e.endpoint||t.push(`S3-compatible endpoint is required`),e.accessKeyId||t.push(`Access Key ID is required`),e.secretAccessKey||t.push(`Secret Access Key is required`);break}return{valid:t.length===0,errors:t}}function P(e){switch(e.provider){case`aws`:return`https://s3.${e.region}.amazonaws.com`;case`cloudflare-r2`:return e.endpoint||`https://${e.accountId}.r2.cloudflarestorage.com`;case`digitalocean-spaces`:return e.endpoint||`https://${e.region}.digitaloceanspaces.com`;case`minio`:{let t=e.useSSL===!1?`http`:`https`,n=e.port?`:${e.port}`:``;return`${t}://${e.endpoint}${n}`}case`gcs`:return`https://storage.googleapis.com`;case`s3-compatible`:return e.endpoint;default:return``}}function F(e,t){async function n(n){try{let t=new URL(n.url),r=t.searchParams.get(`route`),i=t.searchParams.get(`action`)||`presign`;if(!r)return new Response(JSON.stringify({error:`Route parameter is required`}),{status:400,headers:{"Content-Type":`application/json`}});if(!e.getRouteNames().includes(r))return new Response(JSON.stringify({error:`Route "${r}" not found`}),{status:404,headers:{"Content-Type":`application/json`}});let a=await n.json();if(i===`presign`){let{files:t}=a;if(!Array.isArray(t))return new Response(JSON.stringify({error:`Files array is required`}),{status:400,headers:{"Content-Type":`application/json`}});let i=I(n),o=await e.generatePresignedUrls(r,i,t);return new Response(JSON.stringify({success:!0,results:o}),{status:200,headers:{"Content-Type":`application/json`}})}else if(i===`complete`){let{completions:t}=a;if(!Array.isArray(t))return new Response(JSON.stringify({error:`Completions array is required`}),{status:400,headers:{"Content-Type":`application/json`}});let i=I(n),o=await e.handleUploadComplete(r,i,t);return new Response(JSON.stringify({success:!0,results:o}),{status:200,headers:{"Content-Type":`application/json`}})}else return new Response(JSON.stringify({error:`Unknown action: ${i}`}),{status:400,headers:{"Content-Type":`application/json`}})}catch(e){let n=e instanceof Error?e:Error(`Unknown error`);return console.error(`S3 Handler Error:`,n),new Response(JSON.stringify({success:!1,error:n.message,details:t.debug?e:void 0}),{status:500,headers:{"Content-Type":`application/json`}})}}async function r(t){let n=e.getRouteNames();return new Response(JSON.stringify({success:!0,routes:n.map(e=>({name:e,type:`s3-upload`}))}),{status:200,headers:{"Content-Type":`application/json`}})}return{GET:r,POST:n}}function I(e){if(e.constructor.name===`NextRequest`)return e;let t=Object.create(e);return t}var L=class e{constructor(e,t={}){this.schema=e,this.config=t}middleware(t){let n={middleware:[...this.config.middleware||[],t]};return new e(this.schema,n)}paths(e){return this.config.paths={...this.config.paths,...e},this}onUploadStart(e){return this.config.onUploadStart=e,this}onUploadProgress(e){return this.config.onUploadProgress=e,this}onUploadComplete(e){return this.config.onUploadComplete=e,this}onUploadError(e){return this.config.onUploadError=e,this}_getConfig(){return{...this.config,schema:this.schema}}};function R(e,t,n,r,i,a){let s={file:e,metadata:t,globalConfig:i||{},routeName:n};if(r?.generateKey)return r.generateKey(s);let c=[],l=i?.prefix||`uploads`;c.push(l),r?.prefix&&c.push(r.prefix);let u;return i?.generateKey?(u=i.generateKey(e,t),u.startsWith(l+`/`)&&(u=u.substring(l.length+1))):u=o(a,{originalName:e.name,userId:t?.userId||t?.user?.id,prefix:``}),c.push(u),r?.suffix&&c.push(r.suffix),c.join(`/`).replace(/\/+/g,`/`)}var z=class{constructor(t,n){e(this,`config`,void 0),e(this,`routes`,void 0),this.routes=t,this.config=n}getRoute(e){return this.routes[e]}getRouteNames(){return Object.keys(this.routes)}get handlers(){return F(this,this.config)}async generatePresignedUrls(e,t,n){let r=this.getRoute(e);if(!r)throw Error(`Route "${String(e)}" not found`);let i=r._getConfig(),a=this.config,o=[];for(let r of n)try{let n={},s=i.middleware||[];for(let e of s)n=await e({req:t,file:r,metadata:n});let l=new File([],r.name,{type:r.type});Object.defineProperty(l,`size`,{value:r.size});let u=await i.schema.validate(l);if(!u.success)throw Error(u.error?.message||`Validation failed`);i.onUploadStart&&await i.onUploadStart({file:r,metadata:n});let d=R({name:r.name,type:r.type},n,String(e),i.paths,a.paths,a),f=await c(a,{key:d,contentType:r.type,contentLength:r.size,metadata:{originalName:r.name,userId:n.userId||n.user?.id||`anonymous`,routeName:String(e)}});o.push({success:!0,file:r,presignedUrl:f.url,key:f.key,metadata:n})}catch(e){let t=e instanceof Error?e:Error(`Failed to generate presigned URL`);i.onUploadError&&await i.onUploadError({file:r,metadata:{},error:t}),o.push({success:!1,file:r,error:t.message})}return o}async handleUploadComplete(e,t,n){let r=this.getRoute(e);if(!r)throw Error(`Route "${String(e)}" not found`);let i=r._getConfig(),a=[];for(let e of n)try{let t=h(this.config,e.key),n=await s(this.config,e.key,3600);i.onUploadComplete&&await i.onUploadComplete({file:e.file,metadata:e.metadata||{},url:t,key:e.key}),a.push({success:!0,key:e.key,url:t,presignedUrl:n,file:e.file})}catch(t){let n=t instanceof Error?t:Error(`Upload completion failed`);i.onUploadError&&await i.onUploadError({file:e.file,metadata:e.metadata||{},error:n}),a.push({success:!1,key:e.key,error:n.message})}return a}};function B(e,t){return new z(e,t)}var V=class{constructor(){e(this,`_constraints`,{}),e(this,`_transforms`,[]),e(this,`_validators`,[]),e(this,`_optional`,!1)}async validate(e,t){try{if(this._optional&&e==null)return{success:!0,data:void 0};let n=await this._parse(e);if(!n.success)return n;for(let n of this._validators){let r=await n({file:e,fieldName:t?.fieldName||`unknown`,allFiles:t?.allFiles});if(!r.success)return r}let r=n.data;for(let n of this._transforms)r=await n({file:e,metadata:t,originalData:r});return{success:!0,data:r}}catch(e){return{success:!1,error:{code:`VALIDATION_ERROR`,message:e instanceof Error?e.message:`Unknown validation error`,path:[t?.fieldName||`unknown`]}}}}optional(){let e=this._clone();return e._optional=!0,e}transform(e){let t=this._clone();return t._transforms.push(e),t}refine(e,t){let n=this._clone();return n._validators.push(async n=>{let r=await e(n);return r?{success:!0}:{success:!1,error:{code:`CUSTOM_VALIDATION`,message:t,path:[n.fieldName]}}}),n}},H=class t extends V{constructor(t={}){super(),this.constraints=t,e(this,`_type`,`file`),this._constraints={...t}}_parse(e){if(!(e instanceof File))return{success:!1,error:{code:`INVALID_TYPE`,message:`Expected File object`,path:[]}};if(this.constraints.maxSize){let t=this._parseSize(this.constraints.maxSize);if(e.size>t)return{success:!1,error:{code:`FILE_TOO_LARGE`,message:`File size ${this._formatSize(e.size)} exceeds maximum ${this._formatSize(t)}`,path:[]}}}if(this.constraints.minSize){let t=this._parseSize(this.constraints.minSize);if(e.size<t)return{success:!1,error:{code:`FILE_TOO_SMALL`,message:`File size ${this._formatSize(e.size)} is below minimum ${this._formatSize(t)}`,path:[]}}}if(this.constraints.allowedTypes?.length){let t=this.constraints.allowedTypes.some(t=>t.endsWith(`/*`)?e.type.startsWith(t.slice(0,-1)):e.type===t);if(!t)return{success:!1,error:{code:`INVALID_FILE_TYPE`,message:`File type ${e.type} is not allowed. Allowed types: ${this.constraints.allowedTypes.join(`, `)}`,path:[]}}}if(this.constraints.allowedExtensions?.length){let t=e.name.split(`.`).pop()?.toLowerCase();if(!t||!this.constraints.allowedExtensions.includes(t))return{success:!1,error:{code:`INVALID_FILE_EXTENSION`,message:`File extension .${t} is not allowed. Allowed extensions: ${this.constraints.allowedExtensions.join(`, `)}`,path:[]}}}return{success:!0,data:e}}max(e){return new t({...this.constraints,maxSize:e})}min(e){return new t({...this.constraints,minSize:e})}types(e){return new t({...this.constraints,allowedTypes:e})}extensions(e){return new t({...this.constraints,allowedExtensions:e})}array(e){return new W(this,e)}_clone(){return new t(this.constraints)}middleware(e){return new L(this).middleware(e)}onUploadStart(e){return new L(this).onUploadStart(e)}onUploadComplete(e){return new L(this).onUploadComplete(e)}onUploadError(e){return new L(this).onUploadError(e)}_parseSize(e){if(typeof e==`number`)return e;let t=e.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)?$/i);if(!t)throw Error(`Invalid size format: ${e}`);let n=parseFloat(t[1]),r=(t[2]||`B`).toUpperCase(),i={B:1,KB:1024,MB:1024**2,GB:1024**3,TB:1024**4};return n*(i[r]||1)}_formatSize(e){let t=[`B`,`KB`,`MB`,`GB`,`TB`],n=e,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r++;return`${n.toFixed(r===0?0:1)}${t[r]}`}},U=class e extends H{constructor(e={}){let t={allowedTypes:[`image/*`],...e};super(t)}formats(t){let n=t.map(e=>{let t={jpg:`image/jpeg`,jpeg:`image/jpeg`,png:`image/png`,gif:`image/gif`,webp:`image/webp`,avif:`image/avif`,svg:`image/svg+xml`};return t[e.toLowerCase()]||`image/${e}`});return new e({...this.constraints,allowedTypes:n})}max(t){return new e({...this.constraints,maxSize:t})}min(t){return new e({...this.constraints,minSize:t})}types(t){return new e({...this.constraints,allowedTypes:t})}extensions(t){return new e({...this.constraints,allowedExtensions:t})}array(e){return new W(this,e)}_clone(){return new e(this.constraints)}},W=class t extends V{constructor(t,n={}){super(),this.elementSchema=t,this.arrayConstraints=n,e(this,`_type`,`array`)}async _parse(e){if(!Array.isArray(e))return{success:!1,error:{code:`INVALID_TYPE`,message:`Expected array of files`,path:[]}};if(this.arrayConstraints.min!==void 0&&e.length<this.arrayConstraints.min)return{success:!1,error:{code:`ARRAY_TOO_SHORT`,message:`Array must have at least ${this.arrayConstraints.min} items`,path:[]}};if(this.arrayConstraints.max!==void 0&&e.length>this.arrayConstraints.max)return{success:!1,error:{code:`ARRAY_TOO_LONG`,message:`Array must have at most ${this.arrayConstraints.max} items`,path:[]}};if(this.arrayConstraints.length!==void 0&&e.length!==this.arrayConstraints.length)return{success:!1,error:{code:`ARRAY_WRONG_LENGTH`,message:`Array must have exactly ${this.arrayConstraints.length} items`,path:[]}};let t=[];for(let n=0;n<e.length;n++){let r=await this.elementSchema.validate(e[n],{fieldName:`[${n}]`});if(!r.success)return{success:!1,error:{...r.error,path:[`[${n}]`,...r.error.path]}};t.push(r.data)}return{success:!0,data:t}}min(e){return new t(this.elementSchema,{...this.arrayConstraints,min:e})}max(e){return new t(this.elementSchema,{...this.arrayConstraints,max:e})}length(e){return new t(this.elementSchema,{...this.arrayConstraints,length:e})}_clone(){return new t(this.elementSchema,this.arrayConstraints)}},G=class t extends V{constructor(t){super(),this.shape=t,e(this,`_type`,`object`)}async _parse(e){if(!e||typeof e!=`object`)return{success:!1,error:{code:`INVALID_TYPE`,message:`Expected object`,path:[]}};let t={},n=e;for(let[e,r]of Object.entries(this.shape)){let i=await r.validate(n[e],{fieldName:e,allFiles:n});if(!i.success)return{success:!1,error:{...i.error,path:[e,...i.error.path]}};i.data!==void 0&&(t[e]=i.data)}return{success:!0,data:t}}_clone(){return new t(this.shape)}},K=class{constructor(w){if(e(this,`config`,void 0),e(this,`list`,{files:e=>v(this.config,e),paginated:e=>S(this.config,e),byExtension:(e,t)=>b(this.config,e,t),bySize:(e,t,n)=>x(this.config,e,t,n),byDate:(e,t,n)=>y(this.config,e,t,n),directories:e=>_(this.config,e),paginatedGenerator:e=>C(this.config,e)}),e(this,`metadata`,{getInfo:e=>d(this.config,e),getBatch:e=>g(this.config,e),getSize:e=>m(this.config,e),getContentType:e=>u(this.config,e),getLastModified:e=>f(this.config,e),getCustom:e=>p(this.config,e),setCustom:(e,t)=>T(this.config,e,t)}),e(this,`download`,{presignedUrl:(e,t)=>s(this.config,e,t),url:e=>h(this.config,e)}),e(this,`upload`,{file:(e,t,n)=>E(this.config,e,t,n),presignedUrl:e=>c(this.config,e),presignedBatch:e=>l(this.config,e),generateKey:e=>o(this.config,e)}),e(this,`validation`,{exists:e=>t(this.config,e),existsWithInfo:e=>a(this.config,e),validateFile:(e,t)=>D(this.config,e,t),validateFiles:(e,t)=>O(this.config,e,t),connection:()=>k(this.config)}),e(this,`delete`,{file:e=>n(this.config,e),files:e=>r(this.config,e),byPrefix:(e,t)=>i(this.config,e,t)}),!w)throw Error(`StorageInstance requires a valid configuration`);this.config=Object.freeze(w)}getConfig(){return this.config}getProviderInfo(){return{provider:this.config.provider.provider,bucket:this.config.provider.bucket,region:this.config.provider.region}}};function q(e){return new K(e)}var J=class{constructor(t={}){e(this,`metrics`,[]),e(this,`maxMetricsHistory`,1e3),e(this,`isEnabled`,void 0),this.isEnabled=t.enabled??!1}startOperation(e,t){if(!this.isEnabled)return``;let n=`${e}_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,r={operation:e,startTime:performance.now(),success:!1,metadata:{operationId:n,...t}};return this.metrics.push(r),this.trimMetricsHistory(),n}endOperation(e,t,n){if(!this.isEnabled||!e)return;let r=this.metrics.find(t=>t.metadata?.operationId===e);if(!r)return;let i=performance.now();r.endTime=i,r.duration=i-r.startTime,r.success=t,r.fileSize=n?.fileSize,r.provider=n?.provider,r.errorCode=n?.errorCode,n?.metadata&&(r.metadata={...r.metadata,...n.metadata})}recordOperation(e,t,n,r){if(!this.isEnabled)return;let i={operation:e,startTime:performance.now()-t,endTime:performance.now(),duration:t,success:n,fileSize:r?.fileSize,provider:r?.provider,errorCode:r?.errorCode,metadata:r?.metadata};this.metrics.push(i),this.trimMetricsHistory()}getAggregatedMetrics(e){let t=e?Date.now()-e:0,n=this.metrics.filter(e=>e.endTime&&e.startTime>=t),r=n.length,i=n.filter(e=>e.success).length,a=r-i,o=r>0?i/r*100:0,s=n.filter(e=>e.duration!==void 0).map(e=>e.duration),c=s.length>0?s.reduce((e,t)=>e+t,0)/s.length:0,l=n.filter(e=>e.fileSize!==void 0).reduce((e,t)=>e+(t.fileSize||0),0),u=n.reduce((e,t)=>(e[t.operation]=(e[t.operation]||0)+1,e),{}),d=n.filter(e=>!e.success&&e.errorCode).reduce((e,t)=>(e[t.errorCode]=(e[t.errorCode]||0)+1,e),{}),f=n.filter(e=>e.provider).reduce((e,t)=>(e[t.provider]=(e[t.provider]||0)+1,e),{});return{totalOperations:r,successfulOperations:i,failedOperations:a,successRate:o,averageDuration:c,totalDataTransferred:l,operationsByType:u,errorsByCode:d,providerUsage:f}}getRawMetrics(){return[...this.metrics]}clearMetrics(){this.metrics=[]}getPerformanceSummary(){let e=this.getAggregatedMetrics();return`
📊 Pushduck Performance Summary
================================
Total Operations: ${e.totalOperations}
Success Rate: ${e.successRate.toFixed(1)}%
Average Duration: ${e.averageDuration.toFixed(1)}ms
Data Transferred: ${this.formatBytes(e.totalDataTransferred)}
Operations by Type:
${Object.entries(e.operationsByType).map(([e,t])=>` ${e}: ${t}`).join(`
`)}
${Object.keys(e.errorsByCode).length>0?`
Errors by Code:
${Object.entries(e.errorsByCode).map(([e,t])=>` ${e}: ${t}`).join(`
`)}
`:``}
Provider Usage:
${Object.entries(e.providerUsage).map(([e,t])=>` ${e}: ${t}`).join(`
`)}
`.trim()}trimMetricsHistory(){this.metrics.length>this.maxMetricsHistory&&(this.metrics=this.metrics.slice(-this.maxMetricsHistory))}formatBytes(e){if(e===0)return`0 Bytes`;let t=1024,n=[`Bytes`,`KB`,`MB`,`GB`],r=Math.floor(Math.log(e)/Math.log(t));return parseFloat((e/t**+r).toFixed(2))+` `+n[r]}setEnabled(e){this.isEnabled=e}isMetricsEnabled(){return this.isEnabled}};const Y=new J,X=(e,t)=>Y.startOperation(e,t),Z=(e,t,n)=>Y.endOperation(e,t,n),Q=(e,t,n,r)=>Y.recordOperation(e,t,n,r);function $(e){return function(t,n,r){let i=r.value;return r.value=async function(...t){let r=X(e,{method:n,args:t.length});try{let e=await i.apply(this,t);return Z(r,!0),e}catch(e){throw Z(r,!1,{errorCode:e instanceof Error?e.name:`UNKNOWN_ERROR`}),e}},r}}function ee(e,t){let n={};for(let[t,r]of Object.entries(e))r instanceof H||r instanceof U||r instanceof G?n[t]=new L(r):n[t]=r;return B(n,t)}function te(e){return{file:e=>new H(e),image:e=>new U(e),object:e=>new G(e),createRouter:t=>ee(t,e)}}var ne=class{constructor(){e(this,`config`,{})}provider(e,t){return typeof e==`string`?this.config.provider=M(e,t):this.config.provider=e,this}defaults(e){return this.config.defaults={...this.config.defaults,...e},this}paths(e){return this.config.paths={...this.config.paths,...e},this}security(e){return this.config.security={...this.config.security,...e},this}hooks(e){return this.config.hooks={...this.config.hooks,...e},this}debug(e=!0){return this.config.debug=e,this}metrics(e=!0){return this.config.enableMetrics=e,this}build(){if(!this.config.provider)throw Error(`Provider configuration is required`);let e=N(this.config.provider);if(!e.valid)throw Error(`Invalid provider configuration: ${e.errors.join(`, `)}`);let t=this.config;t.debug!==void 0&&w.setDebugMode(t.debug),t.enableMetrics!==void 0&&Y.setEnabled(t.enableMetrics);let n=q(t),r=te(t);return w.info(`📦 Upload configuration initialized`,{provider:t.provider.provider}),{config:t,storage:n,s3:r}}};function re(){return new ne}export{W as S3ArraySchema,H as S3FileSchema,U as S3ImageSchema,G as S3ObjectSchema,L as S3Route,V as S3Schema,K as StorageInstance,M as createProvider,B as createS3RouterWithConfig,q as createStorage,re as createUploadConfig,Z as endOperation,P as getProviderEndpoint,Y as metrics,Q as recordOperation,X as startOperation,$ as trackOperation,N as validateProviderConfig};