node-catbox
Version:
A library for interacting with Catbox.moe written in TypeScript.
1 lines • 7.98 kB
JavaScript
import{createWriteStream as e,openAsBlob as t}from"node:fs";import n,{once as r}from"node:events";import{basename as i,join as a,resolve as o}from"node:path";import{tmpdir as s}from"node:os";import{mkdtemp as c,rm as l,stat as u}from"node:fs/promises";import{randomUUID as d}from"node:crypto";const f=`depthbomb/node-catbox`,p=`https://catbox.moe/user/api.php`,m=`https://litterbox.catbox.moe/resources/internals/api.php`,h=200*1024*1024,g=1024*1024*1024,_=3e4,v=new TextEncoder;async function y(e){try{return(await u(e)).isFile()}catch{return!1}}function b(e){let t=Object.freeze(Object.fromEntries(e.headers.entries()));return Object.freeze({url:e.url,ok:e.ok,status:e.status,statusText:e.statusText,redirected:e.redirected,type:e.type,headers:t})}function x(e){return e?e instanceof Headers?Object.fromEntries(e.entries()):Array.isArray(e)?Object.fromEntries(e):Object.fromEntries(Object.entries(e).filter(([,e])=>e!==void 0)):{}}function S(e,t){return Object.freeze({url:e,method:t.method??`GET`,headers:Object.freeze(x(t.headers)),hasBody:t.body!==void 0&&t.body!==null})}function C(e){try{let t=new URL(e);if(t.protocol!==`http:`&&t.protocol!==`https:`)throw Error()}catch{throw Error(`Invalid URL "${e}", expected an absolute http(s) URL`)}}async function w(e,t){if(!Number.isInteger(t)||t<=0)throw Error(`Invalid max file size "${t}", expected a positive integer`);if((await u(e)).size>t)throw Error(`File exceeds maximum size of ${t} bytes`)}function T(e){return typeof e==`string`?v.encode(e):e instanceof ArrayBuffer?new Uint8Array(e):new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}async function E(n,i){if(!Number.isInteger(i)||i<=0)throw Error(`Invalid max stream size "${i}", expected a positive integer`);let o=await c(a(s(),`node-catbox-`)),u=a(o,`${d()}.upload`),f=e(u,{flags:`wx`}),p=async()=>{await l(o,{recursive:!0,force:!0})},m=0;try{for await(let e of n){if(typeof e!=`string`&&!(e instanceof ArrayBuffer)&&!ArrayBuffer.isView(e))throw Error(`Invalid stream chunk type, expected string, ArrayBuffer, or ArrayBufferView`);let t=T(e);if(m+=t.byteLength,m>i)throw Error(`Stream exceeds maximum size of ${i} bytes`);f.write(t)||await r(f,`drain`)}return f.end(),await r(f,`close`),{blob:await t(u),cleanup:p}}catch(e){throw f.destroy(),await p(),e}}var D=class extends n{#e;constructor(e){super(),e&&this.setUserHash(e)}get userHash(){return this.#e}setUserHash(e){this.#e=e}async uploadURL({url:e}){C(e);let t=new FormData;t.set(`reqtype`,`urlupload`),t.set(`url`,e),this.#e&&t.set(`userhash`,this.#e),this.emit(`uploadingURL`,e);let n=await this.#t(t);if(n.startsWith(`https://files.catbox.moe/`))return n;throw Error(n)}async uploadFile({path:e,maxFileBytes:n=h}){if(e=o(e),!await y(e))throw Error(`Invalid file path "${e}"`);await w(e,n);let r=await t(e),a=new FormData;a.set(`reqtype`,`fileupload`),a.set(`fileToUpload`,r,i(e)),this.#e&&a.set(`userhash`,this.#e),this.emit(`uploadingFile`,e);let s=await this.#t(a);if(s.startsWith(`https://files.catbox.moe/`))return s;throw Error(s)}async uploadFileStream({stream:e,filename:t,maxStreamBytes:n=h}){let{blob:r,cleanup:i}=await E(e,n);try{let e=new FormData;e.set(`reqtype`,`fileupload`),e.set(`fileToUpload`,r,t),this.#e&&e.set(`userhash`,this.#e),this.emit(`uploadingStream`,t);let n=await this.#t(e);if(n.startsWith(`https://files.catbox.moe/`))return n;throw Error(n)}finally{await i()}}async deleteFiles({files:e}){let t=new FormData;t.set(`reqtype`,`deletefiles`),t.set(`userhash`,this.#a()),t.set(`files`,e.join(` `)),this.emit(`deletingFiles`,e);let n=await this.#t(t);if(n.includes(`successfully`))return!0;throw Error(n)}async createAlbum({title:e,description:t,files:n}){let r=new FormData;r.set(`reqtype`,`createalbum`),r.set(`title`,e),r.set(`desc`,t),n&&n.length&&r.set(`files`,n.join(` `)),this.#e&&r.set(`userhash`,this.#e),this.emit(`creatingAlbum`,e,t,n);let i=await this.#t(r);if(i.startsWith(`https://catbox.moe/c/`))return i;throw Error(i)}async editAlbum({id:e,title:t,description:n,files:r}){let i=new FormData;i.set(`reqtype`,`editalbum`),i.set(`short`,e),i.set(`title`,t),i.set(`desc`,n),r&&r.length&&i.set(`files`,r.join(` `)),i.set(`userhash`,this.#a()),this.emit(`editingAlbum`,e,t,n,r);let a=await this.#t(i);if(a===`https://catbox.moe/c/${e}`)return a;throw Error(a)}async addFilesToAlbum({id:e,files:t}){let n=new FormData;n.set(`reqtype`,`addtoalbum`),n.set(`short`,e),n.set(`files`,t.join(` `)),n.set(`userhash`,this.#a()),this.emit(`addingFilesToAlbum`,e,t);let r=await this.#t(n);if(r===`https://catbox.moe/c/${e}`)return r;throw Error(r)}async removeFilesFromAlbum({id:e,files:t}){let n=new FormData;n.set(`reqtype`,`removefromalbum`),n.set(`short`,e),n.set(`files`,t.join(` `)),n.set(`userhash`,this.#a()),this.emit(`removingFilesFromAlbum`,e,t);let r=await this.#t(n);if(r===`https://catbox.moe/c/${e}`)return r;throw Error(r)}async removeAlbum({id:e}){let t=new FormData;t.set(`reqtype`,`deletealbum`),t.set(`short`,e),t.set(`userhash`,this.#a()),this.emit(`removingAlbum`,e);let n=await this.#t(t);if(n.length===0)return!0;throw Error(n)}async#t(e){for(let t=0;t<=2;t++){let n=new AbortController,r=setTimeout(()=>n.abort(),_);try{let r={method:`POST`,headers:{"user-agent":f},body:e,signal:n.signal};this.emit(`request`,S(p,r));let i=await fetch(p,r);if(this.emit(`response`,b(i)),this.#n(i.status)&&t<2){await this.#i(t);continue}return i.text()}catch(e){if(this.#r(e)){if(t<2){await this.#i(t);continue}throw Error(`Request timed out after ${_} ms`)}if(t<2){await this.#i(t);continue}throw e}finally{clearTimeout(r)}}throw Error(`Request failed after retries`)}#n(e){return e===408||e===425||e===429||e>=500}#r(e){return e instanceof DOMException&&e.name===`AbortError`}async#i(e){let t=500*2**e;await new Promise(e=>setTimeout(e,t))}#a(){if(!this.#e)throw Error(`A user hash is required for this operation.`);return this.#e}};const O=[`1h`,`12h`,`24h`,`72h`];let k=function(e){return e.OneHour=`1h`,e.TwelveHours=`12h`,e.OneDay=`24h`,e.ThreeDays=`72h`,e}({}),A=function(e){return e[e.Six=6]=`Six`,e[e.Sixteen=16]=`Sixteen`,e}({});const j=[A.Six,A.Sixteen];var M=class extends n{async uploadFile({path:e,duration:n=k.OneHour,fileNameLength:r=A.Six,maxFileBytes:a=g}){if(e=o(e),!await y(e))throw Error(`Invalid file path "${e}"`);this.#e(n),this.#t(r),await w(e,a);let s=await t(e),c=new FormData;c.set(`reqtype`,`fileupload`),c.set(`fileToUpload`,s,i(e)),c.set(`time`,n),c.set(`fileNameLength`,r),this.emit(`uploadingFile`,e,n);let l=await this.#n(c);if(l.startsWith(`https://litter.catbox.moe/`))return l;throw Error(l)}async uploadFileStream({stream:e,filename:t,duration:n=k.OneHour,fileNameLength:r=A.Six,maxStreamBytes:i=g}){this.#e(n),this.#t(r);let{blob:a,cleanup:o}=await E(e,i);try{let e=new FormData;e.set(`reqtype`,`fileupload`),e.set(`fileToUpload`,a,t),e.set(`time`,n),e.set(`fileNameLength`,r),this.emit(`uploadingStream`,t,n);let i=await this.#n(e);if(i.startsWith(`https://litter.catbox.moe/`))return i;throw Error(i)}finally{await o()}}#e(e){if(!O.includes(e))throw Error(`Invalid duration "${e}", accepted values are ${O.join(`, `)}`)}#t(e){if(!j.includes(e))throw Error(`Invalid file name length "${e}", accepted values are ${j.join(`, `)}`)}async#n(e){for(let t=0;t<=2;t++){let n=new AbortController,r=setTimeout(()=>n.abort(),_);try{let r={method:`POST`,headers:{"user-agent":f},body:e,signal:n.signal};this.emit(`request`,S(m,r));let i=await fetch(m,r);if(this.emit(`response`,b(i)),this.#r(i.status)&&t<2){await this.#a(t);continue}return i.text()}catch(e){if(this.#i(e)){if(t<2){await this.#a(t);continue}throw Error(`Request timed out after ${_} ms`)}if(t<2){await this.#a(t);continue}throw e}finally{clearTimeout(r)}}throw Error(`Request failed after retries`)}#r(e){return e===408||e===425||e===429||e>=500}#i(e){return e instanceof DOMException&&e.name===`AbortError`}async#a(e){let t=500*2**e;await new Promise(e=>setTimeout(e,t))}};export{D as Catbox,k as FileLifetime,A as FileNameLength,M as Litterbox,O as acceptedDurations};