@webnovel-proxy/providers 0.1.3 → 0.1.4
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.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{BCP47LanguageTag as e,NonEmptyString as t,NullableNonEmptyString as n,PersonalName as r,RoyalroadChapterId as i,RoyalroadNovelId as a,RoyalroadVolumeId as o,WebNovelBook as s,WebNovelChapter as c,WebNovelChapterContent as l,WebNovelExtraChapterContent as u,WebNovelVolume as d,WebnovelComChapterId as f,WebnovelComNovelId as p,WebnovelComVolumeId as m,isoDateTimeToPartialDateTime as h,providerImageURLToWebNovelImage as g,timestampMillisecondsToPartialDateTime as _}from"@webnovel-proxy/core";import v,*as y from"zod";import{sha1 as ee}from"@noble/hashes/legacy.js";import{base64 as te,hex as ne,utf8 as b}from"@scure/base";import{existsSync as re}from"node:fs";import{mkdir as ie,readFile as ae,writeFile as x}from"node:fs/promises";import{dirname as S,join as C,normalize as w,resolve as T}from"node:path";import{promisify as E}from"node:util";import{gunzip as oe,gzip as se}from"node:zlib";import ce from"isomorphic-dompurify";import le from"turndown";import*as D from"cheerio";function O(){return typeof process==`object`&&typeof process.env==`object`}function k(){return O()?typeof process.env.DEV==`string`&&(process.env.DEV===`true`||process.env.DEV===`1`):!1}function A(){return k()?!0:O()?typeof process.env.DEBUG==`string`&&(process.env.DEBUG===`true`||process.env.DEBUG===`1`):!1}const ue={"User-Agent":`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36`,Accept:`text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8`,"Accept-Language":`en-US,en;q=0.9`,"sec-ch-ua-platform":`"Windows"`,"sec-ch-ua-mobile":`?0`,"sec-ch-ua":`"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"`,"sec-fetch-dest":`document`,"sec-fetch-user":`?1`,"sec-fetch-mode":`navigate`,"sec-fetch-site":`none`};async function j(e,t,{referrer:n,init:r,includeDefaultHTMLHeaders:i}={}){let a=new Headers(i?ue:{});r?.headers&&new Headers(r.headers).forEach((e,t)=>{a.set(t,e)}),n!=null&&a.set(`Referer`,String(n));let o=String(e);try{let e=await fetch(o,{...r,redirect:r?.redirect??`follow`,headers:a});return e.ok?t===`json`?await e.json():await e.text():(A()&&console.error(`[safeFetch]`,e.url,e.status,e.statusText),null)}catch(e){return A()&&console.error(`[safeFetch]`,e),null}}const M=y.object({message:n,startTimestamp:y.int(),endTimestamp:y.int(),version:t});y.object({name:t,value:t});const N=y.object({name:t,value:y.string(),domain:t,path:t,expiry:y.number().optional(),expires:y.number().optional(),httpOnly:y.boolean(),secure:y.boolean(),sameSite:y.enum([`None`,`Lax`,`Strict`])}),P=y.object({url:y.url(),status:y.int().min(100).max(599),headers:y.record(t,y.string()),response:t,userAgent:t,turnstile_token:t.optional().nullable(),cookies:y.array(N)});y.discriminatedUnion(`type`,[y.object({type:y.literal(`success`),status:y.int().min(100).max(599),result:y.object({...M.shape,solution:P})}),y.object({type:y.literal(`fail`),status:y.int().min(100).max(599),result:y.object({...M.shape,error:y.object({message:t,detail:y.unknown()})})}),y.object({type:y.literal(`error`),error:y.object({message:t,detail:y.unknown().optional()})})]);const F=E(se),I=E(oe);function L(e){let t=ne.encode(ee(b.decode(e.join(``))));return w(T(C(process.cwd(),`.cache`,`dev-cache`,t.substring(0,2),t+`.gz`)))}async function R(e,t,n=!0){if(k()){await ie(S(L(e)),{recursive:!0,mode:504});let r=n?await F(t):t;return await x(L(e),r,{mode:504}),{path:L(e)}}return null}async function z(e,t=!0){if(k()){if(!re(L(e)))return null;let n=await ae(L(e),{encoding:t?void 0:`utf8`}),r=t?(await I(n)).toString(`utf8`):n;if(n&&r)return{path:L(e),value:r}}return null}var B=class{#e=`https://flaresolverr.internal`;#t=null;#n=null;constructor({host:e,hostUsername:t,hostPassword:n}){this.#e=e,this.#t=t??null,this.#n=n??null}async fetch(e,t={},n=fetch){let r=t?.cookies??[],i=new Headers({"Content-Type":`application/json`,"X-Cache-Control":`no-store`});if(this.#t&&i.set(`Authorization`,`Basic `+te.encode(b.decode(`${this.#t}:${this.#n??``}`))),k()){let t=await z([`flaresolverr`,e.toString()]);if(t)return console.log(`[flaresolverr]`,`Retrieved cached response for ${e.toString()} : ${t.path}`),JSON.parse(t.value)}let a;try{A()&&(console.log(`[flaresolverr]`,`Fetching ${e}`),console.time(`[flaresolverr] Timing`)),a=await n(new URL(`/v1`,this.#e),{method:`POST`,headers:i,body:JSON.stringify({cmd:`request.${(t.method??`GET`).toLocaleLowerCase()}`,url:e.toString(),cookies:r,postData:t.payload?new URLSearchParams(t.payload):void 0,disableMedia:!0,maxTimeout:t.maxTimeout??void 0})}),A()&&console.timeEnd(`[flaresolverr] Timing`)}catch(e){return console.timeEnd(`[flaresolverr] Timing`),e instanceof Error?{type:`error`,error:e}:{type:`error`,error:{message:`Could not connect to flaresolverr`}}}if(a.headers.get(`Content-Type`)!==`application/json`)return{type:`error`,error:{message:`Flaresolverr returned a non-JSON Content-Type`}};if(!a.ok)return{type:`error`,error:{message:`Flaresolverr response not ok (${a.status.toString()})`,detail:await a.json()}};let o=v.object({...M.shape,solution:P}).safeParse(await a.json());return o.success?(k()&&await R([`flaresolverr`,e.toString()],JSON.stringify({type:`success`,status:a.status,result:o.data})),{type:`success`,status:a.status,result:o.data}):{type:`error`,error:{message:`Malformed flaresolverr response`,detail:v.prettifyError(o.error)}}}};const V=new le({headingStyle:`atx`,emDelimiter:`_`,strongDelimiter:`**`});function H(e){return V.turndown(e)}function U(e){return ce.sanitize(e,{USE_PROFILES:{html:!0}})}const W=`Royal Road Mobile/2.60.1186 ( Android; 16; Arm; Phone ) MAUI/10.0.10`,G=`2.60.1186`,K={Completed:`completed`,Dropped:`dropped`,Hiatus:`hiatus`,Inactive:null,Ongoing:`ongoing`,Stub:null},q=y.enum([`Completed`,`Dropped`,`Ongoing`,`Stub`,`Hiatus`,`Inactive`]),de=y.object({data:y.array(y.object({id:y.int().min(1),title:t,description:n.nullable(),cover:n.nullable(),firstUpdate:y.iso.datetime(),lastUpdate:y.iso.datetime(),authorId:y.int().min(1),authorName:t,status:q})),paginate:y.object({total:y.int().min(1),perPage:y.int().min(1),page:y.int().min(1)})}),fe=y.object({id:y.int().min(1),title:t,slug:t,description:n.nullable(),cover:y.url().nullable(),thumbnailHash:t.nullable(),status:q,coverBackground:t.nullable(),authorInfo:y.object({userId:y.int().min(1),username:t,avatar:y.url()}),volumes:y.array(y.object({id:y.int().min(1),title:t,cover:y.url().nullable(),order:y.int().min(1)})),chapters:y.array(y.object({id:y.int().min(1),title:t,published:y.iso.datetime(),lastEdit:y.iso.datetime().optional().nullable(),visible:y.boolean(),order:y.int().min(0),volumeId:y.int().min(1).optional().nullable(),isUnlocked:y.boolean()})),collaborators:y.array(y.unknown())}),pe=y.object({preAuthorsNote:n.nullable(),postAuthorsNote:n.nullable(),content:t,fictionInfo:y.object({id:y.int().min(1)}),id:y.int().min(1),title:t,published:y.iso.datetime(),lastEdit:y.iso.datetime().optional().nullable(),isUnlocked:y.boolean(),order:y.int().min(0)});async function J(e,t={}){t={bustCache:!0,...t};let n={Accept:`application/json`,CustomUserAgent:W,"X-Mature-Content":`true`,"X-Govt-Age-Restrict-Mature-Content":`false`,"X-App-Version":G};t.bustCache&&(n[`Cache-Control`]=`no-cache`);let l=await j(`https://api.royalroad.com/v1/fiction/${e}`,`json`,{init:{headers:n}});if(!l)return null;let u=fe.safeParse(l);if(!u.success)return console.warn(`Invalid royalroad fiction response from API`),console.warn(y.prettifyError(u.error)),null;let f=g(u.data.cover??`none`),p={provider:`royalroad`,providerId:a.parse(u.data.id.toString()),chapters:[],contributors:[{name:r.parse({displayName:u.data.authorInfo.username}),role:`aut`}],createdAt:null,updatedAt:null,images:f?[f]:[],language:null,name:u.data.title,overview:u.data.description?[{type:`text/html`,value:U(u.data.description)},{type:`text/markdown`,value:H(U(u.data.description))}]:[],status:K[u.data.status]??null,volumes:[],warnings:[]};for(let e of u.data.chapters){let t={provider:`royalroad`,bookProviderId:a.parse(u.data.id.toString()),volumeProviderId:e.volumeId?o.parse(e.volumeId.toString()):null,providerId:i.parse(e.id.toString()),content:[],extraContent:[],contributors:[],createdAt:h(e.published),updatedAt:h(e.lastEdit??`none`),images:[],isLocked:!e.isUnlocked,name:e.title.trim(),number:null,orderNumber:e.order},n=c.safeParse(t);n.success?p.chapters.push(n.data):(console.warn(`Invalid royalroad fiction book chapter from API`),console.warn(y.prettifyError(n.error)))}for(let e of u.data.volumes){let t={provider:`royalroad`,bookProviderId:a.parse(u.data.id.toString()),providerId:o.parse(e.id.toString()),createdAt:null,updatedAt:null,images:[],name:e.title.trim(),number:null,orderNumber:e.order-1},n=d.safeParse(t);n.success?p.volumes.push(n.data):(console.warn(`Invalid royalroad fiction book volume from API`),console.warn(y.prettifyError(n.error)))}let m=s.safeParse(p);return m.success?m.data:(console.warn(`Invalid royalroad fiction book from API`),console.warn(y.prettifyError(m.error)),null)}async function me(e){let t=await j(`https://api.royalroad.com/v1/fiction/chapters/${e}`,`json`,{init:{headers:{Accept:`application/json`,CustomUserAgent:W,"X-Mature-Content":`true`,"X-Govt-Age-Restrict-Mature-Content":`false`,"X-App-Version":G,"Cache-Control":`no-cache`}}});if(!t)return null;let n=pe.safeParse(t);if(!n.success)return console.warn(`Invalid royalroad fiction chapter response from API`),console.warn(y.prettifyError(n.error)),null;let r=await J(a.parse(n.data.fictionInfo.id.toString()),{bustCache:!1}),o=r?.chapters?.find(e=>e.providerId===i.parse(n.data.id.toString())),s={provider:`royalroad`,bookProviderId:a.parse(n.data.fictionInfo.id.toString()),volumeProviderId:o?r?.volumes?.find(e=>e.providerId===o.volumeProviderId)?.providerId??null:null,providerId:i.parse(n.data.id.toString()),contributors:[],createdAt:h(n.data.published),updatedAt:h(n.data.lastEdit??`none`),images:[],name:n.data.title,content:[],extraContent:[],isLocked:!n.data.isUnlocked,number:null,orderNumber:n.data.order},l=U(n.data.content);s.content.push({size:Buffer.byteLength(l,`utf-8`),type:`text/html`,value:l});let u=H(l);if(s.content.push({size:Buffer.byteLength(u,`utf-8`),type:`text/markdown`,value:u}),n.data.preAuthorsNote){let e=U(n.data.preAuthorsNote),t=H(e);s.extraContent.push({extraType:`preNote`,size:Buffer.byteLength(e,`utf-8`),type:`text/html`,value:e},{extraType:`preNote`,size:Buffer.byteLength(t,`utf-8`),type:`text/markdown`,value:t})}if(n.data.postAuthorsNote){let e=U(n.data.postAuthorsNote),t=H(e);s.extraContent.push({extraType:`postNote`,size:Buffer.byteLength(e,`utf-8`),type:`text/html`,value:e},{extraType:`postNote`,size:Buffer.byteLength(t,`utf-8`),type:`text/markdown`,value:t})}let d=c.safeParse(s);return d.success?d.data:(console.warn(`Invalid royalroad fiction chapter from API`),console.warn(y.prettifyError(d.error)),null)}async function he(e,t={}){let n=await j(`https://api.royalroad.com/v1/fiction/search/advanced`,`json`,{init:{headers:{Accept:`application/json`,CustomUserAgent:W,"X-Mature-Content":`true`,"X-Govt-Age-Restrict-Mature-Content":`false`,"X-App-Version":G,"Cache-Control":`no-cache`,"Content-Type":`application/json; charset=utf-8`},method:`POST`,body:JSON.stringify({Title:e,Status:t.status??null,TagsAdd:[],TagsRemove:[],Page:t.page??1})}});if(!n)return null;let i=de.safeParse(n);if(!i.success)return console.warn(`Invalid royalroad search response from API`),console.warn(v.prettifyError(i.error)),null;let o=[];for(let e of i.data.data){let t=g(e.cover??`invalid`),n={provider:`royalroad`,providerId:a.parse(e.id.toString()),name:e.title,overview:e.description?[{type:`text/html`,value:U(e.description)},{type:`text/markdown`,value:H(U(e.description))}]:[],contributors:[{name:r.parse({displayName:e.authorName}),role:`aut`}],images:t?[t]:[],chapters:[],volumes:[],createdAt:h(e.firstUpdate),updatedAt:h(e.lastUpdate),status:K[e.status]??null,language:null,warnings:[]},i=s.safeParse(n);i.success?o.push(i.data):(console.warn(`Could not parse royalroad book ${e.id.toString()} from search response`),console.warn(v.prettifyError(i.error)))}return o}async function ge(e){return await J(e)}async function _e(e,t){let n=await J(e);return n?n?.volumes?.find(e=>e.providerId===t)??null:null}async function ve(e){return await me(e)}async function ye(e,t={}){return await he(e,t)}function Y(e=null){let t=[[`domain`,`.webnovel.com`],[`lang`,`cn`],[`webnovel-language`,`en`],[`webnovel-content-language`,`en`]];return e&&t.push([`ywkey`,e.ukey],[`ukey`,e.ukey],[`uid`,e.uid]),t.map(e=>`${e[0]}=${e[1]}`).join(`; `)}const be={novel:1,comic:2,ebook:3,fanfic:4},xe={all:0,ongoing:1,completed:2},Se={popular:1,updated:2},X=`Mozilla/mobile QDHWReaderAndroid/8.22.8/1228/2000002/61ce67d733a61d70344e8c706215fd25`,Z={2:`ongoing`,1:`completed`},Ce={30:`ongoing`,40:`hiatus`,50:`completed`},we=y.object({Data:y.object({BookTotalNum:y.int().min(0),BookInfos:y.array(y.object({BookId:y.number().min(1),BookName:t,Author:t,BookCoverID:y.int().min(1),Type:y.int().min(1),BookType:y.int().min(0),Description:n.nullable(),MTL:y.boolean()}))})}),Q=y.object({Data:y.object({BookId:y.number().min(1),BookName:t,LastChapterTime:y.int().min(1),Description:n.nullable(),Type:y.int().min(1),Status:y.int().min(1),LanguageName:e,AuthorInfo:y.object({AuthorName:t,UserId:y.int().min(1)}),TransInfo:y.object({Translators:y.array(y.object({TranslatorName:t,UserId:y.int().min(1)})),Editors:y.array(y.object({TranslatorName:t,UserId:y.int().min(1)}))}),BookCoverID:y.int().min(1),MTL:y.boolean(),ChapterNum:y.int().min(0)}).nullable()});async function Te(e,t=null){let n=new URL(`https://idruid.webnovel.com/app/api/book/get-book`);n.searchParams.set(`bookId`,e),n.searchParams.set(`reqTS`,Math.round(Date.now()).toString());let i=await j(n.toString(),`json`,{referrer:`https://android.webnovel.com`,init:{headers:{"User-Agent":X,Cookie:Y(t),DeviceTime:_(Date.now())}}});if(!i)return null;let a=Q.safeParse(i);if(!a.success)return console.warn(`Invalid webnovel-com response from API`),console.warn(y.prettifyError(a.error)),null;if(a.data.Data===null)return null;let o={provider:`webnovel-com`,providerId:p.parse(a.data.Data.BookId.toString()),chapters:[],volumes:[],contributors:[{name:r.parse({displayName:a.data.Data.AuthorInfo.AuthorName}),role:`aut`}],createdAt:null,updatedAt:null,images:[{type:`book`,height:800,width:600,mediaType:`image/jpeg`,provider:`webnovel-com`,providerId:a.data.Data.BookId.toString(),providerMeta:{imageId:a.data.Data.BookCoverID.toString()}}],language:a.data.Data.LanguageName,name:a.data.Data.BookName,overview:a.data.Data.Description?[{type:`text/plain`,value:a.data.Data.Description}]:[],status:Ce[a.data.Data.Status]??Z[a.data.Data.Type]??null,warnings:a.data.Data.MTL?[`mtl`]:[]};for(let e of a.data.Data.TransInfo.Translators)o.contributors.push({name:r.parse({displayName:e.TranslatorName}),role:`trl`});for(let e of a.data.Data.TransInfo.Editors)o.contributors.push({name:r.parse({displayName:e.TranslatorName}),role:`edt`});let c=s.safeParse(o);return c.success?c.data:(console.warn(`Invalid webnovel-com book from API`),console.warn(y.prettifyError(c.error)),null)}async function Ee(e,t={},n=null){let i=new URL(`https://idruid.webnovel.com/app/api/search/result`);i.searchParams.set(`bookStatus`,xe[t.status??`all`].toString()),i.searchParams.set(`chapterNum`,``),i.searchParams.set(`keyword`,e),i.searchParams.set(`orderBy`,Se[t.orderBy??`popular`].toString()),i.searchParams.set(`pageIndex`,Math.round(t.page??1).toString()),i.searchParams.set(`reqTS`,Math.round(Date.now()).toString()),i.searchParams.set(`type`,be[t.type??`novel`].toString());let a=await j(i.toString(),`json`,{referrer:`https://android.webnovel.com`,init:{headers:{"User-Agent":X,Cookie:Y(n),DeviceTime:_(Date.now())}}});if(!a)return null;let o=we.safeParse(a);if(!o.success)return console.warn(`Invalid webnovel-com search response from API`),console.warn(y.prettifyError(o.error)),null;let c=[];for(let e of o.data.Data.BookInfos){let t={provider:`webnovel-com`,providerId:p.parse(e.BookId.toString()),name:e.BookName,overview:e.Description?[{type:`text/plain`,value:e.Description}]:[],contributors:[{name:r.parse({displayName:e.Author}),role:`aut`}],images:[{type:`book`,width:600,height:800,mediaType:`image/jpeg`,provider:`webnovel-com`,providerId:e.BookId.toString(),providerMeta:{imageId:e.BookCoverID.toString()}}],chapters:[],volumes:[],createdAt:null,updatedAt:null,status:Z[e.Type]??null,language:null,warnings:e.MTL?[`mtl`]:[]},n=s.safeParse(t);n.success?c.push(n.data):(console.warn(`Could not parse webnovel-com book ${e.BookId.toString()} from search response`),console.warn(y.prettifyError(n.error)))}return c}function $(e=null){let t=[{name:`NEXT_LOCALE`,value:`en`},{name:`webnovel-language`,value:`en`},{name:`webnovel-content-language`,value:`en`}];return e&&t.push({name:`ukey`,value:e.ukey},{name:`uid`,value:e.uid}),t}async function De(e,n,r=null){let i=await e.fetch(`https://www.webnovel.com/book/${n}/catalog`,{cookies:$(r)});if(i.type!==`success`)return console.warn(i),null;let a=D.load(i.result.solution.response);function o(e){if(typeof e!=`string`)return null;let n=/^Volume\s+([\d.]+)(?:[\s\t]*[:-][\s\t]*(.*))?$/.exec(e.trim());return n?y.object({number:y.number().min(0),name:t.nullable()}).safeParse({number:Number(n[1]),name:n[2]??null}).data??null:null}function l(e){return typeof e==`string`?t.safeParse(e).data??null:null}function u(e){let n=y.number().min(0).safeParse(Number(t.safeParse(e).data??`0`)).data;return typeof n==`number`?n:null}let p={volumes:[],chapters:[]};for(let e of a(`.j_catalog_wrap .j_catalog_list .volume-item`)){let t=o(a(e).find(`h4`).first().text()),r=t?m.parse(`${n}-${t.number.toString()}`):null;if(t&&r){let e={provider:`webnovel-com`,bookProviderId:n,providerId:r,createdAt:null,updatedAt:null,images:[],name:t.name,number:t.number,orderNumber:null},i=d.safeParse(e);i.success?p.volumes.push(i.data):(console.warn(`Invalid webnovel-com book catalog volume from HTML`),console.warn(y.prettifyError(i.error)))}for(let t of a(e).find(`ol.content-list li`)){let e=f.safeParse(t.attribs[`data-cid`]).data??null;if(!e){console.warn(`Could not infer chapter id on volume ${r??`unknown`}`);continue}let i=l(a(t).find(`a div.oh strong`).first().text().trim()),o=u(a(t).find(`a i`).first().text().trim());if(o===null){console.warn(`Cannot parse chapter number for chapter id ${e}`);continue}let s={provider:`webnovel-com`,bookProviderId:n,volumeProviderId:r,providerId:e,createdAt:null,updatedAt:null,content:[],extraContent:[],images:[],contributors:[],isLocked:a(t).find(`a > svg`).length>0,name:i,number:o,orderNumber:null},d=c.safeParse(s);d.success?p.chapters.push(d.data):(console.warn(`Invalid webnovel-com book catalog chapter from HTML`),console.warn(y.prettifyError(d.error)))}}let h=s.pick({volumes:!0,chapters:!0}).safeParse(p);return h.success?h.data:(console.warn(`Invalid webnovel-com book catalog from HTML`),console.warn(y.prettifyError(h.error)),null)}async function Oe(e,n,r,i=null){let a=await e.fetch(`https://www.webnovel.com/book/${n}/${r}`,{cookies:$(i)});if(a.type!==`success`)return console.warn(a),null;if(a.result.solution.response.includes(`//www.yueimg.com/en/images/404`)||a.result.solution.response.includes(`We might have some troubles to find out this page.`))return null;let o=D.load(a.result.solution.response);function s(e,n){let r=e.trim()===``?0:y.number().min(0).safeParse(Number(/^Chapter\s+([\d.]+)/.exec(e.trim())?.[1]??`not a number`)).data??null;return typeof r==`number`?{number:r,name:t.safeParse(n.trim()).data??null}:null}function d(e){if(typeof e!=`string`)return null;let n=y.object({paragraphId:t,chapterId:t,paraIndex:y.int()}).safeParse(JSON.parse(e)).data??null;if(!n)return null;try{return{paragraphId:t.pipe(y.string().regex(/^[a-f0-9]{32}$/)).parse(n.paragraphId),paragraphIndex:y.int().min(0).parse(n.paraIndex)}}catch{return null}}let f=s(o(`header span.j_chapIdx`).text(),o(`header span.j_chapName`).text());if(!f)return console.warn(`Could not parse chapter meta from chapter id ${r}`),null;let p=o(`#page .j_contentWrap .chapter_content[data-cid="${r}"]`).first(),m={provider:`webnovel-com`,bookProviderId:n,volumeProviderId:null,providerId:r,contributors:[],createdAt:null,updatedAt:null,images:[],name:f.name,number:f.number,orderNumber:null,content:[],extraContent:[],isLocked:y.stringbool().nullable().optional().safeParse(p.attr(`data-islock`)).data??null},h=c.safeParse(m);if(!h.success)return console.warn(`Invalid webnovel-com chapter from HTML`),console.warn(y.prettifyError(h.error)),null;if(!h.data.isLocked){let e=[];for(let t of p.find(`.cha-content > .cha-words > .cha-paragraph`)){let n=o(t).find(`.dib > p`),r=d(t.attribs[`data-ejs`]);r&&(n.attr(`data-paragraph-id`,r.paragraphId),n.attr(`data-paragraph-idx`,r.paragraphIndex.toString()));let i=n.html();i!==null&&n.html(i.trim());let a=n.prop(`outerHTML`);a&&a.trim()!==``&&e.push(a)}if(e.length>0){let t=U(e.join(`
|
|
2
|
-
`)),n=
|
|
3
|
-
`)),n=
|
|
1
|
+
import e from"json-bigint";import{BCP47LanguageTag as t,NonEmptyString as n,NullableNonEmptyString as r,PersonalName as i,RoyalroadChapterId as a,RoyalroadNovelId as o,RoyalroadVolumeId as s,WebNovelBook as c,WebNovelChapter as l,WebNovelChapterContent as u,WebNovelExtraChapterContent as d,WebNovelVolume as f,WebnovelComChapterId as p,WebnovelComNovelId as m,WebnovelComVolumeId as h,isoDateTimeToPartialDateTime as g,providerImageURLToWebNovelImage as _,timestampMillisecondsToPartialDateTime as v}from"@webnovel-proxy/core";import y,*as b from"zod";import{sha1 as ee}from"@noble/hashes/legacy.js";import{base64 as x,hex as S,utf8 as C}from"@scure/base";import{existsSync as te}from"node:fs";import{mkdir as w,readFile as ne,writeFile as re}from"node:fs/promises";import{dirname as ie,join as ae,normalize as oe,resolve as se}from"node:path";import{promisify as T}from"node:util";import{gunzip as ce,gzip as le}from"node:zlib";import ue from"isomorphic-dompurify";import E from"turndown";import*as D from"cheerio";function O(){return typeof process==`object`&&typeof process.env==`object`}function k(){return O()?typeof process.env.DEV==`string`&&(process.env.DEV===`true`||process.env.DEV===`1`):!1}function A(){return k()?!0:O()?typeof process.env.DEBUG==`string`&&(process.env.DEBUG===`true`||process.env.DEBUG===`1`):!1}const de={"User-Agent":`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36`,Accept:`text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8`,"Accept-Language":`en-US,en;q=0.9`,"sec-ch-ua-platform":`"Windows"`,"sec-ch-ua-mobile":`?0`,"sec-ch-ua":`"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"`,"sec-fetch-dest":`document`,"sec-fetch-user":`?1`,"sec-fetch-mode":`navigate`,"sec-fetch-site":`none`};async function j(t,n,{referrer:r,init:i,includeDefaultHTMLHeaders:a}={}){let o=new Headers(a?de:{});i?.headers&&new Headers(i.headers).forEach((e,t)=>{o.set(t,e)}),r!=null&&o.set(`Referer`,String(r));let s=String(t);try{let t=await fetch(s,{...i,redirect:i?.redirect??`follow`,headers:o});return t.ok?n===`json`?e({storeAsString:!0}).parse(await t.text()):await t.text():(A()&&console.error(`[safeFetch]`,t.url,t.status,t.statusText),null)}catch(e){return A()&&console.error(`[safeFetch]`,e),null}}const M=b.object({message:r,startTimestamp:b.int(),endTimestamp:b.int(),version:n});b.object({name:n,value:n});const N=b.object({name:n,value:b.string(),domain:n,path:n,expiry:b.number().optional(),expires:b.number().optional(),httpOnly:b.boolean(),secure:b.boolean(),sameSite:b.enum([`None`,`Lax`,`Strict`])}),P=b.object({url:b.url(),status:b.int().min(100).max(599),headers:b.record(n,b.string()),response:n,userAgent:n,turnstile_token:n.optional().nullable(),cookies:b.array(N)});b.discriminatedUnion(`type`,[b.object({type:b.literal(`success`),status:b.int().min(100).max(599),result:b.object({...M.shape,solution:P})}),b.object({type:b.literal(`fail`),status:b.int().min(100).max(599),result:b.object({...M.shape,error:b.object({message:n,detail:b.unknown()})})}),b.object({type:b.literal(`error`),error:b.object({message:n,detail:b.unknown().optional()})})]);const F=T(le),I=T(ce);function L(e){let t=S.encode(ee(C.decode(e.join(``))));return oe(se(ae(process.cwd(),`.cache`,`dev-cache`,t.substring(0,2),t+`.gz`)))}async function R(e,t,n=!0){if(k()){await w(ie(L(e)),{recursive:!0,mode:504});let r=n?await F(t):t;return await re(L(e),r,{mode:504}),{path:L(e)}}return null}async function z(e,t=!0){if(k()){if(!te(L(e)))return null;let n=await ne(L(e),{encoding:t?void 0:`utf8`}),r=t?(await I(n)).toString(`utf8`):n;if(n&&r)return{path:L(e),value:r}}return null}var B=class{#e=`https://flaresolverr.internal`;#t=null;#n=null;constructor({host:e,hostUsername:t,hostPassword:n}){this.#e=e,this.#t=t??null,this.#n=n??null}async fetch(e,t={},n=fetch){let r=t?.cookies??[],i=new Headers({"Content-Type":`application/json`,"X-Cache-Control":`no-store`});if(this.#t&&i.set(`Authorization`,`Basic `+x.encode(C.decode(`${this.#t}:${this.#n??``}`))),k()){let t=await z([`flaresolverr`,e.toString()]);if(t)return console.log(`[flaresolverr]`,`Retrieved cached response for ${e.toString()} : ${t.path}`),JSON.parse(t.value)}let a;try{A()&&(console.log(`[flaresolverr]`,`Fetching ${e}`),console.time(`[flaresolverr] Timing`)),a=await n(new URL(`/v1`,this.#e),{method:`POST`,headers:i,body:JSON.stringify({cmd:`request.${(t.method??`GET`).toLocaleLowerCase()}`,url:e.toString(),cookies:r,postData:t.payload?new URLSearchParams(t.payload):void 0,disableMedia:!0,maxTimeout:t.maxTimeout??void 0})}),A()&&console.timeEnd(`[flaresolverr] Timing`)}catch(e){return console.timeEnd(`[flaresolverr] Timing`),e instanceof Error?{type:`error`,error:e}:{type:`error`,error:{message:`Could not connect to flaresolverr`}}}if(a.headers.get(`Content-Type`)!==`application/json`)return{type:`error`,error:{message:`Flaresolverr returned a non-JSON Content-Type`}};if(!a.ok)return{type:`error`,error:{message:`Flaresolverr response not ok (${a.status.toString()})`,detail:await a.json()}};let o=y.object({...M.shape,solution:P}).safeParse(await a.json());return o.success?(k()&&await R([`flaresolverr`,e.toString()],JSON.stringify({type:`success`,status:a.status,result:o.data})),{type:`success`,status:a.status,result:o.data}):{type:`error`,error:{message:`Malformed flaresolverr response`,detail:y.prettifyError(o.error)}}}};const V=new E({headingStyle:`atx`,emDelimiter:`_`,strongDelimiter:`**`});function H(e){return V.turndown(e)}function U(e){return ue.sanitize(e,{USE_PROFILES:{html:!0}})}const W=`Royal Road Mobile/2.60.1186 ( Android; 16; Arm; Phone ) MAUI/10.0.10`,G=`2.60.1186`,K={Completed:`completed`,Dropped:`dropped`,Hiatus:`hiatus`,Inactive:null,Ongoing:`ongoing`,Stub:null},q=b.enum([`Completed`,`Dropped`,`Ongoing`,`Stub`,`Hiatus`,`Inactive`]),fe=b.object({data:b.array(b.object({id:b.int().min(1),title:n,description:r.nullable(),cover:r.nullable(),firstUpdate:b.iso.datetime(),lastUpdate:b.iso.datetime(),authorId:b.int().min(1),authorName:n,status:q})),paginate:b.object({total:b.int().min(1),perPage:b.int().min(1),page:b.int().min(1)})}),pe=b.object({id:b.int().min(1),title:n,slug:n,description:r.nullable(),cover:b.url().nullable(),thumbnailHash:n.nullable(),status:q,coverBackground:n.nullable(),authorInfo:b.object({userId:b.int().min(1),username:n,avatar:b.url()}),volumes:b.array(b.object({id:b.int().min(1),title:n,cover:b.url().nullable(),order:b.int().min(1)})),chapters:b.array(b.object({id:b.int().min(1),title:n,published:b.iso.datetime(),lastEdit:b.iso.datetime().optional().nullable(),visible:b.boolean(),order:b.int().min(0),volumeId:b.int().min(1).optional().nullable(),isUnlocked:b.boolean()})),collaborators:b.array(b.unknown())}),me=b.object({preAuthorsNote:r.nullable(),postAuthorsNote:r.nullable(),content:n,fictionInfo:b.object({id:b.int().min(1)}),id:b.int().min(1),title:n,published:b.iso.datetime(),lastEdit:b.iso.datetime().optional().nullable(),isUnlocked:b.boolean(),order:b.int().min(0)});async function J(e,t={}){t={bustCache:!0,...t};let n={Accept:`application/json`,CustomUserAgent:W,"X-Mature-Content":`true`,"X-Govt-Age-Restrict-Mature-Content":`false`,"X-App-Version":G};t.bustCache&&(n[`Cache-Control`]=`no-cache`);let r=await j(`https://api.royalroad.com/v1/fiction/${e}`,`json`,{init:{headers:n}});if(!r)return null;let u=pe.safeParse(r);if(!u.success)return console.warn(`Invalid royalroad fiction response from API`),console.warn(b.prettifyError(u.error)),null;let d=_(u.data.cover??`none`),p={provider:`royalroad`,providerId:o.parse(u.data.id.toString()),chapters:[],contributors:[{name:i.parse({displayName:u.data.authorInfo.username}),role:`aut`}],createdAt:null,updatedAt:null,images:d?[d]:[],language:null,name:u.data.title,overview:u.data.description?[{type:`text/html`,value:U(u.data.description)},{type:`text/markdown`,value:H(U(u.data.description))}]:[],status:K[u.data.status]??null,volumes:[],warnings:[]};for(let e of u.data.chapters){let t={provider:`royalroad`,bookProviderId:o.parse(u.data.id.toString()),volumeProviderId:e.volumeId?s.parse(e.volumeId.toString()):null,providerId:a.parse(e.id.toString()),content:[],extraContent:[],contributors:[],createdAt:g(e.published),updatedAt:g(e.lastEdit??`none`),images:[],isLocked:!e.isUnlocked,name:e.title.trim(),number:null,orderNumber:e.order},n=l.safeParse(t);n.success?p.chapters.push(n.data):(console.warn(`Invalid royalroad fiction book chapter from API`),console.warn(b.prettifyError(n.error)))}for(let e of u.data.volumes){let t={provider:`royalroad`,bookProviderId:o.parse(u.data.id.toString()),providerId:s.parse(e.id.toString()),createdAt:null,updatedAt:null,images:[],name:e.title.trim(),number:null,orderNumber:e.order-1},n=f.safeParse(t);n.success?p.volumes.push(n.data):(console.warn(`Invalid royalroad fiction book volume from API`),console.warn(b.prettifyError(n.error)))}let m=c.safeParse(p);return m.success?m.data:(console.warn(`Invalid royalroad fiction book from API`),console.warn(b.prettifyError(m.error)),null)}async function he(e){let t=await j(`https://api.royalroad.com/v1/fiction/chapters/${e}`,`json`,{init:{headers:{Accept:`application/json`,CustomUserAgent:W,"X-Mature-Content":`true`,"X-Govt-Age-Restrict-Mature-Content":`false`,"X-App-Version":G,"Cache-Control":`no-cache`}}});if(!t)return null;let n=me.safeParse(t);if(!n.success)return console.warn(`Invalid royalroad fiction chapter response from API`),console.warn(b.prettifyError(n.error)),null;let r=await J(o.parse(n.data.fictionInfo.id.toString()),{bustCache:!1}),i=r?.chapters?.find(e=>e.providerId===a.parse(n.data.id.toString())),s={provider:`royalroad`,bookProviderId:o.parse(n.data.fictionInfo.id.toString()),volumeProviderId:i?r?.volumes?.find(e=>e.providerId===i.volumeProviderId)?.providerId??null:null,providerId:a.parse(n.data.id.toString()),contributors:[],createdAt:g(n.data.published),updatedAt:g(n.data.lastEdit??`none`),images:[],name:n.data.title,content:[],extraContent:[],isLocked:!n.data.isUnlocked,number:null,orderNumber:n.data.order},c=U(n.data.content);s.content.push({size:Buffer.byteLength(c,`utf-8`),type:`text/html`,value:c});let u=H(c);if(s.content.push({size:Buffer.byteLength(u,`utf-8`),type:`text/markdown`,value:u}),n.data.preAuthorsNote){let e=U(n.data.preAuthorsNote),t=H(e);s.extraContent.push({extraType:`preNote`,size:Buffer.byteLength(e,`utf-8`),type:`text/html`,value:e},{extraType:`preNote`,size:Buffer.byteLength(t,`utf-8`),type:`text/markdown`,value:t})}if(n.data.postAuthorsNote){let e=U(n.data.postAuthorsNote),t=H(e);s.extraContent.push({extraType:`postNote`,size:Buffer.byteLength(e,`utf-8`),type:`text/html`,value:e},{extraType:`postNote`,size:Buffer.byteLength(t,`utf-8`),type:`text/markdown`,value:t})}let d=l.safeParse(s);return d.success?d.data:(console.warn(`Invalid royalroad fiction chapter from API`),console.warn(b.prettifyError(d.error)),null)}async function ge(e,t={}){let n=await j(`https://api.royalroad.com/v1/fiction/search/advanced`,`json`,{init:{headers:{Accept:`application/json`,CustomUserAgent:W,"X-Mature-Content":`true`,"X-Govt-Age-Restrict-Mature-Content":`false`,"X-App-Version":G,"Cache-Control":`no-cache`,"Content-Type":`application/json; charset=utf-8`},method:`POST`,body:JSON.stringify({Title:e,Status:t.status??null,TagsAdd:[],TagsRemove:[],Page:t.page??1})}});if(!n)return null;let r=fe.safeParse(n);if(!r.success)return console.warn(`Invalid royalroad search response from API`),console.warn(y.prettifyError(r.error)),null;let a=[];for(let e of r.data.data){let t=_(e.cover??`invalid`),n={provider:`royalroad`,providerId:o.parse(e.id.toString()),name:e.title,overview:e.description?[{type:`text/html`,value:U(e.description)},{type:`text/markdown`,value:H(U(e.description))}]:[],contributors:[{name:i.parse({displayName:e.authorName}),role:`aut`}],images:t?[t]:[],chapters:[],volumes:[],createdAt:g(e.firstUpdate),updatedAt:g(e.lastUpdate),status:K[e.status]??null,language:null,warnings:[]},r=c.safeParse(n);r.success?a.push(r.data):(console.warn(`Could not parse royalroad book ${e.id.toString()} from search response`),console.warn(y.prettifyError(r.error)))}return a}async function _e(e){return await J(e)}async function ve(e,t){let n=await J(e);return n?n?.volumes?.find(e=>e.providerId===t)??null:null}async function ye(e){return await he(e)}async function be(e,t={}){return await ge(e,t)}function Y(e=null){let t=[[`domain`,`.webnovel.com`],[`lang`,`cn`],[`webnovel-language`,`en`],[`webnovel-content-language`,`en`]];return e&&t.push([`ywkey`,e.ukey],[`ukey`,e.ukey],[`uid`,e.uid]),t.map(e=>`${e[0]}=${e[1]}`).join(`; `)}const xe={novel:1,comic:2,ebook:3,fanfic:4},Se={all:0,ongoing:1,completed:2},Ce={popular:1,updated:2},X=`Mozilla/mobile QDHWReaderAndroid/8.22.8/1228/2000002/61ce67d733a61d70344e8c706215fd25`,Z={2:`ongoing`,1:`completed`},we={30:`ongoing`,40:`hiatus`,50:`completed`},Te=b.object({Data:b.object({BookTotalNum:b.int().min(0),BookInfos:b.array(b.object({BookId:b.union([b.number().min(1),b.string().regex(/^[1-9]\d*$/)]),BookName:n,Author:n,BookCoverID:b.int().min(1),Type:b.int().min(1),BookType:b.int().min(0),Description:r.nullable(),MTL:b.boolean()}))})}),Ee=b.object({Data:b.object({BookId:b.union([b.number().min(1),b.string().regex(/^[1-9]\d*$/)]),BookName:n,LastChapterTime:b.int().min(1),Description:r.nullable(),Type:b.int().min(1),Status:b.int().min(1),LanguageName:t,AuthorInfo:b.object({AuthorName:n,UserId:b.int().min(1)}),TransInfo:b.object({Translators:b.array(b.object({TranslatorName:n,UserId:b.int().min(1)})),Editors:b.array(b.object({TranslatorName:n,UserId:b.int().min(1)}))}),BookCoverID:b.int().min(1),MTL:b.boolean(),ChapterNum:b.int().min(0)}).nullable()});async function Q(e,t=null){let n=new URL(`https://idruid.webnovel.com/app/api/book/get-book`);n.searchParams.set(`bookId`,e),n.searchParams.set(`reqTS`,Math.round(Date.now()).toString());let r=await j(n.toString(),`json`,{referrer:`https://android.webnovel.com`,init:{headers:{"User-Agent":X,Cookie:Y(t),DeviceTime:v(Date.now())}}});if(!r)return null;let a=Ee.safeParse(r);if(!a.success)return console.warn(`Invalid webnovel-com response from API`),console.warn(b.prettifyError(a.error)),null;if(a.data.Data===null)return null;let o={provider:`webnovel-com`,providerId:m.parse(a.data.Data.BookId.toString()),chapters:[],volumes:[],contributors:[{name:i.parse({displayName:a.data.Data.AuthorInfo.AuthorName}),role:`aut`}],createdAt:null,updatedAt:null,images:[{type:`book`,height:800,width:600,mediaType:`image/jpeg`,provider:`webnovel-com`,providerId:a.data.Data.BookId.toString(),providerMeta:{imageId:a.data.Data.BookCoverID.toString()}}],language:a.data.Data.LanguageName,name:a.data.Data.BookName,overview:a.data.Data.Description?[{type:`text/plain`,value:a.data.Data.Description}]:[],status:we[a.data.Data.Status]??Z[a.data.Data.Type]??null,warnings:a.data.Data.MTL?[`mtl`]:[]};for(let e of a.data.Data.TransInfo.Translators)o.contributors.push({name:i.parse({displayName:e.TranslatorName}),role:`trl`});for(let e of a.data.Data.TransInfo.Editors)o.contributors.push({name:i.parse({displayName:e.TranslatorName}),role:`edt`});let s=c.safeParse(o);return s.success?s.data:(console.warn(`Invalid webnovel-com book from API`),console.warn(b.prettifyError(s.error)),null)}async function De(e,t={},n=null){let r=new URL(`https://idruid.webnovel.com/app/api/search/result`);r.searchParams.set(`bookStatus`,Se[t.status??`all`].toString()),r.searchParams.set(`chapterNum`,``),r.searchParams.set(`keyword`,e),r.searchParams.set(`orderBy`,Ce[t.orderBy??`popular`].toString()),r.searchParams.set(`pageIndex`,Math.round(t.page??1).toString()),r.searchParams.set(`reqTS`,Math.round(Date.now()).toString()),r.searchParams.set(`type`,xe[t.type??`novel`].toString());let a=await j(r.toString(),`json`,{referrer:`https://android.webnovel.com`,init:{headers:{"User-Agent":X,Cookie:Y(n),DeviceTime:v(Date.now())}}});if(!a)return null;let o=Te.safeParse(a);if(!o.success)return console.warn(`Invalid webnovel-com search response from API`),console.warn(b.prettifyError(o.error)),null;let s=[];for(let e of o.data.Data.BookInfos){let t={provider:`webnovel-com`,providerId:m.parse(e.BookId.toString()),name:e.BookName,overview:e.Description?[{type:`text/plain`,value:e.Description}]:[],contributors:[{name:i.parse({displayName:e.Author}),role:`aut`}],images:[{type:`book`,width:600,height:800,mediaType:`image/jpeg`,provider:`webnovel-com`,providerId:e.BookId.toString(),providerMeta:{imageId:e.BookCoverID.toString()}}],chapters:[],volumes:[],createdAt:null,updatedAt:null,status:Z[e.Type]??null,language:null,warnings:e.MTL?[`mtl`]:[]},n=c.safeParse(t);n.success?s.push(n.data):(console.warn(`Could not parse webnovel-com book ${e.BookId.toString()} from search response`),console.warn(b.prettifyError(n.error)))}return s}function $(e=null){let t=[{name:`NEXT_LOCALE`,value:`en`},{name:`webnovel-language`,value:`en`},{name:`webnovel-content-language`,value:`en`}];return e&&t.push({name:`ukey`,value:e.ukey},{name:`uid`,value:e.uid}),t}async function Oe(e,t,r=null){let i=await e.fetch(`https://www.webnovel.com/book/${t}/catalog`,{cookies:$(r)});if(i.type!==`success`)return console.warn(i),null;let a=D.load(i.result.solution.response);function o(e){if(typeof e!=`string`)return null;let t=/^Volume\s+([\d.]+)(?:[\s\t]*[:-][\s\t]*(.*))?$/.exec(e.trim());return t?b.object({number:b.number().min(0),name:n.nullable()}).safeParse({number:Number(t[1]),name:t[2]??null}).data??null:null}function s(e){return typeof e==`string`?n.safeParse(e).data??null:null}function u(e){let t=b.number().min(0).safeParse(Number(n.safeParse(e).data??`0`)).data;return typeof t==`number`?t:null}let d={volumes:[],chapters:[]};for(let e of a(`.j_catalog_wrap .j_catalog_list .volume-item`)){let n=o(a(e).find(`h4`).first().text()),r=n?h.parse(`${t}-${n.number.toString()}`):null;if(n&&r){let e={provider:`webnovel-com`,bookProviderId:t,providerId:r,createdAt:null,updatedAt:null,images:[],name:n.name,number:n.number,orderNumber:null},i=f.safeParse(e);i.success?d.volumes.push(i.data):(console.warn(`Invalid webnovel-com book catalog volume from HTML`),console.warn(b.prettifyError(i.error)))}for(let n of a(e).find(`ol.content-list li`)){let e=p.safeParse(n.attribs[`data-cid`]).data??null;if(!e){console.warn(`Could not infer chapter id on volume ${r??`unknown`}`);continue}let i=s(a(n).find(`a div.oh strong`).first().text().trim()),o=u(a(n).find(`a i`).first().text().trim());if(o===null){console.warn(`Cannot parse chapter number for chapter id ${e}`);continue}let c={provider:`webnovel-com`,bookProviderId:t,volumeProviderId:r,providerId:e,createdAt:null,updatedAt:null,content:[],extraContent:[],images:[],contributors:[],isLocked:a(n).find(`a > svg`).length>0,name:i,number:o,orderNumber:null},f=l.safeParse(c);f.success?d.chapters.push(f.data):(console.warn(`Invalid webnovel-com book catalog chapter from HTML`),console.warn(b.prettifyError(f.error)))}}let m=c.pick({volumes:!0,chapters:!0}).safeParse(d);return m.success?m.data:(console.warn(`Invalid webnovel-com book catalog from HTML`),console.warn(b.prettifyError(m.error)),null)}async function ke(e,t,r,i=null){let a=await e.fetch(`https://www.webnovel.com/book/${t}/${r}`,{cookies:$(i)});if(a.type!==`success`)return console.warn(a),null;if(a.result.solution.response.includes(`//www.yueimg.com/en/images/404`)||a.result.solution.response.includes(`We might have some troubles to find out this page.`))return null;let o=D.load(a.result.solution.response);function s(e,t){let r=e.trim()===``?0:b.number().min(0).safeParse(Number(/^Chapter\s+([\d.]+)/.exec(e.trim())?.[1]??`not a number`)).data??null;return typeof r==`number`?{number:r,name:n.safeParse(t.trim()).data??null}:null}function c(e){if(typeof e!=`string`)return null;let t=b.object({paragraphId:n,chapterId:n,paraIndex:b.int()}).safeParse(JSON.parse(e)).data??null;if(!t)return null;try{return{paragraphId:n.pipe(b.string().regex(/^[a-f0-9]{32}$/)).parse(t.paragraphId),paragraphIndex:b.int().min(0).parse(t.paraIndex)}}catch{return null}}let f=s(o(`header span.j_chapIdx`).text(),o(`header span.j_chapName`).text());if(!f)return console.warn(`Could not parse chapter meta from chapter id ${r}`),null;let p=o(`#page .j_contentWrap .chapter_content[data-cid="${r}"]`).first(),m={provider:`webnovel-com`,bookProviderId:t,volumeProviderId:null,providerId:r,contributors:[],createdAt:null,updatedAt:null,images:[],name:f.name,number:f.number,orderNumber:null,content:[],extraContent:[],isLocked:b.stringbool().nullable().optional().safeParse(p.attr(`data-islock`)).data??null},h=l.safeParse(m);if(!h.success)return console.warn(`Invalid webnovel-com chapter from HTML`),console.warn(b.prettifyError(h.error)),null;if(!h.data.isLocked){let e=[];for(let t of p.find(`.cha-content > .cha-words > .cha-paragraph`)){let n=o(t).find(`.dib > p`),r=c(t.attribs[`data-ejs`]);r&&(n.attr(`data-paragraph-id`,r.paragraphId),n.attr(`data-paragraph-idx`,r.paragraphIndex.toString()));let i=n.html();i!==null&&n.html(i.trim());let a=n.prop(`outerHTML`);a&&a.trim()!==``&&e.push(a)}if(e.length>0){let t=U(e.join(`
|
|
2
|
+
`)),n=u.parse({type:`text/html`,value:t,size:Buffer.byteLength(t,`utf-8`)});h.data.content.push(n);let r=H(t),i=u.parse({type:`text/markdown`,value:r,size:Buffer.byteLength(r,`utf-8`)});h.data.content.push(i)}}let g=o(`#page .cha-content .m-thou`).first();if(g.hasClass(`m-thou`)){let e=[];for(let t of g.find(`p.creators-thought`)){let n=o(t);n.attr(`class`,null);let r=n.html();r!==null&&n.html(r.trim());let i=n.prop(`outerHTML`);i&&i.trim()!==``&&e.push(i)}if(e.length>0){let t=U(e.join(`
|
|
3
|
+
`)),n=d.parse({type:`text/html`,extraType:`postNote`,value:t,size:Buffer.byteLength(t,`utf-8`)});h.data.extraContent.push(n);let r=H(t),i=d.parse({type:`text/markdown`,extraType:`postNote`,value:r,size:Buffer.byteLength(r,`utf-8`)});h.data.extraContent.push(i)}}return h.data}async function Ae(e,t={},n=null){return await De(e,t,n)}async function je(e,t,n=null){let r=await Q(t,n);if(!r)return null;let i=await Oe(e,t,n);return{...r,...i}}async function Me(e,t,n,r=null){return await ke(e,t,n,r)}export{B as Flaresolverr,H as convertHTMLtoMarkdown,_e as royalroadGetBook,ye as royalroadGetChapter,ve as royalroadGetVolume,be as royalroadSearch,j as safeFetch,U as sanitizeHTML,je as webnovelComGetBook,Me as webnovelComGetChapter,Ae as webnovelComSearch};
|
|
4
4
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["z","gzip","gzipRaw","gunzip","gunzipRaw","z","z","z","z","z","z","z","z","z$1","DOMPurify","TurndownService","cheerio","hasProcessEnv","isDev","isDebug","defaultHTMLHeaders","safeFetch","headers","includeDefaultHTMLHeaders","init","key","value","referrer","requestUrl","url","response","outputType","error","FlaresolverrFetchResponseCommon","NullableNonEmptyString","NonEmptyString","FlaresolverrFetchResponseCookie","FlaresolverrFetchResponseSolution","gzip$1","promisify","gzip","gunzip$1","gunzip","devCachePath","id","hex","sha1","utf8","normalize","resolve","join","devSetCache","mkdir","dirname","compressedValue","compression","writeFile","devGetCache","existsSync","readFile","decompressedValue","Flaresolverr","#host","host","#hostUsername","hostUsername","#hostPassword","hostPassword","cookies","flaresolverrHeaders","base64","cachedResponse","res","fetchImpl","parsedData","turndownService","convertHTMLtoMarkdown","safeHTML","sanitizeHTML","input","ROYALROAD_API_USER_AGENT","ROYALROAD_API_APP_VERSION","ROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS","RoyalroadAPIFictionStatus","RoyalroadAPISearchResponse","RoyalroadAPIFiction","RoyalroadAPIFictionChapter","royalroadAPIFiction","opts","resHeaders","novelId","parsedResponse","img","providerImageURLToWebNovelImage","out","RoyalroadNovelId","PersonalName","chapter","webNovelChapter","RoyalroadVolumeId","RoyalroadChapterId","isoDateTimeToPartialDateTime","parsedChapter","WebNovelChapter","volume","webNovelVolume","parsedVolume","WebNovelVolume","outParsed","WebNovelBook","royalroadAPIFictionChapter","chapterId","book","bookChapter","sanitizedContent","markdownContent","sanitizedNote","markdownNote","royalroadAPISearch","q","searchResponse","parsedBook","royalroadGetBook","royalroadNovelId","royalroadGetVolume","royalroadVolumeId","royalroadGetChapter","royalroadChapterId","royalroadSearch","webnovelComAPICreateCookies","auth","WEBNOVELCOM_API_SEARCH_TYPE","WEBNOVELCOM_API_SEARCH_STATUS","WEBNOVELCOM_API_SEARCH_ORDER","WEBNOVELCOM_API_USER_AGENT","WEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS","WEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS","WebnovelComAPISearchResponse","WebnovelComAPIBookResponse","BCP47LanguageTag","webnovelComAPIBook","u","timestampMillisecondsToPartialDateTime","WebnovelComNovelId","translator","editor","webnovelComAPISearch","webnovelComHTMLCreateCookies","webnovelComHTMLBookCatalog","flaresolverr","$","parseVolumeMeta","text","m","parseChapterName","parseChapterNumber","number","volumeContainerElement","volumeMeta","volumeId","WebnovelComVolumeId","chapterContainerElement","WebnovelComChapterId","chapterName","chapterNumber","webnovelComHTMLChapter","parseChapterMeta","textIdx","textName","parseChapterParagraphMeta","rawAttributeValue","data","chapterMeta","paragraphs","paragraphContainerElement","paragraphElement","paragraphMeta","innerHTML","outerHTML","fullHTML","outHTML","WebNovelChapterContent","fullMarkdown","outMarkdown","creatorsThoughtsContainerElement","creatorsThoughtsParagraph","el","WebNovelExtraChapterContent","webnovelComSearch","webnovelComGetBook","api","html","webnovelComGetChapter"],"sources":["../src/utils/env.ts","../src/utils/fetch.ts","../src/schemas/flaresolverr.ts","../src/utils/dev.ts","../src/utils/flaresolverr.ts","../src/utils/string.ts","../src/providers/royalroad/constants.ts","../src/schemas/providers/royalroad/api.ts","../src/providers/royalroad/api/fiction.ts","../src/providers/royalroad/api/search.ts","../src/providers/royalroad/api.ts","../src/providers/webnovel-com/api/utils.ts","../src/providers/webnovel-com/constants.ts","../src/schemas/providers/webnovel-com/api.ts","../src/providers/webnovel-com/api/book.ts","../src/providers/webnovel-com/api/search.ts","../src/providers/webnovel-com/html/utils.ts","../src/providers/webnovel-com/html/book.ts","../src/providers/webnovel-com/html/chapter.ts","../src/providers/webnovel-com/api.ts"],"sourcesContent":["function hasProcessEnv(): boolean {\n\treturn typeof process === \"object\" && typeof process.env === \"object\";\n}\n\n/**\n * @throws When environment variables cannot be accessed via `process.env`\n */\nexport function hasProcessEnvOrThrow(): void {\n\tif (!hasProcessEnv()) {\n\t\tthrow new Error(`Could not parse environment variables`);\n\t}\n}\n\nexport function isDev(): boolean {\n\tif (!hasProcessEnv()) return false;\n\treturn (\n\t\ttypeof process.env.DEV === \"string\" &&\n\t\t(process.env.DEV === \"true\" || process.env.DEV === \"1\")\n\t);\n}\n\nexport function isDebug(): boolean {\n\tif (isDev()) return true;\n\tif (!hasProcessEnv()) return false;\n\treturn (\n\t\ttypeof process.env.DEBUG === \"string\" &&\n\t\t(process.env.DEBUG === \"true\" || process.env.DEBUG === \"1\")\n\t);\n}\n","import { isDebug } from \"#/utils/env.js\";\n\nconst defaultHTMLHeaders: Record<string, string> = {\n\t\"User-Agent\":\n\t\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36\",\n\tAccept: \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\n\t\"Accept-Language\": \"en-US,en;q=0.9\",\n\t\"sec-ch-ua-platform\": '\"Windows\"',\n\t\"sec-ch-ua-mobile\": \"?0\",\n\t\"sec-ch-ua\":\n\t\t'\"Chromium\";v=\"142\", \"Google Chrome\";v=\"142\", \"Not_A Brand\";v=\"99\"',\n\t\"sec-fetch-dest\": \"document\",\n\t\"sec-fetch-user\": \"?1\",\n\t\"sec-fetch-mode\": \"navigate\",\n\t\"sec-fetch-site\": \"none\",\n};\n\ninterface SafeFetchConfig {\n\treferrer?: string | URL | null;\n\tinit?: Omit<RequestInit, \"headers\"> & {\n\t\theaders?: HeadersInit;\n\t};\n\tincludeDefaultHTMLHeaders?: boolean;\n}\n\nexport async function safeFetch<T = unknown>(\n\turl: string | URL,\n\toutputType: \"text\" | \"json\",\n\t{ referrer, init, includeDefaultHTMLHeaders }: SafeFetchConfig = {},\n): Promise<T | null> {\n\tconst headers = new Headers(\n\t\tincludeDefaultHTMLHeaders ? defaultHTMLHeaders : {},\n\t);\n\n\tif (init?.headers) {\n\t\tnew Headers(init.headers).forEach((value, key) => {\n\t\t\theaders.set(key, value);\n\t\t});\n\t}\n\n\t// The correct HTTP header is \"Referer\", not \"Referrer\".\n\tif (referrer != null) {\n\t\theaders.set(\"Referer\", String(referrer));\n\t}\n\n\tconst requestUrl = String(url);\n\n\ttry {\n\t\tconst response = await fetch(requestUrl, {\n\t\t\t...init,\n\t\t\tredirect: init?.redirect ?? \"follow\",\n\t\t\theaders,\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tif (isDebug()) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[safeFetch]\",\n\t\t\t\t\tresponse.url,\n\t\t\t\t\tresponse.status,\n\t\t\t\t\tresponse.statusText,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tconst data = (\n\t\t\toutputType === \"json\"\n\t\t\t\t? await response.json()\n\t\t\t\t: await response.text()\n\t\t) as T;\n\n\t\treturn data;\n\t} catch (error) {\n\t\tif (isDebug()) {\n\t\t\tconsole.error(\"[safeFetch]\", error);\n\t\t}\n\t\treturn null;\n\t}\n}\n","import { NonEmptyString, NullableNonEmptyString } from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport const FlaresolverrFetchResponseCommon = z.object({\n\tmessage: NullableNonEmptyString,\n\tstartTimestamp: z.int(),\n\tendTimestamp: z.int(),\n\tversion: NonEmptyString,\n});\nexport type FlaresolverrFetchResponseCommon = z.infer<\n\ttypeof FlaresolverrFetchResponseCommon\n>;\n\nexport const FlaresolverrRequestCookie = z.object({\n\tname: NonEmptyString,\n\tvalue: NonEmptyString,\n});\nexport type FlaresolverrRequestCookie = z.infer<\n\ttypeof FlaresolverrRequestCookie\n>;\n\nexport const FlaresolverrFetchResponseCookie = z.object({\n\tname: NonEmptyString,\n\tvalue: z.string(),\n\tdomain: NonEmptyString,\n\tpath: NonEmptyString,\n\texpiry: z.number().optional(),\n\texpires: z.number().optional(),\n\thttpOnly: z.boolean(),\n\tsecure: z.boolean(),\n\tsameSite: z.enum([\"None\", \"Lax\", \"Strict\"]),\n});\nexport type FlaresolverrFetchResponseCookie = z.infer<\n\ttypeof FlaresolverrFetchResponseCookie\n>;\n\nexport const FlaresolverrFetchResponseSolution = z.object({\n\turl: z.url(),\n\tstatus: z.int().min(100).max(599),\n\theaders: z.record(NonEmptyString, z.string()),\n\tresponse: NonEmptyString,\n\tuserAgent: NonEmptyString,\n\tturnstile_token: NonEmptyString.optional().nullable(),\n\tcookies: z.array(FlaresolverrFetchResponseCookie),\n});\nexport type FlaresolverrFetchResponseSolution = z.infer<\n\ttypeof FlaresolverrFetchResponseSolution\n>;\n\nexport const FlaresolverrFetchResponse = z.discriminatedUnion(\"type\", [\n\tz.object({\n\t\ttype: z.literal(\"success\"),\n\t\tstatus: z.int().min(100).max(599),\n\t\tresult: z.object({\n\t\t\t...FlaresolverrFetchResponseCommon.shape,\n\t\t\tsolution: FlaresolverrFetchResponseSolution,\n\t\t}),\n\t}),\n\tz.object({\n\t\ttype: z.literal(\"fail\"),\n\t\tstatus: z.int().min(100).max(599),\n\t\tresult: z.object({\n\t\t\t...FlaresolverrFetchResponseCommon.shape,\n\t\t\terror: z.object({\n\t\t\t\tmessage: NonEmptyString,\n\t\t\t\tdetail: z.unknown(),\n\t\t\t}),\n\t\t}),\n\t}),\n\tz.object({\n\t\ttype: z.literal(\"error\"),\n\t\terror: z.object({\n\t\t\tmessage: NonEmptyString,\n\t\t\tdetail: z.unknown().optional(),\n\t\t}),\n\t}),\n]);\nexport type FlaresolverrFetchResponse = z.infer<\n\ttypeof FlaresolverrFetchResponse\n>;\n","import { isDev } from \"#/utils/env.js\";\nimport { sha1 } from \"@noble/hashes/legacy.js\";\nimport { hex, utf8 } from \"@scure/base\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, normalize, resolve } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { gunzip as gunzipRaw, gzip as gzipRaw } from \"node:zlib\";\n\nconst gzip = promisify(gzipRaw);\nconst gunzip = promisify(gunzipRaw);\n\nfunction devCachePath(key: string[]): string {\n\tconst id = hex.encode(sha1(utf8.decode(key.join(\"\\u001F\"))));\n\n\treturn normalize(\n\t\tresolve(\n\t\t\tjoin(\n\t\t\t\tprocess.cwd(),\n\t\t\t\t\".cache\",\n\t\t\t\t\"dev-cache\",\n\t\t\t\tid.substring(0, 2),\n\t\t\t\tid + \".gz\",\n\t\t\t),\n\t\t),\n\t);\n}\n\nexport async function devSetCache(\n\tkey: string[],\n\tvalue: string,\n\tcompression = true,\n): Promise<{ path: string } | null> {\n\tif (isDev()) {\n\t\tawait mkdir(dirname(devCachePath(key)), {\n\t\t\trecursive: true,\n\t\t\tmode: 0o770,\n\t\t});\n\n\t\tconst compressedValue = compression ? await gzip(value) : value;\n\n\t\tawait writeFile(devCachePath(key), compressedValue, {\n\t\t\tmode: 0o770,\n\t\t});\n\t\treturn { path: devCachePath(key) };\n\t}\n\n\treturn null;\n}\n\nexport async function devGetCache(\n\tkey: string[],\n\tcompression = true,\n): Promise<{ path: string; value: string } | null> {\n\tif (isDev()) {\n\t\tif (!existsSync(devCachePath(key))) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst value = await readFile(devCachePath(key), {\n\t\t\tencoding: compression ? undefined : \"utf8\",\n\t\t});\n\t\tconst decompressedValue = compression\n\t\t\t? (await gunzip(value)).toString(\"utf8\")\n\t\t\t: (value as string);\n\t\tif (value && decompressedValue) {\n\t\t\treturn {\n\t\t\t\tpath: devCachePath(key),\n\t\t\t\tvalue: decompressedValue,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn null;\n}\n","import {\n\tFlaresolverrFetchResponseCommon,\n\tFlaresolverrFetchResponseSolution,\n\ttype FlaresolverrFetchResponse,\n\ttype FlaresolverrRequestCookie,\n} from \"#/schemas/flaresolverr.js\";\nimport { devGetCache, devSetCache } from \"#/utils/dev.js\";\nimport { isDebug, isDev } from \"#/utils/env.js\";\nimport { base64, utf8 } from \"@scure/base\";\nimport z from \"zod\";\n\nexport class Flaresolverr {\n\t#host = \"https://flaresolverr.internal\";\n\t#hostUsername: string | null = null;\n\t#hostPassword: string | null = null;\n\n\tconstructor({\n\t\thost,\n\t\thostUsername,\n\t\thostPassword,\n\t}: {\n\t\thost: string;\n\t\thostUsername?: string | null | undefined;\n\t\thostPassword?: string | null | undefined;\n\t}) {\n\t\tthis.#host = host;\n\t\tthis.#hostUsername = hostUsername ?? null;\n\t\tthis.#hostPassword = hostPassword ?? null;\n\t}\n\n\tasync fetch(\n\t\turl: string | URL,\n\t\tinit: Partial<{\n\t\t\tmethod: \"GET\" | \"POST\";\n\t\t\theaders: HeadersInit;\n\t\t\tcookies: FlaresolverrRequestCookie[];\n\t\t\tmaxTimeout: number;\n\t\t\tpayload:\n\t\t\t\tRecord<string, string> | URLSearchParams | [string, string][];\n\t\t}> = {},\n\t\tfetchImpl = fetch,\n\t): Promise<FlaresolverrFetchResponse> {\n\t\t// TODO when supported const headers = new Headers(init.headers);\n\t\t// TODO cookie jar\n\t\tconst cookies = init?.cookies ?? [];\n\n\t\tconst flaresolverrHeaders = new Headers({\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\"X-Cache-Control\": \"no-store\",\n\t\t});\n\t\tif (this.#hostUsername) {\n\t\t\tflaresolverrHeaders.set(\n\t\t\t\t\"Authorization\",\n\t\t\t\t\"Basic \" +\n\t\t\t\t\tbase64.encode(\n\t\t\t\t\t\tutf8.decode(\n\t\t\t\t\t\t\t`${this.#hostUsername}:${this.#hostPassword ?? \"\"}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (isDev()) {\n\t\t\tconst cachedResponse = await devGetCache([\n\t\t\t\t\"flaresolverr\",\n\t\t\t\turl.toString(),\n\t\t\t]);\n\t\t\tif (cachedResponse) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t\"[flaresolverr]\",\n\t\t\t\t\t`Retrieved cached response for ${url.toString()} : ${cachedResponse.path}`,\n\t\t\t\t);\n\t\t\t\treturn JSON.parse(\n\t\t\t\t\tcachedResponse.value,\n\t\t\t\t) as FlaresolverrFetchResponse;\n\t\t\t}\n\t\t}\n\n\t\tlet res: Response;\n\t\ttry {\n\t\t\tif (isDebug()) {\n\t\t\t\tconsole.log(\"[flaresolverr]\", `Fetching ${url}`);\n\t\t\t\tconsole.time(\"[flaresolverr] Timing\");\n\t\t\t}\n\t\t\tres = await fetchImpl(new URL(\"/v1\", this.#host), {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: flaresolverrHeaders,\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tcmd: `request.${(init.method ?? \"GET\").toLocaleLowerCase()}`,\n\t\t\t\t\turl: url.toString(),\n\t\t\t\t\tcookies,\n\t\t\t\t\t// TODO add headers when supported\n\t\t\t\t\tpostData: init.payload\n\t\t\t\t\t\t? new URLSearchParams(init.payload)\n\t\t\t\t\t\t: undefined,\n\n\t\t\t\t\tdisableMedia: true,\n\t\t\t\t\tmaxTimeout: init.maxTimeout ?? undefined,\n\t\t\t\t}),\n\t\t\t});\n\t\t\tif (isDebug()) {\n\t\t\t\tconsole.timeEnd(\"[flaresolverr] Timing\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.timeEnd(\"[flaresolverr] Timing\");\n\n\t\t\tif (error instanceof Error) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\terror,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\terror: {\n\t\t\t\t\tmessage: \"Could not connect to flaresolverr\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (res.headers.get(\"Content-Type\") !== \"application/json\") {\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\terror: {\n\t\t\t\t\tmessage: \"Flaresolverr returned a non-JSON Content-Type\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (!res.ok) {\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\terror: {\n\t\t\t\t\tmessage: `Flaresolverr response not ok (${res.status.toString()})`,\n\t\t\t\t\tdetail: await res.json(),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst parsedData = z\n\t\t\t.object({\n\t\t\t\t...FlaresolverrFetchResponseCommon.shape,\n\t\t\t\tsolution: FlaresolverrFetchResponseSolution,\n\t\t\t})\n\t\t\t.safeParse((await res.json()) as unknown);\n\t\tif (!parsedData.success) {\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\terror: {\n\t\t\t\t\tmessage: \"Malformed flaresolverr response\",\n\t\t\t\t\tdetail: z.prettifyError(parsedData.error),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (isDev()) {\n\t\t\tawait devSetCache(\n\t\t\t\t[\"flaresolverr\", url.toString()],\n\t\t\t\tJSON.stringify({\n\t\t\t\t\ttype: \"success\",\n\t\t\t\t\tstatus: res.status,\n\t\t\t\t\tresult: parsedData.data,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\ttype: \"success\",\n\t\t\tstatus: res.status,\n\t\t\tresult: parsedData.data,\n\t\t};\n\t}\n}\n","import DOMPurify from \"isomorphic-dompurify\";\nimport TurndownService from \"turndown\";\n\nconst turndownService = new TurndownService({\n\theadingStyle: \"atx\",\n\temDelimiter: \"_\",\n\tstrongDelimiter: \"**\",\n});\n\nexport function convertHTMLtoMarkdown(safeHTML: string): string {\n\treturn turndownService.turndown(safeHTML);\n}\n\nexport function sanitizeHTML(input: string): string {\n\treturn DOMPurify.sanitize(input, { USE_PROFILES: { html: true } });\n}\n","import type { WebNovelBookStatus } from \"@webnovel-proxy/core\";\nimport type { RoyalroadAPIFictionStatus } from \"#/schemas/providers/royalroad/api.js\";\n\nexport const ROYALROAD_API_USER_AGENT =\n\t\"Royal Road Mobile/2.60.1186 ( Android; 16; Arm; Phone ) MAUI/10.0.10\";\nexport const ROYALROAD_API_APP_VERSION = \"2.60.1186\";\n\nexport const ROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS: Record<\n\tRoyalroadAPIFictionStatus,\n\tWebNovelBookStatus | null\n> = {\n\tCompleted: \"completed\",\n\tDropped: \"dropped\",\n\tHiatus: \"hiatus\",\n\tInactive: null,\n\tOngoing: \"ongoing\",\n\tStub: null,\n};\n","import { NonEmptyString, NullableNonEmptyString } from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport const RoyalroadAPIFictionStatus = z.enum([\n\t\"Completed\",\n\t\"Dropped\",\n\t\"Ongoing\",\n\t\"Stub\",\n\t\"Hiatus\",\n\t\"Inactive\",\n]);\nexport type RoyalroadAPIFictionStatus = z.infer<\n\ttypeof RoyalroadAPIFictionStatus\n>;\n\nexport const RoyalroadAPISearchResponse = z.object({\n\tdata: z.array(\n\t\tz.object({\n\t\t\tid: z.int().min(1),\n\n\t\t\ttitle: NonEmptyString,\n\t\t\tdescription: NullableNonEmptyString.nullable(),\n\t\t\tcover: NullableNonEmptyString.nullable(),\n\n\t\t\tfirstUpdate: z.iso.datetime(),\n\t\t\tlastUpdate: z.iso.datetime(),\n\n\t\t\tauthorId: z.int().min(1),\n\t\t\tauthorName: NonEmptyString,\n\n\t\t\tstatus: RoyalroadAPIFictionStatus,\n\t\t}),\n\t),\n\tpaginate: z.object({\n\t\ttotal: z.int().min(1),\n\t\tperPage: z.int().min(1),\n\t\tpage: z.int().min(1),\n\t}),\n});\nexport type RoyalroadAPISearchResponse = z.infer<\n\ttypeof RoyalroadAPISearchResponse\n>;\n\nexport const RoyalroadAPIFiction = z.object({\n\tid: z.int().min(1),\n\ttitle: NonEmptyString,\n\tslug: NonEmptyString,\n\tdescription: NullableNonEmptyString.nullable(),\n\n\tcover: z.url().nullable(),\n\tthumbnailHash: NonEmptyString.nullable(),\n\n\tstatus: RoyalroadAPIFictionStatus,\n\n\tcoverBackground: NonEmptyString.nullable(),\n\n\tauthorInfo: z.object({\n\t\tuserId: z.int().min(1),\n\t\tusername: NonEmptyString,\n\t\tavatar: z.url(),\n\t}),\n\n\tvolumes: z.array(\n\t\tz.object({\n\t\t\tid: z.int().min(1),\n\t\t\ttitle: NonEmptyString,\n\t\t\tcover: z.url().nullable(),\n\t\t\torder: z.int().min(1),\n\t\t}),\n\t),\n\n\tchapters: z.array(\n\t\tz.object({\n\t\t\tid: z.int().min(1),\n\t\t\ttitle: NonEmptyString,\n\t\t\tpublished: z.iso.datetime(),\n\t\t\tlastEdit: z.iso.datetime().optional().nullable(),\n\t\t\tvisible: z.boolean(),\n\t\t\torder: z.int().min(0),\n\t\t\tvolumeId: z.int().min(1).optional().nullable(),\n\t\t\tisUnlocked: z.boolean(),\n\t\t}),\n\t),\n\n\tcollaborators: z.array(z.unknown()),\n});\nexport type RoyalroadAPIFiction = z.infer<typeof RoyalroadAPIFiction>;\n\nexport const RoyalroadAPIFictionChapter = z.object({\n\tpreAuthorsNote: NullableNonEmptyString.nullable(),\n\tpostAuthorsNote: NullableNonEmptyString.nullable(),\n\tcontent: NonEmptyString,\n\tfictionInfo: z.object({\n\t\tid: z.int().min(1),\n\t}),\n\tid: z.int().min(1),\n\ttitle: NonEmptyString,\n\tpublished: z.iso.datetime(),\n\tlastEdit: z.iso.datetime().optional().nullable(),\n\tisUnlocked: z.boolean(),\n\torder: z.int().min(0),\n});\nexport type RoyalroadAPIFictionChapter = z.infer<\n\ttypeof RoyalroadAPIFictionChapter\n>;\n","import {\n\tROYALROAD_API_APP_VERSION,\n\tROYALROAD_API_USER_AGENT,\n\tROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS,\n} from \"#/providers/royalroad/constants.js\";\nimport {\n\tRoyalroadAPIFiction,\n\tRoyalroadAPIFictionChapter,\n} from \"#/schemas/providers/royalroad/api.js\";\nimport { safeFetch } from \"#/utils/fetch.js\";\nimport { convertHTMLtoMarkdown, sanitizeHTML } from \"#/utils/string.js\";\nimport {\n\tisoDateTimeToPartialDateTime,\n\tPersonalName,\n\tproviderImageURLToWebNovelImage,\n\tRoyalroadChapterId,\n\tRoyalroadNovelId,\n\tRoyalroadVolumeId,\n\tWebNovelBook,\n\tWebNovelChapter,\n\tWebNovelVolume,\n} from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport async function royalroadAPIFiction(\n\tnovelId: RoyalroadNovelId,\n\topts: Partial<{ bustCache: boolean }> = {},\n): Promise<WebNovelBook | null> {\n\topts = {\n\t\tbustCache: true,\n\t\t...opts,\n\t};\n\tconst resHeaders: Record<string, string> = {\n\t\tAccept: \"application/json\",\n\t\tCustomUserAgent: ROYALROAD_API_USER_AGENT,\n\t\t\"X-Mature-Content\": \"true\",\n\t\t\"X-Govt-Age-Restrict-Mature-Content\": \"false\",\n\t\t\"X-App-Version\": ROYALROAD_API_APP_VERSION,\n\t};\n\tif (opts.bustCache) {\n\t\tresHeaders[\"Cache-Control\"] = \"no-cache\";\n\t}\n\tconst res = await safeFetch<RoyalroadAPIFiction>(\n\t\t`https://api.royalroad.com/v1/fiction/${novelId}`,\n\t\t\"json\",\n\t\t{\n\t\t\tinit: {\n\t\t\t\theaders: resHeaders,\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst parsedResponse = RoyalroadAPIFiction.safeParse(res);\n\tif (!parsedResponse.success) {\n\t\tconsole.warn(\"Invalid royalroad fiction response from API\");\n\t\tconsole.warn(z.prettifyError(parsedResponse.error));\n\t\treturn null;\n\t}\n\n\tconst img = providerImageURLToWebNovelImage(\n\t\tparsedResponse.data.cover ?? \"none\",\n\t);\n\n\tconst out: WebNovelBook = {\n\t\tprovider: \"royalroad\",\n\t\tproviderId: RoyalroadNovelId.parse(parsedResponse.data.id.toString()),\n\n\t\tchapters: [],\n\t\tcontributors: [\n\t\t\t{\n\t\t\t\tname: PersonalName.parse({\n\t\t\t\t\tdisplayName: parsedResponse.data.authorInfo.username,\n\t\t\t\t}),\n\t\t\t\trole: \"aut\",\n\t\t\t},\n\t\t],\n\t\t// TODO grab first created chapter and last updated chapter\n\t\tcreatedAt: null,\n\t\tupdatedAt: null,\n\t\timages: img ? [img] : [],\n\t\tlanguage: null,\n\t\tname: parsedResponse.data.title,\n\t\toverview: parsedResponse.data.description\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text/html\",\n\t\t\t\t\t\tvalue: sanitizeHTML(parsedResponse.data.description),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text/markdown\",\n\t\t\t\t\t\tvalue: convertHTMLtoMarkdown(\n\t\t\t\t\t\t\tsanitizeHTML(parsedResponse.data.description),\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [],\n\t\tstatus:\n\t\t\tROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS[\n\t\t\t\tparsedResponse.data.status\n\t\t\t] ?? null,\n\t\tvolumes: [],\n\t\twarnings: [],\n\t};\n\n\tfor (const chapter of parsedResponse.data.chapters) {\n\t\tconst webNovelChapter: WebNovelChapter = {\n\t\t\tprovider: \"royalroad\",\n\t\t\tbookProviderId: RoyalroadNovelId.parse(\n\t\t\t\tparsedResponse.data.id.toString(),\n\t\t\t),\n\t\t\tvolumeProviderId: chapter.volumeId\n\t\t\t\t? RoyalroadVolumeId.parse(chapter.volumeId.toString())\n\t\t\t\t: null,\n\t\t\tproviderId: RoyalroadChapterId.parse(chapter.id.toString()),\n\n\t\t\tcontent: [],\n\t\t\textraContent: [],\n\t\t\tcontributors: [],\n\t\t\tcreatedAt: isoDateTimeToPartialDateTime(chapter.published),\n\t\t\tupdatedAt: isoDateTimeToPartialDateTime(chapter.lastEdit ?? \"none\"),\n\t\t\timages: [],\n\t\t\tisLocked: !chapter.isUnlocked,\n\t\t\tname: chapter.title.trim(),\n\t\t\tnumber: null,\n\t\t\torderNumber: chapter.order,\n\t\t};\n\t\tconst parsedChapter = WebNovelChapter.safeParse(webNovelChapter);\n\t\tif (!parsedChapter.success) {\n\t\t\tconsole.warn(\"Invalid royalroad fiction book chapter from API\");\n\t\t\tconsole.warn(z.prettifyError(parsedChapter.error));\n\t\t\tcontinue;\n\t\t}\n\n\t\tout.chapters.push(parsedChapter.data);\n\t}\n\n\tfor (const volume of parsedResponse.data.volumes) {\n\t\tconst webNovelVolume: WebNovelVolume = {\n\t\t\tprovider: \"royalroad\",\n\t\t\tbookProviderId: RoyalroadNovelId.parse(\n\t\t\t\tparsedResponse.data.id.toString(),\n\t\t\t),\n\t\t\tproviderId: RoyalroadVolumeId.parse(volume.id.toString()),\n\t\t\tcreatedAt: null,\n\t\t\tupdatedAt: null,\n\t\t\timages: [],\n\n\t\t\tname: volume.title.trim(),\n\t\t\tnumber: null,\n\t\t\torderNumber: volume.order - 1,\n\t\t};\n\t\tconst parsedVolume = WebNovelVolume.safeParse(webNovelVolume);\n\t\tif (!parsedVolume.success) {\n\t\t\tconsole.warn(\"Invalid royalroad fiction book volume from API\");\n\t\t\tconsole.warn(z.prettifyError(parsedVolume.error));\n\t\t\tcontinue;\n\t\t}\n\n\t\tout.volumes.push(parsedVolume.data);\n\t}\n\n\tconst outParsed = WebNovelBook.safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid royalroad fiction book from API\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\treturn outParsed.data;\n}\n\nexport async function royalroadAPIFictionChapter(\n\tchapterId: RoyalroadChapterId,\n): Promise<WebNovelChapter | null> {\n\tconst res = await safeFetch<RoyalroadAPIFictionChapter>(\n\t\t`https://api.royalroad.com/v1/fiction/chapters/${chapterId}`,\n\t\t\"json\",\n\t\t{\n\t\t\tinit: {\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\tCustomUserAgent: ROYALROAD_API_USER_AGENT,\n\t\t\t\t\t\"X-Mature-Content\": \"true\",\n\t\t\t\t\t\"X-Govt-Age-Restrict-Mature-Content\": \"false\",\n\t\t\t\t\t\"X-App-Version\": ROYALROAD_API_APP_VERSION,\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst parsedResponse = RoyalroadAPIFictionChapter.safeParse(res);\n\tif (!parsedResponse.success) {\n\t\tconsole.warn(\"Invalid royalroad fiction chapter response from API\");\n\t\tconsole.warn(z.prettifyError(parsedResponse.error));\n\t\treturn null;\n\t}\n\n\t// TODO: Find better solution\n\tconst book = await royalroadAPIFiction(\n\t\tRoyalroadNovelId.parse(parsedResponse.data.fictionInfo.id.toString()),\n\t\t{ bustCache: false },\n\t);\n\tconst bookChapter = book?.chapters?.find(\n\t\t(e) =>\n\t\t\te.providerId ===\n\t\t\tRoyalroadChapterId.parse(parsedResponse.data.id.toString()),\n\t);\n\n\tconst out: WebNovelChapter = {\n\t\tprovider: \"royalroad\",\n\t\tbookProviderId: RoyalroadNovelId.parse(\n\t\t\tparsedResponse.data.fictionInfo.id.toString(),\n\t\t),\n\t\tvolumeProviderId: bookChapter\n\t\t\t? (book?.volumes?.find(\n\t\t\t\t\t(e) => e.providerId === bookChapter.volumeProviderId,\n\t\t\t\t)?.providerId ?? null)\n\t\t\t: null,\n\t\tproviderId: RoyalroadChapterId.parse(parsedResponse.data.id.toString()),\n\n\t\tcontributors: [],\n\t\tcreatedAt: isoDateTimeToPartialDateTime(parsedResponse.data.published),\n\t\tupdatedAt: isoDateTimeToPartialDateTime(\n\t\t\tparsedResponse.data.lastEdit ?? \"none\",\n\t\t),\n\t\timages: [],\n\t\tname: parsedResponse.data.title,\n\t\tcontent: [],\n\t\textraContent: [],\n\t\tisLocked: !parsedResponse.data.isUnlocked,\n\t\tnumber: null,\n\t\torderNumber: parsedResponse.data.order,\n\t};\n\n\tconst sanitizedContent = sanitizeHTML(parsedResponse.data.content);\n\tout.content.push({\n\t\tsize: Buffer.byteLength(sanitizedContent, \"utf-8\"),\n\t\ttype: \"text/html\",\n\t\tvalue: sanitizedContent,\n\t});\n\tconst markdownContent = convertHTMLtoMarkdown(sanitizedContent);\n\tout.content.push({\n\t\tsize: Buffer.byteLength(markdownContent, \"utf-8\"),\n\t\ttype: \"text/markdown\",\n\t\tvalue: markdownContent,\n\t});\n\n\tif (parsedResponse.data.preAuthorsNote) {\n\t\tconst sanitizedNote = sanitizeHTML(parsedResponse.data.preAuthorsNote);\n\t\tconst markdownNote = convertHTMLtoMarkdown(sanitizedNote);\n\t\tout.extraContent.push(\n\t\t\t{\n\t\t\t\textraType: \"preNote\",\n\t\t\t\tsize: Buffer.byteLength(sanitizedNote, \"utf-8\"),\n\t\t\t\ttype: \"text/html\",\n\t\t\t\tvalue: sanitizedNote,\n\t\t\t},\n\t\t\t{\n\t\t\t\textraType: \"preNote\",\n\t\t\t\tsize: Buffer.byteLength(markdownNote, \"utf-8\"),\n\t\t\t\ttype: \"text/markdown\",\n\t\t\t\tvalue: markdownNote,\n\t\t\t},\n\t\t);\n\t}\n\n\tif (parsedResponse.data.postAuthorsNote) {\n\t\tconst sanitizedNote = sanitizeHTML(parsedResponse.data.postAuthorsNote);\n\t\tconst markdownNote = convertHTMLtoMarkdown(sanitizedNote);\n\t\tout.extraContent.push(\n\t\t\t{\n\t\t\t\textraType: \"postNote\",\n\t\t\t\tsize: Buffer.byteLength(sanitizedNote, \"utf-8\"),\n\t\t\t\ttype: \"text/html\",\n\t\t\t\tvalue: sanitizedNote,\n\t\t\t},\n\t\t\t{\n\t\t\t\textraType: \"postNote\",\n\t\t\t\tsize: Buffer.byteLength(markdownNote, \"utf-8\"),\n\t\t\t\ttype: \"text/markdown\",\n\t\t\t\tvalue: markdownNote,\n\t\t\t},\n\t\t);\n\t}\n\n\tconst outParsed = WebNovelChapter.safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid royalroad fiction chapter from API\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\treturn outParsed.data;\n}\n\n// TODO: royalroadAPIFictionChapter\n","import {\n\tROYALROAD_API_APP_VERSION,\n\tROYALROAD_API_USER_AGENT,\n\tROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS,\n} from \"#/providers/royalroad/constants.js\";\nimport {\n\tRoyalroadAPISearchResponse,\n\ttype RoyalroadAPIFictionStatus,\n} from \"#/schemas/providers/royalroad/api.js\";\nimport { safeFetch } from \"#/utils/fetch.js\";\nimport { convertHTMLtoMarkdown, sanitizeHTML } from \"#/utils/string.js\";\nimport {\n\tisoDateTimeToPartialDateTime,\n\tPersonalName,\n\tproviderImageURLToWebNovelImage,\n\tRoyalroadNovelId,\n\tWebNovelBook,\n} from \"@webnovel-proxy/core\";\nimport z from \"zod\";\n\nexport async function royalroadAPISearch(\n\tq: string,\n\topts: Partial<{\n\t\tstatus: RoyalroadAPIFictionStatus[];\n\t\tpage: number;\n\t}> = {},\n): Promise<WebNovelBook[] | null> {\n\tconst res = await safeFetch<RoyalroadAPISearchResponse>(\n\t\t\"https://api.royalroad.com/v1/fiction/search/advanced\",\n\t\t\"json\",\n\t\t{\n\t\t\tinit: {\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\tCustomUserAgent: ROYALROAD_API_USER_AGENT,\n\t\t\t\t\t\"X-Mature-Content\": \"true\",\n\t\t\t\t\t\"X-Govt-Age-Restrict-Mature-Content\": \"false\",\n\t\t\t\t\t\"X-App-Version\": ROYALROAD_API_APP_VERSION,\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t\t\"Content-Type\": \"application/json; charset=utf-8\",\n\t\t\t\t},\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tTitle: q,\n\t\t\t\t\tStatus: opts.status ?? null,\n\t\t\t\t\tTagsAdd: [],\n\t\t\t\t\tTagsRemove: [],\n\t\t\t\t\tPage: opts.page ?? 1,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst searchResponse = RoyalroadAPISearchResponse.safeParse(res);\n\tif (!searchResponse.success) {\n\t\tconsole.warn(\"Invalid royalroad search response from API\");\n\t\tconsole.warn(z.prettifyError(searchResponse.error));\n\t\treturn null;\n\t}\n\n\tconst out: WebNovelBook[] = [];\n\n\tfor (const response of searchResponse.data.data) {\n\t\tconst img = providerImageURLToWebNovelImage(\n\t\t\tresponse.cover ?? \"invalid\",\n\t\t);\n\n\t\tconst book: WebNovelBook = {\n\t\t\tprovider: \"royalroad\",\n\t\t\tproviderId: RoyalroadNovelId.parse(response.id.toString()),\n\n\t\t\tname: response.title,\n\t\t\toverview: response.description\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text/html\",\n\t\t\t\t\t\t\tvalue: sanitizeHTML(response.description),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text/markdown\",\n\t\t\t\t\t\t\tvalue: convertHTMLtoMarkdown(\n\t\t\t\t\t\t\t\tsanitizeHTML(response.description),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: [],\n\t\t\tcontributors: [\n\t\t\t\t{\n\t\t\t\t\tname: PersonalName.parse({\n\t\t\t\t\t\tdisplayName: response.authorName,\n\t\t\t\t\t}),\n\t\t\t\t\trole: \"aut\",\n\t\t\t\t},\n\t\t\t],\n\t\t\timages: img ? [img] : [],\n\t\t\tchapters: [],\n\t\t\tvolumes: [],\n\t\t\tcreatedAt: isoDateTimeToPartialDateTime(response.firstUpdate),\n\t\t\tupdatedAt: isoDateTimeToPartialDateTime(response.lastUpdate),\n\t\t\tstatus:\n\t\t\t\tROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS[response.status] ??\n\t\t\t\tnull,\n\t\t\tlanguage: null,\n\t\t\twarnings: [],\n\t\t};\n\n\t\tconst parsedBook = WebNovelBook.safeParse(book);\n\t\tif (!parsedBook.success) {\n\t\t\tconsole.warn(\n\t\t\t\t`Could not parse royalroad book ${response.id.toString()} from search response`,\n\t\t\t);\n\t\t\tconsole.warn(z.prettifyError(parsedBook.error));\n\t\t\tcontinue;\n\t\t}\n\t\tout.push(parsedBook.data);\n\t}\n\n\treturn out;\n}\n","import {\n\troyalroadAPIFiction,\n\troyalroadAPIFictionChapter,\n} from \"#/providers/royalroad/api/fiction.js\";\nimport { royalroadAPISearch } from \"#/providers/royalroad/api/search.js\";\nimport type {\n\tRoyalroadChapterId,\n\tRoyalroadNovelId,\n\tRoyalroadVolumeId,\n\tWebNovelBook,\n\tWebNovelChapter,\n\tWebNovelVolume,\n} from \"@webnovel-proxy/core\";\n\nexport async function royalroadGetBook(\n\troyalroadNovelId: RoyalroadNovelId,\n): Promise<WebNovelBook | null> {\n\treturn await royalroadAPIFiction(royalroadNovelId);\n}\n\nexport async function royalroadGetVolume(\n\troyalroadNovelId: RoyalroadNovelId,\n\troyalroadVolumeId: RoyalroadVolumeId,\n): Promise<WebNovelVolume | null> {\n\tconst book = await royalroadAPIFiction(royalroadNovelId);\n\tif (!book) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\tbook?.volumes?.find((e) => e.providerId === royalroadVolumeId) ?? null\n\t);\n}\n\nexport async function royalroadGetChapter(\n\troyalroadChapterId: RoyalroadChapterId,\n): Promise<WebNovelChapter | null> {\n\treturn await royalroadAPIFictionChapter(royalroadChapterId);\n}\n\nexport async function royalroadSearch(\n\tq: string,\n\topts: Partial<{ page: number }> = {},\n): Promise<WebNovelBook[] | null> {\n\treturn await royalroadAPISearch(q, opts);\n}\n","import type { WebnovelComAuth } from \"@webnovel-proxy/core\";\n\nexport function webnovelComAPICreateCookies(\n\tauth: WebnovelComAuth | null = null,\n): string {\n\tconst out: [string, string][] = [\n\t\t[\"domain\", \".webnovel.com\"],\n\t\t[\"lang\", \"cn\"],\n\t\t[\"webnovel-language\", \"en\"],\n\t\t[\"webnovel-content-language\", \"en\"],\n\t];\n\n\tif (auth) {\n\t\tout.push([\"ywkey\", auth.ukey], [\"ukey\", auth.ukey], [\"uid\", auth.uid]);\n\t}\n\n\treturn out.map((e) => `${e[0]}=${e[1]}`).join(\"; \");\n}\n","import type { WebNovelBookStatus } from \"@webnovel-proxy/core\";\n\nexport const WEBNOVELCOM_API_SEARCH_TYPE = {\n\tnovel: 1,\n\tcomic: 2,\n\tebook: 3,\n\tfanfic: 4,\n} as const satisfies Record<string, number>;\n\nexport const WEBNOVELCOM_API_SEARCH_STATUS = {\n\tall: 0,\n\tongoing: 1,\n\tcompleted: 2,\n} as const satisfies Record<string, number>;\n\nexport const WEBNOVELCOM_API_SEARCH_ORDER = {\n\tpopular: 1,\n\tupdated: 2,\n} as const satisfies Record<string, number>;\n\nexport const WEBNOVELCOM_API_USER_AGENT =\n\t\"Mozilla/mobile QDHWReaderAndroid/8.22.8/1228/2000002/61ce67d733a61d70344e8c706215fd25\" as const satisfies string;\n\nexport const WEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS: Record<\n\tnumber,\n\tWebNovelBookStatus | null\n> = {\n\t2: \"ongoing\",\n\t1: \"completed\",\n};\n\nexport const WEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS: Record<\n\tnumber,\n\tWebNovelBookStatus | null\n> = {\n\t30: \"ongoing\",\n\t40: \"hiatus\",\n\t50: \"completed\",\n};\n","import {\n\tBCP47LanguageTag,\n\tNonEmptyString,\n\tNullableNonEmptyString,\n} from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport const WebnovelComAPISearchResponse = z.object({\n\tData: z.object({\n\t\tBookTotalNum: z.int().min(0),\n\t\tBookInfos: z.array(\n\t\t\tz.object({\n\t\t\t\t/** Too big for regular int */\n\t\t\t\tBookId: z.number().min(1),\n\t\t\t\tBookName: NonEmptyString,\n\t\t\t\tAuthor: NonEmptyString,\n\t\t\t\tBookCoverID: z.int().min(1),\n\t\t\t\t/** @see {WEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS} */\n\t\t\t\tType: z.int().min(1),\n\t\t\t\t/** Unknown mapping */\n\t\t\t\tBookType: z.int().min(0),\n\t\t\t\t/** text/plain */\n\t\t\t\tDescription: NullableNonEmptyString.nullable(),\n\t\t\t\tMTL: z.boolean(),\n\t\t\t}),\n\t\t),\n\t}),\n});\nexport type WebnovelComAPISearchResponse = z.infer<\n\ttypeof WebnovelComAPISearchResponse\n>;\n\nexport const WebnovelComAPIBookResponse = z.object({\n\tData: z\n\t\t.object({\n\t\t\t/** Too big for regular int */\n\t\t\tBookId: z.number().min(1),\n\t\t\tBookName: NonEmptyString,\n\t\t\tLastChapterTime: z.int().min(1),\n\t\t\t/** text/plain */\n\t\t\tDescription: NullableNonEmptyString.nullable(),\n\t\t\t/** @see {WEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS} */\n\t\t\tType: z.int().min(1),\n\t\t\t/** @see {WEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS} */\n\t\t\tStatus: z.int().min(1),\n\t\t\tLanguageName: BCP47LanguageTag,\n\t\t\tAuthorInfo: z.object({\n\t\t\t\tAuthorName: NonEmptyString,\n\t\t\t\tUserId: z.int().min(1),\n\t\t\t}),\n\t\t\tTransInfo: z.object({\n\t\t\t\tTranslators: z.array(\n\t\t\t\t\tz.object({\n\t\t\t\t\t\tTranslatorName: NonEmptyString,\n\t\t\t\t\t\tUserId: z.int().min(1),\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tEditors: z.array(\n\t\t\t\t\tz.object({\n\t\t\t\t\t\tTranslatorName: NonEmptyString,\n\t\t\t\t\t\tUserId: z.int().min(1),\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t}),\n\t\t\tBookCoverID: z.int().min(1),\n\t\t\tMTL: z.boolean(),\n\t\t\tChapterNum: z.int().min(0),\n\t\t})\n\t\t.nullable(),\n});\nexport type WebnovelComAPIBookResponse = z.infer<\n\ttypeof WebnovelComAPIBookResponse\n>;\n\n// Gated behind encryption/auth\n// export const WebnovelComAPIChapterResponse = z.object({\n// \tData: z\n// \t\t.object({\n// \t\t\t/** Too big for regular int */\n// \t\t\tId: z.number().min(1),\n// \t\t\tName: NonEmptyString,\n// \t\t\tLanguageName: BCP47LanguageTag,\n// \t\t\tLockType: z.int().min(0),\n// \t\t\tUnlocked: z.int().min(0).max(1),\n// \t\t})\n// \t\t.nullable(),\n// });\n// export type WebnovelComAPIChapterResponse = z.infer<\n// \ttypeof WebnovelComAPIChapterResponse\n// >;\n","import { webnovelComAPICreateCookies } from \"#/providers/webnovel-com/api/utils.js\";\nimport {\n\tWEBNOVELCOM_API_USER_AGENT,\n\tWEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS,\n\tWEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS,\n} from \"#/providers/webnovel-com/constants.js\";\nimport { WebnovelComAPIBookResponse } from \"#/schemas/providers/webnovel-com/api.js\";\nimport { safeFetch } from \"#/utils/fetch.js\";\nimport {\n\tPersonalName,\n\ttimestampMillisecondsToPartialDateTime,\n\tWebNovelBook,\n\tWebnovelComNovelId,\n\ttype WebnovelComAuth,\n} from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport async function webnovelComAPIBook(\n\tnovelId: WebnovelComNovelId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelBook | null> {\n\tconst u = new URL(\"https://idruid.webnovel.com/app/api/book/get-book\");\n\tu.searchParams.set(\"bookId\", novelId);\n\tu.searchParams.set(\"reqTS\", Math.round(Date.now()).toString());\n\n\tconst res = await safeFetch<WebnovelComAPIBookResponse>(\n\t\tu.toString(),\n\t\t\"json\",\n\t\t{\n\t\t\treferrer: \"https://android.webnovel.com\",\n\t\t\tinit: {\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": WEBNOVELCOM_API_USER_AGENT,\n\t\t\t\t\tCookie: webnovelComAPICreateCookies(auth),\n\t\t\t\t\tDeviceTime: timestampMillisecondsToPartialDateTime(\n\t\t\t\t\t\tDate.now(),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst parsedResponse = WebnovelComAPIBookResponse.safeParse(res);\n\tif (!parsedResponse.success) {\n\t\tconsole.warn(\"Invalid webnovel-com response from API\");\n\t\tconsole.warn(z.prettifyError(parsedResponse.error));\n\t\treturn null;\n\t}\n\n\tif (parsedResponse.data.Data === null) {\n\t\t// Book Not Found\n\t\treturn null;\n\t}\n\n\tconst out: WebNovelBook = {\n\t\tprovider: \"webnovel-com\",\n\t\tproviderId: WebnovelComNovelId.parse(\n\t\t\tparsedResponse.data.Data.BookId.toString(),\n\t\t),\n\n\t\tchapters: [],\n\t\tvolumes: [],\n\t\tcontributors: [\n\t\t\t{\n\t\t\t\tname: PersonalName.parse({\n\t\t\t\t\tdisplayName: parsedResponse.data.Data.AuthorInfo.AuthorName,\n\t\t\t\t}),\n\t\t\t\trole: \"aut\",\n\t\t\t},\n\t\t],\n\n\t\tcreatedAt: null,\n\t\tupdatedAt: null,\n\n\t\timages: [\n\t\t\t{\n\t\t\t\ttype: \"book\",\n\t\t\t\theight: 800,\n\t\t\t\twidth: 600,\n\t\t\t\tmediaType: \"image/jpeg\",\n\t\t\t\tprovider: \"webnovel-com\",\n\t\t\t\tproviderId: parsedResponse.data.Data.BookId.toString(),\n\t\t\t\tproviderMeta: {\n\t\t\t\t\timageId: parsedResponse.data.Data.BookCoverID.toString(),\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\tlanguage: parsedResponse.data.Data.LanguageName,\n\t\tname: parsedResponse.data.Data.BookName,\n\t\toverview: parsedResponse.data.Data.Description\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text/plain\",\n\t\t\t\t\t\tvalue: parsedResponse.data.Data.Description,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [],\n\t\tstatus:\n\t\t\tWEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS[\n\t\t\t\tparsedResponse.data.Data.Status\n\t\t\t] ??\n\t\t\tWEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS[\n\t\t\t\tparsedResponse.data.Data.Type\n\t\t\t] ??\n\t\t\tnull,\n\t\twarnings: parsedResponse.data.Data.MTL ? [\"mtl\"] : [],\n\t};\n\n\tfor (const translator of parsedResponse.data.Data.TransInfo.Translators) {\n\t\tout.contributors.push({\n\t\t\tname: PersonalName.parse({\n\t\t\t\tdisplayName: translator.TranslatorName,\n\t\t\t}),\n\t\t\trole: \"trl\",\n\t\t});\n\t}\n\n\tfor (const editor of parsedResponse.data.Data.TransInfo.Editors) {\n\t\tout.contributors.push({\n\t\t\tname: PersonalName.parse({\n\t\t\t\tdisplayName: editor.TranslatorName,\n\t\t\t}),\n\t\t\trole: \"edt\",\n\t\t});\n\t}\n\n\tconst outParsed = WebNovelBook.safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid webnovel-com book from API\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\treturn outParsed.data;\n}\n","import { webnovelComAPICreateCookies } from \"#/providers/webnovel-com/api/utils.js\";\nimport {\n\tWEBNOVELCOM_API_SEARCH_ORDER,\n\tWEBNOVELCOM_API_SEARCH_STATUS,\n\tWEBNOVELCOM_API_SEARCH_TYPE,\n\tWEBNOVELCOM_API_USER_AGENT,\n\tWEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS,\n} from \"#/providers/webnovel-com/constants.js\";\nimport { WebnovelComAPISearchResponse } from \"#/schemas/providers/webnovel-com/api.js\";\nimport { safeFetch } from \"#/utils/fetch.js\";\nimport {\n\tPersonalName,\n\ttimestampMillisecondsToPartialDateTime,\n\tWebNovelBook,\n\tWebnovelComAuth,\n\tWebnovelComNovelId,\n} from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport async function webnovelComAPISearch(\n\tq: string,\n\topts: Partial<{\n\t\tpage: number;\n\t\ttype: keyof typeof WEBNOVELCOM_API_SEARCH_TYPE;\n\t\tstatus: keyof typeof WEBNOVELCOM_API_SEARCH_STATUS;\n\t\torderBy: keyof typeof WEBNOVELCOM_API_SEARCH_ORDER;\n\t}> = {},\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelBook[] | null> {\n\tconst u = new URL(\"https://idruid.webnovel.com/app/api/search/result\");\n\tu.searchParams.set(\n\t\t\"bookStatus\",\n\t\tWEBNOVELCOM_API_SEARCH_STATUS[opts.status ?? \"all\"].toString(),\n\t);\n\tu.searchParams.set(\"chapterNum\", \"\");\n\tu.searchParams.set(\"keyword\", q);\n\tu.searchParams.set(\n\t\t\"orderBy\",\n\t\tWEBNOVELCOM_API_SEARCH_ORDER[opts.orderBy ?? \"popular\"].toString(),\n\t);\n\tu.searchParams.set(\"pageIndex\", Math.round(opts.page ?? 1).toString());\n\tu.searchParams.set(\"reqTS\", Math.round(Date.now()).toString());\n\tu.searchParams.set(\n\t\t\"type\",\n\t\tWEBNOVELCOM_API_SEARCH_TYPE[opts.type ?? \"novel\"].toString(),\n\t);\n\n\tconst res = await safeFetch<WebnovelComAPISearchResponse>(\n\t\tu.toString(),\n\t\t\"json\",\n\t\t{\n\t\t\treferrer: \"https://android.webnovel.com\",\n\t\t\tinit: {\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": WEBNOVELCOM_API_USER_AGENT,\n\t\t\t\t\tCookie: webnovelComAPICreateCookies(auth),\n\t\t\t\t\tDeviceTime: timestampMillisecondsToPartialDateTime(\n\t\t\t\t\t\tDate.now(),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst searchResponse = WebnovelComAPISearchResponse.safeParse(res);\n\tif (!searchResponse.success) {\n\t\tconsole.warn(\"Invalid webnovel-com search response from API\");\n\t\tconsole.warn(z.prettifyError(searchResponse.error));\n\t\treturn null;\n\t}\n\n\tconst out: WebNovelBook[] = [];\n\n\tfor (const response of searchResponse.data.Data.BookInfos) {\n\t\tconst book: WebNovelBook = {\n\t\t\tprovider: \"webnovel-com\",\n\t\t\tproviderId: WebnovelComNovelId.parse(response.BookId.toString()),\n\n\t\t\tname: response.BookName,\n\t\t\toverview: response.Description\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text/plain\",\n\t\t\t\t\t\t\tvalue: response.Description,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: [],\n\t\t\tcontributors: [\n\t\t\t\t{\n\t\t\t\t\tname: PersonalName.parse({\n\t\t\t\t\t\tdisplayName: response.Author,\n\t\t\t\t\t}),\n\t\t\t\t\trole: \"aut\",\n\t\t\t\t},\n\t\t\t],\n\t\t\timages: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"book\",\n\t\t\t\t\twidth: 600,\n\t\t\t\t\theight: 800,\n\t\t\t\t\tmediaType: \"image/jpeg\",\n\t\t\t\t\tprovider: \"webnovel-com\",\n\t\t\t\t\tproviderId: response.BookId.toString(),\n\t\t\t\t\tproviderMeta: {\n\t\t\t\t\t\timageId: response.BookCoverID.toString(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t\tchapters: [],\n\t\t\tvolumes: [],\n\t\t\tcreatedAt: null,\n\t\t\tupdatedAt: null,\n\t\t\tstatus:\n\t\t\t\tWEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS[response.Type] ??\n\t\t\t\tnull,\n\t\t\tlanguage: null,\n\t\t\twarnings: response.MTL ? [\"mtl\"] : [],\n\t\t};\n\n\t\tconst parsedBook = WebNovelBook.safeParse(book);\n\t\tif (!parsedBook.success) {\n\t\t\tconsole.warn(\n\t\t\t\t`Could not parse webnovel-com book ${response.BookId.toString()} from search response`,\n\t\t\t);\n\t\t\tconsole.warn(z.prettifyError(parsedBook.error));\n\t\t\tcontinue;\n\t\t}\n\t\tout.push(parsedBook.data);\n\t}\n\n\treturn out;\n}\n","import type { WebnovelComAuth } from \"@webnovel-proxy/core\";\n\nexport function webnovelComHTMLCreateCookies(\n\tauth: WebnovelComAuth | null = null,\n): {\n\tname: string;\n\tvalue: string;\n}[] {\n\tconst out: {\n\t\tname: string;\n\t\tvalue: string;\n\t}[] = [\n\t\t{ name: \"NEXT_LOCALE\", value: \"en\" },\n\t\t{ name: \"webnovel-language\", value: \"en\" },\n\t\t{ name: \"webnovel-content-language\", value: \"en\" },\n\t];\n\n\tif (auth) {\n\t\tout.push(\n\t\t\t{ name: \"ukey\", value: auth.ukey },\n\t\t\t{ name: \"uid\", value: auth.uid },\n\t\t);\n\t}\n\n\treturn out;\n}\n","import { webnovelComHTMLCreateCookies } from \"#/providers/webnovel-com/html/utils.js\";\nimport type { Flaresolverr } from \"#/utils/flaresolverr.js\";\nimport {\n\tNonEmptyString,\n\tWebNovelBook,\n\tWebNovelChapter,\n\tWebnovelComChapterId,\n\tWebnovelComVolumeId,\n\tWebNovelVolume,\n\ttype WebnovelComAuth,\n\ttype WebnovelComNovelId,\n} from \"@webnovel-proxy/core\";\nimport * as cheerio from \"cheerio\";\nimport * as z from \"zod\";\n\nexport async function webnovelComHTMLBookCatalog(\n\tflaresolverr: Flaresolverr,\n\tnovelId: WebnovelComNovelId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<Pick<WebNovelBook, \"chapters\" | \"volumes\"> | null> {\n\tconst res = await flaresolverr.fetch(\n\t\t`https://www.webnovel.com/book/${novelId}/catalog`,\n\t\t{\n\t\t\tcookies: webnovelComHTMLCreateCookies(auth),\n\t\t},\n\t);\n\tif (res.type !== \"success\") {\n\t\tconsole.warn(res);\n\t\treturn null;\n\t}\n\n\tconst $ = cheerio.load(res.result.solution.response);\n\n\tfunction parseVolumeMeta(text: unknown): {\n\t\tnumber: number;\n\t\tname: string | null;\n\t} | null {\n\t\tif (typeof text !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\t\tconst m = /^Volume\\s+([\\d.]+)(?:[\\s\\t]*[:-][\\s\\t]*(.*))?$/.exec(\n\t\t\ttext.trim(),\n\t\t);\n\t\tif (!m) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn (\n\t\t\tz\n\t\t\t\t.object({\n\t\t\t\t\tnumber: z.number().min(0),\n\t\t\t\t\tname: NonEmptyString.nullable(),\n\t\t\t\t})\n\t\t\t\t.safeParse({\n\t\t\t\t\tnumber: Number(m[1]),\n\t\t\t\t\tname: m[2] ?? null,\n\t\t\t\t}).data ?? null\n\t\t);\n\t}\n\n\tfunction parseChapterName(text: unknown): string | null {\n\t\tif (typeof text !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn NonEmptyString.safeParse(text).data ?? null;\n\t}\n\n\tfunction parseChapterNumber(text: unknown): number | null {\n\t\tconst number = z\n\t\t\t.number()\n\t\t\t.min(0)\n\t\t\t// WebNovel does not label special chapters correctly\n\t\t\t.safeParse(Number(NonEmptyString.safeParse(text).data ?? \"0\")).data;\n\n\t\treturn typeof number === \"number\" ? number : null;\n\t}\n\n\tconst out: Pick<WebNovelBook, \"chapters\" | \"volumes\"> = {\n\t\tvolumes: [],\n\t\tchapters: [],\n\t};\n\n\tfor (const volumeContainerElement of $(\n\t\t\".j_catalog_wrap .j_catalog_list .volume-item\",\n\t)) {\n\t\tconst volumeMeta = parseVolumeMeta(\n\t\t\t$(volumeContainerElement).find(\"h4\").first().text(),\n\t\t);\n\t\tconst volumeId = volumeMeta\n\t\t\t? WebnovelComVolumeId.parse(\n\t\t\t\t\t`${novelId}-${volumeMeta.number.toString()}`,\n\t\t\t\t)\n\t\t\t: null;\n\t\tif (volumeMeta && volumeId) {\n\t\t\tconst volume: WebNovelVolume = {\n\t\t\t\tprovider: \"webnovel-com\",\n\t\t\t\tbookProviderId: novelId,\n\t\t\t\tproviderId: volumeId,\n\n\t\t\t\tcreatedAt: null,\n\t\t\t\tupdatedAt: null,\n\t\t\t\timages: [],\n\n\t\t\t\tname: volumeMeta.name,\n\t\t\t\tnumber: volumeMeta.number,\n\t\t\t\torderNumber: null,\n\t\t\t};\n\t\t\tconst parsedVolume = WebNovelVolume.safeParse(volume);\n\t\t\tif (!parsedVolume.success) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"Invalid webnovel-com book catalog volume from HTML\",\n\t\t\t\t);\n\t\t\t\tconsole.warn(z.prettifyError(parsedVolume.error));\n\t\t\t} else {\n\t\t\t\tout.volumes.push(parsedVolume.data);\n\t\t\t}\n\t\t}\n\n\t\tfor (const chapterContainerElement of $(volumeContainerElement).find(\n\t\t\t\"ol.content-list li\",\n\t\t)) {\n\t\t\tconst chapterId =\n\t\t\t\tWebnovelComChapterId.safeParse(\n\t\t\t\t\tchapterContainerElement.attribs[\"data-cid\"],\n\t\t\t\t).data ?? null;\n\t\t\tif (!chapterId) {\n\t\t\t\t// Cannot infer chapter id\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Could not infer chapter id on volume ${volumeId ?? \"unknown\"}`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst chapterName = parseChapterName(\n\t\t\t\t$(chapterContainerElement)\n\t\t\t\t\t.find(\"a div.oh strong\")\n\t\t\t\t\t.first()\n\t\t\t\t\t.text()\n\t\t\t\t\t.trim(),\n\t\t\t);\n\t\t\tconst chapterNumber = parseChapterNumber(\n\t\t\t\t$(chapterContainerElement).find(\"a i\").first().text().trim(),\n\t\t\t);\n\t\t\tif (chapterNumber === null) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Cannot parse chapter number for chapter id ${chapterId}`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst chapter: WebNovelChapter = {\n\t\t\t\tprovider: \"webnovel-com\",\n\t\t\t\tbookProviderId: novelId,\n\t\t\t\tvolumeProviderId: volumeId,\n\t\t\t\tproviderId: chapterId,\n\n\t\t\t\tcreatedAt: null,\n\t\t\t\tupdatedAt: null,\n\t\t\t\tcontent: [],\n\t\t\t\textraContent: [],\n\t\t\t\timages: [],\n\t\t\t\tcontributors: [],\n\n\t\t\t\tisLocked: $(chapterContainerElement).find(\"a > svg\").length > 0,\n\t\t\t\tname: chapterName,\n\t\t\t\tnumber: chapterNumber,\n\t\t\t\torderNumber: null,\n\t\t\t};\n\t\t\tconst parsedChapter = WebNovelChapter.safeParse(chapter);\n\t\t\tif (!parsedChapter.success) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"Invalid webnovel-com book catalog chapter from HTML\",\n\t\t\t\t);\n\t\t\t\tconsole.warn(z.prettifyError(parsedChapter.error));\n\t\t\t} else {\n\t\t\t\tout.chapters.push(parsedChapter.data);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst outParsed = WebNovelBook.pick({\n\t\tvolumes: true,\n\t\tchapters: true,\n\t}).safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid webnovel-com book catalog from HTML\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\treturn outParsed.data;\n}\n","import { webnovelComHTMLCreateCookies } from \"#/providers/webnovel-com/html/utils.js\";\nimport type { Flaresolverr } from \"#/utils/flaresolverr.js\";\nimport { convertHTMLtoMarkdown, sanitizeHTML } from \"#/utils/string.js\";\nimport {\n\tNonEmptyString,\n\tWebNovelChapter,\n\tWebNovelChapterContent,\n\tWebnovelComChapterId,\n\tWebNovelExtraChapterContent,\n\ttype WebnovelComAuth,\n\ttype WebnovelComNovelId,\n} from \"@webnovel-proxy/core\";\nimport * as cheerio from \"cheerio\";\nimport * as z from \"zod\";\n\nexport async function webnovelComHTMLChapter(\n\tflaresolverr: Flaresolverr,\n\tnovelId: WebnovelComNovelId,\n\tchapterId: WebnovelComChapterId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelChapter | null> {\n\tconst res = await flaresolverr.fetch(\n\t\t`https://www.webnovel.com/book/${novelId}/${chapterId}`,\n\t\t{\n\t\t\tcookies: webnovelComHTMLCreateCookies(auth),\n\t\t},\n\t);\n\tif (res.type !== \"success\") {\n\t\tconsole.warn(res);\n\t\treturn null;\n\t}\n\n\tif (\n\t\tres.result.solution.response.includes(\n\t\t\t\"//www.yueimg.com/en/images/404\",\n\t\t) ||\n\t\tres.result.solution.response.includes(\n\t\t\t\"We might have some troubles to find out this page.\",\n\t\t)\n\t) {\n\t\treturn null;\n\t}\n\n\tconst $ = cheerio.load(res.result.solution.response);\n\n\tfunction parseChapterMeta(\n\t\ttextIdx: string,\n\t\ttextName: string,\n\t): { number: number; name: string | null } | null {\n\t\tconst number =\n\t\t\ttextIdx.trim() === \"\"\n\t\t\t\t? 0\n\t\t\t\t: (z\n\t\t\t\t\t\t.number()\n\t\t\t\t\t\t.min(0)\n\t\t\t\t\t\t.safeParse(\n\t\t\t\t\t\t\tNumber(\n\t\t\t\t\t\t\t\t/^Chapter\\s+([\\d.]+)/.exec(\n\t\t\t\t\t\t\t\t\ttextIdx.trim(),\n\t\t\t\t\t\t\t\t)?.[1] ?? \"not a number\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t).data ?? null);\n\t\tif (typeof number !== \"number\") {\n\t\t\treturn null;\n\t\t}\n\t\tconst name = NonEmptyString.safeParse(textName.trim()).data ?? null;\n\n\t\treturn { number, name };\n\t}\n\n\tfunction parseChapterParagraphMeta(\n\t\trawAttributeValue: unknown,\n\t): { paragraphId: string; paragraphIndex: number } | null {\n\t\tif (typeof rawAttributeValue !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst data =\n\t\t\tz\n\t\t\t\t.object({\n\t\t\t\t\tparagraphId: NonEmptyString,\n\t\t\t\t\tchapterId: NonEmptyString,\n\t\t\t\t\tparaIndex: z.int(),\n\t\t\t\t})\n\t\t\t\t.safeParse(JSON.parse(rawAttributeValue)).data ?? null;\n\t\tif (!data) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst paragraphId = NonEmptyString.pipe(\n\t\t\t\tz.string().regex(/^[a-f0-9]{32}$/),\n\t\t\t).parse(data.paragraphId);\n\t\t\tconst paragraphIndex = z.int().min(0).parse(data.paraIndex);\n\n\t\t\treturn { paragraphId, paragraphIndex };\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tconst chapterMeta = parseChapterMeta(\n\t\t$(\"header span.j_chapIdx\").text(),\n\t\t$(\"header span.j_chapName\").text(),\n\t);\n\tif (!chapterMeta) {\n\t\tconsole.warn(\n\t\t\t`Could not parse chapter meta from chapter id ${chapterId}`,\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst chapterContainerElement = $(\n\t\t`#page .j_contentWrap .chapter_content[data-cid=\"${chapterId}\"]`,\n\t).first();\n\n\tconst out: WebNovelChapter = {\n\t\tprovider: \"webnovel-com\",\n\t\tbookProviderId: novelId,\n\t\tvolumeProviderId: null,\n\t\tproviderId: chapterId,\n\n\t\tcontributors: [],\n\t\tcreatedAt: null,\n\t\tupdatedAt: null,\n\t\timages: [],\n\n\t\tname: chapterMeta.name,\n\t\tnumber: chapterMeta.number,\n\t\torderNumber: null,\n\t\tcontent: [],\n\t\textraContent: [],\n\t\tisLocked:\n\t\t\tz\n\t\t\t\t.stringbool()\n\t\t\t\t.nullable()\n\t\t\t\t.optional()\n\t\t\t\t.safeParse(chapterContainerElement.attr(\"data-islock\")).data ??\n\t\t\tnull,\n\t};\n\n\tconst outParsed = WebNovelChapter.safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid webnovel-com chapter from HTML\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\tif (!outParsed.data.isLocked) {\n\t\tconst paragraphs: string[] = [];\n\n\t\tfor (const paragraphContainerElement of chapterContainerElement.find(\n\t\t\t\".cha-content > .cha-words > .cha-paragraph\",\n\t\t)) {\n\t\t\tconst paragraphElement = $(paragraphContainerElement).find(\n\t\t\t\t\".dib > p\",\n\t\t\t);\n\t\t\tconst paragraphMeta = parseChapterParagraphMeta(\n\t\t\t\tparagraphContainerElement.attribs[\"data-ejs\"],\n\t\t\t);\n\t\t\tif (paragraphMeta) {\n\t\t\t\tparagraphElement.attr(\n\t\t\t\t\t\"data-paragraph-id\",\n\t\t\t\t\tparagraphMeta.paragraphId,\n\t\t\t\t);\n\t\t\t\tparagraphElement.attr(\n\t\t\t\t\t\"data-paragraph-idx\",\n\t\t\t\t\tparagraphMeta.paragraphIndex.toString(),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst innerHTML = paragraphElement.html();\n\t\t\tif (innerHTML !== null) {\n\t\t\t\tparagraphElement.html(innerHTML.trim());\n\t\t\t}\n\n\t\t\tconst outerHTML = paragraphElement.prop(\"outerHTML\");\n\t\t\tif (!outerHTML || outerHTML.trim() === \"\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tparagraphs.push(outerHTML);\n\t\t}\n\n\t\tif (paragraphs.length > 0) {\n\t\t\tconst fullHTML = sanitizeHTML(paragraphs.join(\"\\n\"));\n\t\t\tconst outHTML = WebNovelChapterContent.parse({\n\t\t\t\ttype: \"text/html\",\n\t\t\t\tvalue: fullHTML,\n\t\t\t\tsize: Buffer.byteLength(fullHTML, \"utf-8\"),\n\t\t\t} satisfies WebNovelChapterContent);\n\t\t\toutParsed.data.content.push(outHTML);\n\n\t\t\tconst fullMarkdown = convertHTMLtoMarkdown(fullHTML);\n\t\t\tconst outMarkdown = WebNovelChapterContent.parse({\n\t\t\t\ttype: \"text/markdown\",\n\t\t\t\tvalue: fullMarkdown,\n\t\t\t\tsize: Buffer.byteLength(fullMarkdown, \"utf-8\"),\n\t\t\t} satisfies WebNovelChapterContent);\n\t\t\toutParsed.data.content.push(outMarkdown);\n\t\t}\n\t}\n\n\tconst creatorsThoughtsContainerElement = $(\n\t\t\"#page .cha-content .m-thou\",\n\t).first();\n\tif (creatorsThoughtsContainerElement.hasClass(\"m-thou\")) {\n\t\tconst paragraphs: string[] = [];\n\n\t\tfor (const creatorsThoughtsParagraph of creatorsThoughtsContainerElement.find(\n\t\t\t\"p.creators-thought\",\n\t\t)) {\n\t\t\tconst el = $(creatorsThoughtsParagraph);\n\n\t\t\tel.attr(\"class\", null);\n\n\t\t\tconst innerHTML = el.html();\n\t\t\tif (innerHTML !== null) {\n\t\t\t\tel.html(innerHTML.trim());\n\t\t\t}\n\n\t\t\tconst outerHTML = el.prop(\"outerHTML\");\n\t\t\tif (!outerHTML || outerHTML.trim() === \"\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tparagraphs.push(outerHTML);\n\t\t}\n\n\t\tif (paragraphs.length > 0) {\n\t\t\tconst fullHTML = sanitizeHTML(paragraphs.join(\"\\n\"));\n\t\t\tconst outHTML = WebNovelExtraChapterContent.parse({\n\t\t\t\ttype: \"text/html\",\n\t\t\t\textraType: \"postNote\",\n\t\t\t\tvalue: fullHTML,\n\t\t\t\tsize: Buffer.byteLength(fullHTML, \"utf-8\"),\n\t\t\t} satisfies WebNovelExtraChapterContent);\n\t\t\toutParsed.data.extraContent.push(outHTML);\n\n\t\t\tconst fullMarkdown = convertHTMLtoMarkdown(fullHTML);\n\t\t\tconst outMarkdown = WebNovelExtraChapterContent.parse({\n\t\t\t\ttype: \"text/markdown\",\n\t\t\t\textraType: \"postNote\",\n\t\t\t\tvalue: fullMarkdown,\n\t\t\t\tsize: Buffer.byteLength(fullMarkdown, \"utf-8\"),\n\t\t\t} satisfies WebNovelExtraChapterContent);\n\t\t\toutParsed.data.extraContent.push(outMarkdown);\n\t\t}\n\t}\n\n\treturn outParsed.data;\n}\n","import { webnovelComAPIBook } from \"#/providers/webnovel-com/api/book.js\";\nimport { webnovelComAPISearch } from \"#/providers/webnovel-com/api/search.js\";\nimport { webnovelComHTMLBookCatalog } from \"#/providers/webnovel-com/html/book.js\";\nimport { webnovelComHTMLChapter } from \"#/providers/webnovel-com/html/chapter.js\";\nimport type { Flaresolverr } from \"#/utils/flaresolverr.js\";\nimport type {\n\tWebNovelBook,\n\tWebNovelChapter,\n\tWebnovelComAuth,\n\tWebnovelComChapterId,\n\tWebnovelComNovelId,\n} from \"@webnovel-proxy/core\";\n\nexport async function webnovelComSearch(\n\tq: string,\n\topts: Partial<{ page: number }> = {},\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelBook[] | null> {\n\treturn await webnovelComAPISearch(q, opts, auth);\n}\n\nexport async function webnovelComGetBook(\n\tflaresolverr: Flaresolverr,\n\tnovelId: WebnovelComNovelId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelBook | null> {\n\tconst api = await webnovelComAPIBook(novelId, auth);\n\tif (!api) return null;\n\n\tconst html = await webnovelComHTMLBookCatalog(flaresolverr, novelId, auth);\n\n\treturn {\n\t\t...api,\n\t\t...html,\n\t};\n}\n\nexport async function webnovelComGetChapter(\n\tflaresolverr: Flaresolverr,\n\tnovelId: WebnovelComNovelId,\n\tchapterId: WebnovelComChapterId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelChapter | null> {\n\treturn await webnovelComHTMLChapter(flaresolverr, novelId, chapterId, auth);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["z","gzip","gzipRaw","gunzip","gunzipRaw","z","z","z","z","z","z","z","JSONbig","z","z$1","DOMPurify","TurndownService","cheerio","hasProcessEnv","isDev","isDebug","defaultHTMLHeaders","safeFetch","headers","includeDefaultHTMLHeaders","init","key","value","referrer","requestUrl","url","response","outputType","error","FlaresolverrFetchResponseCommon","NullableNonEmptyString","NonEmptyString","FlaresolverrFetchResponseCookie","FlaresolverrFetchResponseSolution","gzip$1","promisify","gzip","gunzip$1","gunzip","devCachePath","id","hex","sha1","utf8","normalize","resolve","join","devSetCache","mkdir","dirname","compressedValue","compression","writeFile","devGetCache","existsSync","readFile","decompressedValue","Flaresolverr","#host","host","#hostUsername","hostUsername","#hostPassword","hostPassword","cookies","flaresolverrHeaders","base64","cachedResponse","res","fetchImpl","parsedData","turndownService","convertHTMLtoMarkdown","safeHTML","sanitizeHTML","input","ROYALROAD_API_USER_AGENT","ROYALROAD_API_APP_VERSION","ROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS","RoyalroadAPIFictionStatus","RoyalroadAPISearchResponse","RoyalroadAPIFiction","RoyalroadAPIFictionChapter","royalroadAPIFiction","opts","resHeaders","novelId","parsedResponse","img","providerImageURLToWebNovelImage","out","RoyalroadNovelId","PersonalName","chapter","webNovelChapter","RoyalroadVolumeId","RoyalroadChapterId","isoDateTimeToPartialDateTime","parsedChapter","WebNovelChapter","volume","webNovelVolume","parsedVolume","WebNovelVolume","outParsed","WebNovelBook","royalroadAPIFictionChapter","chapterId","book","bookChapter","sanitizedContent","markdownContent","sanitizedNote","markdownNote","royalroadAPISearch","q","searchResponse","parsedBook","royalroadGetBook","royalroadNovelId","royalroadGetVolume","royalroadVolumeId","royalroadGetChapter","royalroadChapterId","royalroadSearch","webnovelComAPICreateCookies","auth","WEBNOVELCOM_API_SEARCH_TYPE","WEBNOVELCOM_API_SEARCH_STATUS","WEBNOVELCOM_API_SEARCH_ORDER","WEBNOVELCOM_API_USER_AGENT","WEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS","WEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS","WebnovelComAPISearchResponse","WebnovelComAPIBookResponse","BCP47LanguageTag","webnovelComAPIBook","u","timestampMillisecondsToPartialDateTime","WebnovelComNovelId","translator","editor","webnovelComAPISearch","webnovelComHTMLCreateCookies","webnovelComHTMLBookCatalog","flaresolverr","$","parseVolumeMeta","text","m","parseChapterName","parseChapterNumber","number","volumeContainerElement","volumeMeta","volumeId","WebnovelComVolumeId","chapterContainerElement","WebnovelComChapterId","chapterName","chapterNumber","webnovelComHTMLChapter","parseChapterMeta","textIdx","textName","parseChapterParagraphMeta","rawAttributeValue","data","chapterMeta","paragraphs","paragraphContainerElement","paragraphElement","paragraphMeta","innerHTML","outerHTML","fullHTML","outHTML","WebNovelChapterContent","fullMarkdown","outMarkdown","creatorsThoughtsContainerElement","creatorsThoughtsParagraph","el","WebNovelExtraChapterContent","webnovelComSearch","webnovelComGetBook","api","html","webnovelComGetChapter"],"sources":["../src/utils/env.ts","../src/utils/fetch.ts","../src/schemas/flaresolverr.ts","../src/utils/dev.ts","../src/utils/flaresolverr.ts","../src/utils/string.ts","../src/providers/royalroad/constants.ts","../src/schemas/providers/royalroad/api.ts","../src/providers/royalroad/api/fiction.ts","../src/providers/royalroad/api/search.ts","../src/providers/royalroad/api.ts","../src/providers/webnovel-com/api/utils.ts","../src/providers/webnovel-com/constants.ts","../src/schemas/providers/webnovel-com/api.ts","../src/providers/webnovel-com/api/book.ts","../src/providers/webnovel-com/api/search.ts","../src/providers/webnovel-com/html/utils.ts","../src/providers/webnovel-com/html/book.ts","../src/providers/webnovel-com/html/chapter.ts","../src/providers/webnovel-com/api.ts"],"sourcesContent":["function hasProcessEnv(): boolean {\n\treturn typeof process === \"object\" && typeof process.env === \"object\";\n}\n\n/**\n * @throws When environment variables cannot be accessed via `process.env`\n */\nexport function hasProcessEnvOrThrow(): void {\n\tif (!hasProcessEnv()) {\n\t\tthrow new Error(`Could not parse environment variables`);\n\t}\n}\n\nexport function isDev(): boolean {\n\tif (!hasProcessEnv()) return false;\n\treturn (\n\t\ttypeof process.env.DEV === \"string\" &&\n\t\t(process.env.DEV === \"true\" || process.env.DEV === \"1\")\n\t);\n}\n\nexport function isDebug(): boolean {\n\tif (isDev()) return true;\n\tif (!hasProcessEnv()) return false;\n\treturn (\n\t\ttypeof process.env.DEBUG === \"string\" &&\n\t\t(process.env.DEBUG === \"true\" || process.env.DEBUG === \"1\")\n\t);\n}\n","import { isDebug } from \"#/utils/env.js\";\nimport JSONbig from \"json-bigint\";\n\nconst defaultHTMLHeaders: Record<string, string> = {\n\t\"User-Agent\":\n\t\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36\",\n\tAccept: \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\n\t\"Accept-Language\": \"en-US,en;q=0.9\",\n\t\"sec-ch-ua-platform\": '\"Windows\"',\n\t\"sec-ch-ua-mobile\": \"?0\",\n\t\"sec-ch-ua\":\n\t\t'\"Chromium\";v=\"142\", \"Google Chrome\";v=\"142\", \"Not_A Brand\";v=\"99\"',\n\t\"sec-fetch-dest\": \"document\",\n\t\"sec-fetch-user\": \"?1\",\n\t\"sec-fetch-mode\": \"navigate\",\n\t\"sec-fetch-site\": \"none\",\n};\n\ninterface SafeFetchConfig {\n\treferrer?: string | URL | null;\n\tinit?: Omit<RequestInit, \"headers\"> & {\n\t\theaders?: HeadersInit;\n\t};\n\tincludeDefaultHTMLHeaders?: boolean;\n}\n\nexport async function safeFetch<T = unknown>(\n\turl: string | URL,\n\toutputType: \"text\" | \"json\",\n\t{ referrer, init, includeDefaultHTMLHeaders }: SafeFetchConfig = {},\n): Promise<T | null> {\n\tconst headers = new Headers(\n\t\tincludeDefaultHTMLHeaders ? defaultHTMLHeaders : {},\n\t);\n\n\tif (init?.headers) {\n\t\tnew Headers(init.headers).forEach((value, key) => {\n\t\t\theaders.set(key, value);\n\t\t});\n\t}\n\n\t// The correct HTTP header is \"Referer\", not \"Referrer\".\n\tif (referrer != null) {\n\t\theaders.set(\"Referer\", String(referrer));\n\t}\n\n\tconst requestUrl = String(url);\n\n\ttry {\n\t\tconst response = await fetch(requestUrl, {\n\t\t\t...init,\n\t\t\tredirect: init?.redirect ?? \"follow\",\n\t\t\theaders,\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tif (isDebug()) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[safeFetch]\",\n\t\t\t\t\tresponse.url,\n\t\t\t\t\tresponse.status,\n\t\t\t\t\tresponse.statusText,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tif (outputType === \"json\") {\n\t\t\t// Special handling for webnovel-com non-safe integer ids\n\t\t\treturn JSONbig({ storeAsString: true }).parse(\n\t\t\t\tawait response.text(),\n\t\t\t) as T;\n\t\t} else {\n\t\t\treturn (await response.text()) as T;\n\t\t}\n\t} catch (error) {\n\t\tif (isDebug()) {\n\t\t\tconsole.error(\"[safeFetch]\", error);\n\t\t}\n\t\treturn null;\n\t}\n}\n","import { NonEmptyString, NullableNonEmptyString } from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport const FlaresolverrFetchResponseCommon = z.object({\n\tmessage: NullableNonEmptyString,\n\tstartTimestamp: z.int(),\n\tendTimestamp: z.int(),\n\tversion: NonEmptyString,\n});\nexport type FlaresolverrFetchResponseCommon = z.infer<\n\ttypeof FlaresolverrFetchResponseCommon\n>;\n\nexport const FlaresolverrRequestCookie = z.object({\n\tname: NonEmptyString,\n\tvalue: NonEmptyString,\n});\nexport type FlaresolverrRequestCookie = z.infer<\n\ttypeof FlaresolverrRequestCookie\n>;\n\nexport const FlaresolverrFetchResponseCookie = z.object({\n\tname: NonEmptyString,\n\tvalue: z.string(),\n\tdomain: NonEmptyString,\n\tpath: NonEmptyString,\n\texpiry: z.number().optional(),\n\texpires: z.number().optional(),\n\thttpOnly: z.boolean(),\n\tsecure: z.boolean(),\n\tsameSite: z.enum([\"None\", \"Lax\", \"Strict\"]),\n});\nexport type FlaresolverrFetchResponseCookie = z.infer<\n\ttypeof FlaresolverrFetchResponseCookie\n>;\n\nexport const FlaresolverrFetchResponseSolution = z.object({\n\turl: z.url(),\n\tstatus: z.int().min(100).max(599),\n\theaders: z.record(NonEmptyString, z.string()),\n\tresponse: NonEmptyString,\n\tuserAgent: NonEmptyString,\n\tturnstile_token: NonEmptyString.optional().nullable(),\n\tcookies: z.array(FlaresolverrFetchResponseCookie),\n});\nexport type FlaresolverrFetchResponseSolution = z.infer<\n\ttypeof FlaresolverrFetchResponseSolution\n>;\n\nexport const FlaresolverrFetchResponse = z.discriminatedUnion(\"type\", [\n\tz.object({\n\t\ttype: z.literal(\"success\"),\n\t\tstatus: z.int().min(100).max(599),\n\t\tresult: z.object({\n\t\t\t...FlaresolverrFetchResponseCommon.shape,\n\t\t\tsolution: FlaresolverrFetchResponseSolution,\n\t\t}),\n\t}),\n\tz.object({\n\t\ttype: z.literal(\"fail\"),\n\t\tstatus: z.int().min(100).max(599),\n\t\tresult: z.object({\n\t\t\t...FlaresolverrFetchResponseCommon.shape,\n\t\t\terror: z.object({\n\t\t\t\tmessage: NonEmptyString,\n\t\t\t\tdetail: z.unknown(),\n\t\t\t}),\n\t\t}),\n\t}),\n\tz.object({\n\t\ttype: z.literal(\"error\"),\n\t\terror: z.object({\n\t\t\tmessage: NonEmptyString,\n\t\t\tdetail: z.unknown().optional(),\n\t\t}),\n\t}),\n]);\nexport type FlaresolverrFetchResponse = z.infer<\n\ttypeof FlaresolverrFetchResponse\n>;\n","import { isDev } from \"#/utils/env.js\";\nimport { sha1 } from \"@noble/hashes/legacy.js\";\nimport { hex, utf8 } from \"@scure/base\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, normalize, resolve } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { gunzip as gunzipRaw, gzip as gzipRaw } from \"node:zlib\";\n\nconst gzip = promisify(gzipRaw);\nconst gunzip = promisify(gunzipRaw);\n\nfunction devCachePath(key: string[]): string {\n\tconst id = hex.encode(sha1(utf8.decode(key.join(\"\\u001F\"))));\n\n\treturn normalize(\n\t\tresolve(\n\t\t\tjoin(\n\t\t\t\tprocess.cwd(),\n\t\t\t\t\".cache\",\n\t\t\t\t\"dev-cache\",\n\t\t\t\tid.substring(0, 2),\n\t\t\t\tid + \".gz\",\n\t\t\t),\n\t\t),\n\t);\n}\n\nexport async function devSetCache(\n\tkey: string[],\n\tvalue: string,\n\tcompression = true,\n): Promise<{ path: string } | null> {\n\tif (isDev()) {\n\t\tawait mkdir(dirname(devCachePath(key)), {\n\t\t\trecursive: true,\n\t\t\tmode: 0o770,\n\t\t});\n\n\t\tconst compressedValue = compression ? await gzip(value) : value;\n\n\t\tawait writeFile(devCachePath(key), compressedValue, {\n\t\t\tmode: 0o770,\n\t\t});\n\t\treturn { path: devCachePath(key) };\n\t}\n\n\treturn null;\n}\n\nexport async function devGetCache(\n\tkey: string[],\n\tcompression = true,\n): Promise<{ path: string; value: string } | null> {\n\tif (isDev()) {\n\t\tif (!existsSync(devCachePath(key))) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst value = await readFile(devCachePath(key), {\n\t\t\tencoding: compression ? undefined : \"utf8\",\n\t\t});\n\t\tconst decompressedValue = compression\n\t\t\t? (await gunzip(value)).toString(\"utf8\")\n\t\t\t: (value as string);\n\t\tif (value && decompressedValue) {\n\t\t\treturn {\n\t\t\t\tpath: devCachePath(key),\n\t\t\t\tvalue: decompressedValue,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn null;\n}\n","import {\n\tFlaresolverrFetchResponseCommon,\n\tFlaresolverrFetchResponseSolution,\n\ttype FlaresolverrFetchResponse,\n\ttype FlaresolverrRequestCookie,\n} from \"#/schemas/flaresolverr.js\";\nimport { devGetCache, devSetCache } from \"#/utils/dev.js\";\nimport { isDebug, isDev } from \"#/utils/env.js\";\nimport { base64, utf8 } from \"@scure/base\";\nimport z from \"zod\";\n\nexport class Flaresolverr {\n\t#host = \"https://flaresolverr.internal\";\n\t#hostUsername: string | null = null;\n\t#hostPassword: string | null = null;\n\n\tconstructor({\n\t\thost,\n\t\thostUsername,\n\t\thostPassword,\n\t}: {\n\t\thost: string;\n\t\thostUsername?: string | null | undefined;\n\t\thostPassword?: string | null | undefined;\n\t}) {\n\t\tthis.#host = host;\n\t\tthis.#hostUsername = hostUsername ?? null;\n\t\tthis.#hostPassword = hostPassword ?? null;\n\t}\n\n\tasync fetch(\n\t\turl: string | URL,\n\t\tinit: Partial<{\n\t\t\tmethod: \"GET\" | \"POST\";\n\t\t\theaders: HeadersInit;\n\t\t\tcookies: FlaresolverrRequestCookie[];\n\t\t\tmaxTimeout: number;\n\t\t\tpayload:\n\t\t\t\tRecord<string, string> | URLSearchParams | [string, string][];\n\t\t}> = {},\n\t\tfetchImpl = fetch,\n\t): Promise<FlaresolverrFetchResponse> {\n\t\t// TODO when supported const headers = new Headers(init.headers);\n\t\t// TODO cookie jar\n\t\tconst cookies = init?.cookies ?? [];\n\n\t\tconst flaresolverrHeaders = new Headers({\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\"X-Cache-Control\": \"no-store\",\n\t\t});\n\t\tif (this.#hostUsername) {\n\t\t\tflaresolverrHeaders.set(\n\t\t\t\t\"Authorization\",\n\t\t\t\t\"Basic \" +\n\t\t\t\t\tbase64.encode(\n\t\t\t\t\t\tutf8.decode(\n\t\t\t\t\t\t\t`${this.#hostUsername}:${this.#hostPassword ?? \"\"}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (isDev()) {\n\t\t\tconst cachedResponse = await devGetCache([\n\t\t\t\t\"flaresolverr\",\n\t\t\t\turl.toString(),\n\t\t\t]);\n\t\t\tif (cachedResponse) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t\"[flaresolverr]\",\n\t\t\t\t\t`Retrieved cached response for ${url.toString()} : ${cachedResponse.path}`,\n\t\t\t\t);\n\t\t\t\treturn JSON.parse(\n\t\t\t\t\tcachedResponse.value,\n\t\t\t\t) as FlaresolverrFetchResponse;\n\t\t\t}\n\t\t}\n\n\t\tlet res: Response;\n\t\ttry {\n\t\t\tif (isDebug()) {\n\t\t\t\tconsole.log(\"[flaresolverr]\", `Fetching ${url}`);\n\t\t\t\tconsole.time(\"[flaresolverr] Timing\");\n\t\t\t}\n\t\t\tres = await fetchImpl(new URL(\"/v1\", this.#host), {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: flaresolverrHeaders,\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tcmd: `request.${(init.method ?? \"GET\").toLocaleLowerCase()}`,\n\t\t\t\t\turl: url.toString(),\n\t\t\t\t\tcookies,\n\t\t\t\t\t// TODO add headers when supported\n\t\t\t\t\tpostData: init.payload\n\t\t\t\t\t\t? new URLSearchParams(init.payload)\n\t\t\t\t\t\t: undefined,\n\n\t\t\t\t\tdisableMedia: true,\n\t\t\t\t\tmaxTimeout: init.maxTimeout ?? undefined,\n\t\t\t\t}),\n\t\t\t});\n\t\t\tif (isDebug()) {\n\t\t\t\tconsole.timeEnd(\"[flaresolverr] Timing\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.timeEnd(\"[flaresolverr] Timing\");\n\n\t\t\tif (error instanceof Error) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t\terror,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\terror: {\n\t\t\t\t\tmessage: \"Could not connect to flaresolverr\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (res.headers.get(\"Content-Type\") !== \"application/json\") {\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\terror: {\n\t\t\t\t\tmessage: \"Flaresolverr returned a non-JSON Content-Type\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (!res.ok) {\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\terror: {\n\t\t\t\t\tmessage: `Flaresolverr response not ok (${res.status.toString()})`,\n\t\t\t\t\tdetail: await res.json(),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst parsedData = z\n\t\t\t.object({\n\t\t\t\t...FlaresolverrFetchResponseCommon.shape,\n\t\t\t\tsolution: FlaresolverrFetchResponseSolution,\n\t\t\t})\n\t\t\t.safeParse((await res.json()) as unknown);\n\t\tif (!parsedData.success) {\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\terror: {\n\t\t\t\t\tmessage: \"Malformed flaresolverr response\",\n\t\t\t\t\tdetail: z.prettifyError(parsedData.error),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (isDev()) {\n\t\t\tawait devSetCache(\n\t\t\t\t[\"flaresolverr\", url.toString()],\n\t\t\t\tJSON.stringify({\n\t\t\t\t\ttype: \"success\",\n\t\t\t\t\tstatus: res.status,\n\t\t\t\t\tresult: parsedData.data,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\ttype: \"success\",\n\t\t\tstatus: res.status,\n\t\t\tresult: parsedData.data,\n\t\t};\n\t}\n}\n","import DOMPurify from \"isomorphic-dompurify\";\nimport TurndownService from \"turndown\";\n\nconst turndownService = new TurndownService({\n\theadingStyle: \"atx\",\n\temDelimiter: \"_\",\n\tstrongDelimiter: \"**\",\n});\n\nexport function convertHTMLtoMarkdown(safeHTML: string): string {\n\treturn turndownService.turndown(safeHTML);\n}\n\nexport function sanitizeHTML(input: string): string {\n\treturn DOMPurify.sanitize(input, { USE_PROFILES: { html: true } });\n}\n","import type { WebNovelBookStatus } from \"@webnovel-proxy/core\";\nimport type { RoyalroadAPIFictionStatus } from \"#/schemas/providers/royalroad/api.js\";\n\nexport const ROYALROAD_API_USER_AGENT =\n\t\"Royal Road Mobile/2.60.1186 ( Android; 16; Arm; Phone ) MAUI/10.0.10\";\nexport const ROYALROAD_API_APP_VERSION = \"2.60.1186\";\n\nexport const ROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS: Record<\n\tRoyalroadAPIFictionStatus,\n\tWebNovelBookStatus | null\n> = {\n\tCompleted: \"completed\",\n\tDropped: \"dropped\",\n\tHiatus: \"hiatus\",\n\tInactive: null,\n\tOngoing: \"ongoing\",\n\tStub: null,\n};\n","import { NonEmptyString, NullableNonEmptyString } from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport const RoyalroadAPIFictionStatus = z.enum([\n\t\"Completed\",\n\t\"Dropped\",\n\t\"Ongoing\",\n\t\"Stub\",\n\t\"Hiatus\",\n\t\"Inactive\",\n]);\nexport type RoyalroadAPIFictionStatus = z.infer<\n\ttypeof RoyalroadAPIFictionStatus\n>;\n\nexport const RoyalroadAPISearchResponse = z.object({\n\tdata: z.array(\n\t\tz.object({\n\t\t\tid: z.int().min(1),\n\n\t\t\ttitle: NonEmptyString,\n\t\t\tdescription: NullableNonEmptyString.nullable(),\n\t\t\tcover: NullableNonEmptyString.nullable(),\n\n\t\t\tfirstUpdate: z.iso.datetime(),\n\t\t\tlastUpdate: z.iso.datetime(),\n\n\t\t\tauthorId: z.int().min(1),\n\t\t\tauthorName: NonEmptyString,\n\n\t\t\tstatus: RoyalroadAPIFictionStatus,\n\t\t}),\n\t),\n\tpaginate: z.object({\n\t\ttotal: z.int().min(1),\n\t\tperPage: z.int().min(1),\n\t\tpage: z.int().min(1),\n\t}),\n});\nexport type RoyalroadAPISearchResponse = z.infer<\n\ttypeof RoyalroadAPISearchResponse\n>;\n\nexport const RoyalroadAPIFiction = z.object({\n\tid: z.int().min(1),\n\ttitle: NonEmptyString,\n\tslug: NonEmptyString,\n\tdescription: NullableNonEmptyString.nullable(),\n\n\tcover: z.url().nullable(),\n\tthumbnailHash: NonEmptyString.nullable(),\n\n\tstatus: RoyalroadAPIFictionStatus,\n\n\tcoverBackground: NonEmptyString.nullable(),\n\n\tauthorInfo: z.object({\n\t\tuserId: z.int().min(1),\n\t\tusername: NonEmptyString,\n\t\tavatar: z.url(),\n\t}),\n\n\tvolumes: z.array(\n\t\tz.object({\n\t\t\tid: z.int().min(1),\n\t\t\ttitle: NonEmptyString,\n\t\t\tcover: z.url().nullable(),\n\t\t\torder: z.int().min(1),\n\t\t}),\n\t),\n\n\tchapters: z.array(\n\t\tz.object({\n\t\t\tid: z.int().min(1),\n\t\t\ttitle: NonEmptyString,\n\t\t\tpublished: z.iso.datetime(),\n\t\t\tlastEdit: z.iso.datetime().optional().nullable(),\n\t\t\tvisible: z.boolean(),\n\t\t\torder: z.int().min(0),\n\t\t\tvolumeId: z.int().min(1).optional().nullable(),\n\t\t\tisUnlocked: z.boolean(),\n\t\t}),\n\t),\n\n\tcollaborators: z.array(z.unknown()),\n});\nexport type RoyalroadAPIFiction = z.infer<typeof RoyalroadAPIFiction>;\n\nexport const RoyalroadAPIFictionChapter = z.object({\n\tpreAuthorsNote: NullableNonEmptyString.nullable(),\n\tpostAuthorsNote: NullableNonEmptyString.nullable(),\n\tcontent: NonEmptyString,\n\tfictionInfo: z.object({\n\t\tid: z.int().min(1),\n\t}),\n\tid: z.int().min(1),\n\ttitle: NonEmptyString,\n\tpublished: z.iso.datetime(),\n\tlastEdit: z.iso.datetime().optional().nullable(),\n\tisUnlocked: z.boolean(),\n\torder: z.int().min(0),\n});\nexport type RoyalroadAPIFictionChapter = z.infer<\n\ttypeof RoyalroadAPIFictionChapter\n>;\n","import {\n\tROYALROAD_API_APP_VERSION,\n\tROYALROAD_API_USER_AGENT,\n\tROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS,\n} from \"#/providers/royalroad/constants.js\";\nimport {\n\tRoyalroadAPIFiction,\n\tRoyalroadAPIFictionChapter,\n} from \"#/schemas/providers/royalroad/api.js\";\nimport { safeFetch } from \"#/utils/fetch.js\";\nimport { convertHTMLtoMarkdown, sanitizeHTML } from \"#/utils/string.js\";\nimport {\n\tisoDateTimeToPartialDateTime,\n\tPersonalName,\n\tproviderImageURLToWebNovelImage,\n\tRoyalroadChapterId,\n\tRoyalroadNovelId,\n\tRoyalroadVolumeId,\n\tWebNovelBook,\n\tWebNovelChapter,\n\tWebNovelVolume,\n} from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport async function royalroadAPIFiction(\n\tnovelId: RoyalroadNovelId,\n\topts: Partial<{ bustCache: boolean }> = {},\n): Promise<WebNovelBook | null> {\n\topts = {\n\t\tbustCache: true,\n\t\t...opts,\n\t};\n\tconst resHeaders: Record<string, string> = {\n\t\tAccept: \"application/json\",\n\t\tCustomUserAgent: ROYALROAD_API_USER_AGENT,\n\t\t\"X-Mature-Content\": \"true\",\n\t\t\"X-Govt-Age-Restrict-Mature-Content\": \"false\",\n\t\t\"X-App-Version\": ROYALROAD_API_APP_VERSION,\n\t};\n\tif (opts.bustCache) {\n\t\tresHeaders[\"Cache-Control\"] = \"no-cache\";\n\t}\n\tconst res = await safeFetch<RoyalroadAPIFiction>(\n\t\t`https://api.royalroad.com/v1/fiction/${novelId}`,\n\t\t\"json\",\n\t\t{\n\t\t\tinit: {\n\t\t\t\theaders: resHeaders,\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst parsedResponse = RoyalroadAPIFiction.safeParse(res);\n\tif (!parsedResponse.success) {\n\t\tconsole.warn(\"Invalid royalroad fiction response from API\");\n\t\tconsole.warn(z.prettifyError(parsedResponse.error));\n\t\treturn null;\n\t}\n\n\tconst img = providerImageURLToWebNovelImage(\n\t\tparsedResponse.data.cover ?? \"none\",\n\t);\n\n\tconst out: WebNovelBook = {\n\t\tprovider: \"royalroad\",\n\t\tproviderId: RoyalroadNovelId.parse(parsedResponse.data.id.toString()),\n\n\t\tchapters: [],\n\t\tcontributors: [\n\t\t\t{\n\t\t\t\tname: PersonalName.parse({\n\t\t\t\t\tdisplayName: parsedResponse.data.authorInfo.username,\n\t\t\t\t}),\n\t\t\t\trole: \"aut\",\n\t\t\t},\n\t\t],\n\t\t// TODO grab first created chapter and last updated chapter\n\t\tcreatedAt: null,\n\t\tupdatedAt: null,\n\t\timages: img ? [img] : [],\n\t\tlanguage: null,\n\t\tname: parsedResponse.data.title,\n\t\toverview: parsedResponse.data.description\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text/html\",\n\t\t\t\t\t\tvalue: sanitizeHTML(parsedResponse.data.description),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text/markdown\",\n\t\t\t\t\t\tvalue: convertHTMLtoMarkdown(\n\t\t\t\t\t\t\tsanitizeHTML(parsedResponse.data.description),\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [],\n\t\tstatus:\n\t\t\tROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS[\n\t\t\t\tparsedResponse.data.status\n\t\t\t] ?? null,\n\t\tvolumes: [],\n\t\twarnings: [],\n\t};\n\n\tfor (const chapter of parsedResponse.data.chapters) {\n\t\tconst webNovelChapter: WebNovelChapter = {\n\t\t\tprovider: \"royalroad\",\n\t\t\tbookProviderId: RoyalroadNovelId.parse(\n\t\t\t\tparsedResponse.data.id.toString(),\n\t\t\t),\n\t\t\tvolumeProviderId: chapter.volumeId\n\t\t\t\t? RoyalroadVolumeId.parse(chapter.volumeId.toString())\n\t\t\t\t: null,\n\t\t\tproviderId: RoyalroadChapterId.parse(chapter.id.toString()),\n\n\t\t\tcontent: [],\n\t\t\textraContent: [],\n\t\t\tcontributors: [],\n\t\t\tcreatedAt: isoDateTimeToPartialDateTime(chapter.published),\n\t\t\tupdatedAt: isoDateTimeToPartialDateTime(chapter.lastEdit ?? \"none\"),\n\t\t\timages: [],\n\t\t\tisLocked: !chapter.isUnlocked,\n\t\t\tname: chapter.title.trim(),\n\t\t\tnumber: null,\n\t\t\torderNumber: chapter.order,\n\t\t};\n\t\tconst parsedChapter = WebNovelChapter.safeParse(webNovelChapter);\n\t\tif (!parsedChapter.success) {\n\t\t\tconsole.warn(\"Invalid royalroad fiction book chapter from API\");\n\t\t\tconsole.warn(z.prettifyError(parsedChapter.error));\n\t\t\tcontinue;\n\t\t}\n\n\t\tout.chapters.push(parsedChapter.data);\n\t}\n\n\tfor (const volume of parsedResponse.data.volumes) {\n\t\tconst webNovelVolume: WebNovelVolume = {\n\t\t\tprovider: \"royalroad\",\n\t\t\tbookProviderId: RoyalroadNovelId.parse(\n\t\t\t\tparsedResponse.data.id.toString(),\n\t\t\t),\n\t\t\tproviderId: RoyalroadVolumeId.parse(volume.id.toString()),\n\t\t\tcreatedAt: null,\n\t\t\tupdatedAt: null,\n\t\t\timages: [],\n\n\t\t\tname: volume.title.trim(),\n\t\t\tnumber: null,\n\t\t\torderNumber: volume.order - 1,\n\t\t};\n\t\tconst parsedVolume = WebNovelVolume.safeParse(webNovelVolume);\n\t\tif (!parsedVolume.success) {\n\t\t\tconsole.warn(\"Invalid royalroad fiction book volume from API\");\n\t\t\tconsole.warn(z.prettifyError(parsedVolume.error));\n\t\t\tcontinue;\n\t\t}\n\n\t\tout.volumes.push(parsedVolume.data);\n\t}\n\n\tconst outParsed = WebNovelBook.safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid royalroad fiction book from API\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\treturn outParsed.data;\n}\n\nexport async function royalroadAPIFictionChapter(\n\tchapterId: RoyalroadChapterId,\n): Promise<WebNovelChapter | null> {\n\tconst res = await safeFetch<RoyalroadAPIFictionChapter>(\n\t\t`https://api.royalroad.com/v1/fiction/chapters/${chapterId}`,\n\t\t\"json\",\n\t\t{\n\t\t\tinit: {\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\tCustomUserAgent: ROYALROAD_API_USER_AGENT,\n\t\t\t\t\t\"X-Mature-Content\": \"true\",\n\t\t\t\t\t\"X-Govt-Age-Restrict-Mature-Content\": \"false\",\n\t\t\t\t\t\"X-App-Version\": ROYALROAD_API_APP_VERSION,\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst parsedResponse = RoyalroadAPIFictionChapter.safeParse(res);\n\tif (!parsedResponse.success) {\n\t\tconsole.warn(\"Invalid royalroad fiction chapter response from API\");\n\t\tconsole.warn(z.prettifyError(parsedResponse.error));\n\t\treturn null;\n\t}\n\n\t// TODO: Find better solution\n\tconst book = await royalroadAPIFiction(\n\t\tRoyalroadNovelId.parse(parsedResponse.data.fictionInfo.id.toString()),\n\t\t{ bustCache: false },\n\t);\n\tconst bookChapter = book?.chapters?.find(\n\t\t(e) =>\n\t\t\te.providerId ===\n\t\t\tRoyalroadChapterId.parse(parsedResponse.data.id.toString()),\n\t);\n\n\tconst out: WebNovelChapter = {\n\t\tprovider: \"royalroad\",\n\t\tbookProviderId: RoyalroadNovelId.parse(\n\t\t\tparsedResponse.data.fictionInfo.id.toString(),\n\t\t),\n\t\tvolumeProviderId: bookChapter\n\t\t\t? (book?.volumes?.find(\n\t\t\t\t\t(e) => e.providerId === bookChapter.volumeProviderId,\n\t\t\t\t)?.providerId ?? null)\n\t\t\t: null,\n\t\tproviderId: RoyalroadChapterId.parse(parsedResponse.data.id.toString()),\n\n\t\tcontributors: [],\n\t\tcreatedAt: isoDateTimeToPartialDateTime(parsedResponse.data.published),\n\t\tupdatedAt: isoDateTimeToPartialDateTime(\n\t\t\tparsedResponse.data.lastEdit ?? \"none\",\n\t\t),\n\t\timages: [],\n\t\tname: parsedResponse.data.title,\n\t\tcontent: [],\n\t\textraContent: [],\n\t\tisLocked: !parsedResponse.data.isUnlocked,\n\t\tnumber: null,\n\t\torderNumber: parsedResponse.data.order,\n\t};\n\n\tconst sanitizedContent = sanitizeHTML(parsedResponse.data.content);\n\tout.content.push({\n\t\tsize: Buffer.byteLength(sanitizedContent, \"utf-8\"),\n\t\ttype: \"text/html\",\n\t\tvalue: sanitizedContent,\n\t});\n\tconst markdownContent = convertHTMLtoMarkdown(sanitizedContent);\n\tout.content.push({\n\t\tsize: Buffer.byteLength(markdownContent, \"utf-8\"),\n\t\ttype: \"text/markdown\",\n\t\tvalue: markdownContent,\n\t});\n\n\tif (parsedResponse.data.preAuthorsNote) {\n\t\tconst sanitizedNote = sanitizeHTML(parsedResponse.data.preAuthorsNote);\n\t\tconst markdownNote = convertHTMLtoMarkdown(sanitizedNote);\n\t\tout.extraContent.push(\n\t\t\t{\n\t\t\t\textraType: \"preNote\",\n\t\t\t\tsize: Buffer.byteLength(sanitizedNote, \"utf-8\"),\n\t\t\t\ttype: \"text/html\",\n\t\t\t\tvalue: sanitizedNote,\n\t\t\t},\n\t\t\t{\n\t\t\t\textraType: \"preNote\",\n\t\t\t\tsize: Buffer.byteLength(markdownNote, \"utf-8\"),\n\t\t\t\ttype: \"text/markdown\",\n\t\t\t\tvalue: markdownNote,\n\t\t\t},\n\t\t);\n\t}\n\n\tif (parsedResponse.data.postAuthorsNote) {\n\t\tconst sanitizedNote = sanitizeHTML(parsedResponse.data.postAuthorsNote);\n\t\tconst markdownNote = convertHTMLtoMarkdown(sanitizedNote);\n\t\tout.extraContent.push(\n\t\t\t{\n\t\t\t\textraType: \"postNote\",\n\t\t\t\tsize: Buffer.byteLength(sanitizedNote, \"utf-8\"),\n\t\t\t\ttype: \"text/html\",\n\t\t\t\tvalue: sanitizedNote,\n\t\t\t},\n\t\t\t{\n\t\t\t\textraType: \"postNote\",\n\t\t\t\tsize: Buffer.byteLength(markdownNote, \"utf-8\"),\n\t\t\t\ttype: \"text/markdown\",\n\t\t\t\tvalue: markdownNote,\n\t\t\t},\n\t\t);\n\t}\n\n\tconst outParsed = WebNovelChapter.safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid royalroad fiction chapter from API\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\treturn outParsed.data;\n}\n\n// TODO: royalroadAPIFictionChapter\n","import {\n\tROYALROAD_API_APP_VERSION,\n\tROYALROAD_API_USER_AGENT,\n\tROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS,\n} from \"#/providers/royalroad/constants.js\";\nimport {\n\tRoyalroadAPISearchResponse,\n\ttype RoyalroadAPIFictionStatus,\n} from \"#/schemas/providers/royalroad/api.js\";\nimport { safeFetch } from \"#/utils/fetch.js\";\nimport { convertHTMLtoMarkdown, sanitizeHTML } from \"#/utils/string.js\";\nimport {\n\tisoDateTimeToPartialDateTime,\n\tPersonalName,\n\tproviderImageURLToWebNovelImage,\n\tRoyalroadNovelId,\n\tWebNovelBook,\n} from \"@webnovel-proxy/core\";\nimport z from \"zod\";\n\nexport async function royalroadAPISearch(\n\tq: string,\n\topts: Partial<{\n\t\tstatus: RoyalroadAPIFictionStatus[];\n\t\tpage: number;\n\t}> = {},\n): Promise<WebNovelBook[] | null> {\n\tconst res = await safeFetch<RoyalroadAPISearchResponse>(\n\t\t\"https://api.royalroad.com/v1/fiction/search/advanced\",\n\t\t\"json\",\n\t\t{\n\t\t\tinit: {\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\tCustomUserAgent: ROYALROAD_API_USER_AGENT,\n\t\t\t\t\t\"X-Mature-Content\": \"true\",\n\t\t\t\t\t\"X-Govt-Age-Restrict-Mature-Content\": \"false\",\n\t\t\t\t\t\"X-App-Version\": ROYALROAD_API_APP_VERSION,\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t\t\"Content-Type\": \"application/json; charset=utf-8\",\n\t\t\t\t},\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tTitle: q,\n\t\t\t\t\tStatus: opts.status ?? null,\n\t\t\t\t\tTagsAdd: [],\n\t\t\t\t\tTagsRemove: [],\n\t\t\t\t\tPage: opts.page ?? 1,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst searchResponse = RoyalroadAPISearchResponse.safeParse(res);\n\tif (!searchResponse.success) {\n\t\tconsole.warn(\"Invalid royalroad search response from API\");\n\t\tconsole.warn(z.prettifyError(searchResponse.error));\n\t\treturn null;\n\t}\n\n\tconst out: WebNovelBook[] = [];\n\n\tfor (const response of searchResponse.data.data) {\n\t\tconst img = providerImageURLToWebNovelImage(\n\t\t\tresponse.cover ?? \"invalid\",\n\t\t);\n\n\t\tconst book: WebNovelBook = {\n\t\t\tprovider: \"royalroad\",\n\t\t\tproviderId: RoyalroadNovelId.parse(response.id.toString()),\n\n\t\t\tname: response.title,\n\t\t\toverview: response.description\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text/html\",\n\t\t\t\t\t\t\tvalue: sanitizeHTML(response.description),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text/markdown\",\n\t\t\t\t\t\t\tvalue: convertHTMLtoMarkdown(\n\t\t\t\t\t\t\t\tsanitizeHTML(response.description),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: [],\n\t\t\tcontributors: [\n\t\t\t\t{\n\t\t\t\t\tname: PersonalName.parse({\n\t\t\t\t\t\tdisplayName: response.authorName,\n\t\t\t\t\t}),\n\t\t\t\t\trole: \"aut\",\n\t\t\t\t},\n\t\t\t],\n\t\t\timages: img ? [img] : [],\n\t\t\tchapters: [],\n\t\t\tvolumes: [],\n\t\t\tcreatedAt: isoDateTimeToPartialDateTime(response.firstUpdate),\n\t\t\tupdatedAt: isoDateTimeToPartialDateTime(response.lastUpdate),\n\t\t\tstatus:\n\t\t\t\tROYALROAD_MAP_API_STATUS_TO_BOOK_STATUS[response.status] ??\n\t\t\t\tnull,\n\t\t\tlanguage: null,\n\t\t\twarnings: [],\n\t\t};\n\n\t\tconst parsedBook = WebNovelBook.safeParse(book);\n\t\tif (!parsedBook.success) {\n\t\t\tconsole.warn(\n\t\t\t\t`Could not parse royalroad book ${response.id.toString()} from search response`,\n\t\t\t);\n\t\t\tconsole.warn(z.prettifyError(parsedBook.error));\n\t\t\tcontinue;\n\t\t}\n\t\tout.push(parsedBook.data);\n\t}\n\n\treturn out;\n}\n","import {\n\troyalroadAPIFiction,\n\troyalroadAPIFictionChapter,\n} from \"#/providers/royalroad/api/fiction.js\";\nimport { royalroadAPISearch } from \"#/providers/royalroad/api/search.js\";\nimport type {\n\tRoyalroadChapterId,\n\tRoyalroadNovelId,\n\tRoyalroadVolumeId,\n\tWebNovelBook,\n\tWebNovelChapter,\n\tWebNovelVolume,\n} from \"@webnovel-proxy/core\";\n\nexport async function royalroadGetBook(\n\troyalroadNovelId: RoyalroadNovelId,\n): Promise<WebNovelBook | null> {\n\treturn await royalroadAPIFiction(royalroadNovelId);\n}\n\nexport async function royalroadGetVolume(\n\troyalroadNovelId: RoyalroadNovelId,\n\troyalroadVolumeId: RoyalroadVolumeId,\n): Promise<WebNovelVolume | null> {\n\tconst book = await royalroadAPIFiction(royalroadNovelId);\n\tif (!book) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\tbook?.volumes?.find((e) => e.providerId === royalroadVolumeId) ?? null\n\t);\n}\n\nexport async function royalroadGetChapter(\n\troyalroadChapterId: RoyalroadChapterId,\n): Promise<WebNovelChapter | null> {\n\treturn await royalroadAPIFictionChapter(royalroadChapterId);\n}\n\nexport async function royalroadSearch(\n\tq: string,\n\topts: Partial<{ page: number }> = {},\n): Promise<WebNovelBook[] | null> {\n\treturn await royalroadAPISearch(q, opts);\n}\n","import type { WebnovelComAuth } from \"@webnovel-proxy/core\";\n\nexport function webnovelComAPICreateCookies(\n\tauth: WebnovelComAuth | null = null,\n): string {\n\tconst out: [string, string][] = [\n\t\t[\"domain\", \".webnovel.com\"],\n\t\t[\"lang\", \"cn\"],\n\t\t[\"webnovel-language\", \"en\"],\n\t\t[\"webnovel-content-language\", \"en\"],\n\t];\n\n\tif (auth) {\n\t\tout.push([\"ywkey\", auth.ukey], [\"ukey\", auth.ukey], [\"uid\", auth.uid]);\n\t}\n\n\treturn out.map((e) => `${e[0]}=${e[1]}`).join(\"; \");\n}\n","import type { WebNovelBookStatus } from \"@webnovel-proxy/core\";\n\nexport const WEBNOVELCOM_API_SEARCH_TYPE = {\n\tnovel: 1,\n\tcomic: 2,\n\tebook: 3,\n\tfanfic: 4,\n} as const satisfies Record<string, number>;\n\nexport const WEBNOVELCOM_API_SEARCH_STATUS = {\n\tall: 0,\n\tongoing: 1,\n\tcompleted: 2,\n} as const satisfies Record<string, number>;\n\nexport const WEBNOVELCOM_API_SEARCH_ORDER = {\n\tpopular: 1,\n\tupdated: 2,\n} as const satisfies Record<string, number>;\n\nexport const WEBNOVELCOM_API_USER_AGENT =\n\t\"Mozilla/mobile QDHWReaderAndroid/8.22.8/1228/2000002/61ce67d733a61d70344e8c706215fd25\" as const satisfies string;\n\nexport const WEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS: Record<\n\tnumber,\n\tWebNovelBookStatus | null\n> = {\n\t2: \"ongoing\",\n\t1: \"completed\",\n};\n\nexport const WEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS: Record<\n\tnumber,\n\tWebNovelBookStatus | null\n> = {\n\t30: \"ongoing\",\n\t40: \"hiatus\",\n\t50: \"completed\",\n};\n","import {\n\tBCP47LanguageTag,\n\tNonEmptyString,\n\tNullableNonEmptyString,\n} from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport const WebnovelComAPISearchResponse = z.object({\n\tData: z.object({\n\t\tBookTotalNum: z.int().min(0),\n\t\tBookInfos: z.array(\n\t\t\tz.object({\n\t\t\t\t/** Too big for regular int */\n\t\t\t\tBookId: z.union([\n\t\t\t\t\tz.number().min(1),\n\t\t\t\t\tz.string().regex(/^[1-9]\\d*$/),\n\t\t\t\t]),\n\t\t\t\tBookName: NonEmptyString,\n\t\t\t\tAuthor: NonEmptyString,\n\t\t\t\tBookCoverID: z.int().min(1),\n\t\t\t\t/** @see {WEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS} */\n\t\t\t\tType: z.int().min(1),\n\t\t\t\t/** Unknown mapping */\n\t\t\t\tBookType: z.int().min(0),\n\t\t\t\t/** text/plain */\n\t\t\t\tDescription: NullableNonEmptyString.nullable(),\n\t\t\t\tMTL: z.boolean(),\n\t\t\t}),\n\t\t),\n\t}),\n});\nexport type WebnovelComAPISearchResponse = z.infer<\n\ttypeof WebnovelComAPISearchResponse\n>;\n\nexport const WebnovelComAPIBookResponse = z.object({\n\tData: z\n\t\t.object({\n\t\t\t/** Too big for regular int */\n\t\t\tBookId: z.union([\n\t\t\t\tz.number().min(1),\n\t\t\t\tz.string().regex(/^[1-9]\\d*$/),\n\t\t\t]),\n\t\t\tBookName: NonEmptyString,\n\t\t\tLastChapterTime: z.int().min(1),\n\t\t\t/** text/plain */\n\t\t\tDescription: NullableNonEmptyString.nullable(),\n\t\t\t/** @see {WEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS} */\n\t\t\tType: z.int().min(1),\n\t\t\t/** @see {WEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS} */\n\t\t\tStatus: z.int().min(1),\n\t\t\tLanguageName: BCP47LanguageTag,\n\t\t\tAuthorInfo: z.object({\n\t\t\t\tAuthorName: NonEmptyString,\n\t\t\t\tUserId: z.int().min(1),\n\t\t\t}),\n\t\t\tTransInfo: z.object({\n\t\t\t\tTranslators: z.array(\n\t\t\t\t\tz.object({\n\t\t\t\t\t\tTranslatorName: NonEmptyString,\n\t\t\t\t\t\tUserId: z.int().min(1),\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tEditors: z.array(\n\t\t\t\t\tz.object({\n\t\t\t\t\t\tTranslatorName: NonEmptyString,\n\t\t\t\t\t\tUserId: z.int().min(1),\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t}),\n\t\t\tBookCoverID: z.int().min(1),\n\t\t\tMTL: z.boolean(),\n\t\t\tChapterNum: z.int().min(0),\n\t\t})\n\t\t.nullable(),\n});\nexport type WebnovelComAPIBookResponse = z.infer<\n\ttypeof WebnovelComAPIBookResponse\n>;\n\n// Gated behind encryption/auth\n// export const WebnovelComAPIChapterResponse = z.object({\n// \tData: z\n// \t\t.object({\n// \t\t\t/** Too big for regular int */\n// \t\t\tId: z.number().min(1),\n// \t\t\tName: NonEmptyString,\n// \t\t\tLanguageName: BCP47LanguageTag,\n// \t\t\tLockType: z.int().min(0),\n// \t\t\tUnlocked: z.int().min(0).max(1),\n// \t\t})\n// \t\t.nullable(),\n// });\n// export type WebnovelComAPIChapterResponse = z.infer<\n// \ttypeof WebnovelComAPIChapterResponse\n// >;\n","import { webnovelComAPICreateCookies } from \"#/providers/webnovel-com/api/utils.js\";\nimport {\n\tWEBNOVELCOM_API_USER_AGENT,\n\tWEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS,\n\tWEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS,\n} from \"#/providers/webnovel-com/constants.js\";\nimport { WebnovelComAPIBookResponse } from \"#/schemas/providers/webnovel-com/api.js\";\nimport { safeFetch } from \"#/utils/fetch.js\";\nimport {\n\tPersonalName,\n\ttimestampMillisecondsToPartialDateTime,\n\tWebNovelBook,\n\tWebnovelComNovelId,\n\ttype WebnovelComAuth,\n} from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport async function webnovelComAPIBook(\n\tnovelId: WebnovelComNovelId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelBook | null> {\n\tconst u = new URL(\"https://idruid.webnovel.com/app/api/book/get-book\");\n\tu.searchParams.set(\"bookId\", novelId);\n\tu.searchParams.set(\"reqTS\", Math.round(Date.now()).toString());\n\n\tconst res = await safeFetch<WebnovelComAPIBookResponse>(\n\t\tu.toString(),\n\t\t\"json\",\n\t\t{\n\t\t\treferrer: \"https://android.webnovel.com\",\n\t\t\tinit: {\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": WEBNOVELCOM_API_USER_AGENT,\n\t\t\t\t\tCookie: webnovelComAPICreateCookies(auth),\n\t\t\t\t\tDeviceTime: timestampMillisecondsToPartialDateTime(\n\t\t\t\t\t\tDate.now(),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst parsedResponse = WebnovelComAPIBookResponse.safeParse(res);\n\tif (!parsedResponse.success) {\n\t\tconsole.warn(\"Invalid webnovel-com response from API\");\n\t\tconsole.warn(z.prettifyError(parsedResponse.error));\n\t\treturn null;\n\t}\n\n\tif (parsedResponse.data.Data === null) {\n\t\t// Book Not Found\n\t\treturn null;\n\t}\n\n\tconst out: WebNovelBook = {\n\t\tprovider: \"webnovel-com\",\n\t\tproviderId: WebnovelComNovelId.parse(\n\t\t\tparsedResponse.data.Data.BookId.toString(),\n\t\t),\n\n\t\tchapters: [],\n\t\tvolumes: [],\n\t\tcontributors: [\n\t\t\t{\n\t\t\t\tname: PersonalName.parse({\n\t\t\t\t\tdisplayName: parsedResponse.data.Data.AuthorInfo.AuthorName,\n\t\t\t\t}),\n\t\t\t\trole: \"aut\",\n\t\t\t},\n\t\t],\n\n\t\tcreatedAt: null,\n\t\tupdatedAt: null,\n\n\t\timages: [\n\t\t\t{\n\t\t\t\ttype: \"book\",\n\t\t\t\theight: 800,\n\t\t\t\twidth: 600,\n\t\t\t\tmediaType: \"image/jpeg\",\n\t\t\t\tprovider: \"webnovel-com\",\n\t\t\t\tproviderId: parsedResponse.data.Data.BookId.toString(),\n\t\t\t\tproviderMeta: {\n\t\t\t\t\timageId: parsedResponse.data.Data.BookCoverID.toString(),\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\tlanguage: parsedResponse.data.Data.LanguageName,\n\t\tname: parsedResponse.data.Data.BookName,\n\t\toverview: parsedResponse.data.Data.Description\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text/plain\",\n\t\t\t\t\t\tvalue: parsedResponse.data.Data.Description,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [],\n\t\tstatus:\n\t\t\tWEBNOVELCOM_MAP_API_BOOK_STATUS_TO_BOOK_STATUS[\n\t\t\t\tparsedResponse.data.Data.Status\n\t\t\t] ??\n\t\t\tWEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS[\n\t\t\t\tparsedResponse.data.Data.Type\n\t\t\t] ??\n\t\t\tnull,\n\t\twarnings: parsedResponse.data.Data.MTL ? [\"mtl\"] : [],\n\t};\n\n\tfor (const translator of parsedResponse.data.Data.TransInfo.Translators) {\n\t\tout.contributors.push({\n\t\t\tname: PersonalName.parse({\n\t\t\t\tdisplayName: translator.TranslatorName,\n\t\t\t}),\n\t\t\trole: \"trl\",\n\t\t});\n\t}\n\n\tfor (const editor of parsedResponse.data.Data.TransInfo.Editors) {\n\t\tout.contributors.push({\n\t\t\tname: PersonalName.parse({\n\t\t\t\tdisplayName: editor.TranslatorName,\n\t\t\t}),\n\t\t\trole: \"edt\",\n\t\t});\n\t}\n\n\tconst outParsed = WebNovelBook.safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid webnovel-com book from API\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\treturn outParsed.data;\n}\n","import { webnovelComAPICreateCookies } from \"#/providers/webnovel-com/api/utils.js\";\nimport {\n\tWEBNOVELCOM_API_SEARCH_ORDER,\n\tWEBNOVELCOM_API_SEARCH_STATUS,\n\tWEBNOVELCOM_API_SEARCH_TYPE,\n\tWEBNOVELCOM_API_USER_AGENT,\n\tWEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS,\n} from \"#/providers/webnovel-com/constants.js\";\nimport { WebnovelComAPISearchResponse } from \"#/schemas/providers/webnovel-com/api.js\";\nimport { safeFetch } from \"#/utils/fetch.js\";\nimport {\n\tPersonalName,\n\ttimestampMillisecondsToPartialDateTime,\n\tWebNovelBook,\n\tWebnovelComAuth,\n\tWebnovelComNovelId,\n} from \"@webnovel-proxy/core\";\nimport * as z from \"zod\";\n\nexport async function webnovelComAPISearch(\n\tq: string,\n\topts: Partial<{\n\t\tpage: number;\n\t\ttype: keyof typeof WEBNOVELCOM_API_SEARCH_TYPE;\n\t\tstatus: keyof typeof WEBNOVELCOM_API_SEARCH_STATUS;\n\t\torderBy: keyof typeof WEBNOVELCOM_API_SEARCH_ORDER;\n\t}> = {},\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelBook[] | null> {\n\tconst u = new URL(\"https://idruid.webnovel.com/app/api/search/result\");\n\tu.searchParams.set(\n\t\t\"bookStatus\",\n\t\tWEBNOVELCOM_API_SEARCH_STATUS[opts.status ?? \"all\"].toString(),\n\t);\n\tu.searchParams.set(\"chapterNum\", \"\");\n\tu.searchParams.set(\"keyword\", q);\n\tu.searchParams.set(\n\t\t\"orderBy\",\n\t\tWEBNOVELCOM_API_SEARCH_ORDER[opts.orderBy ?? \"popular\"].toString(),\n\t);\n\tu.searchParams.set(\"pageIndex\", Math.round(opts.page ?? 1).toString());\n\tu.searchParams.set(\"reqTS\", Math.round(Date.now()).toString());\n\tu.searchParams.set(\n\t\t\"type\",\n\t\tWEBNOVELCOM_API_SEARCH_TYPE[opts.type ?? \"novel\"].toString(),\n\t);\n\n\tconst res = await safeFetch<WebnovelComAPISearchResponse>(\n\t\tu.toString(),\n\t\t\"json\",\n\t\t{\n\t\t\treferrer: \"https://android.webnovel.com\",\n\t\t\tinit: {\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": WEBNOVELCOM_API_USER_AGENT,\n\t\t\t\t\tCookie: webnovelComAPICreateCookies(auth),\n\t\t\t\t\tDeviceTime: timestampMillisecondsToPartialDateTime(\n\t\t\t\t\t\tDate.now(),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t);\n\tif (!res) return null;\n\n\tconst searchResponse = WebnovelComAPISearchResponse.safeParse(res);\n\tif (!searchResponse.success) {\n\t\tconsole.warn(\"Invalid webnovel-com search response from API\");\n\t\tconsole.warn(z.prettifyError(searchResponse.error));\n\t\treturn null;\n\t}\n\n\tconst out: WebNovelBook[] = [];\n\n\tfor (const response of searchResponse.data.Data.BookInfos) {\n\t\tconst book: WebNovelBook = {\n\t\t\tprovider: \"webnovel-com\",\n\t\t\tproviderId: WebnovelComNovelId.parse(response.BookId.toString()),\n\n\t\t\tname: response.BookName,\n\t\t\toverview: response.Description\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text/plain\",\n\t\t\t\t\t\t\tvalue: response.Description,\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: [],\n\t\t\tcontributors: [\n\t\t\t\t{\n\t\t\t\t\tname: PersonalName.parse({\n\t\t\t\t\t\tdisplayName: response.Author,\n\t\t\t\t\t}),\n\t\t\t\t\trole: \"aut\",\n\t\t\t\t},\n\t\t\t],\n\t\t\timages: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"book\",\n\t\t\t\t\twidth: 600,\n\t\t\t\t\theight: 800,\n\t\t\t\t\tmediaType: \"image/jpeg\",\n\t\t\t\t\tprovider: \"webnovel-com\",\n\t\t\t\t\tproviderId: response.BookId.toString(),\n\t\t\t\t\tproviderMeta: {\n\t\t\t\t\t\timageId: response.BookCoverID.toString(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t\tchapters: [],\n\t\t\tvolumes: [],\n\t\t\tcreatedAt: null,\n\t\t\tupdatedAt: null,\n\t\t\tstatus:\n\t\t\t\tWEBNOVELCOM_MAP_API_BOOK_TYPE_TO_BOOK_STATUS[response.Type] ??\n\t\t\t\tnull,\n\t\t\tlanguage: null,\n\t\t\twarnings: response.MTL ? [\"mtl\"] : [],\n\t\t};\n\n\t\tconst parsedBook = WebNovelBook.safeParse(book);\n\t\tif (!parsedBook.success) {\n\t\t\tconsole.warn(\n\t\t\t\t`Could not parse webnovel-com book ${response.BookId.toString()} from search response`,\n\t\t\t);\n\t\t\tconsole.warn(z.prettifyError(parsedBook.error));\n\t\t\tcontinue;\n\t\t}\n\t\tout.push(parsedBook.data);\n\t}\n\n\treturn out;\n}\n","import type { WebnovelComAuth } from \"@webnovel-proxy/core\";\n\nexport function webnovelComHTMLCreateCookies(\n\tauth: WebnovelComAuth | null = null,\n): {\n\tname: string;\n\tvalue: string;\n}[] {\n\tconst out: {\n\t\tname: string;\n\t\tvalue: string;\n\t}[] = [\n\t\t{ name: \"NEXT_LOCALE\", value: \"en\" },\n\t\t{ name: \"webnovel-language\", value: \"en\" },\n\t\t{ name: \"webnovel-content-language\", value: \"en\" },\n\t];\n\n\tif (auth) {\n\t\tout.push(\n\t\t\t{ name: \"ukey\", value: auth.ukey },\n\t\t\t{ name: \"uid\", value: auth.uid },\n\t\t);\n\t}\n\n\treturn out;\n}\n","import { webnovelComHTMLCreateCookies } from \"#/providers/webnovel-com/html/utils.js\";\nimport type { Flaresolverr } from \"#/utils/flaresolverr.js\";\nimport {\n\tNonEmptyString,\n\tWebNovelBook,\n\tWebNovelChapter,\n\tWebnovelComChapterId,\n\tWebnovelComVolumeId,\n\tWebNovelVolume,\n\ttype WebnovelComAuth,\n\ttype WebnovelComNovelId,\n} from \"@webnovel-proxy/core\";\nimport * as cheerio from \"cheerio\";\nimport * as z from \"zod\";\n\nexport async function webnovelComHTMLBookCatalog(\n\tflaresolverr: Flaresolverr,\n\tnovelId: WebnovelComNovelId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<Pick<WebNovelBook, \"chapters\" | \"volumes\"> | null> {\n\tconst res = await flaresolverr.fetch(\n\t\t`https://www.webnovel.com/book/${novelId}/catalog`,\n\t\t{\n\t\t\tcookies: webnovelComHTMLCreateCookies(auth),\n\t\t},\n\t);\n\tif (res.type !== \"success\") {\n\t\tconsole.warn(res);\n\t\treturn null;\n\t}\n\n\tconst $ = cheerio.load(res.result.solution.response);\n\n\tfunction parseVolumeMeta(text: unknown): {\n\t\tnumber: number;\n\t\tname: string | null;\n\t} | null {\n\t\tif (typeof text !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\t\tconst m = /^Volume\\s+([\\d.]+)(?:[\\s\\t]*[:-][\\s\\t]*(.*))?$/.exec(\n\t\t\ttext.trim(),\n\t\t);\n\t\tif (!m) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn (\n\t\t\tz\n\t\t\t\t.object({\n\t\t\t\t\tnumber: z.number().min(0),\n\t\t\t\t\tname: NonEmptyString.nullable(),\n\t\t\t\t})\n\t\t\t\t.safeParse({\n\t\t\t\t\tnumber: Number(m[1]),\n\t\t\t\t\tname: m[2] ?? null,\n\t\t\t\t}).data ?? null\n\t\t);\n\t}\n\n\tfunction parseChapterName(text: unknown): string | null {\n\t\tif (typeof text !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn NonEmptyString.safeParse(text).data ?? null;\n\t}\n\n\tfunction parseChapterNumber(text: unknown): number | null {\n\t\tconst number = z\n\t\t\t.number()\n\t\t\t.min(0)\n\t\t\t// WebNovel does not label special chapters correctly\n\t\t\t.safeParse(Number(NonEmptyString.safeParse(text).data ?? \"0\")).data;\n\n\t\treturn typeof number === \"number\" ? number : null;\n\t}\n\n\tconst out: Pick<WebNovelBook, \"chapters\" | \"volumes\"> = {\n\t\tvolumes: [],\n\t\tchapters: [],\n\t};\n\n\tfor (const volumeContainerElement of $(\n\t\t\".j_catalog_wrap .j_catalog_list .volume-item\",\n\t)) {\n\t\tconst volumeMeta = parseVolumeMeta(\n\t\t\t$(volumeContainerElement).find(\"h4\").first().text(),\n\t\t);\n\t\tconst volumeId = volumeMeta\n\t\t\t? WebnovelComVolumeId.parse(\n\t\t\t\t\t`${novelId}-${volumeMeta.number.toString()}`,\n\t\t\t\t)\n\t\t\t: null;\n\t\tif (volumeMeta && volumeId) {\n\t\t\tconst volume: WebNovelVolume = {\n\t\t\t\tprovider: \"webnovel-com\",\n\t\t\t\tbookProviderId: novelId,\n\t\t\t\tproviderId: volumeId,\n\n\t\t\t\tcreatedAt: null,\n\t\t\t\tupdatedAt: null,\n\t\t\t\timages: [],\n\n\t\t\t\tname: volumeMeta.name,\n\t\t\t\tnumber: volumeMeta.number,\n\t\t\t\torderNumber: null,\n\t\t\t};\n\t\t\tconst parsedVolume = WebNovelVolume.safeParse(volume);\n\t\t\tif (!parsedVolume.success) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"Invalid webnovel-com book catalog volume from HTML\",\n\t\t\t\t);\n\t\t\t\tconsole.warn(z.prettifyError(parsedVolume.error));\n\t\t\t} else {\n\t\t\t\tout.volumes.push(parsedVolume.data);\n\t\t\t}\n\t\t}\n\n\t\tfor (const chapterContainerElement of $(volumeContainerElement).find(\n\t\t\t\"ol.content-list li\",\n\t\t)) {\n\t\t\tconst chapterId =\n\t\t\t\tWebnovelComChapterId.safeParse(\n\t\t\t\t\tchapterContainerElement.attribs[\"data-cid\"],\n\t\t\t\t).data ?? null;\n\t\t\tif (!chapterId) {\n\t\t\t\t// Cannot infer chapter id\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Could not infer chapter id on volume ${volumeId ?? \"unknown\"}`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst chapterName = parseChapterName(\n\t\t\t\t$(chapterContainerElement)\n\t\t\t\t\t.find(\"a div.oh strong\")\n\t\t\t\t\t.first()\n\t\t\t\t\t.text()\n\t\t\t\t\t.trim(),\n\t\t\t);\n\t\t\tconst chapterNumber = parseChapterNumber(\n\t\t\t\t$(chapterContainerElement).find(\"a i\").first().text().trim(),\n\t\t\t);\n\t\t\tif (chapterNumber === null) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Cannot parse chapter number for chapter id ${chapterId}`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst chapter: WebNovelChapter = {\n\t\t\t\tprovider: \"webnovel-com\",\n\t\t\t\tbookProviderId: novelId,\n\t\t\t\tvolumeProviderId: volumeId,\n\t\t\t\tproviderId: chapterId,\n\n\t\t\t\tcreatedAt: null,\n\t\t\t\tupdatedAt: null,\n\t\t\t\tcontent: [],\n\t\t\t\textraContent: [],\n\t\t\t\timages: [],\n\t\t\t\tcontributors: [],\n\n\t\t\t\tisLocked: $(chapterContainerElement).find(\"a > svg\").length > 0,\n\t\t\t\tname: chapterName,\n\t\t\t\tnumber: chapterNumber,\n\t\t\t\torderNumber: null,\n\t\t\t};\n\t\t\tconst parsedChapter = WebNovelChapter.safeParse(chapter);\n\t\t\tif (!parsedChapter.success) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"Invalid webnovel-com book catalog chapter from HTML\",\n\t\t\t\t);\n\t\t\t\tconsole.warn(z.prettifyError(parsedChapter.error));\n\t\t\t} else {\n\t\t\t\tout.chapters.push(parsedChapter.data);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst outParsed = WebNovelBook.pick({\n\t\tvolumes: true,\n\t\tchapters: true,\n\t}).safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid webnovel-com book catalog from HTML\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\treturn outParsed.data;\n}\n","import { webnovelComHTMLCreateCookies } from \"#/providers/webnovel-com/html/utils.js\";\nimport type { Flaresolverr } from \"#/utils/flaresolverr.js\";\nimport { convertHTMLtoMarkdown, sanitizeHTML } from \"#/utils/string.js\";\nimport {\n\tNonEmptyString,\n\tWebNovelChapter,\n\tWebNovelChapterContent,\n\tWebnovelComChapterId,\n\tWebNovelExtraChapterContent,\n\ttype WebnovelComAuth,\n\ttype WebnovelComNovelId,\n} from \"@webnovel-proxy/core\";\nimport * as cheerio from \"cheerio\";\nimport * as z from \"zod\";\n\nexport async function webnovelComHTMLChapter(\n\tflaresolverr: Flaresolverr,\n\tnovelId: WebnovelComNovelId,\n\tchapterId: WebnovelComChapterId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelChapter | null> {\n\tconst res = await flaresolverr.fetch(\n\t\t`https://www.webnovel.com/book/${novelId}/${chapterId}`,\n\t\t{\n\t\t\tcookies: webnovelComHTMLCreateCookies(auth),\n\t\t},\n\t);\n\tif (res.type !== \"success\") {\n\t\tconsole.warn(res);\n\t\treturn null;\n\t}\n\n\tif (\n\t\tres.result.solution.response.includes(\n\t\t\t\"//www.yueimg.com/en/images/404\",\n\t\t) ||\n\t\tres.result.solution.response.includes(\n\t\t\t\"We might have some troubles to find out this page.\",\n\t\t)\n\t) {\n\t\treturn null;\n\t}\n\n\tconst $ = cheerio.load(res.result.solution.response);\n\n\tfunction parseChapterMeta(\n\t\ttextIdx: string,\n\t\ttextName: string,\n\t): { number: number; name: string | null } | null {\n\t\tconst number =\n\t\t\ttextIdx.trim() === \"\"\n\t\t\t\t? 0\n\t\t\t\t: (z\n\t\t\t\t\t\t.number()\n\t\t\t\t\t\t.min(0)\n\t\t\t\t\t\t.safeParse(\n\t\t\t\t\t\t\tNumber(\n\t\t\t\t\t\t\t\t/^Chapter\\s+([\\d.]+)/.exec(\n\t\t\t\t\t\t\t\t\ttextIdx.trim(),\n\t\t\t\t\t\t\t\t)?.[1] ?? \"not a number\",\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t).data ?? null);\n\t\tif (typeof number !== \"number\") {\n\t\t\treturn null;\n\t\t}\n\t\tconst name = NonEmptyString.safeParse(textName.trim()).data ?? null;\n\n\t\treturn { number, name };\n\t}\n\n\tfunction parseChapterParagraphMeta(\n\t\trawAttributeValue: unknown,\n\t): { paragraphId: string; paragraphIndex: number } | null {\n\t\tif (typeof rawAttributeValue !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst data =\n\t\t\tz\n\t\t\t\t.object({\n\t\t\t\t\tparagraphId: NonEmptyString,\n\t\t\t\t\tchapterId: NonEmptyString,\n\t\t\t\t\tparaIndex: z.int(),\n\t\t\t\t})\n\t\t\t\t.safeParse(JSON.parse(rawAttributeValue)).data ?? null;\n\t\tif (!data) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst paragraphId = NonEmptyString.pipe(\n\t\t\t\tz.string().regex(/^[a-f0-9]{32}$/),\n\t\t\t).parse(data.paragraphId);\n\t\t\tconst paragraphIndex = z.int().min(0).parse(data.paraIndex);\n\n\t\t\treturn { paragraphId, paragraphIndex };\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tconst chapterMeta = parseChapterMeta(\n\t\t$(\"header span.j_chapIdx\").text(),\n\t\t$(\"header span.j_chapName\").text(),\n\t);\n\tif (!chapterMeta) {\n\t\tconsole.warn(\n\t\t\t`Could not parse chapter meta from chapter id ${chapterId}`,\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst chapterContainerElement = $(\n\t\t`#page .j_contentWrap .chapter_content[data-cid=\"${chapterId}\"]`,\n\t).first();\n\n\tconst out: WebNovelChapter = {\n\t\tprovider: \"webnovel-com\",\n\t\tbookProviderId: novelId,\n\t\tvolumeProviderId: null,\n\t\tproviderId: chapterId,\n\n\t\tcontributors: [],\n\t\tcreatedAt: null,\n\t\tupdatedAt: null,\n\t\timages: [],\n\n\t\tname: chapterMeta.name,\n\t\tnumber: chapterMeta.number,\n\t\torderNumber: null,\n\t\tcontent: [],\n\t\textraContent: [],\n\t\tisLocked:\n\t\t\tz\n\t\t\t\t.stringbool()\n\t\t\t\t.nullable()\n\t\t\t\t.optional()\n\t\t\t\t.safeParse(chapterContainerElement.attr(\"data-islock\")).data ??\n\t\t\tnull,\n\t};\n\n\tconst outParsed = WebNovelChapter.safeParse(out);\n\tif (!outParsed.success) {\n\t\tconsole.warn(\"Invalid webnovel-com chapter from HTML\");\n\t\tconsole.warn(z.prettifyError(outParsed.error));\n\t\treturn null;\n\t}\n\n\tif (!outParsed.data.isLocked) {\n\t\tconst paragraphs: string[] = [];\n\n\t\tfor (const paragraphContainerElement of chapterContainerElement.find(\n\t\t\t\".cha-content > .cha-words > .cha-paragraph\",\n\t\t)) {\n\t\t\tconst paragraphElement = $(paragraphContainerElement).find(\n\t\t\t\t\".dib > p\",\n\t\t\t);\n\t\t\tconst paragraphMeta = parseChapterParagraphMeta(\n\t\t\t\tparagraphContainerElement.attribs[\"data-ejs\"],\n\t\t\t);\n\t\t\tif (paragraphMeta) {\n\t\t\t\tparagraphElement.attr(\n\t\t\t\t\t\"data-paragraph-id\",\n\t\t\t\t\tparagraphMeta.paragraphId,\n\t\t\t\t);\n\t\t\t\tparagraphElement.attr(\n\t\t\t\t\t\"data-paragraph-idx\",\n\t\t\t\t\tparagraphMeta.paragraphIndex.toString(),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst innerHTML = paragraphElement.html();\n\t\t\tif (innerHTML !== null) {\n\t\t\t\tparagraphElement.html(innerHTML.trim());\n\t\t\t}\n\n\t\t\tconst outerHTML = paragraphElement.prop(\"outerHTML\");\n\t\t\tif (!outerHTML || outerHTML.trim() === \"\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tparagraphs.push(outerHTML);\n\t\t}\n\n\t\tif (paragraphs.length > 0) {\n\t\t\tconst fullHTML = sanitizeHTML(paragraphs.join(\"\\n\"));\n\t\t\tconst outHTML = WebNovelChapterContent.parse({\n\t\t\t\ttype: \"text/html\",\n\t\t\t\tvalue: fullHTML,\n\t\t\t\tsize: Buffer.byteLength(fullHTML, \"utf-8\"),\n\t\t\t} satisfies WebNovelChapterContent);\n\t\t\toutParsed.data.content.push(outHTML);\n\n\t\t\tconst fullMarkdown = convertHTMLtoMarkdown(fullHTML);\n\t\t\tconst outMarkdown = WebNovelChapterContent.parse({\n\t\t\t\ttype: \"text/markdown\",\n\t\t\t\tvalue: fullMarkdown,\n\t\t\t\tsize: Buffer.byteLength(fullMarkdown, \"utf-8\"),\n\t\t\t} satisfies WebNovelChapterContent);\n\t\t\toutParsed.data.content.push(outMarkdown);\n\t\t}\n\t}\n\n\tconst creatorsThoughtsContainerElement = $(\n\t\t\"#page .cha-content .m-thou\",\n\t).first();\n\tif (creatorsThoughtsContainerElement.hasClass(\"m-thou\")) {\n\t\tconst paragraphs: string[] = [];\n\n\t\tfor (const creatorsThoughtsParagraph of creatorsThoughtsContainerElement.find(\n\t\t\t\"p.creators-thought\",\n\t\t)) {\n\t\t\tconst el = $(creatorsThoughtsParagraph);\n\n\t\t\tel.attr(\"class\", null);\n\n\t\t\tconst innerHTML = el.html();\n\t\t\tif (innerHTML !== null) {\n\t\t\t\tel.html(innerHTML.trim());\n\t\t\t}\n\n\t\t\tconst outerHTML = el.prop(\"outerHTML\");\n\t\t\tif (!outerHTML || outerHTML.trim() === \"\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tparagraphs.push(outerHTML);\n\t\t}\n\n\t\tif (paragraphs.length > 0) {\n\t\t\tconst fullHTML = sanitizeHTML(paragraphs.join(\"\\n\"));\n\t\t\tconst outHTML = WebNovelExtraChapterContent.parse({\n\t\t\t\ttype: \"text/html\",\n\t\t\t\textraType: \"postNote\",\n\t\t\t\tvalue: fullHTML,\n\t\t\t\tsize: Buffer.byteLength(fullHTML, \"utf-8\"),\n\t\t\t} satisfies WebNovelExtraChapterContent);\n\t\t\toutParsed.data.extraContent.push(outHTML);\n\n\t\t\tconst fullMarkdown = convertHTMLtoMarkdown(fullHTML);\n\t\t\tconst outMarkdown = WebNovelExtraChapterContent.parse({\n\t\t\t\ttype: \"text/markdown\",\n\t\t\t\textraType: \"postNote\",\n\t\t\t\tvalue: fullMarkdown,\n\t\t\t\tsize: Buffer.byteLength(fullMarkdown, \"utf-8\"),\n\t\t\t} satisfies WebNovelExtraChapterContent);\n\t\t\toutParsed.data.extraContent.push(outMarkdown);\n\t\t}\n\t}\n\n\treturn outParsed.data;\n}\n","import { webnovelComAPIBook } from \"#/providers/webnovel-com/api/book.js\";\nimport { webnovelComAPISearch } from \"#/providers/webnovel-com/api/search.js\";\nimport { webnovelComHTMLBookCatalog } from \"#/providers/webnovel-com/html/book.js\";\nimport { webnovelComHTMLChapter } from \"#/providers/webnovel-com/html/chapter.js\";\nimport type { Flaresolverr } from \"#/utils/flaresolverr.js\";\nimport type {\n\tWebNovelBook,\n\tWebNovelChapter,\n\tWebnovelComAuth,\n\tWebnovelComChapterId,\n\tWebnovelComNovelId,\n} from \"@webnovel-proxy/core\";\n\nexport async function webnovelComSearch(\n\tq: string,\n\topts: Partial<{ page: number }> = {},\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelBook[] | null> {\n\treturn await webnovelComAPISearch(q, opts, auth);\n}\n\nexport async function webnovelComGetBook(\n\tflaresolverr: Flaresolverr,\n\tnovelId: WebnovelComNovelId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelBook | null> {\n\tconst api = await webnovelComAPIBook(novelId, auth);\n\tif (!api) return null;\n\n\tconst html = await webnovelComHTMLBookCatalog(flaresolverr, novelId, auth);\n\n\treturn {\n\t\t...api,\n\t\t...html,\n\t};\n}\n\nexport async function webnovelComGetChapter(\n\tflaresolverr: Flaresolverr,\n\tnovelId: WebnovelComNovelId,\n\tchapterId: WebnovelComChapterId,\n\tauth: WebnovelComAuth | null = null,\n): Promise<WebNovelChapter | null> {\n\treturn await webnovelComHTMLChapter(flaresolverr, novelId, chapterId, auth);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webnovel-proxy/providers",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"files": [
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@eslint/js": "^10.0.1",
|
|
42
|
+
"@types/json-bigint": "^1.0.4",
|
|
42
43
|
"@types/node": "^26.6.6",
|
|
43
44
|
"@types/turndown": "^5.0.6",
|
|
44
45
|
"@webnovel-proxy/core": "^0.5.4",
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"@scure/base": "^2.4.0",
|
|
59
60
|
"cheerio": "^1.2.0",
|
|
60
61
|
"isomorphic-dompurify": "^4.6.0",
|
|
62
|
+
"json-bigint": "^1.0.0",
|
|
61
63
|
"turndown": "^7.2.4",
|
|
62
64
|
"zod": "^4.6.7"
|
|
63
65
|
},
|