@dropgate/core 2.2.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
- "use strict";var DropgateCore=(()=>{var Ne=Object.defineProperty;var et=Object.getOwnPropertyDescriptor;var tt=Object.getOwnPropertyNames;var rt=Object.prototype.hasOwnProperty;var nt=(r,e)=>{for(var t in e)Ne(r,t,{get:e[t],enumerable:!0})},ot=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of tt(e))!rt.call(r,o)&&o!==t&&Ne(r,o,{get:()=>e[o],enumerable:!(n=et(e,o))||n.enumerable});return r};var st=r=>ot(Ne({},"__esModule",{value:!0}),r);var lt={};nt(lt,{AES_GCM_IV_BYTES:()=>he,AES_GCM_TAG_BYTES:()=>We,DEFAULT_CHUNK_SIZE:()=>Ee,DropgateAbortError:()=>J,DropgateClient:()=>Me,DropgateError:()=>G,DropgateNetworkError:()=>M,DropgateProtocolError:()=>V,DropgateTimeoutError:()=>de,DropgateValidationError:()=>f,ENCRYPTION_OVERHEAD_PER_CHUNK:()=>Ae,arrayBufferToBase64:()=>ue,base64ToBytes:()=>qe,buildBaseUrl:()=>ke,buildPeerOptions:()=>ae,bytesToBase64:()=>$e,createPeerWithRetries:()=>Be,decryptChunk:()=>se,decryptFilenameFromBase64:()=>be,encryptFilenameToBase64:()=>Te,encryptToBlob:()=>Pe,estimateTotalUploadSizeBytes:()=>Ie,exportKeyBase64:()=>Oe,fetchJson:()=>re,generateAesGcmKey:()=>Fe,generateP2PCode:()=>ve,getDefaultBase64:()=>ee,getDefaultCrypto:()=>fe,getDefaultFetch:()=>ye,getServerInfo:()=>ze,importKeyFromBase64:()=>we,isLocalhostHostname:()=>De,isP2PCodeLike:()=>Se,isSecureContextForP2P:()=>Le,lifetimeToMs:()=>Xe,makeAbortSignal:()=>oe,parseSemverMajorMinor:()=>ge,parseServerUrl:()=>Ze,resolvePeerConfig:()=>ie,sha256Hex:()=>Re,sleep:()=>me,startP2PReceive:()=>Ke,startP2PSend:()=>Ge,validatePlainFilename:()=>xe});var Ee=5242880,he=12,We=16,Ae=28;var G=class extends Error{constructor(e,t={}){super(e),this.name=this.constructor.name,this.code=t.code||"DROPGATE_ERROR",this.details=t.details,t.cause!==void 0&&Object.defineProperty(this,"cause",{value:t.cause,writable:!1,enumerable:!1,configurable:!0})}},f=class extends G{constructor(e,t={}){super(e,{...t,code:t.code||"VALIDATION_ERROR"})}},M=class extends G{constructor(e,t={}){super(e,{...t,code:t.code||"NETWORK_ERROR"})}},V=class extends G{constructor(e,t={}){super(e,{...t,code:t.code||"PROTOCOL_ERROR"})}},J=class extends G{constructor(e="Operation aborted"){super(e,{code:"ABORT_ERROR"}),this.name="AbortError"}},de=class extends G{constructor(e="Request timed out"){super(e,{code:"TIMEOUT_ERROR"}),this.name="TimeoutError"}};function ee(){if(typeof Buffer<"u"&&typeof Buffer.from=="function")return{encode(r){return Buffer.from(r).toString("base64")},decode(r){return new Uint8Array(Buffer.from(r,"base64"))}};if(typeof btoa=="function"&&typeof atob=="function")return{encode(r){let e="";for(let t=0;t<r.length;t++)e+=String.fromCharCode(r[t]);return btoa(e)},decode(r){let e=atob(r),t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n);return t}};throw new Error("No Base64 implementation available. Provide a Base64Adapter via options.")}function fe(){return globalThis.crypto}function ye(){return globalThis.fetch?.bind(globalThis)}var _e=null;function Ye(r){return r||(_e||(_e=ee()),_e)}function $e(r,e){return Ye(e).encode(r)}function ue(r,e){return $e(new Uint8Array(r),e)}function qe(r,e){return Ye(e).decode(r)}var it={minutes:6e4,hours:36e5,days:864e5};function Xe(r,e){let t=String(e||"").toLowerCase(),n=Number(r);if(t==="unlimited"||!Number.isFinite(n)||n<=0)return 0;let o=it[t];return o?Math.round(n*o):0}function ge(r){let e=String(r||"").split(".").map(o=>Number(o)),t=Number.isFinite(e[0])?e[0]:0,n=Number.isFinite(e[1])?e[1]:0;return{major:t,minor:n}}function xe(r){if(typeof r!="string"||r.trim().length===0)throw new f("Invalid filename. Must be a non-empty string.");if(r.length>255||/[\/\\]/.test(r))throw new f("Invalid filename. Contains illegal characters or is too long.")}function Ze(r){let e=r.trim();!e.startsWith("http://")&&!e.startsWith("https://")&&(e="https://"+e);let t=new URL(e);return{host:t.hostname,port:t.port?Number(t.port):void 0,secure:t.protocol==="https:"}}function ke(r){let{host:e,port:t,secure:n}=r;if(!e||typeof e!="string")throw new f("Server host is required.");let o=n===!1?"http":"https",s=t?`:${t}`:"";return`${o}://${e}${s}`}function me(r,e){return new Promise((t,n)=>{if(e?.aborted)return n(e.reason||new J);let o=setTimeout(t,r);e&&e.addEventListener("abort",()=>{clearTimeout(o),n(e.reason||new J)},{once:!0})})}function oe(r,e){let t=new AbortController,n=null,o=s=>{t.signal.aborted||t.abort(s)};return r&&(r.aborted?o(r.reason):r.addEventListener("abort",()=>o(r.reason),{once:!0})),Number.isFinite(e)&&e>0&&(n=setTimeout(()=>{o(new de)},e)),{signal:t.signal,cleanup:()=>{n&&clearTimeout(n)}}}async function re(r,e,t={}){let{timeoutMs:n,signal:o,...s}=t,{signal:d,cleanup:y}=oe(o,n);try{let i=await r(e,{...s,signal:d}),u=await i.text(),m=null;try{m=u?JSON.parse(u):null}catch{}return{res:i,json:m,text:u}}finally{y()}}async function we(r,e,t){let o=(t||ee()).decode(e),s=new Uint8Array(o).buffer;return r.subtle.importKey("raw",s,{name:"AES-GCM"},!0,["decrypt"])}async function se(r,e,t){let n=e.slice(0,12),o=e.slice(12);return r.subtle.decrypt({name:"AES-GCM",iv:n},t,o)}async function be(r,e,t,n){let s=(n||ee()).decode(e),d=await se(r,s,t);return new TextDecoder().decode(d)}async function Re(r,e){let t=await r.subtle.digest("SHA-256",e),n=new Uint8Array(t),o="";for(let s=0;s<n.length;s++)o+=n[s].toString(16).padStart(2,"0");return o}async function Fe(r){return r.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}async function Oe(r,e){let t=await r.subtle.exportKey("raw",e);return ue(t)}async function Pe(r,e,t){let n=r.getRandomValues(new Uint8Array(12)),o=await r.subtle.encrypt({name:"AES-GCM",iv:n},t,e);return new Blob([n,new Uint8Array(o)])}async function Te(r,e,t){let n=new TextEncoder().encode(String(e)),s=await(await Pe(r,n.buffer,t)).arrayBuffer();return ue(s)}function Ie(r,e,t){let n=Number(r)||0;return t?n+(Number(e)||0)*28:n}async function ze(r){let{host:e,port:t,secure:n,timeoutMs:o=5e3,signal:s,fetchFn:d}=r,y=d||ye();if(!y)throw new f("No fetch() implementation found.");let i=ke({host:e,port:t,secure:n});try{let{res:u,json:m}=await re(y,`${i}/api/info`,{method:"GET",timeoutMs:o,signal:s,headers:{Accept:"application/json"}});if(u.ok&&m&&typeof m=="object"&&"version"in m)return{baseUrl:i,serverInfo:m};throw new V(`Server info request failed (status ${u.status}).`)}catch(u){throw u instanceof G?u:new M("Could not reach server /api/info.",{cause:u})}}var Me=class{constructor(e){if(!e||typeof e.clientVersion!="string")throw new f("DropgateClient requires clientVersion (string).");this.clientVersion=e.clientVersion,this.chunkSize=Number.isFinite(e.chunkSize)?e.chunkSize:5242880;let t=e.fetchFn||ye();if(!t)throw new f("No fetch() implementation found.");this.fetchFn=t;let n=e.cryptoObj||fe();if(!n)throw new f("No crypto implementation found.");this.cryptoObj=n,this.base64=e.base64||ee(),this.logger=e.logger||null}async resolveShareTarget(e,t){let{timeoutMs:n=5e3,signal:o}=t,s=await this.checkCompatibility(t);if(!s.compatible)throw new f(s.message);let{baseUrl:d}=s,{res:y,json:i}=await re(this.fetchFn,`${d}/api/resolve`,{method:"POST",timeoutMs:n,signal:o,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({value:e})});if(!y.ok){let u=(i&&typeof i=="object"&&"error"in i?i.error:null)||`Share lookup failed (status ${y.status}).`;throw new V(u,{details:i})}return i||{valid:!1,reason:"Unknown response."}}async checkCompatibility(e){let t,n;try{let i=await ze({...e,fetchFn:this.fetchFn});t=i.baseUrl,n=i.serverInfo}catch(i){throw i instanceof G?i:new M("Could not connect to the server.",{cause:i})}let o=String(n?.version||"0.0.0"),s=String(this.clientVersion||"0.0.0"),d=ge(s),y=ge(o);return d.major!==y.major?{compatible:!1,clientVersion:s,serverVersion:o,message:`Incompatible versions. Client v${s}, Server v${o}${n?.name?` (${n.name})`:""}.`,serverInfo:n,baseUrl:t}:d.minor>y.minor?{compatible:!0,clientVersion:s,serverVersion:o,message:`Client (v${s}) is newer than Server (v${o})${n?.name?` (${n.name})`:""}. Some features may not work.`,serverInfo:n,baseUrl:t}:{compatible:!0,clientVersion:s,serverVersion:o,message:`Server: v${o}, Client: v${s}${n?.name?` (${n.name})`:""}.`,serverInfo:n,baseUrl:t}}validateUploadInputs(e){let{file:t,lifetimeMs:n,encrypt:o,serverInfo:s}=e,d=s?.capabilities?.upload;if(!d||!d.enabled)throw new f("Server does not support file uploads.");let y=Number(t?.size||0);if(!t||!Number.isFinite(y)||y<=0)throw new f("File is missing or invalid.");let i=Number(d.maxSizeMB);if(Number.isFinite(i)&&i>0){let E=i*1e3*1e3,R=Math.ceil(y/this.chunkSize);if(Ie(y,R,!!o)>E){let D=o?`File too large once encryption overhead is included. Server limit: ${i} MB.`:`File too large. Server limit: ${i} MB.`;throw new f(D)}}let u=Number(d.maxLifetimeHours),m=Number(n);if(!Number.isFinite(m)||m<0||!Number.isInteger(m))throw new f("Invalid lifetime. Must be a non-negative integer (milliseconds).");if(Number.isFinite(u)&&u>0){let E=Math.round(u*60*60*1e3);if(m===0)throw new f(`Server does not allow unlimited file lifetime. Max: ${u} hours.`);if(m>E)throw new f(`File lifetime too long. Server limit: ${u} hours.`)}if(o&&!d.e2ee)throw new f("End-to-end encryption is not supported on this server.");return!0}async uploadFile(e){let{host:t,port:n,secure:o,file:s,lifetimeMs:d,encrypt:y,maxDownloads:i,filenameOverride:u,onProgress:m,onCancel:E,signal:R,timeouts:_={},retry:D={}}=e,q=R?null:new AbortController,O=R||q?.signal,$="initializing",v=null,S=null,L=(async()=>{try{let b=w=>{try{m&&m(w)}catch{}};if(!this.cryptoObj?.subtle)throw new f("Web Crypto API not available (crypto.subtle).");let h=s.size;b({phase:"server-info",text:"Checking server...",percent:0,processedBytes:0,totalBytes:h});let x=await this.checkCompatibility({host:t,port:n,secure:o,timeoutMs:_.serverInfoMs??5e3,signal:O}),{baseUrl:p,serverInfo:B}=x;if(b({phase:"server-compat",text:x.message,percent:0,processedBytes:0,totalBytes:h}),!x.compatible)throw new f(x.message);let l=u??s.name??"file",K=!!B?.capabilities?.upload?.e2ee,C=y??K;C||xe(l),this.validateUploadInputs({file:s,lifetimeMs:d,encrypt:C,serverInfo:B});let P=null,k=null,H=l;if(C){b({phase:"crypto",text:"Generating encryption key...",percent:0,processedBytes:0,totalBytes:h});try{P=await Fe(this.cryptoObj),k=await Oe(this.cryptoObj,P),H=await Te(this.cryptoObj,l,P)}catch(w){throw new G("Failed to prepare encryption.",{code:"CRYPTO_PREP_FAILED",cause:w})}}let a=Math.ceil(s.size/this.chunkSize),g=Ie(s.size,a,C);b({phase:"init",text:"Reserving server storage...",percent:0,processedBytes:0,totalBytes:h});let U={filename:H,lifetime:d,isEncrypted:C,totalSize:g,totalChunks:a,...i!==void 0?{maxDownloads:i}:{}},T=await re(this.fetchFn,`${p}/upload/init`,{method:"POST",timeoutMs:_.initMs??15e3,signal:O,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(U)});if(!T.res.ok){let Z=T.json?.error||`Server initialisation failed: ${T.res.status}`;throw new V(Z,{details:T.json||T.text})}let j=T.json?.uploadId;if(!j||typeof j!="string")throw new V("Server did not return a valid uploadId.");v=j,S=p,$="uploading";let Q=Number.isFinite(D.retries)?D.retries:5,W=Number.isFinite(D.backoffMs)?D.backoffMs:1e3,A=Number.isFinite(D.maxBackoffMs)?D.maxBackoffMs:3e4;for(let w=0;w<a;w++){if(O?.aborted)throw O.reason||new J;let Z=w*this.chunkSize,Ue=Math.min(Z+this.chunkSize,s.size),N=s.slice(Z,Ue),I=w/a*100,je=w*this.chunkSize;b({phase:"chunk",text:`Uploading chunk ${w+1} of ${a}...`,percent:I,processedBytes:je,totalBytes:h,chunkIndex:w,totalChunks:a});let ce=await N.arrayBuffer(),te;if(C&&P?te=await Pe(this.cryptoObj,ce,P):te=new Blob([ce]),te.size>5243904)throw new f("Chunk too large (client-side). Check chunk size settings.");let le=await te.arrayBuffer(),Ce=await Re(this.cryptoObj,le),pe={"Content-Type":"application/octet-stream","X-Upload-ID":j,"X-Chunk-Index":String(w),"X-Chunk-Hash":Ce},Ve=`${p}/upload/chunk`;await this.attemptChunkUpload(Ve,{method:"POST",headers:pe,body:te},{retries:Q,backoffMs:W,maxBackoffMs:A,timeoutMs:_.chunkMs??6e4,signal:O,progress:b,chunkIndex:w,totalChunks:a,chunkSize:this.chunkSize,fileSizeBytes:h})}b({phase:"complete",text:"Finalising upload...",percent:100,processedBytes:h,totalBytes:h}),$="completing";let F=await re(this.fetchFn,`${p}/upload/complete`,{method:"POST",timeoutMs:_.completeMs??3e4,signal:O,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({uploadId:j})});if(!F.res.ok){let Z=F.json?.error||"Finalisation failed.";throw new V(Z,{details:F.json||F.text})}let c=F.json?.id;if(!c||typeof c!="string")throw new V("Server did not return a valid file id.");let z=`${p}/${c}`;return C&&k&&(z+=`#${k}`),b({phase:"done",text:"Upload successful!",percent:100,processedBytes:h,totalBytes:h}),$="completed",{downloadUrl:z,fileId:c,uploadId:j,baseUrl:p,...C&&k?{keyB64:k}:{}}}catch(b){throw b instanceof Error&&(b.name==="AbortError"||b.message?.includes("abort"))?($="cancelled",E?.()):$="error",b}})(),X=async(b,h)=>{try{await re(this.fetchFn,`${h}/upload/cancel`,{method:"POST",timeoutMs:5e3,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({uploadId:b})})}catch{}};return{result:L,cancel:b=>{$==="completed"||$==="cancelled"||($="cancelled",v&&S&&X(v,S).catch(()=>{}),q?.abort(new J(b||"Upload cancelled by user.")))},getStatus:()=>$}}async downloadFile(e){let{host:t,port:n,secure:o,fileId:s,keyB64:d,onProgress:y,onData:i,signal:u,timeoutMs:m=6e4}=e,E=l=>{try{y&&y(l)}catch{}};if(!s||typeof s!="string")throw new f("File ID is required.");E({phase:"server-info",text:"Checking server...",processedBytes:0,totalBytes:0,percent:0});let R=await this.checkCompatibility({host:t,port:n,secure:o,timeoutMs:m,signal:u}),{baseUrl:_}=R;if(E({phase:"server-compat",text:R.message,processedBytes:0,totalBytes:0,percent:0}),!R.compatible)throw new f(R.message);E({phase:"metadata",text:"Fetching file info...",processedBytes:0,totalBytes:0,percent:0});let{signal:D,cleanup:q}=oe(u,m),O;try{let l=await this.fetchFn(`${_}/api/file/${s}/meta`,{method:"GET",headers:{Accept:"application/json"},signal:D});if(!l.ok)throw l.status===404?new V("File not found or has expired."):new V(`Failed to fetch file metadata (status ${l.status}).`);O=await l.json()}catch(l){throw l instanceof G?l:l instanceof Error&&l.name==="AbortError"?new J("Download cancelled."):new M("Could not fetch file metadata.",{cause:l})}finally{q()}let $=!!O.isEncrypted,v=O.sizeBytes||0;if(!i&&v>104857600){let l=Math.round(v/1048576),K=Math.round(104857600/(1024*1024));throw new f(`File is too large (${l}MB) to download without streaming. Provide an onData callback to stream files larger than ${K}MB.`)}let S,L;if($){if(!d)throw new f("Decryption key is required for encrypted files.");if(!this.cryptoObj?.subtle)throw new f("Web Crypto API not available for decryption.");E({phase:"decrypting",text:"Preparing decryption...",processedBytes:0,totalBytes:0,percent:0});try{L=await we(this.cryptoObj,d,this.base64),S=await be(this.cryptoObj,O.encryptedFilename,L,this.base64)}catch(l){throw new G("Failed to decrypt filename. Invalid key or corrupted data.",{code:"DECRYPT_FILENAME_FAILED",cause:l})}}else S=O.filename||"file";E({phase:"downloading",text:"Starting download...",percent:0,processedBytes:0,totalBytes:v});let{signal:X,cleanup:b}=oe(u,m),h=0,x=[],p=!i;try{let l=await this.fetchFn(`${_}/api/file/${s}`,{method:"GET",signal:X});if(!l.ok)throw new V(`Download failed (status ${l.status}).`);if(!l.body)throw new V("Streaming response not available.");let K=l.body.getReader();if($&&L){let C=this.chunkSize+28,P=[],k=0,H=()=>{if(P.length===0)return new Uint8Array(0);if(P.length===1){let U=P[0];return P.length=0,k=0,U}let a=new Uint8Array(k),g=0;for(let U of P)a.set(U,g),g+=U.length;return P.length=0,k=0,a};for(;;){if(u?.aborted)throw new J("Download cancelled.");let{done:a,value:g}=await K.read();if(a)break;for(P.push(g),k+=g.length;k>=C;){let T=H(),ne=T.subarray(0,C);if(T.length>C){let W=T.subarray(C);P.push(W),k=W.length}let j=await se(this.cryptoObj,ne,L),Q=new Uint8Array(j);p?x.push(Q):await i(Q)}h+=g.length;let U=v>0?Math.round(h/v*100):0;E({phase:"decrypting",text:`Downloading & decrypting... (${U}%)`,percent:U,processedBytes:h,totalBytes:v})}if(k>0){let a=H(),g=await se(this.cryptoObj,a,L),U=new Uint8Array(g);p?x.push(U):await i(U)}}else for(;;){if(u?.aborted)throw new J("Download cancelled.");let{done:C,value:P}=await K.read();if(C)break;p?x.push(P):await i(P),h+=P.length;let k=v>0?Math.round(h/v*100):0;E({phase:"downloading",text:`Downloading... (${k}%)`,percent:k,processedBytes:h,totalBytes:v})}}catch(l){throw l instanceof G?l:l instanceof Error&&l.name==="AbortError"?new J("Download cancelled."):new M("Download failed.",{cause:l})}finally{b()}E({phase:"complete",text:"Download complete!",percent:100,processedBytes:h,totalBytes:v});let B;if(p&&x.length>0){let l=x.reduce((C,P)=>C+P.length,0);B=new Uint8Array(l);let K=0;for(let C of x)B.set(C,K),K+=C.length}return{filename:S,receivedBytes:h,wasEncrypted:$,...B?{data:B}:{}}}async attemptChunkUpload(e,t,n){let{retries:o,backoffMs:s,maxBackoffMs:d,timeoutMs:y,signal:i,progress:u,chunkIndex:m,totalChunks:E,chunkSize:R,fileSizeBytes:_}=n,D=o,q=s,O=o;for(;;){if(i?.aborted)throw i.reason||new J;let{signal:$,cleanup:v}=oe(i,y);try{let S=await this.fetchFn(e,{...t,signal:$});if(S.ok)return;let L=await S.text().catch(()=>"");throw new V(`Chunk ${m+1} failed (HTTP ${S.status}).`,{details:{status:S.status,bodySnippet:L.slice(0,120)}})}catch(S){if(v(),S instanceof Error&&(S.name==="AbortError"||S.code==="ABORT_ERR"))throw S;if(i?.aborted)throw i.reason||new J;if(D<=0)throw S instanceof G?S:new M("Chunk upload failed.",{cause:S});let L=O-D+1,X=m*R,b=m/E*100,h=q,x=100;for(;h>0;){let p=(h/1e3).toFixed(1);u({phase:"retry-wait",text:`Chunk upload failed. Retrying in ${p}s... (${L}/${O})`,percent:b,processedBytes:X,totalBytes:_,chunkIndex:m,totalChunks:E}),await me(Math.min(x,h),i),h-=x}u({phase:"retry",text:`Chunk upload failed. Retrying now... (${L}/${O})`,percent:b,processedBytes:X,totalBytes:_,chunkIndex:m,totalChunks:E}),D-=1,q=Math.min(q*2,d);continue}finally{v()}}}};function De(r){let e=String(r||"").toLowerCase();return e==="localhost"||e==="127.0.0.1"||e==="::1"}function Le(r,e){return!!e||De(r||"")}function ve(r){let e=r||fe(),t="ABCDEFGHJKLMNPQRSTUVWXYZ";if(e){let s=new Uint8Array(8);e.getRandomValues(s);let d="";for(let i=0;i<4;i++)d+=t[s[i]%t.length];let y="";for(let i=4;i<8;i++)y+=(s[i]%10).toString();return`${d}-${y}`}let n="";for(let s=0;s<4;s++)n+=t[Math.floor(Math.random()*t.length)];let o="";for(let s=0;s<4;s++)o+=Math.floor(Math.random()*10);return`${n}-${o}`}function Se(r){return/^[A-Z]{4}-\d{4}$/.test(String(r||"").trim())}function ie(r,e){return{path:r.peerjsPath??e?.peerjsPath??"/peerjs",iceServers:r.iceServers??e?.iceServers??[]}}function ae(r={}){let{host:e,port:t,peerjsPath:n="/peerjs",secure:o=!1,iceServers:s=[]}=r,d={host:e,path:n,secure:o,config:{iceServers:s},debug:0};return t&&(d.port=t),d}async function Be(r){let{code:e,codeGenerator:t,maxAttempts:n,buildPeer:o,onCode:s}=r,d=e||t(),y=null,i=null;for(let u=0;u<n;u++){s?.(d,u);try{return y=await new Promise((m,E)=>{let R=o(d);R.on("open",()=>m(R)),R.on("error",_=>{try{R.destroy()}catch{}E(_)})}),{peer:y,code:d}}catch(m){i=m,d=t()}}throw i||new M("Could not establish PeerJS connection.")}function ct(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,11)}`}async function Ge(r){let{file:e,Peer:t,serverInfo:n,host:o,port:s,peerjsPath:d,secure:y=!1,iceServers:i,codeGenerator:u,cryptoObj:m,maxAttempts:E=4,chunkSize:R=256*1024,endAckTimeoutMs:_=15e3,bufferHighWaterMark:D=8*1024*1024,bufferLowWaterMark:q=2*1024*1024,heartbeatIntervalMs:O=5e3,onCode:$,onStatus:v,onProgress:S,onComplete:L,onError:X,onDisconnect:b,onCancel:h}=r;if(!e)throw new f("File is missing.");if(!t)throw new f("PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.");let x=n?.capabilities?.p2p;if(n&&!x?.enabled)throw new f("Direct transfer is disabled on this server.");let{path:p,iceServers:B}=ie({peerjsPath:d,iceServers:i},x),l=ae({host:o,port:s,peerjsPath:p,secure:y,iceServers:B}),K=u||(()=>ve(m)),C=c=>new t(c,l),{peer:P,code:k}=await Be({code:null,codeGenerator:K,maxAttempts:E,buildPeer:C,onCode:$}),H=ct(),a="listening",g=null,U=0,T=null,ne=c=>{let z=Number.isFinite(c.total)&&c.total>0?c.total:e.size,w=Math.min(Number(c.received)||0,z||0),Z=z?w/z*100:0;S?.({processedBytes:w,totalBytes:z,percent:Z})},j=c=>{a==="closed"||a==="completed"||a==="cancelled"||(a="closed",X?.(c),W())},Q=()=>{a==="finishing"&&(a="completed",L?.(),W())},W=()=>{T&&(clearInterval(T),T=null),typeof window<"u"&&window.removeEventListener("beforeunload",A);try{g?.close()}catch{}try{P.destroy()}catch{}},A=()=>{try{g?.send({t:"error",message:"Sender closed the connection."})}catch{}F()};typeof window<"u"&&window.addEventListener("beforeunload",A);let F=()=>{if(a==="closed"||a==="cancelled")return;let c=a==="transferring"||a==="finishing";a="cancelled";try{g&&g.open&&g.send({t:"cancelled",message:"Sender cancelled the transfer."})}catch{}c&&h&&h({cancelledBy:"sender"}),W()},Y=()=>a==="closed"||a==="cancelled";return P.on("connection",c=>{if(a==="closed")return;if(g){let N=g.open!==!1;if(N&&a==="transferring"){try{c.send({t:"error",message:"Transfer already in progress."})}catch{}try{c.close()}catch{}return}else if(N){try{c.send({t:"error",message:"Another receiver is already connected."})}catch{}try{c.close()}catch{}return}else{try{g.close()}catch{}g=null,a="listening",U=0}}g=c,a="negotiating",v?.({phase:"waiting",message:"Connected. Waiting for receiver to accept..."});let z=null,w=null,Z=new Promise(N=>{z=N}),Ue=new Promise(N=>{w=N});c.on("data",N=>{if(!N||typeof N!="object"||N instanceof ArrayBuffer||ArrayBuffer.isView(N))return;let I=N;if(I.t){if(I.t==="ready"){v?.({phase:"transferring",message:"Receiver accepted. Starting transfer..."}),z?.();return}if(I.t==="progress"){ne({received:I.received||0,total:I.total||0});return}if(I.t==="ack"&&I.phase==="end"){w?.(I);return}if(I.t!=="pong"){if(I.t==="error"){j(new M(I.message||"Receiver reported an error."));return}if(I.t==="cancelled"){if(a==="cancelled"||a==="closed"||a==="completed")return;a="cancelled",h?.({cancelledBy:"receiver",message:I.message}),W()}}}}),c.on("open",async()=>{try{if(Y())return;c.send({t:"meta",sessionId:H,name:e.name,size:e.size,mime:e.type||"application/octet-stream"});let N=e.size,I=c._dc;if(I&&Number.isFinite(q))try{I.bufferedAmountLowThreshold=q}catch{}if(await Z,Y())return;O>0&&(T=setInterval(()=>{if(a==="transferring"||a==="finishing")try{c.send({t:"ping"})}catch{}},O)),a="transferring";for(let pe=0;pe<N;pe+=R){if(Y())return;let Je=await e.slice(pe,pe+R).arrayBuffer();if(Y())return;if(c.send(Je),U+=Je.byteLength,I)for(;I.bufferedAmount>D;)await new Promise(He=>{let Qe=setTimeout(He,60);try{I.addEventListener("bufferedamountlow",()=>{clearTimeout(Qe),He()},{once:!0})}catch{}})}if(Y())return;a="finishing",c.send({t:"end"});let je=Number.isFinite(_)?Math.max(_,Math.ceil(e.size/(1024*1024))*1e3):null,ce=await Promise.race([Ue,me(je||15e3).catch(()=>null)]);if(Y())return;if(!ce||typeof ce!="object")throw new M("Receiver did not confirm completion.");let te=ce,le=Number(te.total)||e.size,Ce=Number(te.received)||0;if(le&&Ce<le)throw new M("Receiver reported an incomplete transfer.");ne({received:Ce||le,total:le}),Q()}catch(N){j(N)}}),c.on("error",N=>{j(N)}),c.on("close",()=>{if(a==="closed"||a==="completed"||a==="cancelled"){W();return}a==="transferring"||a==="finishing"?(a="cancelled",h?.({cancelledBy:"receiver"}),W()):(g=null,a="listening",U=0,b?.())})}),{peer:P,code:k,sessionId:H,stop:F,getStatus:()=>a,getBytesSent:()=>U,getConnectedPeerId:()=>g&&g.peer||null}}async function Ke(r){let{code:e,Peer:t,serverInfo:n,host:o,port:s,peerjsPath:d,secure:y=!1,iceServers:i,autoReady:u=!0,watchdogTimeoutMs:m=15e3,onStatus:E,onMeta:R,onData:_,onProgress:D,onComplete:q,onError:O,onDisconnect:$,onCancel:v}=r;if(!e)throw new f("No sharing code was provided.");if(!t)throw new f("PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.");let S=n?.capabilities?.p2p;if(n&&!S?.enabled)throw new f("Direct transfer is disabled on this server.");let L=String(e).trim().replace(/\s+/g,"").toUpperCase();if(!Se(L))throw new f("Invalid direct transfer code.");let{path:X,iceServers:b}=ie({peerjsPath:d,iceServers:i},S),h=ae({host:o,port:s,peerjsPath:X,secure:y,iceServers:b}),x=new t(void 0,h),p="initializing",B=0,l=0,K=null,C=0,P=120,k=Promise.resolve(),H=null,a=null,g=()=>{m<=0||(H&&clearTimeout(H),H=setTimeout(()=>{p==="transferring"&&T(new M("Connection timed out (no data received)."))},m))},U=()=>{H&&(clearTimeout(H),H=null)},T=A=>{p==="closed"||p==="completed"||p==="cancelled"||(p="closed",O?.(A),j())},ne=A=>{p==="transferring"&&(p="completed",q?.(A),j())},j=()=>{U(),typeof window<"u"&&window.removeEventListener("beforeunload",Q);try{x.destroy()}catch{}},Q=()=>{try{a?.send({t:"error",message:"Receiver closed the connection."})}catch{}W()};typeof window<"u"&&window.addEventListener("beforeunload",Q);let W=()=>{if(p==="closed"||p==="cancelled")return;let A=p==="transferring";p="cancelled";try{a&&a.open&&a.send({t:"cancelled",message:"Receiver cancelled the transfer."})}catch{}A&&v&&v({cancelledBy:"receiver"}),j()};return x.on("error",A=>{T(A)}),x.on("open",()=>{p="connecting";let A=x.connect(L,{reliable:!0});a=A,A.on("open",()=>{p="negotiating",E?.({phase:"connected",message:"Waiting for file details..."})}),A.on("data",async F=>{try{if(g(),F&&typeof F=="object"&&!(F instanceof ArrayBuffer)&&!ArrayBuffer.isView(F)){let c=F;if(c.t==="meta"){if(K&&c.sessionId&&c.sessionId!==K){try{A.send({t:"error",message:"Busy with another session."})}catch{}return}c.sessionId&&(K=c.sessionId);let z=String(c.name||"file");B=Number(c.size)||0,l=0,k=Promise.resolve();let w=()=>{p="transferring",g();try{A.send({t:"ready"})}catch{}};u?(R?.({name:z,total:B}),D?.({processedBytes:l,totalBytes:B,percent:0}),w()):(R?.({name:z,total:B,sendReady:w}),D?.({processedBytes:l,totalBytes:B,percent:0}));return}if(c.t==="ping"){try{A.send({t:"pong"})}catch{}return}if(c.t==="end"){if(U(),await k,B&&l<B){let z=new M("Transfer ended before the full file was received.");try{A.send({t:"error",message:z.message})}catch{}throw z}try{A.send({t:"ack",phase:"end",received:l,total:B})}catch{}ne({received:l,total:B});return}if(c.t==="error")throw new M(c.message||"Sender reported an error.");if(c.t==="cancelled"){if(p==="cancelled"||p==="closed"||p==="completed")return;p="cancelled",v?.({cancelledBy:"sender",message:c.message}),j();return}return}let Y;if(F instanceof ArrayBuffer)Y=Promise.resolve(new Uint8Array(F));else if(ArrayBuffer.isView(F))Y=Promise.resolve(new Uint8Array(F.buffer,F.byteOffset,F.byteLength));else if(typeof Blob<"u"&&F instanceof Blob)Y=F.arrayBuffer().then(c=>new Uint8Array(c));else return;k=k.then(async()=>{let c=await Y;_&&await _(c),l+=c.byteLength;let z=B?Math.min(100,l/B*100):0;D?.({processedBytes:l,totalBytes:B,percent:z});let w=Date.now();if(l===B||w-C>=P){C=w;try{A.send({t:"progress",received:l,total:B})}catch{}}}).catch(c=>{try{A.send({t:"error",message:c?.message||"Receiver write failed."})}catch{}T(c)})}catch(Y){T(Y)}}),A.on("close",()=>{if(p==="closed"||p==="completed"||p==="cancelled"){j();return}p==="transferring"?(p="cancelled",v?.({cancelledBy:"sender"}),j()):p==="negotiating"?(p="closed",j(),$?.()):T(new M("Sender disconnected before file details were received."))})}),{peer:x,stop:W,getStatus:()=>p,getBytesReceived:()=>l,getTotalBytes:()=>B,getSessionId:()=>K}}return st(lt);})();
1
+ "use strict";var DropgateCore=(()=>{var rt=Object.defineProperty;var tn=Object.getOwnPropertyDescriptor;var nn=Object.getOwnPropertyNames;var rn=Object.prototype.hasOwnProperty;var sn=(n,e,t)=>e in n?rt(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var on=(n=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(n,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):n)(function(n){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+n+'" is not supported')});var an=(n,e)=>{for(var t in e)rt(n,t,{get:e[t],enumerable:!0})},ln=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of nn(e))!rn.call(n,i)&&i!==t&&rt(n,i,{get:()=>e[i],enumerable:!(r=tn(e,i))||r.enumerable});return n};var cn=n=>ln(rt({},"__esModule",{value:!0}),n);var te=(n,e,t)=>sn(n,typeof e!="symbol"?e+"":e,t);var Un={};an(Un,{AES_GCM_IV_BYTES:()=>qe,AES_GCM_TAG_BYTES:()=>kt,DEFAULT_CHUNK_SIZE:()=>wt,DropgateAbortError:()=>ne,DropgateClient:()=>vt,DropgateError:()=>X,DropgateNetworkError:()=>D,DropgateProtocolError:()=>L,DropgateTimeoutError:()=>Ne,DropgateValidationError:()=>y,ENCRYPTION_OVERHEAD_PER_CHUNK:()=>it,StreamingZipWriter:()=>Ge,arrayBufferToBase64:()=>$e,base64ToBytes:()=>Et,buildBaseUrl:()=>Je,bytesToBase64:()=>bt,decryptChunk:()=>Ee,decryptFilenameFromBase64:()=>Ke,encryptFilenameToBase64:()=>ft,encryptToBlob:()=>He,estimateTotalUploadSizeBytes:()=>Qe,exportKeyBase64:()=>ct,fetchJson:()=>ce,generateAesGcmKey:()=>lt,generateP2PCode:()=>Ze,getDefaultBase64:()=>Be,getDefaultCrypto:()=>je,getDefaultFetch:()=>Ve,getServerInfo:()=>gt,importKeyFromBase64:()=>Re,isLocalhostHostname:()=>ht,isP2PCodeLike:()=>Ye,isSecureContextForP2P:()=>xt,lifetimeToMs:()=>Ft,makeAbortSignal:()=>Le,parseSemverMajorMinor:()=>We,parseServerUrl:()=>ot,sha256Hex:()=>at,sleep:()=>Pe,validatePlainFilename:()=>st});var wt=5242880,qe=12,kt=16,it=28;var X=class extends Error{constructor(t,r={}){super(t,r.cause!==void 0?{cause:r.cause}:void 0);te(this,"code");te(this,"details");this.name=this.constructor.name,this.code=r.code||"DROPGATE_ERROR",this.details=r.details}},y=class extends X{constructor(e,t={}){super(e,{...t,code:t.code||"VALIDATION_ERROR"})}},D=class extends X{constructor(e,t={}){super(e,{...t,code:t.code||"NETWORK_ERROR"})}},L=class extends X{constructor(e,t={}){super(e,{...t,code:t.code||"PROTOCOL_ERROR"})}},ne=class extends X{constructor(e="Operation aborted"){super(e,{code:"ABORT_ERROR"}),this.name="AbortError"}},Ne=class extends X{constructor(e="Request timed out"){super(e,{code:"TIMEOUT_ERROR"}),this.name="TimeoutError"}};function Be(){if(typeof Buffer<"u"&&typeof Buffer.from=="function")return{encode(n){return Buffer.from(n).toString("base64")},decode(n){return new Uint8Array(Buffer.from(n,"base64"))}};if(typeof btoa=="function"&&typeof atob=="function")return{encode(n){let e="";for(let t=0;t<n.length;t++)e+=String.fromCharCode(n[t]);return btoa(e)},decode(n){let e=atob(n),t=new Uint8Array(e.length);for(let r=0;r<e.length;r++)t[r]=e.charCodeAt(r);return t}};throw new Error("No Base64 implementation available. Provide a Base64Adapter via options.")}function je(){return globalThis.crypto}function Ve(){return globalThis.fetch?.bind(globalThis)}var Pt=null;function Bt(n){return n||(Pt||(Pt=Be()),Pt)}function bt(n,e){return Bt(e).encode(n)}function $e(n,e){return bt(new Uint8Array(n),e)}function Et(n,e){return Bt(e).decode(n)}var fn={minutes:6e4,hours:36e5,days:864e5};function Ft(n,e){let t=String(e||"").toLowerCase(),r=Number(n);if(t==="unlimited"||!Number.isFinite(r)||r<=0)return 0;let i=fn[t];return i?Math.round(r*i):0}function We(n){let e=String(n||"").split(".").map(i=>Number(i)),t=Number.isFinite(e[0])?e[0]:0,r=Number.isFinite(e[1])?e[1]:0;return{major:t,minor:r}}function st(n){if(typeof n!="string"||n.trim().length===0)throw new y("Invalid filename. Must be a non-empty string.");if(n.length>255||/[\/\\]/.test(n))throw new y("Invalid filename. Contains illegal characters or is too long.")}function ot(n){let e=n.trim();!e.startsWith("http://")&&!e.startsWith("https://")&&(e="https://"+e);let t=new URL(e);return{host:t.hostname,port:t.port?Number(t.port):void 0,secure:t.protocol==="https:"}}function Je(n){let{host:e,port:t,secure:r}=n;if(!e||typeof e!="string")throw new y("Server host is required.");let i=r===!1?"http":"https",s=t?`:${t}`:"";return`${i}://${e}${s}`}function Pe(n,e){return new Promise((t,r)=>{if(e?.aborted)return r(e.reason||new ne);let i=setTimeout(t,n);e&&e.addEventListener("abort",()=>{clearTimeout(i),r(e.reason||new ne)},{once:!0})})}function Le(n,e){let t=new AbortController,r=null,i=s=>{t.signal.aborted||t.abort(s)};return n&&(n.aborted?i(n.reason):n.addEventListener("abort",()=>i(n.reason),{once:!0})),Number.isFinite(e)&&e>0&&(r=setTimeout(()=>{i(new Ne)},e)),{signal:t.signal,cleanup:()=>{r&&clearTimeout(r)}}}async function ce(n,e,t={}){let{timeoutMs:r,signal:i,...s}=t,{signal:a,cleanup:l}=Le(i,r);try{let o=await n(e,{...s,signal:a}),c=await o.text(),h=null;try{h=c?JSON.parse(c):null}catch{}return{res:o,json:h,text:c}}finally{l()}}var un=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]);function Ce(n,e){return n>>>e|n<<32-e}function It(n){let e=new Uint8Array(n),t=e.length*8,r=new Uint8Array(Math.ceil((e.length+9)/64)*64);r.set(e),r[e.length]=128;let i=new DataView(r.buffer);i.setUint32(r.length-8,t/4294967296>>>0,!1),i.setUint32(r.length-4,t>>>0,!1);let s=1779033703,a=3144134277,l=1013904242,o=2773480762,c=1359893119,h=2600822924,B=528734635,p=1541459225,d=new Uint32Array(64);for(let b=0;b<r.length;b+=64){for(let F=0;F<16;F++)d[F]=i.getUint32(b+F*4,!1);for(let F=16;F<64;F++){let M=Ce(d[F-15],7)^Ce(d[F-15],18)^d[F-15]>>>3,m=Ce(d[F-2],17)^Ce(d[F-2],19)^d[F-2]>>>10;d[F]=d[F-16]+M+d[F-7]+m|0}let u=s,E=a,g=l,f=o,C=c,S=h,A=B,z=p;for(let F=0;F<64;F++){let M=Ce(C,6)^Ce(C,11)^Ce(C,25),m=C&S^~C&A,T=z+M+m+un[F]+d[F]|0,R=Ce(u,2)^Ce(u,13)^Ce(u,22),Z=u&E^u&g^E&g,oe=R+Z|0;z=A,A=S,S=C,C=f+T|0,f=g,g=E,E=u,u=T+oe|0}s=s+u|0,a=a+E|0,l=l+g|0,o=o+f|0,c=c+C|0,h=h+S|0,B=B+A|0,p=p+z|0}let w=new ArrayBuffer(32),v=new DataView(w);return v.setUint32(0,s,!1),v.setUint32(4,a,!1),v.setUint32(8,l,!1),v.setUint32(12,o,!1),v.setUint32(16,c,!1),v.setUint32(20,h,!1),v.setUint32(24,B,!1),v.setUint32(28,p,!1),w}async function Re(n,e,t){let i=(t||Be()).decode(e),s=new Uint8Array(i).buffer;return n.subtle.importKey("raw",s,{name:"AES-GCM"},!0,["decrypt"])}async function Ee(n,e,t){let r=e.slice(0,12),i=e.slice(12);return n.subtle.decrypt({name:"AES-GCM",iv:r},t,i)}async function Ke(n,e,t,r){let s=(r||Be()).decode(e),a=await Ee(n,s,t);return new TextDecoder().decode(a)}function zt(n){let e=new Uint8Array(n),t="";for(let r=0;r<e.length;r++)t+=e[r].toString(16).padStart(2,"0");return t}async function at(n,e){if(n?.subtle){let t=await n.subtle.digest("SHA-256",e);return zt(t)}return zt(It(e))}async function lt(n){return n.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}async function ct(n,e){let t=await n.subtle.exportKey("raw",e);return $e(t)}async function He(n,e,t){let r=n.getRandomValues(new Uint8Array(12)),i=await n.subtle.encrypt({name:"AES-GCM",iv:r},t,e);return new Blob([r,new Uint8Array(i)])}async function ft(n,e,t){let r=new TextEncoder().encode(String(e)),s=await(await He(n,r.buffer,t)).arrayBuffer();return $e(s)}var Ot=on("module"),pn=(0,Ot.createRequire)("/"),hn;try{hn=pn("worker_threads").Worker}catch{}var ue=Uint8Array,Dt=Uint16Array,dn=Int32Array,mn=new ue([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),gn=new ue([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),ir=new ue([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),_t=function(n,e){for(var t=new Dt(31),r=0;r<31;++r)t[r]=e+=1<<n[r-1];for(var i=new dn(t[30]),r=1;r<30;++r)for(var s=t[r];s<t[r+1];++s)i[s]=s-t[r]<<5|r;return{b:t,r:i}},Nt=_t(mn,2),vn=Nt.b,yn=Nt.r;vn[28]=258,yn[258]=28;var jt=_t(gn,0),sr=jt.b,or=jt.r,wn=new Dt(32768);for(O=0;O<32768;++O)Fe=(O&43690)>>1|(O&21845)<<1,Fe=(Fe&52428)>>2|(Fe&13107)<<2,Fe=(Fe&61680)>>4|(Fe&3855)<<4,wn[O]=((Fe&65280)>>8|(Fe&255)<<8)>>1;var Fe,O;var pt=new ue(288);for(O=0;O<144;++O)pt[O]=8;var O;for(O=144;O<256;++O)pt[O]=9;var O;for(O=256;O<280;++O)pt[O]=7;var O;for(O=280;O<288;++O)pt[O]=8;var O,Pn=new ue(32);for(O=0;O<32;++O)Pn[O]=5;var O;var bn=function(n,e,t){return(e==null||e<0)&&(e=0),(t==null||t>n.length)&&(t=n.length),new ue(n.subarray(e,t))};var xn=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],Ie=function(n,e,t){var r=new Error(e||xn[n]);if(r.code=n,Error.captureStackTrace&&Error.captureStackTrace(r,Ie),!t)throw r;return r};var Sn=new ue(0);var Cn=(function(){for(var n=new Int32Array(256),e=0;e<256;++e){for(var t=e,r=9;--r;)t=(t&1&&-306674912)^t>>>1;n[e]=t}return n})(),An=function(){var n=-1;return{p:function(e){for(var t=n,r=0;r<e.length;++r)t=Cn[t&255^e[r]]^t>>>8;n=t},d:function(){return~n}}};var Mn=function(n,e){var t={};for(var r in n)t[r]=n[r];for(var r in e)t[r]=e[r];return t};var J=function(n,e,t){for(;t;++e)n[e]=t,t>>>=8};var Tt=typeof TextEncoder<"u"&&new TextEncoder,kn=typeof TextDecoder<"u"&&new TextDecoder,Bn=0;try{kn.decode(Sn,{stream:!0}),Bn=1}catch{}function Rt(n,e){if(e){for(var t=new ue(n.length),r=0;r<n.length;++r)t[r]=n.charCodeAt(r);return t}if(Tt)return Tt.encode(n);for(var i=n.length,s=new ue(n.length+(n.length>>1)),a=0,l=function(h){s[a++]=h},r=0;r<i;++r){if(a+5>s.length){var o=new ue(a+8+(i-r<<1));o.set(s),s=o}var c=n.charCodeAt(r);c<128||e?l(c):c<2048?(l(192|c>>6),l(128|c&63)):c>55295&&c<57344?(c=65536+(c&1047552)|n.charCodeAt(++r)&1023,l(240|c>>18),l(128|c>>12&63),l(128|c>>6&63),l(128|c&63)):(l(224|c>>12),l(128|c>>6&63),l(128|c&63))}return bn(s,0,a)}var ut=function(n){var e=0;if(n)for(var t in n){var r=n[t].length;r>65535&&Ie(9),e+=r+4}return e},Ut=function(n,e,t,r,i,s,a,l){var o=r.length,c=t.extra,h=l&&l.length,B=ut(c);J(n,e,a!=null?33639248:67324752),e+=4,a!=null&&(n[e++]=20,n[e++]=t.os),n[e]=20,e+=2,n[e++]=t.flag<<1|(s<0&&8),n[e++]=i&&8,n[e++]=t.compression&255,n[e++]=t.compression>>8;var p=new Date(t.mtime==null?Date.now():t.mtime),d=p.getFullYear()-1980;if((d<0||d>119)&&Ie(10),J(n,e,d<<25|p.getMonth()+1<<21|p.getDate()<<16|p.getHours()<<11|p.getMinutes()<<5|p.getSeconds()>>1),e+=4,s!=-1&&(J(n,e,t.crc),J(n,e+4,s<0?-s-2:s),J(n,e+8,t.size)),J(n,e+12,o),J(n,e+14,B),e+=16,a!=null&&(J(n,e,h),J(n,e+6,t.attrs),J(n,e+10,a),e+=14),n.set(r,e),e+=o,B)for(var w in c){var v=c[w],b=v.length;J(n,e,+w),J(n,e+2,b),n.set(v,e+4),e+=4+b}return h&&(n.set(l,e),e+=h),e},En=function(n,e,t,r,i){J(n,e,101010256),J(n,e+8,t),J(n,e+10,t),J(n,e+12,r),J(n,e+16,i)},$t=(function(){function n(e){this.filename=e,this.c=An(),this.size=0,this.compression=0}return n.prototype.process=function(e,t){this.ondata(null,e,t)},n.prototype.push=function(e,t){this.ondata||Ie(5),this.c.p(e),this.size+=e.length,t&&(this.crc=this.c.d()),this.process(e,t||!1)},n})();var Lt=(function(){function n(e){this.ondata=e,this.u=[],this.d=1}return n.prototype.add=function(e){var t=this;if(this.ondata||Ie(5),this.d&2)this.ondata(Ie(4+(this.d&1)*8,0,1),null,!1);else{var r=Rt(e.filename),i=r.length,s=e.comment,a=s&&Rt(s),l=i!=e.filename.length||a&&s.length!=a.length,o=i+ut(e.extra)+30;i>65535&&this.ondata(Ie(11,0,1),null,!1);var c=new ue(o);Ut(c,0,e,r,l,-1);var h=[c],B=function(){for(var b=0,u=h;b<u.length;b++){var E=u[b];t.ondata(null,E,!1)}h=[]},p=this.d;this.d=0;var d=this.u.length,w=Mn(e,{f:r,u:l,o:a,t:function(){e.terminate&&e.terminate()},r:function(){if(B(),p){var b=t.u[d+1];b?b.r():t.d=1}p=1}}),v=0;e.ondata=function(b,u,E){if(b)t.ondata(b,u,E),t.terminate();else if(v+=u.length,h.push(u),E){var g=new ue(16);J(g,0,134695760),J(g,4,e.crc),J(g,8,v),J(g,12,e.size),h.push(g),w.c=v,w.b=o+v+16,w.crc=e.crc,w.size=e.size,p&&w.r(),p=1}else p&&B()},this.u.push(w)}},n.prototype.end=function(){var e=this;if(this.d&2){this.ondata(Ie(4+(this.d&1)*8,0,1),null,!0);return}this.d?this.e():this.u.push({r:function(){e.d&1&&(e.u.splice(-1,1),e.e())},t:function(){}}),this.d=3},n.prototype.e=function(){for(var e=0,t=0,r=0,i=0,s=this.u;i<s.length;i++){var a=s[i];r+=46+a.f.length+ut(a.extra)+(a.o?a.o.length:0)}for(var l=new ue(r+22),o=0,c=this.u;o<c.length;o++){var a=c[o];Ut(l,e,a,a.f,a.u,-a.c-2,t,a.o),e+=46+a.f.length+ut(a.extra)+(a.o?a.o.length:0),t+=a.b}En(l,e,this.u.length,r,t),this.ondata(null,l,!0),this.d=2},n.prototype.terminate=function(){for(var e=0,t=this.u;e<t.length;e++){var r=t[e];r.t()}this.d=2},n})();var Ge=class{constructor(e){te(this,"zip");te(this,"currentFile",null);te(this,"onData");te(this,"finalized",!1);te(this,"pendingWrites",Promise.resolve());this.onData=e,this.zip=new Lt((t,r,i)=>{if(t)throw t;this.pendingWrites=this.pendingWrites.then(()=>this.onData(r))})}startFile(e){if(this.currentFile)throw new Error("Must call endFile() before starting a new file.");if(this.finalized)throw new Error("ZIP has already been finalized.");let t=new $t(e);this.zip.add(t),this.currentFile=t}writeChunk(e){if(!this.currentFile)throw new Error("No file started. Call startFile() first.");this.currentFile.push(e,!1)}endFile(){if(!this.currentFile)throw new Error("No file to end.");this.currentFile.push(new Uint8Array(0),!0),this.currentFile=null}async finalize(){if(this.currentFile)throw new Error("Cannot finalize with an open file. Call endFile() first.");this.finalized||(this.finalized=!0,this.zip.end(),await this.pendingWrites)}};function ht(n){let e=String(n||"").toLowerCase();return e==="localhost"||e==="127.0.0.1"||e==="::1"}function xt(n,e){return!!e||ht(n||"")}function Ze(n){let e=n||je(),t="ABCDEFGHJKLMNPQRSTUVWXYZ";if(e){let s=new Uint8Array(8);e.getRandomValues(s);let a="";for(let o=0;o<4;o++)a+=t[s[o]%t.length];let l="";for(let o=4;o<8;o++)l+=(s[o]%10).toString();return`${a}-${l}`}let r="";for(let s=0;s<4;s++)r+=t[Math.floor(Math.random()*t.length)];let i="";for(let s=0;s<4;s++)i+=Math.floor(Math.random()*10);return`${r}-${i}`}function Ye(n){return/^[A-Z]{4}-\d{4}$/.test(String(n||"").trim())}function Ue(n,e){return{path:n.peerjsPath??e?.peerjsPath??"/peerjs",iceServers:n.iceServers??e?.iceServers??[]}}function dt(n={}){let{host:e,port:t,peerjsPath:r="/peerjs",secure:i=!1,iceServers:s=[]}=n,a={host:e,path:r,secure:i,config:{iceServers:s},debug:0};return t&&(a.port=t),a}async function Kt(n){let{code:e,codeGenerator:t,maxAttempts:r,buildPeer:i,onCode:s}=n,a=e||t(),l=null,o=null;for(let c=0;c<r;c++){s?.(a,c);try{return l=await new Promise((h,B)=>{let p=i(a);p.on("open",()=>h(p)),p.on("error",d=>{try{p.destroy()}catch{}B(d)})}),{peer:l,code:a}}catch(h){o=h,a=t()}}throw o||new D("Could not establish PeerJS connection.")}function mt(n){if(!n||typeof n!="object")return!1;let e=n;return typeof e.t=="string"&&["hello","file_list","meta","ready","chunk","chunk_ack","file_end","file_end_ack","end","end_ack","ping","pong","error","cancelled","resume","resume_ack"].includes(e.t)}var Ht=64*1024,Gt=32,qt=15e3,Vt=3,Wt=100,Jt=2e3;var Fn=3e4;function In(){return crypto.randomUUID()}var zn={initializing:["listening","closed"],listening:["handshaking","closed","cancelled"],handshaking:["negotiating","closed","cancelled"],negotiating:["transferring","closed","cancelled"],transferring:["finishing","closed","cancelled"],finishing:["awaiting_ack","closed","cancelled"],awaiting_ack:["completed","closed","cancelled"],completed:["closed"],cancelled:["closed"],closed:[]};async function Zt(n){let{file:e,Peer:t,serverInfo:r,host:i,port:s,peerjsPath:a,secure:l=!1,iceServers:o,codeGenerator:c,cryptoObj:h,maxAttempts:B=4,chunkSize:p=Ht,endAckTimeoutMs:d=qt,bufferHighWaterMark:w=8*1024*1024,bufferLowWaterMark:v=2*1024*1024,heartbeatIntervalMs:b=5e3,chunkAcknowledgments:u=!0,maxUnackedChunks:E=Gt,onCode:g,onStatus:f,onProgress:C,onComplete:S,onError:A,onDisconnect:z,onCancel:F,onConnectionHealth:M}=n,m=Array.isArray(e)?e:[e],T=m.length>1,R=m.reduce((x,$)=>x+$.size,0);if(!m.length)throw new y("At least one file is required.");if(!t)throw new y("PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.");let Z=r?.capabilities?.p2p;if(r&&!Z?.enabled)throw new y("Direct transfer is disabled on this server.");let{path:oe,iceServers:me}=Ue({peerjsPath:a,iceServers:o},Z),be=dt({host:i,port:s,peerjsPath:oe,secure:l,iceServers:me}),Ae=c||(()=>Ze(h)),K=x=>new t(x,be),{peer:H,code:Me}=await Kt({code:null,codeGenerator:Ae,maxAttempts:B,buildPeer:K,onCode:g}),ge=In(),I="listening",Q=null,fe=0,ee=null,ae=null,pe=Date.now(),le=new Map,P=0,Y=[],G=!1,q=[],re=10,se=1e4,k=x=>zn[I].includes(x)?(I=x,!0):(console.warn(`[P2P Send] Invalid state transition: ${I} -> ${x}`),!1),j=x=>{if(_())return;let $=Number.isFinite(x.total)&&x.total>0?x.total:R,de=Math.min(Number(x.received)||0,$||0),ye=$?de/$*100:0;C?.({processedBytes:de,totalBytes:$,percent:ye})},N=x=>{I==="closed"||I==="completed"||I==="cancelled"||(k("closed"),A?.(x),ie())},ve=()=>{I!=="awaiting_ack"&&I!=="finishing"||(k("completed"),S?.(),ie())},ie=()=>{ee&&(clearInterval(ee),ee=null),ae&&(clearInterval(ae),ae=null),Y.forEach(x=>x()),Y=[],le.clear(),typeof window<"u"&&window.removeEventListener("beforeunload",U);try{Q?.close()}catch{}try{H.destroy()}catch{}},U=()=>{try{Q?.send({t:"error",message:"Sender closed the connection."})}catch{}V()};typeof window<"u"&&window.addEventListener("beforeunload",U);let V=()=>{if(I==="closed"||I==="cancelled")return;if(I==="completed"){ie();return}let x=I==="transferring"||I==="finishing"||I==="awaiting_ack";k("cancelled");try{Q&&Q.open&&Q.send({t:"cancelled",message:"Sender cancelled the transfer."})}catch{}x&&F&&F({cancelledBy:"sender"}),ie()},_=()=>I==="closed"||I==="cancelled",ke=x=>{M&&(ae=setInterval(()=>{if(_())return;let $=x._dc;if(!$)return;let de={iceConnectionState:$.readyState==="open"?"connected":"disconnected",bufferedAmount:$.bufferedAmount,lastActivityMs:Date.now()-pe};M(de)},2e3))},he=x=>{pe=Date.now(),le.delete(x.seq),j({received:x.received,total:R});let $=Y.shift();$&&$()},Oe=()=>new Promise(x=>{Y.push(x)}),Qt=async(x,$,de,ye)=>{if(u)for(;le.size>=E;){let De=Date.now();for(let[et,yt]of le)if(De-yt.sentAt>Fn)throw new D("Receiver stopped acknowledging chunks");if(await Promise.race([Oe(),Pe(1e3)]),_())return}let ze=P++;u&&le.set(ze,{offset:de,size:$.byteLength,sentAt:Date.now()}),x.send({t:"chunk",seq:ze,offset:de,size:$.byteLength,total:ye??R}),x.send($),fe+=$.byteLength;let xe=x._dc;if(xe&&w>0){for(;xe.bufferedAmount>w;)if(await new Promise(De=>{let et=setTimeout(De,60);try{xe.addEventListener("bufferedamountlow",()=>{clearTimeout(et),De()},{once:!0})}catch{}}),_())return}},en=async(x,$)=>{let de=d;for(let ye=0;ye<Vt;ye++){x.send({t:"end",attempt:ye});let ze=de*Math.pow(1.5,ye),xe=await Promise.race([$,Pe(ze).then(()=>null)]);if(xe&&xe.t==="end_ack")return xe;if(_())throw new D("Connection closed during completion.")}throw new D("Receiver did not confirm completion after retries.")};return H.on("connection",x=>{if(_())return;let $=Date.now();for(;q.length>0&&q[0]<$-se;)q.shift();if(q.length>=re){console.warn("[P2P Send] Connection rate limit exceeded, rejecting connection");try{x.send({t:"error",message:"Too many connection attempts. Please wait."})}catch{}try{x.close()}catch{}return}if(q.push($),Q){let W=Q.open!==!1;if(W&&I==="transferring"){try{x.send({t:"error",message:"Transfer already in progress."})}catch{}try{x.close()}catch{}return}else if(W){try{x.send({t:"error",message:"Another receiver is already connected."})}catch{}try{x.close()}catch{}return}else{try{Q.close()}catch{}if(Q=null,G){try{x.send({t:"error",message:"Transfer already started with another receiver. Cannot reconnect."})}catch{}try{x.close()}catch{}return}I="listening",fe=0,P=0,le.clear()}}Q=x,k("handshaking"),_()||f?.({phase:"connected",message:"Receiver connected."}),pe=Date.now();let de=null,ye=null,ze=null,xe=null,De=new Promise(W=>{de=W}),et=new Promise(W=>{ye=W}),yt=new Promise(W=>{ze=W});x.on("data",W=>{if(pe=Date.now(),W instanceof ArrayBuffer||ArrayBuffer.isView(W)||!mt(W))return;let Se=W;switch(Se.t){case"hello":de?.(Se.protocolVersion);break;case"ready":_()||f?.({phase:"transferring",message:"Receiver accepted. Starting transfer..."}),ye?.();break;case"chunk_ack":he(Se);break;case"file_end_ack":xe?.(Se);break;case"end_ack":ze?.(Se);break;case"pong":break;case"error":N(new D(Se.message||"Receiver reported an error."));break;case"cancelled":if(I==="cancelled"||I==="closed"||I==="completed")return;k("cancelled"),F?.({cancelledBy:"receiver",message:Se.reason}),ie();break}}),x.on("open",async()=>{try{if(_())return;ke(x),x.send({t:"hello",protocolVersion:3,sessionId:ge});let W=await Promise.race([De,Pe(1e4).then(()=>null)]);if(_())return;if(W===null)throw new D("Receiver did not respond to handshake.");if(W!==3)throw new D(`Protocol version mismatch: sender v${3}, receiver v${W}`);k("negotiating"),_()||f?.({phase:"waiting",message:"Connected. Waiting for receiver to accept..."}),T&&x.send({t:"file_list",fileCount:m.length,files:m.map(we=>({name:we.name,size:we.size,mime:we.type||"application/octet-stream"})),totalSize:R}),x.send({t:"meta",sessionId:ge,name:m[0].name,size:m[0].size,mime:m[0].type||"application/octet-stream",...T?{fileIndex:0}:{}});let Se=x._dc;if(Se&&Number.isFinite(v))try{Se.bufferedAmountLowThreshold=v}catch{}if(await et,_())return;b>0&&(ee=setInterval(()=>{if(I==="transferring"||I==="finishing"||I==="awaiting_ack")try{x.send({t:"ping",timestamp:Date.now()})}catch{}},b)),k("transferring"),G=!0;let St=0;for(let we=0;we<m.length;we++){let _e=m[we];T&&we>0&&x.send({t:"meta",sessionId:ge,name:_e.name,size:_e.size,mime:_e.type||"application/octet-stream",fileIndex:we});for(let Te=0;Te<_e.size;Te+=p){if(_())return;let nt=await _e.slice(Te,Te+p).arrayBuffer();if(_())return;await Qt(x,nt,Te,_e.size),St+=nt.byteLength,j({received:St,total:R})}if(_())return;if(T){let Te=new Promise(nt=>{xe=nt});x.send({t:"file_end",fileIndex:we});let Mt=await Promise.race([Te,Pe(d).then(()=>null)]);if(_())return;if(!Mt)throw new D(`Receiver did not confirm receipt of file ${we+1}/${m.length}.`)}}if(_())return;k("finishing"),k("awaiting_ack");let Ct=await en(x,yt);if(_())return;let tt=Number(Ct.total)||R,At=Number(Ct.received)||0;if(tt&&At<tt)throw new D("Receiver reported an incomplete transfer.");j({received:At||tt,total:tt}),ve()}catch(W){N(W)}}),x.on("error",W=>{N(W)}),x.on("close",()=>{if(I==="closed"||I==="completed"||I==="cancelled"){ie();return}if(I==="awaiting_ack"){setTimeout(()=>{I==="awaiting_ack"&&N(new D("Connection closed while awaiting confirmation."))},Jt);return}I==="transferring"||I==="finishing"?(k("cancelled"),F?.({cancelledBy:"receiver"}),ie()):(Q=null,I="listening",fe=0,P=0,le.clear(),z?.())})}),{peer:H,code:Me,sessionId:ge,stop:V,getStatus:()=>I,getBytesSent:()=>fe,getConnectedPeerId:()=>Q&&Q.peer||null}}var Tn={initializing:["connecting","closed"],connecting:["handshaking","closed","cancelled"],handshaking:["negotiating","closed","cancelled"],negotiating:["transferring","closed","cancelled"],transferring:["completed","closed","cancelled"],completed:["closed"],cancelled:["closed"],closed:[]};async function Yt(n){let{code:e,Peer:t,serverInfo:r,host:i,port:s,peerjsPath:a,secure:l=!1,iceServers:o,autoReady:c=!0,watchdogTimeoutMs:h=15e3,onStatus:B,onMeta:p,onData:d,onProgress:w,onFileStart:v,onFileEnd:b,onComplete:u,onError:E,onDisconnect:g,onCancel:f}=n;if(!e)throw new y("No sharing code was provided.");if(!t)throw new y("PeerJS Peer constructor is required. Install peerjs and pass it as the Peer option.");let C=r?.capabilities?.p2p;if(r&&!C?.enabled)throw new y("Direct transfer is disabled on this server.");let S=String(e).trim().replace(/\s+/g,"").toUpperCase();if(!Ye(S))throw new y("Invalid direct transfer code.");let{path:A,iceServers:z}=Ue({peerjsPath:a,iceServers:o},C),F=dt({host:i,port:s,peerjsPath:A,secure:l,iceServers:z}),M=new t(void 0,F),m="initializing",T=0,R=0,Z=null,oe=Promise.resolve(),me=null,be=null,Ae=null,K=null,H=0,Me=0,ge=0,I=0,Q=100,fe=1e4,ee=k=>Tn[m].includes(k)?(m=k,!0):(console.warn(`[P2P Receive] Invalid state transition: ${m} -> ${k}`),!1),ae=()=>m==="closed"||m==="cancelled",pe=()=>{h<=0||(me&&clearTimeout(me),me=setTimeout(()=>{m==="transferring"&&P(new D("Connection timed out (no data received)."))},h))},le=()=>{me&&(clearTimeout(me),me=null)},P=k=>{m==="closed"||m==="completed"||m==="cancelled"||(ee("closed"),E?.(k),G())},Y=k=>{m==="transferring"&&(ee("completed"),u?.(k))},G=()=>{le(),typeof window<"u"&&window.removeEventListener("beforeunload",q);try{M.destroy()}catch{}},q=()=>{try{be?.send({t:"error",message:"Receiver closed the connection."})}catch{}re()};typeof window<"u"&&window.addEventListener("beforeunload",q);let re=()=>{if(m==="closed"||m==="cancelled")return;if(m==="completed"){G();return}let k=m==="transferring";ee("cancelled");try{be&&be.open&&be.send({t:"cancelled",reason:"Receiver cancelled the transfer."})}catch{}k&&f&&f({cancelledBy:"receiver"}),G()},se=(k,j)=>{try{k.send({t:"chunk_ack",seq:j,received:R})}catch{}};return M.on("error",k=>{P(k)}),M.on("open",()=>{ee("connecting");let k=M.connect(S,{reliable:!0});be=k,k.on("open",()=>{ee("handshaking"),B?.({phase:"connected",message:"Connected."}),k.send({t:"hello",protocolVersion:3,sessionId:""})}),k.on("data",async j=>{try{if(j instanceof ArrayBuffer||ArrayBuffer.isView(j)||typeof Blob<"u"&&j instanceof Blob){if(m!=="transferring")throw new y("Received binary data before transfer was accepted. Possible malicious sender.");if(pe(),I>=Q)throw new D("Write queue overflow - receiver cannot keep up");let ve;if(j instanceof ArrayBuffer)ve=Promise.resolve(new Uint8Array(j));else if(ArrayBuffer.isView(j))ve=Promise.resolve(new Uint8Array(j.buffer,j.byteOffset,j.byteLength));else if(typeof Blob<"u"&&j instanceof Blob)ve=j.arrayBuffer().then(V=>new Uint8Array(V));else return;let ie=Ae?.seq??-1,U=Ae?.size;Ae=null,I++,oe=oe.then(async()=>{let V=await ve;if(U!==void 0&&V.byteLength!==U)throw new y(`Chunk size mismatch: expected ${U}, got ${V.byteLength}`);let _=R+V.byteLength;if(T>0&&_>T)throw new y(`Received more data than expected: ${_} > ${T}`);d&&await d(V),R+=V.byteLength,H+=V.byteLength;let ke=K?Me+H:R,he=K?K.totalSize:T,Oe=he?Math.min(100,ke/he*100):0;ae()||w?.({processedBytes:ke,totalBytes:he,percent:Oe}),ie>=0&&se(k,ie)}).catch(V=>{try{k.send({t:"error",message:V?.message||"Receiver write failed."})}catch{}P(V)}).finally(()=>{I--});return}if(!mt(j))return;let N=j;switch(N.t){case"hello":Z=N.sessionId||null,ee("negotiating"),B?.({phase:"waiting",message:"Waiting for file details..."});break;case"file_list":{let U=N;if(U.fileCount>fe)throw new y(`Too many files: ${U.fileCount}`);let V=U.files.reduce((_,ke)=>_+ke.size,0);if(V!==U.totalSize)throw new y(`File list size mismatch: declared ${U.totalSize}, actual sum ${V}`);K=U,T=U.totalSize;break}case"meta":{if(m!=="negotiating"&&!(m==="transferring"&&K))return;if(Z&&N.sessionId&&N.sessionId!==Z){try{k.send({t:"error",message:"Busy with another session."})}catch{}return}N.sessionId&&(Z=N.sessionId);let U=String(N.name||"file"),V=Number(N.size)||0,_=N.fileIndex;if(K&&typeof _=="number"&&_>0){H=0,v?.({fileIndex:_,name:U,size:V});break}R=0,H=0,Me=0,K||(T=V),oe=Promise.resolve();let ke=()=>{ee("transferring"),pe(),K&&v?.({fileIndex:0,name:U,size:V});try{k.send({t:"ready"})}catch{}},he={name:U,total:T};K&&(he.fileCount=K.fileCount,he.files=K.files.map(Oe=>({name:Oe.name,size:Oe.size})),he.totalSize=K.totalSize),c?(ae()||(p?.(he),w?.({processedBytes:R,totalBytes:T,percent:0})),ke()):(he.sendReady=ke,ae()||(p?.(he),w?.({processedBytes:R,totalBytes:T,percent:0})));break}case"chunk":{let U=N;if(m!=="transferring")throw new y("Received chunk message before transfer was accepted.");if(U.seq!==ge)throw new y(`Chunk sequence error: expected ${ge}, got ${U.seq}`);ge++,Ae=U;break}case"ping":try{k.send({t:"pong",timestamp:Date.now()})}catch{}break;case"file_end":{le(),await oe;let U=N.fileIndex;b?.({fileIndex:U,receivedBytes:H});try{k.send({t:"file_end_ack",fileIndex:U,received:H,size:H})}catch{}Me+=H,H=0,pe();break}case"end":le(),await oe;let ve=K?Me+H:R,ie=K?K.totalSize:T;if(ie&&ve<ie){let U=new D("Transfer ended before all data was received.");try{k.send({t:"error",message:U.message})}catch{}throw U}try{k.send({t:"end_ack",received:ve,total:ie})}catch{}Y({received:ve,total:ie}),(async()=>{for(let U=0;U<2;U++){await Pe(Wt);try{k.send({t:"end_ack",received:ve,total:ie})}catch{break}}})().catch(()=>{});break;case"error":throw new D(N.message||"Sender reported an error.");case"cancelled":if(m==="cancelled"||m==="closed"||m==="completed")return;ee("cancelled"),f?.({cancelledBy:"sender",message:N.reason}),G();break}}catch(N){P(N)}}),k.on("close",()=>{if(m==="closed"||m==="completed"||m==="cancelled"){G();return}m==="transferring"?(ee("cancelled"),f?.({cancelledBy:"sender"}),G()):m==="negotiating"?(ee("closed"),G(),g?.()):P(new D("Sender disconnected before file details were received."))})}),{peer:M,stop:re,getStatus:()=>m,getBytesReceived:()=>R,getTotalBytes:()=>T,getSessionId:()=>Z}}function Xt(n){return typeof n=="string"?Je(ot(n)):Je(n)}function Qe(n,e,t){let r=Number(n)||0;return t?r+(Number(e)||0)*28:r}async function gt(n){let{server:e,timeoutMs:t=5e3,signal:r,fetchFn:i}=n,s=i||Ve();if(!s)throw new y("No fetch() implementation found.");let a=Xt(e);try{let{res:l,json:o}=await ce(s,`${a}/api/info`,{method:"GET",timeoutMs:t,signal:r,headers:{Accept:"application/json"}});if(l.ok&&o&&typeof o=="object"&&"version"in o)return{baseUrl:a,serverInfo:o};throw new L(`Server info request failed (status ${l.status}).`)}catch(l){throw l instanceof X?l:new D("Could not reach server /api/info.",{cause:l})}}var vt=class{constructor(e){te(this,"clientVersion");te(this,"chunkSize");te(this,"fetchFn");te(this,"cryptoObj");te(this,"base64");te(this,"baseUrl");te(this,"_fallbackToHttp");te(this,"_compat",null);te(this,"_connectPromise",null);if(!e||typeof e.clientVersion!="string")throw new y("DropgateClient requires clientVersion (string).");if(!e.server)throw new y("DropgateClient requires server (URL string or ServerTarget object).");this.clientVersion=e.clientVersion,this.chunkSize=Number.isFinite(e.chunkSize)?e.chunkSize:5242880;let t=e.fetchFn||Ve();if(!t)throw new y("No fetch() implementation found.");this.fetchFn=t;let r=e.cryptoObj||je();if(!r)throw new y("No crypto implementation found.");this.cryptoObj=r,this.base64=e.base64||Be(),this._fallbackToHttp=!!e.fallbackToHttp,this.baseUrl=Xt(e.server)}get serverTarget(){let e=new URL(this.baseUrl);return{host:e.hostname,port:e.port?Number(e.port):void 0,secure:e.protocol==="https:"}}async connect(e){return this._compat?this._compat:(this._connectPromise||(this._connectPromise=this._fetchAndCheckCompat(e).finally(()=>{this._connectPromise=null})),this._connectPromise)}async _fetchAndCheckCompat(e){let{timeoutMs:t=5e3,signal:r}=e??{},i=this.baseUrl,s;try{let l=await gt({server:i,timeoutMs:t,signal:r,fetchFn:this.fetchFn});i=l.baseUrl,s=l.serverInfo}catch(l){if(this._fallbackToHttp&&this.baseUrl.startsWith("https://")){let o=this.baseUrl.replace("https://","http://");try{let c=await gt({server:o,timeoutMs:t,signal:r,fetchFn:this.fetchFn});this.baseUrl=o,i=c.baseUrl,s=c.serverInfo}catch{throw l instanceof X?l:new D("Could not connect to the server.",{cause:l})}}else throw l instanceof X?l:new D("Could not connect to the server.",{cause:l})}let a=this._checkVersionCompat(s);return this._compat={...a,serverInfo:s,baseUrl:i},this._compat}_checkVersionCompat(e){let t=String(e?.version||"0.0.0"),r=String(this.clientVersion||"0.0.0"),i=We(r),s=We(t);return i.major!==s.major?{compatible:!1,clientVersion:r,serverVersion:t,message:`Incompatible versions. Client v${r}, Server v${t}${e?.name?` (${e.name})`:""}.`}:i.minor>s.minor?{compatible:!0,clientVersion:r,serverVersion:t,message:`Client (v${r}) is newer than Server (v${t})${e?.name?` (${e.name})`:""}. Some features may not work.`}:{compatible:!0,clientVersion:r,serverVersion:t,message:`Server: v${t}, Client: v${r}${e?.name?` (${e.name})`:""}.`}}async resolveShareTarget(e,t){let{timeoutMs:r=5e3,signal:i}=t??{},s=await this.connect(t);if(!s.compatible)throw new y(s.message);let{baseUrl:a}=s,{res:l,json:o}=await ce(this.fetchFn,`${a}/api/resolve`,{method:"POST",timeoutMs:r,signal:i,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({value:e})});if(!l.ok){let c=(o&&typeof o=="object"&&"error"in o?o.error:null)||`Share lookup failed (status ${l.status}).`;throw new L(c,{details:o})}return o||{valid:!1,reason:"Unknown response."}}async getFileMetadata(e,t){if(!e||typeof e!="string")throw new y("File ID is required.");let{timeoutMs:r=5e3,signal:i}=t??{},s=`${this.baseUrl}/api/file/${encodeURIComponent(e)}/meta`,{res:a,json:l}=await ce(this.fetchFn,s,{method:"GET",timeoutMs:r,signal:i});if(!a.ok){let o=(l&&typeof l=="object"&&"error"in l?l.error:null)||`Failed to fetch file metadata (status ${a.status}).`;throw new L(o,{details:l})}return l}async getBundleMetadata(e,t,r){if(!e||typeof e!="string")throw new y("Bundle ID is required.");let{timeoutMs:i=5e3,signal:s}=r??{},a=`${this.baseUrl}/api/bundle/${encodeURIComponent(e)}/meta`,{res:l,json:o}=await ce(this.fetchFn,a,{method:"GET",timeoutMs:i,signal:s});if(!l.ok){let d=(o&&typeof o=="object"&&"error"in o?o.error:null)||`Failed to fetch bundle metadata (status ${l.status}).`;throw new L(d,{details:o})}let c=o,h=[];if(c.sealed&&c.encryptedManifest){if(!t)throw new y("Decryption key (keyB64) is required for encrypted sealed bundles.");let d=await Re(this.cryptoObj,t),w=this.base64.decode(c.encryptedManifest),v=await Ee(this.cryptoObj,w,d),b=new TextDecoder().decode(v);h=JSON.parse(b).files.map(E=>({fileId:E.fileId,sizeBytes:E.sizeBytes,filename:E.name}))}else if(c.files)h=c.files;else throw new L("Invalid bundle metadata: missing files or manifest.");let B=h.reduce((d,w)=>d+(w.sizeBytes||0),0),p=h.length;return{isEncrypted:c.isEncrypted,sealed:c.sealed,encryptedManifest:c.encryptedManifest,files:h,totalSizeBytes:B,fileCount:p}}validateUploadInputs(e){let{files:t,lifetimeMs:r,encrypt:i,serverInfo:s}=e,a=s?.capabilities?.upload;if(!a||!a.enabled)throw new y("Server does not support file uploads.");let l=Array.isArray(t)?t:[t];if(l.length===0)throw new y("At least one file is required.");for(let h=0;h<l.length;h++){let B=l[h],p=Number(B?.size||0);if(!B||!Number.isFinite(p)||p<=0)throw new y(`File at index ${h} is missing or invalid.`);let d=Number(a.maxSizeMB);if(Number.isFinite(d)&&d>0){let w=d*1e3*1e3,v=Number.isFinite(a.chunkSize)&&a.chunkSize>0?a.chunkSize:this.chunkSize,b=Math.ceil(p/v);if(Qe(p,b,!!i)>w){let E=i?`File at index ${h} too large once encryption overhead is included. Server limit: ${d} MB.`:`File at index ${h} too large. Server limit: ${d} MB.`;throw new y(E)}}}let o=Number(a.maxLifetimeHours),c=Number(r);if(!Number.isFinite(c)||c<0||!Number.isInteger(c))throw new y("Invalid lifetime. Must be a non-negative integer (milliseconds).");if(Number.isFinite(o)&&o>0){let h=Math.round(o*60*60*1e3);if(c===0)throw new y(`Server does not allow unlimited file lifetime. Max: ${o} hours.`);if(c>h)throw new y(`File lifetime too long. Server limit: ${o} hours.`)}if(i&&!a.e2ee)throw new y("End-to-end encryption is not supported on this server.");return!0}async uploadFiles(e){let{files:t,lifetimeMs:r,encrypt:i,maxDownloads:s,filenameOverrides:a,onProgress:l,onCancel:o,signal:c,timeouts:h={},retry:B={}}=e,p=Array.isArray(t)?t:[t];if(p.length===0)throw new y("At least one file is required.");let d=c?null:new AbortController,w=c||d?.signal,v="initializing",b=[],u=p.reduce((f,C)=>f+C.size,0),E=(async()=>{try{let f=P=>{try{l&&l(P)}catch{}};f({phase:"server-info",text:"Checking server...",percent:0,processedBytes:0,totalBytes:u});let C=await this.connect({timeoutMs:h.serverInfoMs??5e3,signal:w}),{baseUrl:S,serverInfo:A}=C;if(f({phase:"server-compat",text:C.message,percent:0,processedBytes:0,totalBytes:u}),!C.compatible)throw new y(C.message);let z=p.map((P,Y)=>a?.[Y]??P.name??"file"),F=!!A?.capabilities?.upload?.e2ee,M=i??F;if(!M)for(let P of z)st(P);this.validateUploadInputs({files:p,lifetimeMs:r,encrypt:M,serverInfo:A});let m=null,T=null,R=[];if(M){if(!this.cryptoObj?.subtle)throw new y("Web Crypto API not available (crypto.subtle). Encryption requires a secure context (HTTPS or localhost).");f({phase:"crypto",text:"Generating encryption key...",percent:0,processedBytes:0,totalBytes:u});try{m=await lt(this.cryptoObj),T=await ct(this.cryptoObj,m);for(let P of z)R.push(await ft(this.cryptoObj,P,m))}catch(P){throw new X("Failed to prepare encryption.",{code:"CRYPTO_PREP_FAILED",cause:P})}}else R.push(...z);let Z=A?.capabilities?.upload?.chunkSize,oe=Number.isFinite(Z)&&Z>0?Z:this.chunkSize,me=Number.isFinite(B.retries)?B.retries:5,be=Number.isFinite(B.backoffMs)?B.backoffMs:1e3,Ae=Number.isFinite(B.maxBackoffMs)?B.maxBackoffMs:3e4;if(p.length===1){let P=p[0],Y=Math.ceil(P.size/oe),G=Qe(P.size,Y,M);f({phase:"init",text:"Reserving server storage...",percent:0,processedBytes:0,totalBytes:P.size});let q=await ce(this.fetchFn,`${S}/upload/init`,{method:"POST",timeoutMs:h.initMs??15e3,signal:w,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({filename:R[0],lifetime:r,isEncrypted:M,totalSize:G,totalChunks:Y,...s!==void 0?{maxDownloads:s}:{}})});if(!q.res.ok){let N=q.json;throw new L(N?.error||`Server initialisation failed: ${q.res.status}`,{details:q.json||q.text})}let re=q.json?.uploadId;if(!re)throw new L("Server did not return a valid uploadId.");b.push(re),v="uploading",await this._uploadFileChunks({file:P,uploadId:re,cryptoKey:m,effectiveChunkSize:oe,totalChunks:Y,totalUploadSize:G,baseOffset:0,totalBytesAllFiles:P.size,progress:f,signal:w,baseUrl:S,retries:me,backoffMs:be,maxBackoffMs:Ae,chunkTimeoutMs:h.chunkMs??6e4}),f({phase:"complete",text:"Finalising upload...",percent:100,processedBytes:P.size,totalBytes:P.size}),v="completing";let se=await ce(this.fetchFn,`${S}/upload/complete`,{method:"POST",timeoutMs:h.completeMs??3e4,signal:w,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({uploadId:re})});if(!se.res.ok){let N=se.json;throw new L(N?.error||"Finalisation failed.",{details:se.json||se.text})}let k=se.json?.id;if(!k)throw new L("Server did not return a valid file id.");let j=`${S}/${k}`;return M&&T&&(j+=`#${T}`),f({phase:"done",text:"Upload successful!",percent:100,processedBytes:P.size,totalBytes:P.size}),v="completed",{downloadUrl:j,fileId:k,uploadId:re,baseUrl:S,...M&&T?{keyB64:T}:{}}}let K=p.map((P,Y)=>{let G=Math.ceil(P.size/oe),q=Qe(P.size,G,M);return{filename:R[Y],totalSize:q,totalChunks:G}});f({phase:"init",text:`Reserving server storage for ${p.length} files...`,percent:0,processedBytes:0,totalBytes:u,totalFiles:p.length});let H=await ce(this.fetchFn,`${S}/upload/init-bundle`,{method:"POST",timeoutMs:h.initMs??15e3,signal:w,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({fileCount:p.length,files:K,lifetime:r,isEncrypted:M,...s!==void 0?{maxDownloads:s}:{}})});if(!H.res.ok){let P=H.json;throw new L(P?.error||`Bundle initialisation failed: ${H.res.status}`,{details:H.json||H.text})}let Me=H.json,ge=Me?.bundleUploadId,I=Me?.fileUploadIds;if(!ge||!I||I.length!==p.length)throw new L("Server did not return valid bundle upload IDs.");b.push(...I),v="uploading";let Q=[],fe=0;for(let P=0;P<p.length;P++){let Y=p[P],G=I[P],q=K[P].totalChunks,re=K[P].totalSize;f({phase:"file-start",text:`Uploading file ${P+1} of ${p.length}: ${z[P]}`,percent:u>0?fe/u*100:0,processedBytes:fe,totalBytes:u,fileIndex:P,totalFiles:p.length,currentFileName:z[P]}),await this._uploadFileChunks({file:Y,uploadId:G,cryptoKey:m,effectiveChunkSize:oe,totalChunks:q,totalUploadSize:re,baseOffset:fe,totalBytesAllFiles:u,progress:f,signal:w,baseUrl:S,retries:me,backoffMs:be,maxBackoffMs:Ae,chunkTimeoutMs:h.chunkMs??6e4,fileIndex:P,totalFiles:p.length,currentFileName:z[P]});let se=await ce(this.fetchFn,`${S}/upload/complete`,{method:"POST",timeoutMs:h.completeMs??3e4,signal:w,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({uploadId:G})});if(!se.res.ok){let j=se.json;throw new L(j?.error||`File ${P+1} finalisation failed.`,{details:se.json||se.text})}let k=se.json?.id;if(!k)throw new L(`Server did not return a valid file id for file ${P+1}.`);Q.push({fileId:k,name:z[P],size:Y.size}),fe+=Y.size,f({phase:"file-complete",text:`File ${P+1} of ${p.length} uploaded.`,percent:u>0?fe/u*100:0,processedBytes:fe,totalBytes:u,fileIndex:P,totalFiles:p.length,currentFileName:z[P]})}f({phase:"complete",text:"Finalising bundle...",percent:100,processedBytes:u,totalBytes:u}),v="completing";let ee;if(M&&m){let P=JSON.stringify({files:Q.map(re=>({fileId:re.fileId,name:re.name,sizeBytes:re.size}))}),Y=new TextEncoder().encode(P),G=await He(this.cryptoObj,Y.buffer,m),q=new Uint8Array(await G.arrayBuffer());ee=this.base64.encode(q)}let ae=await ce(this.fetchFn,`${S}/upload/complete-bundle`,{method:"POST",timeoutMs:h.completeMs??3e4,signal:w,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({bundleUploadId:ge,...ee?{encryptedManifest:ee}:{}})});if(!ae.res.ok){let P=ae.json;throw new L(P?.error||"Bundle finalisation failed.",{details:ae.json||ae.text})}let pe=ae.json?.bundleId;if(!pe)throw new L("Server did not return a valid bundle id.");let le=`${S}/b/${pe}`;return M&&T&&(le+=`#${T}`),f({phase:"done",text:"Upload successful!",percent:100,processedBytes:u,totalBytes:u}),v="completed",{downloadUrl:le,bundleId:pe,baseUrl:S,files:Q,...M&&T?{keyB64:T}:{}}}catch(f){throw f instanceof Error&&(f.name==="AbortError"||f.message?.includes("abort"))?(v="cancelled",o?.()):v="error",f}})(),g=async f=>{try{await ce(this.fetchFn,`${this.baseUrl}/upload/cancel`,{method:"POST",timeoutMs:5e3,headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({uploadId:f})})}catch{}};return{result:E,cancel:f=>{if(!(v==="completed"||v==="cancelled")){v="cancelled";for(let C of b)g(C).catch(()=>{});d?.abort(new ne(f||"Upload cancelled by user."))}},getStatus:()=>v}}async _uploadFileChunks(e){let{file:t,uploadId:r,cryptoKey:i,effectiveChunkSize:s,totalChunks:a,baseOffset:l,totalBytesAllFiles:o,progress:c,signal:h,baseUrl:B,retries:p,backoffMs:d,maxBackoffMs:w,chunkTimeoutMs:v,fileIndex:b,totalFiles:u,currentFileName:E}=e;for(let g=0;g<a;g++){if(h?.aborted)throw h.reason||new ne;let f=g*s,C=Math.min(f+s,t.size),S=t.slice(f,C),A=l+f,z=o>0?A/o*100:0;c({phase:"chunk",text:`Uploading chunk ${g+1} of ${a}...`,percent:z,processedBytes:A,totalBytes:o,chunkIndex:g,totalChunks:a,...b!==void 0?{fileIndex:b,totalFiles:u,currentFileName:E}:{}});let F=await S.arrayBuffer(),M;if(i?M=await He(this.cryptoObj,F,i):M=new Blob([F]),M.size>s+1024)throw new y("Chunk too large (client-side). Check chunk size settings.");let m=await M.arrayBuffer(),T=await at(this.cryptoObj,m);await this._attemptChunkUpload(`${B}/upload/chunk`,{method:"POST",headers:{"Content-Type":"application/octet-stream","X-Upload-ID":r,"X-Chunk-Index":String(g),"X-Chunk-Hash":T},body:M},{retries:p,backoffMs:d,maxBackoffMs:w,timeoutMs:v,signal:h,progress:c,chunkIndex:g,totalChunks:a,chunkSize:s,fileSizeBytes:o})}}async downloadFiles(e){let{fileId:t,bundleId:r,keyB64:i,asZip:s,zipFilename:a,onProgress:l,onData:o,onFileStart:c,onFileData:h,onFileEnd:B,signal:p,timeoutMs:d=6e4}=e,w=A=>{try{l&&l(A)}catch{}};if(!t&&!r)throw new y("Either fileId or bundleId is required.");w({phase:"server-info",text:"Checking server...",processedBytes:0,totalBytes:0,percent:0});let v=await this.connect({timeoutMs:d,signal:p}),{baseUrl:b}=v;if(w({phase:"server-compat",text:v.message,processedBytes:0,totalBytes:0,percent:0}),!v.compatible)throw new y(v.message);if(t)return this._downloadSingleFile({fileId:t,keyB64:i,onProgress:l,onData:o,signal:p,timeoutMs:d,baseUrl:b,compat:v});w({phase:"metadata",text:"Fetching bundle info...",processedBytes:0,totalBytes:0,percent:0});let u;try{u=await this.getBundleMetadata(r,i,{timeoutMs:d,signal:p})}catch(A){throw A instanceof X?A:A instanceof Error&&A.name==="AbortError"?new ne("Download cancelled."):new D("Could not fetch bundle metadata.",{cause:A})}let E=!!u.isEncrypted,g=u.totalSizeBytes||0,f,C=[];if(E){if(!i)throw new y("Decryption key is required for encrypted bundles.");if(!this.cryptoObj?.subtle)throw new y("Web Crypto API not available for decryption.");try{if(f=await Re(this.cryptoObj,i,this.base64),u.sealed&&u.encryptedManifest){let A=this.base64.decode(u.encryptedManifest),z=await Ee(this.cryptoObj,A,f),F=new TextDecoder().decode(z),M=JSON.parse(F);u.files=M.files.map(m=>({fileId:m.fileId,sizeBytes:m.sizeBytes,filename:m.name})),u.fileCount=u.files.length;for(let m of u.files)C.push(m.filename||"file")}else for(let A of u.files)C.push(await Ke(this.cryptoObj,A.encryptedFilename,f,this.base64))}catch(A){throw new X("Failed to decrypt bundle manifest.",{code:"DECRYPT_MANIFEST_FAILED",cause:A})}}else for(let A of u.files)C.push(A.filename||"file");let S=0;if(s&&o){let A=new Ge(o);for(let z=0;z<u.files.length;z++){let F=u.files[z],M=C[z];w({phase:"zipping",text:`Downloading ${M}...`,percent:g>0?S/g*100:0,processedBytes:S,totalBytes:g,fileIndex:z,totalFiles:u.files.length,currentFileName:M}),A.startFile(M);let m=S,T=await this._streamFileIntoCallback(b,F.fileId,E,f,v,p,d,R=>{A.writeChunk(R)},R=>{let Z=m+R;w({phase:"zipping",text:`Downloading ${M}...`,percent:g>0?Z/g*100:0,processedBytes:Z,totalBytes:g,fileIndex:z,totalFiles:u.files.length,currentFileName:M})});A.endFile(),S+=T}await A.finalize();try{await ce(this.fetchFn,`${b}/api/bundle/${r}/downloaded`,{method:"POST",timeoutMs:5e3,headers:{"Content-Type":"application/json",Accept:"application/json"},body:"{}"})}catch{}return w({phase:"complete",text:"Download complete!",percent:100,processedBytes:S,totalBytes:g}),{filenames:C,receivedBytes:S,wasEncrypted:E}}else{let A=h||o;for(let z=0;z<u.files.length;z++){let F=u.files[z],M=C[z];w({phase:"downloading",text:`Downloading ${M}...`,percent:g>0?S/g*100:0,processedBytes:S,totalBytes:g,fileIndex:z,totalFiles:u.files.length,currentFileName:M}),c?.({name:M,size:F.sizeBytes,index:z});let m=S,T=await this._streamFileIntoCallback(b,F.fileId,E,f,v,p,d,A?R=>A(R):void 0,R=>{let Z=m+R;w({phase:"downloading",text:`Downloading ${M}...`,percent:g>0?Z/g*100:0,processedBytes:Z,totalBytes:g,fileIndex:z,totalFiles:u.files.length,currentFileName:M})});B?.({name:M,index:z}),S+=T}return w({phase:"complete",text:"Download complete!",percent:100,processedBytes:S,totalBytes:g}),{filenames:C,receivedBytes:S,wasEncrypted:E}}}async _downloadSingleFile(e){let{fileId:t,keyB64:r,onProgress:i,onData:s,signal:a,timeoutMs:l,baseUrl:o,compat:c}=e,h=f=>{try{i&&i(f)}catch{}};h({phase:"metadata",text:"Fetching file info...",processedBytes:0,totalBytes:0,percent:0});let B;try{B=await this.getFileMetadata(t,{timeoutMs:l,signal:a})}catch(f){throw f instanceof X?f:f instanceof Error&&f.name==="AbortError"?new ne("Download cancelled."):new D("Could not fetch file metadata.",{cause:f})}let p=!!B.isEncrypted,d=B.sizeBytes||0;if(!s&&d>104857600){let f=Math.round(d/1048576),C=Math.round(104857600/(1024*1024));throw new y(`File is too large (${f}MB) to download without streaming. Provide an onData callback to stream files larger than ${C}MB.`)}let w,v;if(p){if(!r)throw new y("Decryption key is required for encrypted files.");if(!this.cryptoObj?.subtle)throw new y("Web Crypto API not available for decryption.");h({phase:"decrypting",text:"Preparing decryption...",processedBytes:0,totalBytes:0,percent:0});try{v=await Re(this.cryptoObj,r,this.base64),w=await Ke(this.cryptoObj,B.encryptedFilename,v,this.base64)}catch(f){throw new X("Failed to decrypt filename.",{code:"DECRYPT_FILENAME_FAILED",cause:f})}}else w=B.filename||"file";h({phase:"downloading",text:"Starting download...",percent:0,processedBytes:0,totalBytes:d});let b=[],u=!s,E=await this._streamFileIntoCallback(o,t,p,v,c,a,l,async f=>{u?b.push(f):await s(f)},f=>{h({phase:"downloading",text:"Downloading...",percent:d>0?f/d*100:0,processedBytes:f,totalBytes:d})});h({phase:"complete",text:"Download complete!",percent:100,processedBytes:E,totalBytes:d});let g;if(u&&b.length>0){let f=b.reduce((S,A)=>S+A.length,0);g=new Uint8Array(f);let C=0;for(let S of b)g.set(S,C),C+=S.length}return{filename:w,receivedBytes:E,wasEncrypted:p,...g?{data:g}:{}}}async _streamFileIntoCallback(e,t,r,i,s,a,l,o,c){let{signal:h,cleanup:B}=Le(a,l),p=0;try{let d=await this.fetchFn(`${e}/api/file/${t}`,{method:"GET",signal:h});if(!d.ok)throw new L(`Download failed (status ${d.status}).`);if(!d.body)throw new L("Streaming response not available.");let w=d.body.getReader();if(r&&i){let b=(Number.isFinite(s.serverInfo?.capabilities?.upload?.chunkSize)&&s.serverInfo.capabilities.upload.chunkSize>0?s.serverInfo.capabilities.upload.chunkSize:this.chunkSize)+28,u=[],E=0,g=()=>{if(u.length===0)return new Uint8Array(0);if(u.length===1){let S=u[0];return u.length=0,E=0,S}let f=new Uint8Array(E),C=0;for(let S of u)f.set(S,C),C+=S.length;return u.length=0,E=0,f};for(;;){if(a?.aborted)throw new ne("Download cancelled.");let{done:f,value:C}=await w.read();if(f)break;for(u.push(C),E+=C.length,p+=C.length,c&&c(p);E>=b;){let S=g(),A=S.subarray(0,b);S.length>b&&(u.push(S.subarray(b)),E=S.length-b);let z=await Ee(this.cryptoObj,A,i);o&&await o(new Uint8Array(z))}}if(E>0){let f=g(),C=await Ee(this.cryptoObj,f,i);o&&await o(new Uint8Array(C))}}else for(;;){if(a?.aborted)throw new ne("Download cancelled.");let{done:v,value:b}=await w.read();if(v)break;p+=b.length,c&&c(p),o&&await o(b)}}catch(d){throw d instanceof X?d:d instanceof Error&&d.name==="AbortError"?new ne("Download cancelled."):new D("Download failed.",{cause:d})}finally{B()}return p}async p2pSend(e){let t=await this.connect();if(!t.compatible)throw new y(t.message);let{serverInfo:r}=t,i=r?.capabilities?.p2p;if(!i?.enabled)throw new y("Direct transfer is disabled on this server.");let{host:s,port:a,secure:l}=this.serverTarget,{path:o,iceServers:c}=Ue({},i);return Zt({...e,host:s,port:a,secure:l,peerjsPath:o,iceServers:c,serverInfo:r,cryptoObj:this.cryptoObj})}async p2pReceive(e){let t=await this.connect();if(!t.compatible)throw new y(t.message);let{serverInfo:r}=t,i=r?.capabilities?.p2p;if(!i?.enabled)throw new y("Direct transfer is disabled on this server.");let{host:s,port:a,secure:l}=this.serverTarget,{path:o,iceServers:c}=Ue({},i);return Yt({...e,host:s,port:a,secure:l,peerjsPath:o,iceServers:c,serverInfo:r})}async _attemptChunkUpload(e,t,r){let{retries:i,backoffMs:s,maxBackoffMs:a,timeoutMs:l,signal:o,progress:c,chunkIndex:h,totalChunks:B,chunkSize:p,fileSizeBytes:d}=r,w=i,v=s,b=i;for(;;){if(o?.aborted)throw o.reason||new ne;let{signal:u,cleanup:E}=Le(o,l);try{let g=await this.fetchFn(e,{...t,signal:u});if(g.ok)return;let f=await g.text().catch(()=>"");throw new L(`Chunk ${h+1} failed (HTTP ${g.status}).`,{details:{status:g.status,bodySnippet:f.slice(0,120)}})}catch(g){if(E(),g instanceof Error&&(g.name==="AbortError"||g.code==="ABORT_ERR"))throw g;if(o?.aborted)throw o.reason||new ne;if(w<=0)throw g instanceof X?g:new D("Chunk upload failed.",{cause:g});let f=b-w+1,C=h*p,S=h/B*100,A=v,z=100;for(;A>0;){let F=(A/1e3).toFixed(1);c({phase:"retry-wait",text:`Chunk upload failed. Retrying in ${F}s... (${f}/${b})`,percent:S,processedBytes:C,totalBytes:d,chunkIndex:h,totalChunks:B}),await Pe(Math.min(z,A),o),A-=z}c({phase:"retry",text:`Chunk upload failed. Retrying now... (${f}/${b})`,percent:S,processedBytes:C,totalBytes:d,chunkIndex:h,totalChunks:B}),w-=1,v=Math.min(v*2,a);continue}finally{E()}}}};return cn(Un);})();
2
2
  if(typeof window!=="undefined"){window.DropgateCore=DropgateCore;}
3
3
  //# sourceMappingURL=index.browser.js.map