@modrinth/api-client 0.65.0 → 0.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,5 +6,5 @@ var o=class{constructor(e){this.client=e}};var O=class extends o{getModuleID(){r
6
6
  `),t=this.buffer.indexOf("\r");if(e===-1&&t===-1)return null;if(t!==-1&&(e===-1||t<e)){if(t===this.buffer.length-1)return null;let r=this.buffer[t+1]===`
7
7
  `?t+2:t+1;return{line:this.buffer.slice(0,t),length:r}}return{line:this.buffer.slice(0,e),length:e+1}}processLine(e,t){if(e===""){this.dispatch(t);return}if(e.startsWith(":"))return;let r=e.indexOf(":"),n=r===-1?e:e.slice(0,r),s=r===-1?"":e.slice(r+1);switch(s.startsWith(" ")&&(s=s.slice(1)),n){case"event":this.eventName=s;break;case"data":this.data+=`${s}
8
8
  `;break;case"id":this.id=s;break;case"retry":{let a=Number(s);Number.isInteger(a)&&a>=0&&t.push({kind:"retry",retry:a});break}}}dispatch(e){if(!this.data){this.eventName="",this.id=void 0;return}e.push({kind:"event",id:this.id,event:this.eventName||void 0,data:this.data.endsWith(`
9
- `)?this.data.slice(0,-1):this.data}),this.eventName="",this.data="",this.id=void 0}};function st(i){let e;try{e=JSON.parse(i)}catch{return null}return!e||typeof e!="object"||typeof e.type!="string"?null:e}var Ut=1e3,At=3e4,Dt=1e3,P=class extends C{constructor(){super(...arguments);this.emitter=$t()}async safeConnectServer(t,r={}){let n=this.connections.get(t);if(n&&!r.force&&!n.stopped&&n.status!=="disconnected")return;n&&this.closeConnection(t);let s={serverId:t,intent:r.intent??"all",reconnectAttempts:0,retryDelay:Ut,stopped:!1,status:"idle"};this.connections.set(t,s),this.runConnection(s)}disconnect(t){this.closeConnection(t),this.clearListeners(t)}disconnectAll(){for(let t of this.connections.keys())this.disconnect(t)}async runConnection(t){for(;!t.stopped;){let r=t.status==="connected";this.updateStatus(t,r?"reconnecting":"connecting");let n=new AbortController;t.controller=n;try{let s=await this.client.stream("/sync",{api:"archon",version:1,method:"GET",params:{scope:`server:${t.serverId}`,intent:this.intentToParam(t.intent)},headers:t.lastEventId?{"Last-Event-Id":t.lastEventId}:void 0,signal:n.signal,retry:!1,circuitBreaker:!1});if(t.stopped)return;t.reconnectAttempts=0,this.updateStatus(t,"connected");let a=await this.consumeStream(t,s);if(t.controller=void 0,t.stopped)return;if(a==="protocol-reconnect"){t.reconnectAttempts=0;continue}await this.waitForReconnect(t)}catch(s){if(t.controller=void 0,t.stopped||this.isAbortError(s))return;t.reconnectAttempts++,this.updateStatus(t,"error",s),console.warn(`[Sync] Connection failed for server ${t.serverId}:`,s),await this.waitForReconnect(t)}}}async consumeStream(t,r){let n=r.getReader(),s=new TextDecoder,a=new I;try{for(;!t.stopped;){let{done:u,value:d}=await n.read();if(u)break;let m=s.decode(d,{stream:!0}),b=this.processParsedItems(t,a.feed(m));if(b==="protocol-reconnect")return await n.cancel(),t.controller?.abort(),b}let l=s.decode(),c=l?a.feed(l):[],p=this.processParsedItems(t,[...c,...a.end()]);if(p==="protocol-reconnect")return await n.cancel(),t.controller?.abort(),p}finally{n.releaseLock()}return"closed"}processParsedItems(t,r){for(let n of r){if(n.kind==="retry"){t.retryDelay=Math.min(n.retry,At);continue}this.updateLastEventId(t,n.id);let s=st(n.data);if(!s){console.warn("[Sync] Dropping malformed SSE payload:",{serverId:t.serverId,event:n.event,data:n.data});continue}if(this.emitSyncEvent(t.serverId,s),s.type==="protocol.reset"||s.type==="protocol.invalid")return t.lastEventId=void 0,"protocol-reconnect"}return"closed"}async waitForReconnect(t){if(t.stopped)return;this.updateStatus(t,"reconnecting");let r=this.getReconnectDelay(t);await new Promise(n=>{t.reconnectResolve=n,t.reconnectTimer=setTimeout(()=>{t.reconnectTimer=void 0,t.reconnectResolve=void 0,n()},r)})}closeConnection(t){let r=this.connections.get(t);r&&(r.stopped=!0,r.controller?.abort(),r.reconnectTimer&&(clearTimeout(r.reconnectTimer),r.reconnectTimer=void 0),r.reconnectResolve?.(),r.reconnectResolve=void 0,this.updateStatus(r,"disconnected"),this.connections.delete(t))}getReconnectDelay(t){let r=t.retryDelay*Math.pow(2,Math.max(t.reconnectAttempts-1,0));return Math.min(r,At)+Math.random()*Dt}updateLastEventId(t,r){r!==void 0&&(t.lastEventId=r||void 0)}intentToParam(t){return Array.isArray(t)?t.join(","):t}isAbortError(t){return t instanceof Error?t.name==="AbortError"||t.message.toLowerCase().includes("abort"):!1}};import jt from"mitt";var _t=1e3,A=class extends q{constructor(){super(...arguments);this.emitter=jt()}async connect(t,r){return this.connections.has(t)&&this.closeConnection(t),new Promise((n,s)=>{let a=!1,l=null,c=()=>{a||(a=!0,l&&clearTimeout(l),n())},p=u=>{a||(a=!0,l&&clearTimeout(l),s(u))};try{let u=new WebSocket(Xe(r.url)),d={serverId:t,socket:u,authenticated:!1,reconnectAttempts:0,reconnectTimer:void 0,isReconnecting:!1};this.connections.set(t,d),l=setTimeout(()=>{p(new Error(`WebSocket authentication timed out for server ${t}`)),this.connections.get(t)===d&&this.closeConnection(t)},this.AUTHENTICATION_TIMEOUT),u.onopen=()=>{u.send(JSON.stringify({event:"auth",jwt:r.token})),d.reconnectAttempts=0,d.isReconnecting=!1},u.onmessage=m=>{try{let b=JSON.parse(m.data);b.event==="auth-ok"?d.authenticated=!0:b.event==="auth-incorrect"&&(d.authenticated=!1);let v=`${t}:${b.event}`;this.emitter.emit(v,b),b.event==="auth-ok"&&c(),(b.event==="auth-expiring"||b.event==="auth-incorrect")&&this.handleAuthExpiring(t).catch(console.error)}catch(b){console.error("[WebSocket] Failed to parse message:",b)}},u.onclose=m=>{d.authenticated=!1,console.debug(`[WebSocket] Closed for server ${t}:`,{code:m.code,reason:m.reason,wasClean:m.wasClean}),p(new Error(`WebSocket closed before authentication for server ${t} (code: ${m.code})`)),m.code!==_t&&this.scheduleReconnect(t,r)},u.onerror=m=>{let b=u.url,v=u.readyState;console.error(`[WebSocket] Error for server ${t}:`,{url:b,readyState:v,readyStateLabel:["CONNECTING","OPEN","CLOSING","CLOSED"][v],type:m.type}),p(new Error(`WebSocket connection failed for server ${t} (readyState: ${v})`))}}catch(u){p(u)}})}disconnect(t){this.closeConnection(t),this.emitter.all.forEach((r,n)=>{n.toString().startsWith(`${t}:`)&&this.emitter.all.delete(n)})}closeConnection(t){let r=this.connections.get(t);r&&(r.reconnectTimer&&(clearTimeout(r.reconnectTimer),r.reconnectTimer=void 0),(r.socket.readyState===WebSocket.OPEN||r.socket.readyState===WebSocket.CONNECTING)&&r.socket.close(_t,"Client disconnecting"),this.connections.delete(t))}disconnectAll(){for(let t of this.connections.keys())this.disconnect(t)}send(t,r){let n=this.connections.get(t);if(!n||n.socket.readyState!==WebSocket.OPEN){console.warn(`Cannot send message: WebSocket not connected for server ${t}`);return}n.socket.send(JSON.stringify(r))}scheduleReconnect(t,r){let n=this.connections.get(t);if(!n)return;if(n.reconnectAttempts>=this.MAX_RECONNECT_ATTEMPTS){this.disconnect(t);return}n.isReconnecting=!0,n.reconnectAttempts++;let s=this.getReconnectDelay(n.reconnectAttempts);n.reconnectTimer=setTimeout(()=>{this.connect(t,r).catch(a=>{console.error(`[WebSocket] Reconnection failed for server ${t}:`,a)})},s)}async handleAuthExpiring(t){try{let r=await this.client.archon.servers_v0.getWebSocketAuth(t),n=this.connections.get(t);n&&n.socket.readyState===WebSocket.OPEN&&n.socket.send(JSON.stringify({event:"auth",jwt:r.token}))}catch(r){console.error(`[WebSocket] Failed to refresh auth for server ${t}:`,r),this.disconnect(t)}}};var S=class extends L{upload(e,t){let r;t.api==="labrinth"?r=this.resolveBaseUrl(this.config.labrinthBaseUrl):t.api==="archon"?r=this.resolveBaseUrl(this.config.archonBaseUrl):t.api==="sharedinstances"?r=this.resolveBaseUrl(this.config.sharedInstancesBaseUrl):r=t.api;let n=this.buildUrl(e,r,t.version),s=[];t.onProgress&&s.push(t.onProgress);let a=new AbortController;t.signal&&t.signal.addEventListener("abort",()=>a.abort());let l,c={promise:(async()=>{let p="formData"in t&&t.formData instanceof FormData,u=await this.buildDefaultHeaders();p?delete u["Content-Type"]:u["Content-Type"]="application/octet-stream";let d={retry:!1,...t,headers:{...u,...t.headers}};this.attachArchonSentryCaptureHeader(d);let m=this.buildUploadContext(n,e,d);if(l=m,a.signal.aborted)throw new g("Upload cancelled");let b=await this.executeUploadFeatureChain(m,s,a);return await this.config.hooks?.onResponse?.(b,m),b})().catch(async p=>{let u=this.normalizeError(p,l);throw l&&await this.config.hooks?.onError?.(u,l),u}),onProgress:p=>(s.push(p),c),cancel:()=>a.abort()};return c}executeXHRUpload(e,t,r){return new Promise((n,s)=>{let a=new XMLHttpRequest,l=e.metadata;a.upload.addEventListener("progress",u=>{if(u.lengthComputable){let d={loaded:u.loaded,total:u.total,progress:u.loaded/u.total};t.forEach(m=>m(d))}}),a.addEventListener("load",()=>{if(a.status>=200&&a.status<300)try{n(a.response?JSON.parse(a.response):void 0)}catch{n(void 0)}else s(this.createUploadError(a))}),a.addEventListener("error",()=>s(new g("Upload failed"))),a.addEventListener("abort",()=>s(new g("Upload cancelled"))),a.addEventListener("timeout",()=>s(new g("Upload timed out"))),e.options.timeout!==void 0&&(a.timeout=e.options.timeout);let c=e.url;if(e.options.params){let u=new URLSearchParams(Object.entries(e.options.params).map(([d,m])=>[d,String(m)])).toString();c+=(c.includes("?")?"&":"?")+u}a.open("POST",c);for(let[u,d]of Object.entries(e.options.headers??{}))a.setRequestHeader(u,d);let p="formData"in l?l.formData:l.file;a.send(p),r.signal.addEventListener("abort",()=>a.abort())})}createUploadError(e){let t;try{t=e.response?JSON.parse(e.response):void 0}catch{t=e.responseText}return this.createNormalizedError(new Error(`Upload failed with status ${e.status}`),e.status,t)}};var ot=class extends S{constructor(e){super(e),Object.defineProperty(this.archon,"sockets",{value:new A(this),writable:!1,enumerable:!0,configurable:!1}),Object.defineProperty(this.archon,"sync",{value:new P(this),writable:!1,enumerable:!0,configurable:!1})}async executeRequest(e,t){try{return await Nt(e,{method:t.method??"GET",headers:t.headers,body:t.body,params:t.params,timeout:t.timeout,signal:t.signal})}catch(r){throw this.normalizeError(r)}}async executeStreamRequest(e,t){try{let r=await fetch(_(e,t.params),{method:t.method??"GET",headers:t.headers,body:E(t.body),signal:t.signal});if(!r.ok)throw this.createNormalizedError(new Error(`HTTP ${r.status}: ${r.statusText}`),r.status,await R(r));if(!r.body)throw new g("Streaming response has no readable body",{statusCode:r.status});return r.body}catch(r){throw this.normalizeError(r)}}normalizeError(e){return e instanceof Bt?this.createNormalizedError(e,e.response?.status,e.data):super.normalizeError(e)}};import{FetchError as Ft}from"ofetch";var at=class{constructor(){let e=useState("circuit-breaker-state",()=>new Map);this.state=e.value}get(e){return this.state.get(e)}set(e,t){this.state.set(e,t)}clear(e){this.state.delete(e)}},lt=class extends S{constructor(e){super(e),Object.defineProperty(this.archon,"sockets",{value:new A(this),writable:!1,enumerable:!0,configurable:!1}),Object.defineProperty(this.archon,"sync",{value:new P(this),writable:!1,enumerable:!0,configurable:!1})}async resolveRateLimitKey(){if(this.rateLimitKeyResolved!==void 0)return this.rateLimitKeyResolved;let e=this.config.rateLimitKey;return typeof e=="string"?this.rateLimitKeyResolved=e:typeof e=="function"&&(this.rateLimitKeyPromise||(this.rateLimitKeyPromise=e()),this.rateLimitKeyResolved=await this.rateLimitKeyPromise),this.rateLimitKeyResolved}async request(e,t){return import.meta.server&&await this.resolveRateLimitKey(),super.request(e,t)}upload(e,t){if(import.meta.server)throw new g("upload() is not supported during SSR");return super.upload(e,t)}async executeRequest(e,t){try{return await $fetch(e,{method:t.method??"GET",headers:t.headers,body:t.body,params:t.params,timeout:t.timeout,signal:t.signal,cache:import.meta.server?void 0:"no-store"})}catch(r){throw this.normalizeError(r)}}async executeStreamRequest(e,t){try{let r=await fetch(_(e,t.params),{method:t.method??"GET",headers:t.headers,body:E(t.body),signal:t.signal,cache:import.meta.server?void 0:"no-store"});if(!r.ok)throw this.createNormalizedError(new Error(`HTTP ${r.status}: ${r.statusText}`),r.status,await R(r));if(!r.body)throw new g("Streaming response has no readable body",{statusCode:r.status});return r.body}catch(r){throw this.normalizeError(r)}}normalizeError(e){return e instanceof Ft?this.createNormalizedError(e,e.response?.status,e.data):super.normalizeError(e)}async buildDefaultHeaders(){let e={...await super.buildDefaultHeaders()};return import.meta.server&&this.rateLimitKeyResolved&&(e["x-ratelimit-key"]=this.rateLimitKeyResolved),e}};var ct=class extends S{constructor(e){super(e),Object.defineProperty(this.archon,"sockets",{value:new A(this),writable:!1,enumerable:!0,configurable:!1}),Object.defineProperty(this.archon,"sync",{value:new P(this),writable:!1,enumerable:!0,configurable:!1})}async executeRequest(e,t){try{let{fetch:r}=await import("@tauri-apps/plugin-http"),n=E(t.body),s=_(e,t.params),a=await r(s,{method:t.method??"GET",headers:t.headers,body:n});if(!a.ok){let p;try{p=await a.json()}catch{p=void 0}let u=new Error(`HTTP ${a.status}: ${a.statusText}`);throw u.statusCode=a.status,u.responseData=p,u}let l=a.headers.get("content-type")?.toLowerCase()??"";if(s.includes("/fs/download"))return await a.blob();if(l.startsWith("image/")||l.startsWith("audio/")||l.startsWith("video/")||l.includes("application/octet-stream"))return await a.blob();if(a.status===204||a.status===205)return;if(l.includes("application/json")||l.includes("+json"))return await a.json();let c=await a.text();if(!c)return;try{return JSON.parse(c)}catch{return c}}catch(r){throw this.normalizeError(r)}}async executeStreamRequest(e,t){try{let{fetch:r}=await import("@tauri-apps/plugin-http"),n=await r(_(e,t.params),{method:t.method??"GET",headers:t.headers,body:E(t.body),signal:t.signal});if(!n.ok)throw this.createNormalizedError(new Error(`HTTP ${n.status}: ${n.statusText}`),n.status,await R(n));if(!n.body)throw this.createNormalizedError(new Error("Streaming response has no readable body"),n.status,void 0);return n.body}catch(r){throw this.normalizeError(r)}}normalizeError(e){if(e instanceof Error){let t=e,r=t.statusCode,n=t.responseData;return this.createNormalizedError(e,r,n)}return super.normalizeError(e)}};var M={getAuth:null,refreshAuth:null};function Gt(i,e){M.getAuth=i,M.refreshAuth=e}function Wt(){M.getAuth=null,M.refreshAuth=null}async function Vt(i,e){try{return await i()}catch(t){if(t instanceof g&&t.statusCode===401)return await e(),await i();throw t}}async function Ht(i,e={}){let t=e.count??5,r=e.intervalMs??200,n=e.settleDelayMs??1e3,s=e.timeoutMs??t*r+n+1e3;return e.signal?.aborted?-1:await new Promise(a=>{let l=[],c=new Set,p,u=!1,d=(y,T)=>{let h=setTimeout(()=>{c.delete(h),y()},T);return c.add(h),h},m=()=>{for(let y of c)clearTimeout(y);c.clear(),e.signal?.removeEventListener("abort",v),p&&(p.readyState===WebSocket.CONNECTING||p.readyState===WebSocket.OPEN)&&p.close()},b=y=>{u||(u=!0,m(),a(y))},v=()=>b(-1);e.signal?.addEventListener("abort",v,{once:!0});try{p=new WebSocket(i)}catch{b(-1);return}d(()=>b(-1),s),p.onopen=()=>{for(let y=0;y<t;y++)d(()=>{p?.readyState===WebSocket.OPEN&&p.send(String(performance.now()))},y*r);d(()=>{let y=l.length>0?Math.round([...l].sort((T,h)=>T-h)[Math.floor(l.length/2)]):-1;b(y)},t*r+n)},p.onmessage=y=>{l.push(performance.now()-Number(y.data))},p.onerror=()=>b(-1),p.onclose=()=>{!u&&l.length===0&&b(-1)}})}export{f as AbstractFeature,L as AbstractModrinthClient,C as AbstractSyncClient,w as AbstractUploadClient,q as AbstractWebSocketClient,St as Archon,Ze as AuthFeature,et as CircuitBreakerFeature,ot as GenericModrinthClient,P as GenericSyncClient,Qe as InMemoryCircuitBreakerStorage,g as ModrinthApiError,k as ModrinthServerError,tt as NodeAuthFeature,at as NuxtCircuitBreakerStorage,lt as NuxtModrinthClient,Pt as PANEL_VERSION,rt as PanelVersionFeature,nt as RetryFeature,I as SseParser,ct as TauriModrinthClient,it as VerboseLoggingFeature,S as XHRUploadClient,Wt as clearNodeAuthState,Xe as getNodeWebSocketUrl,Ye as isModrinthErrorResponse,M as nodeAuthState,st as parseSyncEventData,Ht as pingWebSocketUrl,Gt as setNodeAuthState,Vt as withJWTRetry};
9
+ `)?this.data.slice(0,-1):this.data}),this.eventName="",this.data="",this.id=void 0}};function st(i){let e;try{e=JSON.parse(i)}catch{return null}return!e||typeof e!="object"||typeof e.type!="string"?null:e}var Ut=1e3,At=3e4,Dt=1e3,P=class extends C{constructor(){super(...arguments);this.emitter=$t()}async safeConnectServer(t,r={}){let n=this.connections.get(t);if(n&&!r.force&&!n.stopped&&n.status!=="disconnected")return;n&&this.closeConnection(t);let s={serverId:t,intent:r.intent??"all",reconnectAttempts:0,retryDelay:Ut,stopped:!1,status:"idle"};this.connections.set(t,s),this.runConnection(s)}disconnect(t){this.closeConnection(t),this.clearListeners(t)}disconnectAll(){for(let t of this.connections.keys())this.disconnect(t)}async runConnection(t){for(;!t.stopped;){let r=t.status==="connected";this.updateStatus(t,r?"reconnecting":"connecting");let n=new AbortController;t.controller=n;let s;try{let a=await this.client.stream("/sync",{api:"archon",version:1,method:"GET",params:{scope:`server:${t.serverId}`,intent:this.intentToParam(t.intent)},headers:t.lastEventId?{"Last-Event-Id":t.lastEventId}:void 0,signal:n.signal,retry:!1,circuitBreaker:!1});if(t.stopped)return;t.reconnectAttempts=0,this.updateStatus(t,"connected"),s=await this.consumeStream(t,a)}catch(a){if(t.stopped||this.isAbortError(a))return;t.reconnectAttempts++,this.updateStatus(t,"error",a),console.warn(`[Sync] Connection failed for server ${t.serverId}:`,a)}finally{n.abort(),t.controller=void 0}if(t.stopped)return;if(s==="protocol-reconnect"){t.reconnectAttempts=0;continue}await this.waitForReconnect(t)}}async consumeStream(t,r){let n=r.getReader(),s=new TextDecoder,a=new I;try{for(;!t.stopped;){let{done:u,value:d}=await n.read();if(u)break;let m=s.decode(d,{stream:!0}),b=this.processParsedItems(t,a.feed(m));if(b==="protocol-reconnect")return await n.cancel(),t.controller?.abort(),b}let l=s.decode(),c=l?a.feed(l):[],p=this.processParsedItems(t,[...c,...a.end()]);if(p==="protocol-reconnect")return await n.cancel(),t.controller?.abort(),p}finally{n.releaseLock()}return"closed"}processParsedItems(t,r){for(let n of r){if(n.kind==="retry"){t.retryDelay=Math.min(n.retry,At);continue}this.updateLastEventId(t,n.id);let s=st(n.data);if(!s){console.warn("[Sync] Dropping malformed SSE payload:",{serverId:t.serverId,event:n.event,data:n.data});continue}if(this.emitSyncEvent(t.serverId,s),s.type==="protocol.reset"||s.type==="protocol.invalid")return t.lastEventId=void 0,"protocol-reconnect"}return"closed"}async waitForReconnect(t){if(t.stopped)return;this.updateStatus(t,"reconnecting");let r=this.getReconnectDelay(t);await new Promise(n=>{t.reconnectResolve=n,t.reconnectTimer=setTimeout(()=>{t.reconnectTimer=void 0,t.reconnectResolve=void 0,n()},r)})}closeConnection(t){let r=this.connections.get(t);r&&(r.stopped=!0,r.controller?.abort(),r.reconnectTimer&&(clearTimeout(r.reconnectTimer),r.reconnectTimer=void 0),r.reconnectResolve?.(),r.reconnectResolve=void 0,this.updateStatus(r,"disconnected"),this.connections.delete(t))}getReconnectDelay(t){let r=t.retryDelay*Math.pow(2,Math.max(t.reconnectAttempts-1,0));return Math.min(r,At)+Math.random()*Dt}updateLastEventId(t,r){r!==void 0&&(t.lastEventId=r||void 0)}intentToParam(t){return Array.isArray(t)?t.join(","):t}isAbortError(t){return t instanceof Error?t.name==="AbortError"||t.message.toLowerCase().includes("abort"):!1}};import jt from"mitt";var _t=1e3,A=class extends q{constructor(){super(...arguments);this.emitter=jt()}async connect(t,r){return this.connections.has(t)&&this.closeConnection(t),new Promise((n,s)=>{let a=!1,l=null,c=()=>{a||(a=!0,l&&clearTimeout(l),n())},p=u=>{a||(a=!0,l&&clearTimeout(l),s(u))};try{let u=new WebSocket(Xe(r.url)),d={serverId:t,socket:u,authenticated:!1,reconnectAttempts:0,reconnectTimer:void 0,isReconnecting:!1};this.connections.set(t,d),l=setTimeout(()=>{p(new Error(`WebSocket authentication timed out for server ${t}`)),this.connections.get(t)===d&&this.closeConnection(t)},this.AUTHENTICATION_TIMEOUT),u.onopen=()=>{u.send(JSON.stringify({event:"auth",jwt:r.token})),d.reconnectAttempts=0,d.isReconnecting=!1},u.onmessage=m=>{try{let b=JSON.parse(m.data);b.event==="auth-ok"?d.authenticated=!0:b.event==="auth-incorrect"&&(d.authenticated=!1);let v=`${t}:${b.event}`;this.emitter.emit(v,b),b.event==="auth-ok"&&c(),(b.event==="auth-expiring"||b.event==="auth-incorrect")&&this.handleAuthExpiring(t).catch(console.error)}catch(b){console.error("[WebSocket] Failed to parse message:",b)}},u.onclose=m=>{d.authenticated=!1,console.debug(`[WebSocket] Closed for server ${t}:`,{code:m.code,reason:m.reason,wasClean:m.wasClean}),p(new Error(`WebSocket closed before authentication for server ${t} (code: ${m.code})`)),m.code!==_t&&this.scheduleReconnect(t,r)},u.onerror=m=>{let b=u.url,v=u.readyState;console.error(`[WebSocket] Error for server ${t}:`,{url:b,readyState:v,readyStateLabel:["CONNECTING","OPEN","CLOSING","CLOSED"][v],type:m.type}),p(new Error(`WebSocket connection failed for server ${t} (readyState: ${v})`))}}catch(u){p(u)}})}disconnect(t){this.closeConnection(t),this.emitter.all.forEach((r,n)=>{n.toString().startsWith(`${t}:`)&&this.emitter.all.delete(n)})}closeConnection(t){let r=this.connections.get(t);r&&(r.reconnectTimer&&(clearTimeout(r.reconnectTimer),r.reconnectTimer=void 0),(r.socket.readyState===WebSocket.OPEN||r.socket.readyState===WebSocket.CONNECTING)&&r.socket.close(_t,"Client disconnecting"),this.connections.delete(t))}disconnectAll(){for(let t of this.connections.keys())this.disconnect(t)}send(t,r){let n=this.connections.get(t);if(!n||n.socket.readyState!==WebSocket.OPEN){console.warn(`Cannot send message: WebSocket not connected for server ${t}`);return}n.socket.send(JSON.stringify(r))}scheduleReconnect(t,r){let n=this.connections.get(t);if(!n)return;if(n.reconnectAttempts>=this.MAX_RECONNECT_ATTEMPTS){this.disconnect(t);return}n.isReconnecting=!0,n.reconnectAttempts++;let s=this.getReconnectDelay(n.reconnectAttempts);n.reconnectTimer=setTimeout(()=>{this.connect(t,r).catch(a=>{console.error(`[WebSocket] Reconnection failed for server ${t}:`,a)})},s)}async handleAuthExpiring(t){try{let r=await this.client.archon.servers_v0.getWebSocketAuth(t),n=this.connections.get(t);n&&n.socket.readyState===WebSocket.OPEN&&n.socket.send(JSON.stringify({event:"auth",jwt:r.token}))}catch(r){console.error(`[WebSocket] Failed to refresh auth for server ${t}:`,r),this.disconnect(t)}}};var S=class extends L{upload(e,t){let r;t.api==="labrinth"?r=this.resolveBaseUrl(this.config.labrinthBaseUrl):t.api==="archon"?r=this.resolveBaseUrl(this.config.archonBaseUrl):t.api==="sharedinstances"?r=this.resolveBaseUrl(this.config.sharedInstancesBaseUrl):r=t.api;let n=this.buildUrl(e,r,t.version),s=[];t.onProgress&&s.push(t.onProgress);let a=new AbortController;t.signal&&t.signal.addEventListener("abort",()=>a.abort());let l,c={promise:(async()=>{let p="formData"in t&&t.formData instanceof FormData,u=await this.buildDefaultHeaders();p?delete u["Content-Type"]:u["Content-Type"]="application/octet-stream";let d={retry:!1,...t,headers:{...u,...t.headers}};this.attachArchonSentryCaptureHeader(d);let m=this.buildUploadContext(n,e,d);if(l=m,a.signal.aborted)throw new g("Upload cancelled");let b=await this.executeUploadFeatureChain(m,s,a);return await this.config.hooks?.onResponse?.(b,m),b})().catch(async p=>{let u=this.normalizeError(p,l);throw l&&await this.config.hooks?.onError?.(u,l),u}),onProgress:p=>(s.push(p),c),cancel:()=>a.abort()};return c}executeXHRUpload(e,t,r){return new Promise((n,s)=>{let a=new XMLHttpRequest,l=e.metadata;a.upload.addEventListener("progress",u=>{if(u.lengthComputable){let d={loaded:u.loaded,total:u.total,progress:u.loaded/u.total};t.forEach(m=>m(d))}}),a.addEventListener("load",()=>{if(a.status>=200&&a.status<300)try{n(a.response?JSON.parse(a.response):void 0)}catch{n(void 0)}else s(this.createUploadError(a))}),a.addEventListener("error",()=>s(new g("Upload failed"))),a.addEventListener("abort",()=>s(new g("Upload cancelled"))),a.addEventListener("timeout",()=>s(new g("Upload timed out"))),e.options.timeout!==void 0&&(a.timeout=e.options.timeout);let c=e.url;if(e.options.params){let u=new URLSearchParams(Object.entries(e.options.params).map(([d,m])=>[d,String(m)])).toString();c+=(c.includes("?")?"&":"?")+u}a.open("POST",c);for(let[u,d]of Object.entries(e.options.headers??{}))a.setRequestHeader(u,d);let p="formData"in l?l.formData:l.file;a.send(p),r.signal.addEventListener("abort",()=>a.abort())})}createUploadError(e){let t;try{t=e.response?JSON.parse(e.response):void 0}catch{t=e.responseText}return this.createNormalizedError(new Error(`Upload failed with status ${e.status}`),e.status,t)}};var ot=class extends S{constructor(e){super(e),Object.defineProperty(this.archon,"sockets",{value:new A(this),writable:!1,enumerable:!0,configurable:!1}),Object.defineProperty(this.archon,"sync",{value:new P(this),writable:!1,enumerable:!0,configurable:!1})}async executeRequest(e,t){try{return await Nt(e,{method:t.method??"GET",headers:t.headers,body:t.body,params:t.params,timeout:t.timeout,signal:t.signal})}catch(r){throw this.normalizeError(r)}}async executeStreamRequest(e,t){try{let r=await fetch(_(e,t.params),{method:t.method??"GET",headers:t.headers,body:E(t.body),signal:t.signal});if(!r.ok)throw this.createNormalizedError(new Error(`HTTP ${r.status}: ${r.statusText}`),r.status,await R(r));if(!r.body)throw new g("Streaming response has no readable body",{statusCode:r.status});return r.body}catch(r){throw this.normalizeError(r)}}normalizeError(e){return e instanceof Bt?this.createNormalizedError(e,e.response?.status,e.data):super.normalizeError(e)}};import{FetchError as Ft}from"ofetch";var at=class{constructor(){let e=useState("circuit-breaker-state",()=>new Map);this.state=e.value}get(e){return this.state.get(e)}set(e,t){this.state.set(e,t)}clear(e){this.state.delete(e)}},lt=class extends S{constructor(e){super(e),Object.defineProperty(this.archon,"sockets",{value:new A(this),writable:!1,enumerable:!0,configurable:!1}),Object.defineProperty(this.archon,"sync",{value:new P(this),writable:!1,enumerable:!0,configurable:!1})}async resolveRateLimitKey(){if(this.rateLimitKeyResolved!==void 0)return this.rateLimitKeyResolved;let e=this.config.rateLimitKey;return typeof e=="string"?this.rateLimitKeyResolved=e:typeof e=="function"&&(this.rateLimitKeyPromise||(this.rateLimitKeyPromise=e()),this.rateLimitKeyResolved=await this.rateLimitKeyPromise),this.rateLimitKeyResolved}async request(e,t){return import.meta.server&&await this.resolveRateLimitKey(),super.request(e,t)}upload(e,t){if(import.meta.server)throw new g("upload() is not supported during SSR");return super.upload(e,t)}async executeRequest(e,t){try{return await $fetch(e,{method:t.method??"GET",headers:t.headers,body:t.body,params:t.params,timeout:t.timeout,signal:t.signal,cache:import.meta.server?void 0:"no-store"})}catch(r){throw this.normalizeError(r)}}async executeStreamRequest(e,t){try{let r=await fetch(_(e,t.params),{method:t.method??"GET",headers:t.headers,body:E(t.body),signal:t.signal,cache:import.meta.server?void 0:"no-store"});if(!r.ok)throw this.createNormalizedError(new Error(`HTTP ${r.status}: ${r.statusText}`),r.status,await R(r));if(!r.body)throw new g("Streaming response has no readable body",{statusCode:r.status});return r.body}catch(r){throw this.normalizeError(r)}}normalizeError(e){return e instanceof Ft?this.createNormalizedError(e,e.response?.status,e.data):super.normalizeError(e)}async buildDefaultHeaders(){let e={...await super.buildDefaultHeaders()};return import.meta.server&&this.rateLimitKeyResolved&&(e["x-ratelimit-key"]=this.rateLimitKeyResolved),e}};var ct=class extends S{constructor(e){super(e),Object.defineProperty(this.archon,"sockets",{value:new A(this),writable:!1,enumerable:!0,configurable:!1}),Object.defineProperty(this.archon,"sync",{value:new P(this),writable:!1,enumerable:!0,configurable:!1})}async executeRequest(e,t){try{let{fetch:r}=await import("@tauri-apps/plugin-http"),n=E(t.body),s=_(e,t.params),a=await r(s,{method:t.method??"GET",headers:t.headers,body:n});if(!a.ok){let p;try{p=await a.json()}catch{p=void 0}let u=new Error(`HTTP ${a.status}: ${a.statusText}`);throw u.statusCode=a.status,u.responseData=p,u}let l=a.headers.get("content-type")?.toLowerCase()??"";if(s.includes("/fs/download"))return await a.blob();if(l.startsWith("image/")||l.startsWith("audio/")||l.startsWith("video/")||l.includes("application/octet-stream"))return await a.blob();if(a.status===204||a.status===205)return;if(l.includes("application/json")||l.includes("+json"))return await a.json();let c=await a.text();if(!c)return;try{return JSON.parse(c)}catch{return c}}catch(r){throw this.normalizeError(r)}}async executeStreamRequest(e,t){try{let{fetch:r}=await import("@tauri-apps/plugin-http"),n=await r(_(e,t.params),{method:t.method??"GET",headers:t.headers,body:E(t.body),signal:t.signal});if(!n.ok)throw this.createNormalizedError(new Error(`HTTP ${n.status}: ${n.statusText}`),n.status,await R(n));if(!n.body)throw this.createNormalizedError(new Error("Streaming response has no readable body"),n.status,void 0);return n.body}catch(r){throw this.normalizeError(r)}}normalizeError(e){if(e instanceof Error){let t=e,r=t.statusCode,n=t.responseData;return this.createNormalizedError(e,r,n)}return super.normalizeError(e)}};var M={getAuth:null,refreshAuth:null};function Gt(i,e){M.getAuth=i,M.refreshAuth=e}function Wt(){M.getAuth=null,M.refreshAuth=null}async function Vt(i,e){try{return await i()}catch(t){if(t instanceof g&&t.statusCode===401)return await e(),await i();throw t}}async function Ht(i,e={}){let t=e.count??5,r=e.intervalMs??200,n=e.settleDelayMs??1e3,s=e.timeoutMs??t*r+n+1e3;return e.signal?.aborted?-1:await new Promise(a=>{let l=[],c=new Set,p,u=!1,d=(y,T)=>{let h=setTimeout(()=>{c.delete(h),y()},T);return c.add(h),h},m=()=>{for(let y of c)clearTimeout(y);c.clear(),e.signal?.removeEventListener("abort",v),p&&(p.readyState===WebSocket.CONNECTING||p.readyState===WebSocket.OPEN)&&p.close()},b=y=>{u||(u=!0,m(),a(y))},v=()=>b(-1);e.signal?.addEventListener("abort",v,{once:!0});try{p=new WebSocket(i)}catch{b(-1);return}d(()=>b(-1),s),p.onopen=()=>{for(let y=0;y<t;y++)d(()=>{p?.readyState===WebSocket.OPEN&&p.send(String(performance.now()))},y*r);d(()=>{let y=l.length>0?Math.round([...l].sort((T,h)=>T-h)[Math.floor(l.length/2)]):-1;b(y)},t*r+n)},p.onmessage=y=>{l.push(performance.now()-Number(y.data))},p.onerror=()=>b(-1),p.onclose=()=>{!u&&l.length===0&&b(-1)}})}export{f as AbstractFeature,L as AbstractModrinthClient,C as AbstractSyncClient,w as AbstractUploadClient,q as AbstractWebSocketClient,St as Archon,Ze as AuthFeature,et as CircuitBreakerFeature,ot as GenericModrinthClient,P as GenericSyncClient,Qe as InMemoryCircuitBreakerStorage,g as ModrinthApiError,k as ModrinthServerError,tt as NodeAuthFeature,at as NuxtCircuitBreakerStorage,lt as NuxtModrinthClient,Pt as PANEL_VERSION,rt as PanelVersionFeature,nt as RetryFeature,I as SseParser,ct as TauriModrinthClient,it as VerboseLoggingFeature,S as XHRUploadClient,Wt as clearNodeAuthState,Xe as getNodeWebSocketUrl,Ye as isModrinthErrorResponse,M as nodeAuthState,st as parseSyncEventData,Ht as pingWebSocketUrl,Gt as setNodeAuthState,Vt as withJWTRetry};
10
10
  //# sourceMappingURL=index.js.map