@mash43/relnext 0.1.0 → 0.1.2

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.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e={REL:{next:/rel\s*=\s*(['"])[^'"]*?\bnext\b[^'"]*?\1/i,prev:/rel\s*=\s*(['"])[^'"]*?\b(prev|previous)\b[^'"]*?\1/i},TEXT:{next:/^\s*(((Next)\s*(page)?|older|forward)|((次|つぎ)(のページ)?(へ)?)|((下|后)\s*(一)?(页|頁))|(다음)|»|>|→)\s*>*$/i,prev:/^\s*<*(((Prev|Previous)\s*(page)?|older|back)|((前)(のページ)?(へ)?)|((上|前)\s*(一)?(页|頁))|(이전)|«|<|←)\s*$/i},CLASS_NAME:{next:/next/i,prev:/prev|previous/i},PAGINATION_LI:{next:/<li[^>]+class\s*=\s*['"][^'"]*(?:current|active)[^'"]*['"][^>]*>.*?<\/li>\s*<li[^>]*>\s*<a\s+(?<attributes>[^>]+)>/is,prev:/<li[^>]*>\s*<a\s+(?<attributes>[^>]+)>.*?<\/a>\s*<\/li>\s*<li[^>]+class\s*=\s*['"][^'"]*(?:current|active)[^'"]*['"][^>]*>/is},PAGINATION_FALLBACK:{next:/(?:<(?:span|a)[^>]+(?:class\s*=\s*['"](?:current|active)['"]|aria-current\s*=\s*['"]page['"])|strong)\s*[^<]*\s*<\/(?:span|a|strong)>\s*<a\s+(?<attributes>[^>]+)>/i,prev:/<a\s+(?<attributes>[^>]+)>.*?<\/a>\s*(?:<(?:span|a)[^>]+(?:class\s*=\s*['"](?:current|active)['"]|aria-current\s*=\s*['"]page['"])|strong)\s*[^<]*/i},POTENTIAL_LINK_TAGS:/<(?:a|link)\s+(?<attributes>[^>]*?)>/gi,ANCHOR_TAG:/<a\s+(?<attributes>[^>]+)>(?<innerText>.*?)<\/a>/gis,ANCHOR_TAG_START:/<a\s+(?<attributes>[^>]+)>/gi,PAGINATION_CONTAINER:/<(?:div|nav|ul)[^>]+(?:class|id)\s*=\s*['"][^'"]*(?:pagination|pager|page-nav)[^'"]*['"][^>]*>(?<containerHtml>[\s\S]*?)<\/(?:div|nav|ul)>/gi,IMG_TAG:/<img[^>]+>/gi,PATH_PAGE_NUMBER:/^(.*[/\-_])(\d+)$/,HTML_TAGS:/<[^>]+>/g,HTML_ENTITIES:/&[a-z]+;|&#[0-9]+;|&#x[0-9a-f]+;/gi},t=8e3,n=new Map;function r(e,t){let r=n.get(t);return r||(r=RegExp(`${t}\\s*=\\s*(['"])(?<value>[^"']*)\\1`,`i`),n.set(t,r)),e.match(r)?.groups?.value??null}function i(e,t,n){let i=r(e,`href`);if(i)try{return new URL(i,t).href}catch(e){let r=e instanceof Error?e.message:String(e);return n?.logger?.(`warn`,`Invalid URL '${i}' for base '${t}': ${r}`),null}return null}async function a(e,n){let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??t);try{let t=await fetch(e,{signal:r.signal});if(!t.ok)return n?.logger?.(`warn`,`Failed to fetch ${e}: ${t.status} ${t.statusText}`),null;let i=t.headers.get(`content-type`);return!i||!i.includes(`text/html`)?(n?.logger?.(`warn`,`URL ${e} did not return HTML content.`),null):await t.text()}catch(t){let r=t instanceof Error?t.message:String(t);return n?.logger?.(`error`,`Error fetching or parsing ${e}: ${r}`),null}finally{clearTimeout(i)}}function o(t,n,r,a){let o=t.matchAll(e.POTENTIAL_LINK_TAGS);for(let e of o){let t=e.groups?.attributes;if(t&&r.test(t)){let e=i(t,n,a);if(e)return e}}return null}function s(t,n,r,a,o){let s=t.matchAll(e.PAGINATION_CONTAINER);for(let e of s){let t=e.groups?.containerHtml;if(!t)continue;let s=t.match(r);if(s?.groups?.attributes){let e=i(s.groups.attributes,n,o);if(e)return e}let c=t.match(a);if(c?.groups?.attributes){let e=i(c.groups.attributes,n,o);if(e)return e}}return null}function c(t,n,r,a){let o=t.matchAll(e.ANCHOR_TAG);for(let t of o){let o=t.groups?.attributes,s=t.groups?.innerText;if(!o||!s)continue;let c=s.replace(e.HTML_TAGS,``).replace(e.HTML_ENTITIES,``).trim();if(c&&r.test(c)){let e=i(o,n,a);if(e)return e}}return null}function l(t,n,a,o,s){let c=t.matchAll(e.ANCHOR_TAG_START);for(let e of c){let t=e.groups?.attributes;if(!t)continue;let c=r(t,`class`),l=r(t,`id`);if(c&&(o??a).test(c)||l&&a.test(l)){let e=i(t,n,s);if(e)return e}}return null}function u(t,n,a,o){let s=t.matchAll(e.ANCHOR_TAG_START);for(let e of s){let t=e.groups?.attributes;if(!t)continue;let s=r(t,`aria-label`);if(s&&a.test(s)){let e=i(t,n,o);if(e)return e}}return null}function d(t,n,a,o){let s=t.matchAll(e.ANCHOR_TAG);for(let t of s){let s=t.groups?.attributes,c=t.groups?.innerText;if(!s||!c)continue;let l=c.matchAll(e.IMG_TAG);for(let[e]of l){let t=r(e,`alt`);if(t&&a.test(t.trim())){let e=i(s,n,o);if(e)return e}}}return null}function f(t,n,r,i){let a=i?.methods??[`rel`,`pagination`,`text`,`className`,`aria-label`,`alt`],f={rel:()=>o(t,n,e.REL[r],i),pagination:()=>s(t,n,e.PAGINATION_LI[r],e.PAGINATION_FALLBACK[r],i),text:()=>c(t,n,e.TEXT[r],i),className:()=>l(t,n,e.CLASS_NAME[r],i?.classNameRegex,i),"aria-label":()=>u(t,n,e.TEXT[r],i),alt:()=>d(t,n,e.TEXT[r],i)};for(let e of a){let t=f[e]();if(t)return t}return null}function p(e,t,n){return f(e,t,`next`,n)}function m(e,t,n){return f(e,t,`prev`,n)}async function h(e,n){let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??t);try{return(await fetch(e,{method:`HEAD`,signal:r.signal})).ok}catch{return!1}finally{clearTimeout(i)}}async function g(e,t,n){try{let r=new URL(e),i=r.searchParams,a=null,o=null;for(let e of[`page`,`p`,`index`]){let t=i.get(e);if(t){let n=parseInt(t,10);if(!Number.isNaN(n)){a=e,o=n;break}}}if(a&&o!==null){let e=t===`next`?o+1:o-1;if(e>0){i.set(a,String(e));let t=r.toString();if(n?.verifyExists===!1||await h(t,n))return t}}}catch(e){let t=e instanceof Error?e.message:String(e);n?.logger?.(`warn`,`Invalid URL provided to findUrlByQueryParam: ${t}`)}return null}async function _(t,n,r){try{let i=new URL(t),a=i.pathname.replace(/\/$/,``).match(e.PATH_PAGE_NUMBER);if(a){let[e,t,o]=a;if(t&&o){let e=parseInt(o,10),a=n===`next`?e+1:e-1;if(a>0){let e=`${t}${a}`,n=`${i.origin}${e}${i.search}${i.hash}`;if(r?.verifyExists===!1||await h(n,r))return n}}}}catch(e){let t=e instanceof Error?e.message:String(e);r?.logger?.(`warn`,`Invalid URL provided to findUrlByPathSegment: ${t}`)}return null}async function v(e,t,n){return await g(e,t,n)||await _(e,t,n)||null}function y(e,t){return v(e,`next`,t)}function b(e,t){return v(e,`prev`,t)}exports.fetchHtml=a,exports.findNext=p,exports.findNextByUrl=y,exports.findPrev=m,exports.findPrevByUrl=b;
1
+ const e={REL:{next:/rel\s*=\s*(['"])[^'"]*?\bnext\b[^'"]*?\1/i,prev:/rel\s*=\s*(['"])[^'"]*?\b(prev|previous)\b[^'"]*?\1/i},TEXT:{next:/^\s*(((Next)\s*(page)?|older|forward)|((次|つぎ)(のページ)?(へ)?)|((下|后)\s*(一)?(页|頁))|(다음)|»|>|→)\s*[»>→]*$/i,prev:/^\s*[«<←]*\s*(((Prev|Previous)\s*(page)?|older|back)|((前)(のページ)?(へ)?)|((上|前)\s*(一)?(页|頁))|(이전)|«|<|←)\s*$/i},CLASS_NAME:{next:/next/i,prev:/prev|previous/i},PAGINATION_LI:{next:/<li[^>]+class\s*=\s*['"][^'"]*(?:current|active)[^'"]*['"][^>]*>.*?<\/li>\s*<li[^>]*>\s*<a\s+(?<attributes>[^>]+)>/is,prev:/<li[^>]*>\s*<a\s+(?<attributes>[^>]+)>.*?<\/a>\s*<\/li>\s*<li[^>]+class\s*=\s*['"][^'"]*(?:current|active)[^'"]*['"][^>]*>/is},PAGINATION_FALLBACK:{next:/(?:<(?:span|a)[^>]+(?:class\s*=\s*['"](?:current|active)['"]|aria-current\s*=\s*['"]page['"])|strong)\s*[^<]*\s*<\/(?:span|a|strong)>\s*<a\s+(?<attributes>[^>]+)>/i,prev:/<a\s+(?<attributes>[^>]+)>.*?<\/a>\s*(?:<(?:span|a)[^>]+(?:class\s*=\s*['"](?:current|active)['"]|aria-current\s*=\s*['"]page['"])|strong)\s*[^<]*/i},POTENTIAL_LINK_TAGS:/<(?:a|link)\s+(?<attributes>[^>]*?)>/gi,ANCHOR_TAG:/<a\s+(?<attributes>[^>]+)>(?<innerText>.*?)<\/a>/gis,ANCHOR_TAG_START:/<a\s+(?<attributes>[^>]+)>/gi,PAGINATION_CONTAINER:/<(?:div|nav|ul)[^>]+(?:class|id)\s*=\s*['"][^'"]*(?:pagination|pager|page-nav)[^'"]*['"][^>]*>(?<containerHtml>[\s\S]*?)<\/(?:div|nav|ul)>/gi,IMG_TAG:/<img[^>]+>/gi,PATH_PAGE_NUMBER:/^(.*[/\-_])(\d+)$/,HTML_TAGS:/<[^>]+>/g,HTML_ENTITIES:/&[a-z]+;|&#[0-9]+;|&#x[0-9a-f]+;/gi},t=8e3,n=new Map;function r(e,t){let r=n.get(t);return r||(r=RegExp(`${t}\\s*=\\s*(['"])(?<value>[^"']*)\\1`,`i`),n.set(t,r)),e.match(r)?.groups?.value??null}function i(e,t,n){let i=r(e,`href`);if(i){let e=i.replace(/&amp;/g,`&`);try{return new URL(e,t).href}catch(e){let r=e instanceof Error?e.message:String(e);return n?.logger?.(`warn`,`Invalid URL '${i}' for base '${t}': ${r}`),null}}return null}async function a(e,n){let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??t);try{let t=await fetch(e,{signal:r.signal});if(!t.ok)return n?.logger?.(`warn`,`Failed to fetch ${e}: ${t.status} ${t.statusText}`),null;let i=t.headers.get(`content-type`);return!i||!i.includes(`text/html`)?(n?.logger?.(`warn`,`URL ${e} did not return HTML content.`),null):await t.text()}catch(t){let r=t instanceof Error?t.message:String(t);return n?.logger?.(`error`,`Error fetching or parsing ${e}: ${r}`),null}finally{clearTimeout(i)}}function o(t,n,r,a){let o=t.matchAll(e.POTENTIAL_LINK_TAGS);for(let e of o){let t=e.groups?.attributes;if(t&&r.test(t)){let e=i(t,n,a);if(e)return e}}return null}function s(t,n,r,a,o){let s=t.matchAll(e.PAGINATION_CONTAINER);for(let e of s){let t=e.groups?.containerHtml;if(!t)continue;let s=t.match(r);if(s?.groups?.attributes){let e=i(s.groups.attributes,n,o);if(e)return e}let c=t.match(a);if(c?.groups?.attributes){let e=i(c.groups.attributes,n,o);if(e)return e}}return null}function c(t,n,r,a){let o=t.matchAll(e.ANCHOR_TAG);for(let t of o){let o=t.groups?.attributes,s=t.groups?.innerText;if(!o||!s)continue;let c=s.replace(e.HTML_TAGS,``).replace(e.HTML_ENTITIES,``).trim();if(c&&r.test(c)){console.log(r,c);let e=i(o,n,a);if(e)return e}}return null}function l(t,n,a,o,s){let c=t.matchAll(e.ANCHOR_TAG_START);for(let e of c){let t=e.groups?.attributes;if(!t)continue;let c=r(t,`class`),l=r(t,`id`);if(c&&(o??a).test(c)||l&&a.test(l)){let e=i(t,n,s);if(e)return e}}return null}function u(t,n,a,o){let s=t.matchAll(e.ANCHOR_TAG_START);for(let e of s){let t=e.groups?.attributes;if(!t)continue;let s=r(t,`aria-label`);if(s&&a.test(s)){let e=i(t,n,o);if(e)return e}}return null}function d(t,n,a,o){let s=t.matchAll(e.ANCHOR_TAG);for(let t of s){let s=t.groups?.attributes,c=t.groups?.innerText;if(!s||!c)continue;let l=c.matchAll(e.IMG_TAG);for(let[e]of l){let t=r(e,`alt`);if(t&&a.test(t.trim())){let e=i(s,n,o);if(e)return e}}}return null}function f(t,n,r,i){let a=i?.methods??[`rel`,`pagination`,`text`,`className`,`aria-label`,`alt`],f={rel:()=>o(t,n,e.REL[r],i),pagination:()=>s(t,n,e.PAGINATION_LI[r],e.PAGINATION_FALLBACK[r],i),text:()=>c(t,n,e.TEXT[r],i),className:()=>l(t,n,e.CLASS_NAME[r],i?.classNameRegex,i),"aria-label":()=>u(t,n,e.TEXT[r],i),alt:()=>d(t,n,e.TEXT[r],i)};for(let e of a){let t=f[e]();if(t)return t}return null}function p(e,t,n){return f(e,t,`next`,n)}function m(e,t,n){return f(e,t,`prev`,n)}async function h(e,n){let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??t);try{return(await fetch(e,{method:`HEAD`,signal:r.signal})).ok}catch{return!1}finally{clearTimeout(i)}}async function g(e,t,n){try{let r=new URL(e),i=r.searchParams,a=null,o=null;for(let e of[`page`,`p`,`index`]){let t=i.get(e);if(t){let n=parseInt(t,10);if(!Number.isNaN(n)){a=e,o=n;break}}}if(a&&o!==null){let e=t===`next`?o+1:o-1;if(e>0){i.set(a,String(e));let t=r.toString();if(n?.verifyExists===!1||await h(t,n))return t}}}catch(e){let t=e instanceof Error?e.message:String(e);n?.logger?.(`warn`,`Invalid URL provided to findUrlByQueryParam: ${t}`)}return null}async function _(t,n,r){try{let i=new URL(t),a=i.pathname.replace(/\/$/,``).match(e.PATH_PAGE_NUMBER);if(a){let[e,t,o]=a;if(t&&o){let e=parseInt(o,10),a=n===`next`?e+1:e-1;if(a>0){let e=`${t}${a}`,n=`${i.origin}${e}${i.search}${i.hash}`;if(r?.verifyExists===!1||await h(n,r))return n}}}}catch(e){let t=e instanceof Error?e.message:String(e);r?.logger?.(`warn`,`Invalid URL provided to findUrlByPathSegment: ${t}`)}return null}async function v(e,t,n){return await g(e,t,n)||await _(e,t,n)||null}function y(e,t){return v(e,`next`,t)}function b(e,t){return v(e,`prev`,t)}exports.fetchHtml=a,exports.findNext=p,exports.findNextByUrl=y,exports.findPrev=m,exports.findPrevByUrl=b;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["// src/index.ts\n\nexport interface BaseOptions {\n\tlogger?: (level: \"warn\" | \"error\", message: string) => void;\n\ttimeout?: number;\n\tverifyExists?: boolean;\n}\n\nexport type Method =\n\t| \"rel\"\n\t| \"pagination\"\n\t| \"text\"\n\t| \"className\"\n\t| \"aria-label\"\n\t| \"alt\";\n\nexport interface FindNextOptions extends BaseOptions {\n\tmethods?: Method[];\n\tclassNameRegex?: RegExp;\n}\n\ntype Direction = \"next\" | \"prev\";\n\nconst REGEX = {\n\tREL: {\n\t\tnext: /rel\\s*=\\s*(['\"])[^'\"]*?\\bnext\\b[^'\"]*?\\1/i,\n\t\tprev: /rel\\s*=\\s*(['\"])[^'\"]*?\\b(prev|previous)\\b[^'\"]*?\\1/i,\n\t},\n\tTEXT: {\n\t\tnext: /^\\s*(((Next)\\s*(page)?|older|forward)|((次|つぎ)(のページ)?(へ)?)|((下|后)\\s*(一)?(页|頁))|(다음)|»|>|→)\\s*>*$/i,\n\t\tprev: /^\\s*<*(((Prev|Previous)\\s*(page)?|older|back)|((前)(のページ)?(へ)?)|((上|前)\\s*(一)?(页|頁))|(이전)|«|<|←)\\s*$/i,\n\t},\n\tCLASS_NAME: {\n\t\tnext: /next/i,\n\t\tprev: /prev|previous/i,\n\t},\n\tPAGINATION_LI: {\n\t\t// Extracts attributes of an <a> tag within an <li> element that follows a current/active <li> element (e.g., <li class=\"current\">1</li> <li class=\"\"><a href=\"/page/2\">2</a></li>)\n\t\tnext: /<li[^>]+class\\s*=\\s*['\"][^'\"]*(?:current|active)[^'\"]*['\"][^>]*>.*?<\\/li>\\s*<li[^>]*>\\s*<a\\s+(?<attributes>[^>]+)>/is,\n\t\t// Extracts attributes of an <a> tag within an <li> element that precedes a current/active <li> element (e.g., <li class=\"\"><a href=\"/page/1\">1</a></li> <li class=\"current\">2</li>)\n\t\tprev: /<li[^>]*>\\s*<a\\s+(?<attributes>[^>]+)>.*?<\\/a>\\s*<\\/li>\\s*<li[^>]+class\\s*=\\s*['\"][^'\"]*(?:current|active)[^'\"]*['\"][^>]*>/is,\n\t},\n\tPAGINATION_FALLBACK: {\n\t\t// Extracts attributes of an <a> tag that follows a current/active element (span, a, or strong tag with current/active class or aria-current=\"page\").\n\t\t// This handles cases where there's no <li> element structure or for more general pagination.\n\t\t// Example: <span class=\"current\">1</span> <a href=\"/page/2\">2</a>\n\t\tnext: /(?:<(?:span|a)[^>]+(?:class\\s*=\\s*['\"](?:current|active)['\"]|aria-current\\s*=\\s*['\"]page['\"])|strong)\\s*[^<]*\\s*<\\/(?:span|a|strong)>\\s*<a\\s+(?<attributes>[^>]+)>/i,\n\t\t// Extracts attributes of an <a> tag that precedes a current/active element.\n\t\t// Example: <a href=\"/page/1\">1</a> <span class=\"current\">2</span>\n\t\tprev: /<a\\s+(?<attributes>[^>]+)>.*?<\\/a>\\s*(?:<(?:span|a)[^>]+(?:class\\s*=\\s*['\"](?:current|active)['\"]|aria-current\\s*=\\s*['\"]page['\"])|strong)\\s*[^<]*/i,\n\t},\n\t// Common Regex\n\tPOTENTIAL_LINK_TAGS: /<(?:a|link)\\s+(?<attributes>[^>]*?)>/gi,\n\tANCHOR_TAG: /<a\\s+(?<attributes>[^>]+)>(?<innerText>.*?)<\\/a>/gis,\n\tANCHOR_TAG_START: /<a\\s+(?<attributes>[^>]+)>/gi,\n\tPAGINATION_CONTAINER:\n\t\t/<(?:div|nav|ul)[^>]+(?:class|id)\\s*=\\s*['\"][^'\"]*(?:pagination|pager|page-nav)[^'\"]*['\"][^>]*>(?<containerHtml>[\\s\\S]*?)<\\/(?:div|nav|ul)>/gi,\n\tIMG_TAG: /<img[^>]+>/gi,\n\tPATH_PAGE_NUMBER: /^(.*[/\\-_])(\\d+)$/,\n\tHTML_TAGS: /<[^>]+>/g,\n\tHTML_ENTITIES: /&[a-z]+;|&#[0-9]+;|&#x[0-9a-f]+;/gi,\n};\n\nconst DEFAULT_TIMEOUT_MS = 8000;\n\nconst attributeRegexCache = new Map<string, RegExp>();\n\n/**\n * Extracts the value of a specified attribute from an attribute string.\n * @param attributes The string containing attributes.\n * @param attributeName The name of the attribute to extract (e.g., \"href\", \"class\", \"id\").\n * @returns The value of the attribute, or null if not found.\n */\nfunction extractAttribute(\n\tattributes: string,\n\tattributeName: string,\n): string | null {\n\tlet regex = attributeRegexCache.get(attributeName);\n\tif (!regex) {\n\t\tregex = new RegExp(\n\t\t\t`${attributeName}\\\\s*=\\\\s*(['\"])(?<value>[^\"']*)\\\\1`,\n\t\t\t\"i\",\n\t\t);\n\t\tattributeRegexCache.set(attributeName, regex);\n\t}\n\tconst match = attributes.match(regex);\n\treturn match?.groups?.value ?? null;\n}\n\n/**\n * Extracts the href attribute from an attribute string and converts it to an absolute URL.\n * @param attributes The string containing attributes.\n * @param baseUrl The base URL for resolving relative paths.\n * @returns The absolute URL, or null if not found/invalid.\n */\nfunction extractAbsoluteHref(\n\tattributes: string,\n\tbaseUrl: string,\n\toptions?: BaseOptions,\n): string | null {\n\tconst href = extractAttribute(attributes, \"href\");\n\tif (href) {\n\t\ttry {\n\t\t\treturn new URL(href, baseUrl).href;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\toptions?.logger?.(\n\t\t\t\t\"warn\",\n\t\t\t\t`Invalid URL '${href}' for base '${baseUrl}': ${message}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Asynchronously fetches HTML content from a given URL.\n * @param url The URL to fetch.\n * @param options Options.\n * @returns The HTML string, or null if fetching failed.\n */\nexport async function fetchHtml(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(\n\t\t() => controller.abort(),\n\t\toptions?.timeout ?? DEFAULT_TIMEOUT_MS,\n\t);\n\n\ttry {\n\t\tconst response = await fetch(url, { signal: controller.signal });\n\n\t\tif (!response.ok) {\n\t\t\toptions?.logger?.(\n\t\t\t\t\"warn\",\n\t\t\t\t`Failed to fetch ${url}: ${response.status} ${response.statusText}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst contentType = response.headers.get(\"content-type\");\n\t\tif (!contentType || !contentType.includes(\"text/html\")) {\n\t\t\toptions?.logger?.(\"warn\", `URL ${url} did not return HTML content.`);\n\t\t\treturn null;\n\t\t}\n\n\t\treturn await response.text();\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\"error\", `Error fetching or parsing ${url}: ${message}`);\n\t\treturn null;\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t}\n}\n\nfunction findLinkByRel(\n\thtml: string,\n\tbaseUrl: string,\n\trelRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst allPotentialLinks = html.matchAll(REGEX.POTENTIAL_LINK_TAGS);\n\n\tfor (const match of allPotentialLinks) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (relRegex.test(attributes)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByPaginationStructure(\n\thtml: string,\n\tbaseUrl: string,\n\tliRegex: RegExp,\n\tfallbackRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst paginationContainers = html.matchAll(REGEX.PAGINATION_CONTAINER);\n\n\tfor (const match of paginationContainers) {\n\t\tconst containerHtml = match.groups?.containerHtml;\n\n\t\tif (!containerHtml) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst liMatch = containerHtml.match(liRegex);\n\t\tif (liMatch?.groups?.attributes) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(\n\t\t\t\tliMatch.groups.attributes,\n\t\t\t\tbaseUrl,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\n\t\tconst fallbackMatch = containerHtml.match(fallbackRegex);\n\t\tif (fallbackMatch?.groups?.attributes) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(\n\t\t\t\tfallbackMatch.groups.attributes,\n\t\t\t\tbaseUrl,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByText(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tconst innerText = match.groups?.innerText;\n\n\t\tif (!attributes || !innerText) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst cleanText = innerText\n\t\t\t.replace(REGEX.HTML_TAGS, \"\")\n\t\t\t.replace(REGEX.HTML_ENTITIES, \"\")\n\t\t\t.trim();\n\t\tif (!cleanText) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (textRegex.test(cleanText)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByClassName(\n\thtml: string,\n\tbaseUrl: string,\n\tdefaultRegex: RegExp,\n\tclassNameRegex?: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG_START);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst classAttr = extractAttribute(attributes, \"class\");\n\t\tconst idAttr = extractAttribute(attributes, \"id\");\n\n\t\tif (\n\t\t\t(classAttr && (classNameRegex ?? defaultRegex).test(classAttr)) ||\n\t\t\t(idAttr && defaultRegex.test(idAttr))\n\t\t) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByAriaLabel(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG_START);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst ariaLabelAttr = extractAttribute(attributes, \"aria-label\");\n\t\tif (ariaLabelAttr && textRegex.test(ariaLabelAttr)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByAltText(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tconst innerHtml = match.groups?.innerText;\n\n\t\tif (!attributes || !innerHtml) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst imgTags = innerHtml.matchAll(REGEX.IMG_TAG);\n\t\tfor (const [imgTag] of imgTags) {\n\t\t\tconst altText = extractAttribute(imgTag, \"alt\");\n\t\t\tif (altText && textRegex.test(altText.trim())) {\n\t\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\t\tif (absoluteUrl) {\n\t\t\t\t\treturn absoluteUrl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLink(\n\thtml: string,\n\tbaseUrl: string,\n\tdirection: Direction,\n\toptions?: FindNextOptions,\n): string | null {\n\tconst methods: Method[] = options?.methods ?? [\n\t\t\"rel\",\n\t\t\"pagination\",\n\t\t\"text\",\n\t\t\"className\",\n\t\t\"aria-label\",\n\t\t\"alt\",\n\t];\n\n\t// Dispatch table (map of strategies)\n\tconst strategies: { [key in Method]: () => string | null } = {\n\t\trel: () => findLinkByRel(html, baseUrl, REGEX.REL[direction], options),\n\t\tpagination: () =>\n\t\t\tfindLinkByPaginationStructure(\n\t\t\t\thtml,\n\t\t\t\tbaseUrl,\n\t\t\t\tREGEX.PAGINATION_LI[direction],\n\t\t\t\tREGEX.PAGINATION_FALLBACK[direction],\n\t\t\t\toptions,\n\t\t\t),\n\t\ttext: () => findLinkByText(html, baseUrl, REGEX.TEXT[direction], options),\n\t\tclassName: () =>\n\t\t\tfindLinkByClassName(\n\t\t\t\thtml,\n\t\t\t\tbaseUrl,\n\t\t\t\tREGEX.CLASS_NAME[direction],\n\t\t\t\toptions?.classNameRegex,\n\t\t\t\toptions,\n\t\t\t),\n\t\t\"aria-label\": () =>\n\t\t\tfindLinkByAriaLabel(html, baseUrl, REGEX.TEXT[direction], options),\n\t\talt: () => findLinkByAltText(html, baseUrl, REGEX.TEXT[direction], options),\n\t};\n\n\tfor (const method of methods) {\n\t\tconst url = strategies[method]();\n\t\tif (url) {\n\t\t\treturn url;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Attempts multiple strategies in order to find the next page link URL from an HTML string.\n * @param html The HTML string to parse.\n * @param baseUrl The base URL from which the HTML was fetched (for resolving relative paths).\n * @param options Options for specifying search strategies.\n * @returns The URL of the next page, or null if not found.\n */\nexport function findNext(\n\thtml: string,\n\tbaseUrl: string,\n\toptions?: FindNextOptions,\n): string | null {\n\treturn findLink(html, baseUrl, \"next\", options);\n}\n\n/**\n * Attempts multiple strategies in order to find the previous page link URL from an HTML string.\n * @param html The HTML string to parse.\n * @param baseUrl The base URL from which the HTML was fetched.\n * @param options Options for specifying search strategies.\n * @returns The URL of the previous page, or null if not found.\n */\nexport function findPrev(\n\thtml: string,\n\tbaseUrl: string,\n\toptions?: FindNextOptions,\n): string | null {\n\treturn findLink(html, baseUrl, \"prev\", options);\n}\n\n/**\n * Checks if a URL actually exists using a HEAD request.\n * @param url The URL to check.\n * @returns True if the URL exists, false otherwise.\n */\nasync function urlExists(url: string, options?: BaseOptions): Promise<boolean> {\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(\n\t\t() => controller.abort(),\n\t\toptions?.timeout ?? DEFAULT_TIMEOUT_MS,\n\t);\n\n\ttry {\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"HEAD\",\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\treturn response.ok;\n\t} catch {\n\t\treturn false;\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t}\n}\n\nasync function findUrlByQueryParam(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\ttry {\n\t\tconst urlObject = new URL(url);\n\t\tconst params = urlObject.searchParams;\n\t\tlet targetKey: string | null = null;\n\t\tlet currentValue: number | null = null;\n\n\t\tfor (const key of [\"page\", \"p\", \"index\"]) {\n\t\t\tconst value = params.get(key);\n\t\t\tif (value) {\n\t\t\t\tconst num = parseInt(value, 10);\n\t\t\t\tif (!Number.isNaN(num)) {\n\t\t\t\t\ttargetKey = key;\n\t\t\t\t\tcurrentValue = num;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (targetKey && currentValue !== null) {\n\t\t\tconst newNumber =\n\t\t\t\tdirection === \"next\" ? currentValue + 1 : currentValue - 1;\n\n\t\t\tif (newNumber > 0) {\n\t\t\t\tparams.set(targetKey, String(newNumber));\n\t\t\t\tconst newUrl = urlObject.toString();\n\t\t\t\tif (\n\t\t\t\t\toptions?.verifyExists === false ||\n\t\t\t\t\t(await urlExists(newUrl, options))\n\t\t\t\t) {\n\t\t\t\t\treturn newUrl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\n\t\t\t\"warn\",\n\t\t\t`Invalid URL provided to findUrlByQueryParam: ${message}`,\n\t\t);\n\t}\n\n\treturn null;\n}\n\nasync function findUrlByPathSegment(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\ttry {\n\t\tconst urlObject = new URL(url);\n\t\tconst pathname = urlObject.pathname.replace(/\\/$/, \"\");\n\t\tconst pathMatch = pathname.match(REGEX.PATH_PAGE_NUMBER);\n\n\t\tif (pathMatch) {\n\t\t\tconst [_, prefix, currentNumberStr] = pathMatch;\n\t\t\tif (prefix && currentNumberStr) {\n\t\t\t\tconst currentNumber = parseInt(currentNumberStr, 10);\n\t\t\t\tconst newNumber =\n\t\t\t\t\tdirection === \"next\" ? currentNumber + 1 : currentNumber - 1;\n\n\t\t\t\tif (newNumber > 0) {\n\t\t\t\t\tconst newPath = `${prefix}${newNumber}`;\n\t\t\t\t\tconst newUrl = `${urlObject.origin}${newPath}${urlObject.search}${urlObject.hash}`;\n\t\t\t\t\tif (\n\t\t\t\t\t\toptions?.verifyExists === false ||\n\t\t\t\t\t\t(await urlExists(newUrl, options))\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn newUrl;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\n\t\t\t\"warn\",\n\t\t\t`Invalid URL provided to findUrlByPathSegment: ${message}`,\n\t\t);\n\t}\n\n\treturn null;\n}\n\n/**\n * Asynchronously infers and finds the next/previous page URL based on URL patterns.\n * @param url The URL of the current page.\n * @param direction \"next\" or \"prev\".\n * @param options Options for URL pattern inference.\n * @returns The next/previous page URL, or null if not found.\n */\nasync function findUrlByPattern(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\tconst urlByQuery = await findUrlByQueryParam(url, direction, options);\n\tif (urlByQuery) {\n\t\treturn urlByQuery;\n\t}\n\n\tconst urlByPath = await findUrlByPathSegment(url, direction, options);\n\tif (urlByPath) {\n\t\treturn urlByPath;\n\t}\n\n\treturn null;\n}\n\n/**\n * Asynchronously infers and finds the next page URL based on URL patterns.\n * Increments the page number in the URL and checks if the URL exists.\n * @param url The URL of the current page.\n * @param options Options for URL pattern inference.\n * @returns The next page URL, or null if not found.\n */\nexport function findNextByUrl(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\treturn findUrlByPattern(url, \"next\", options);\n}\n\n/**\n * Asynchronously infers and finds the previous page URL based on URL patterns.\n * @param url The URL of the current page.\n * @param options Options for URL pattern inference.\n * @returns The previous page URL, or null if not found.\n */\nexport function findPrevByUrl(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\treturn findUrlByPattern(url, \"prev\", options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["// src/index.ts\n\nexport interface BaseOptions {\n\tlogger?: (level: \"warn\" | \"error\", message: string) => void;\n\ttimeout?: number;\n\tverifyExists?: boolean;\n}\n\nexport type Method =\n\t| \"rel\"\n\t| \"pagination\"\n\t| \"text\"\n\t| \"className\"\n\t| \"aria-label\"\n\t| \"alt\";\n\nexport interface FindNextOptions extends BaseOptions {\n\tmethods?: Method[];\n\tclassNameRegex?: RegExp;\n}\n\ntype Direction = \"next\" | \"prev\";\n\nconst REGEX = {\n\tREL: {\n\t\tnext: /rel\\s*=\\s*(['\"])[^'\"]*?\\bnext\\b[^'\"]*?\\1/i,\n\t\tprev: /rel\\s*=\\s*(['\"])[^'\"]*?\\b(prev|previous)\\b[^'\"]*?\\1/i,\n\t},\n\tTEXT: {\n\t\tnext: /^\\s*(((Next)\\s*(page)?|older|forward)|((次|つぎ)(のページ)?(へ)?)|((下|后)\\s*(一)?(页|頁))|(다음)|»|>|→)\\s*[»>→]*$/i,\n\t\tprev: /^\\s*[«<←]*\\s*(((Prev|Previous)\\s*(page)?|older|back)|((前)(のページ)?(へ)?)|((上|前)\\s*(一)?(页|頁))|(이전)|«|<|←)\\s*$/i,\n\t},\n\tCLASS_NAME: {\n\t\tnext: /next/i,\n\t\tprev: /prev|previous/i,\n\t},\n\tPAGINATION_LI: {\n\t\t// Extracts attributes of an <a> tag within an <li> element that follows a current/active <li> element (e.g., <li class=\"current\">1</li> <li class=\"\"><a href=\"/page/2\">2</a></li>)\n\t\tnext: /<li[^>]+class\\s*=\\s*['\"][^'\"]*(?:current|active)[^'\"]*['\"][^>]*>.*?<\\/li>\\s*<li[^>]*>\\s*<a\\s+(?<attributes>[^>]+)>/is,\n\t\t// Extracts attributes of an <a> tag within an <li> element that precedes a current/active <li> element (e.g., <li class=\"\"><a href=\"/page/1\">1</a></li> <li class=\"current\">2</li>)\n\t\tprev: /<li[^>]*>\\s*<a\\s+(?<attributes>[^>]+)>.*?<\\/a>\\s*<\\/li>\\s*<li[^>]+class\\s*=\\s*['\"][^'\"]*(?:current|active)[^'\"]*['\"][^>]*>/is,\n\t},\n\tPAGINATION_FALLBACK: {\n\t\t// Extracts attributes of an <a> tag that follows a current/active element (span, a, or strong tag with current/active class or aria-current=\"page\").\n\t\t// This handles cases where there's no <li> element structure or for more general pagination.\n\t\t// Example: <span class=\"current\">1</span> <a href=\"/page/2\">2</a>\n\t\tnext: /(?:<(?:span|a)[^>]+(?:class\\s*=\\s*['\"](?:current|active)['\"]|aria-current\\s*=\\s*['\"]page['\"])|strong)\\s*[^<]*\\s*<\\/(?:span|a|strong)>\\s*<a\\s+(?<attributes>[^>]+)>/i,\n\t\t// Extracts attributes of an <a> tag that precedes a current/active element.\n\t\t// Example: <a href=\"/page/1\">1</a> <span class=\"current\">2</span>\n\t\tprev: /<a\\s+(?<attributes>[^>]+)>.*?<\\/a>\\s*(?:<(?:span|a)[^>]+(?:class\\s*=\\s*['\"](?:current|active)['\"]|aria-current\\s*=\\s*['\"]page['\"])|strong)\\s*[^<]*/i,\n\t},\n\t// Common Regex\n\tPOTENTIAL_LINK_TAGS: /<(?:a|link)\\s+(?<attributes>[^>]*?)>/gi,\n\tANCHOR_TAG: /<a\\s+(?<attributes>[^>]+)>(?<innerText>.*?)<\\/a>/gis,\n\tANCHOR_TAG_START: /<a\\s+(?<attributes>[^>]+)>/gi,\n\tPAGINATION_CONTAINER:\n\t\t/<(?:div|nav|ul)[^>]+(?:class|id)\\s*=\\s*['\"][^'\"]*(?:pagination|pager|page-nav)[^'\"]*['\"][^>]*>(?<containerHtml>[\\s\\S]*?)<\\/(?:div|nav|ul)>/gi,\n\tIMG_TAG: /<img[^>]+>/gi,\n\tPATH_PAGE_NUMBER: /^(.*[/\\-_])(\\d+)$/,\n\tHTML_TAGS: /<[^>]+>/g,\n\tHTML_ENTITIES: /&[a-z]+;|&#[0-9]+;|&#x[0-9a-f]+;/gi,\n};\n\nconst DEFAULT_TIMEOUT_MS = 8000;\n\nconst attributeRegexCache = new Map<string, RegExp>();\n\n/**\n * Extracts the value of a specified attribute from an attribute string.\n * @param attributes The string containing attributes.\n * @param attributeName The name of the attribute to extract (e.g., \"href\", \"class\", \"id\").\n * @returns The value of the attribute, or null if not found.\n */\nfunction extractAttribute(\n\tattributes: string,\n\tattributeName: string,\n): string | null {\n\tlet regex = attributeRegexCache.get(attributeName);\n\tif (!regex) {\n\t\tregex = new RegExp(\n\t\t\t`${attributeName}\\\\s*=\\\\s*(['\"])(?<value>[^\"']*)\\\\1`,\n\t\t\t\"i\",\n\t\t);\n\t\tattributeRegexCache.set(attributeName, regex);\n\t}\n\tconst match = attributes.match(regex);\n\treturn match?.groups?.value ?? null;\n}\n\n/**\n * Extracts the href attribute from an attribute string and converts it to an absolute URL.\n * @param attributes The string containing attributes.\n * @param baseUrl The base URL for resolving relative paths.\n * @returns The absolute URL, or null if not found/invalid.\n */\nfunction extractAbsoluteHref(\n\tattributes: string,\n\tbaseUrl: string,\n\toptions?: BaseOptions,\n): string | null {\n\tconst href = extractAttribute(attributes, \"href\");\n\tif (href) {\n\t\tconst decodedHref = href.replace(/&amp;/g, \"&\");\n\t\ttry {\n\t\t\treturn new URL(decodedHref, baseUrl).href;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\toptions?.logger?.(\n\t\t\t\t\"warn\",\n\t\t\t\t`Invalid URL '${href}' for base '${baseUrl}': ${message}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Asynchronously fetches HTML content from a given URL.\n * @param url The URL to fetch.\n * @param options Options.\n * @returns The HTML string, or null if fetching failed.\n */\nexport async function fetchHtml(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(\n\t\t() => controller.abort(),\n\t\toptions?.timeout ?? DEFAULT_TIMEOUT_MS,\n\t);\n\n\ttry {\n\t\tconst response = await fetch(url, { signal: controller.signal });\n\n\t\tif (!response.ok) {\n\t\t\toptions?.logger?.(\n\t\t\t\t\"warn\",\n\t\t\t\t`Failed to fetch ${url}: ${response.status} ${response.statusText}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst contentType = response.headers.get(\"content-type\");\n\t\tif (!contentType || !contentType.includes(\"text/html\")) {\n\t\t\toptions?.logger?.(\"warn\", `URL ${url} did not return HTML content.`);\n\t\t\treturn null;\n\t\t}\n\n\t\treturn await response.text();\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\"error\", `Error fetching or parsing ${url}: ${message}`);\n\t\treturn null;\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t}\n}\n\nfunction findLinkByRel(\n\thtml: string,\n\tbaseUrl: string,\n\trelRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst allPotentialLinks = html.matchAll(REGEX.POTENTIAL_LINK_TAGS);\n\n\tfor (const match of allPotentialLinks) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (relRegex.test(attributes)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByPaginationStructure(\n\thtml: string,\n\tbaseUrl: string,\n\tliRegex: RegExp,\n\tfallbackRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst paginationContainers = html.matchAll(REGEX.PAGINATION_CONTAINER);\n\n\tfor (const match of paginationContainers) {\n\t\tconst containerHtml = match.groups?.containerHtml;\n\n\t\tif (!containerHtml) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst liMatch = containerHtml.match(liRegex);\n\t\tif (liMatch?.groups?.attributes) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(\n\t\t\t\tliMatch.groups.attributes,\n\t\t\t\tbaseUrl,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\n\t\tconst fallbackMatch = containerHtml.match(fallbackRegex);\n\t\tif (fallbackMatch?.groups?.attributes) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(\n\t\t\t\tfallbackMatch.groups.attributes,\n\t\t\t\tbaseUrl,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByText(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tconst innerText = match.groups?.innerText;\n\n\t\tif (!attributes || !innerText) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst cleanText = innerText\n\t\t\t.replace(REGEX.HTML_TAGS, \"\")\n\t\t\t.replace(REGEX.HTML_ENTITIES, \"\")\n\t\t\t.trim();\n\t\tif (!cleanText) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (textRegex.test(cleanText)) {\n\t\t\tconsole.log(textRegex, cleanText);\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByClassName(\n\thtml: string,\n\tbaseUrl: string,\n\tdefaultRegex: RegExp,\n\tclassNameRegex?: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG_START);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst classAttr = extractAttribute(attributes, \"class\");\n\t\tconst idAttr = extractAttribute(attributes, \"id\");\n\n\t\tif (\n\t\t\t(classAttr && (classNameRegex ?? defaultRegex).test(classAttr)) ||\n\t\t\t(idAttr && defaultRegex.test(idAttr))\n\t\t) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByAriaLabel(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG_START);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst ariaLabelAttr = extractAttribute(attributes, \"aria-label\");\n\t\tif (ariaLabelAttr && textRegex.test(ariaLabelAttr)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByAltText(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tconst innerHtml = match.groups?.innerText;\n\n\t\tif (!attributes || !innerHtml) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst imgTags = innerHtml.matchAll(REGEX.IMG_TAG);\n\t\tfor (const [imgTag] of imgTags) {\n\t\t\tconst altText = extractAttribute(imgTag, \"alt\");\n\t\t\tif (altText && textRegex.test(altText.trim())) {\n\t\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\t\tif (absoluteUrl) {\n\t\t\t\t\treturn absoluteUrl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLink(\n\thtml: string,\n\tbaseUrl: string,\n\tdirection: Direction,\n\toptions?: FindNextOptions,\n): string | null {\n\tconst methods: Method[] = options?.methods ?? [\n\t\t\"rel\",\n\t\t\"pagination\",\n\t\t\"text\",\n\t\t\"className\",\n\t\t\"aria-label\",\n\t\t\"alt\",\n\t];\n\n\t// Dispatch table (map of strategies)\n\tconst strategies: { [key in Method]: () => string | null } = {\n\t\trel: () => findLinkByRel(html, baseUrl, REGEX.REL[direction], options),\n\t\tpagination: () =>\n\t\t\tfindLinkByPaginationStructure(\n\t\t\t\thtml,\n\t\t\t\tbaseUrl,\n\t\t\t\tREGEX.PAGINATION_LI[direction],\n\t\t\t\tREGEX.PAGINATION_FALLBACK[direction],\n\t\t\t\toptions,\n\t\t\t),\n\t\ttext: () => findLinkByText(html, baseUrl, REGEX.TEXT[direction], options),\n\t\tclassName: () =>\n\t\t\tfindLinkByClassName(\n\t\t\t\thtml,\n\t\t\t\tbaseUrl,\n\t\t\t\tREGEX.CLASS_NAME[direction],\n\t\t\t\toptions?.classNameRegex,\n\t\t\t\toptions,\n\t\t\t),\n\t\t\"aria-label\": () =>\n\t\t\tfindLinkByAriaLabel(html, baseUrl, REGEX.TEXT[direction], options),\n\t\talt: () => findLinkByAltText(html, baseUrl, REGEX.TEXT[direction], options),\n\t};\n\n\tfor (const method of methods) {\n\t\tconst url = strategies[method]();\n\t\tif (url) {\n\t\t\treturn url;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Attempts multiple strategies in order to find the next page link URL from an HTML string.\n * @param html The HTML string to parse.\n * @param baseUrl The base URL from which the HTML was fetched (for resolving relative paths).\n * @param options Options for specifying search strategies.\n * @returns The URL of the next page, or null if not found.\n */\nexport function findNext(\n\thtml: string,\n\tbaseUrl: string,\n\toptions?: FindNextOptions,\n): string | null {\n\treturn findLink(html, baseUrl, \"next\", options);\n}\n\n/**\n * Attempts multiple strategies in order to find the previous page link URL from an HTML string.\n * @param html The HTML string to parse.\n * @param baseUrl The base URL from which the HTML was fetched.\n * @param options Options for specifying search strategies.\n * @returns The URL of the previous page, or null if not found.\n */\nexport function findPrev(\n\thtml: string,\n\tbaseUrl: string,\n\toptions?: FindNextOptions,\n): string | null {\n\treturn findLink(html, baseUrl, \"prev\", options);\n}\n\n/**\n * Checks if a URL actually exists using a HEAD request.\n * @param url The URL to check.\n * @returns True if the URL exists, false otherwise.\n */\nasync function urlExists(url: string, options?: BaseOptions): Promise<boolean> {\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(\n\t\t() => controller.abort(),\n\t\toptions?.timeout ?? DEFAULT_TIMEOUT_MS,\n\t);\n\n\ttry {\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"HEAD\",\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\treturn response.ok;\n\t} catch {\n\t\treturn false;\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t}\n}\n\nasync function findUrlByQueryParam(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\ttry {\n\t\tconst urlObject = new URL(url);\n\t\tconst params = urlObject.searchParams;\n\t\tlet targetKey: string | null = null;\n\t\tlet currentValue: number | null = null;\n\n\t\tfor (const key of [\"page\", \"p\", \"index\"]) {\n\t\t\tconst value = params.get(key);\n\t\t\tif (value) {\n\t\t\t\tconst num = parseInt(value, 10);\n\t\t\t\tif (!Number.isNaN(num)) {\n\t\t\t\t\ttargetKey = key;\n\t\t\t\t\tcurrentValue = num;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (targetKey && currentValue !== null) {\n\t\t\tconst newNumber =\n\t\t\t\tdirection === \"next\" ? currentValue + 1 : currentValue - 1;\n\n\t\t\tif (newNumber > 0) {\n\t\t\t\tparams.set(targetKey, String(newNumber));\n\t\t\t\tconst newUrl = urlObject.toString();\n\t\t\t\tif (\n\t\t\t\t\toptions?.verifyExists === false ||\n\t\t\t\t\t(await urlExists(newUrl, options))\n\t\t\t\t) {\n\t\t\t\t\treturn newUrl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\n\t\t\t\"warn\",\n\t\t\t`Invalid URL provided to findUrlByQueryParam: ${message}`,\n\t\t);\n\t}\n\n\treturn null;\n}\n\nasync function findUrlByPathSegment(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\ttry {\n\t\tconst urlObject = new URL(url);\n\t\tconst pathname = urlObject.pathname.replace(/\\/$/, \"\");\n\t\tconst pathMatch = pathname.match(REGEX.PATH_PAGE_NUMBER);\n\n\t\tif (pathMatch) {\n\t\t\tconst [_, prefix, currentNumberStr] = pathMatch;\n\t\t\tif (prefix && currentNumberStr) {\n\t\t\t\tconst currentNumber = parseInt(currentNumberStr, 10);\n\t\t\t\tconst newNumber =\n\t\t\t\t\tdirection === \"next\" ? currentNumber + 1 : currentNumber - 1;\n\n\t\t\t\tif (newNumber > 0) {\n\t\t\t\t\tconst newPath = `${prefix}${newNumber}`;\n\t\t\t\t\tconst newUrl = `${urlObject.origin}${newPath}${urlObject.search}${urlObject.hash}`;\n\t\t\t\t\tif (\n\t\t\t\t\t\toptions?.verifyExists === false ||\n\t\t\t\t\t\t(await urlExists(newUrl, options))\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn newUrl;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\n\t\t\t\"warn\",\n\t\t\t`Invalid URL provided to findUrlByPathSegment: ${message}`,\n\t\t);\n\t}\n\n\treturn null;\n}\n\n/**\n * Asynchronously infers and finds the next/previous page URL based on URL patterns.\n * @param url The URL of the current page.\n * @param direction \"next\" or \"prev\".\n * @param options Options for URL pattern inference.\n * @returns The next/previous page URL, or null if not found.\n */\nasync function findUrlByPattern(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\tconst urlByQuery = await findUrlByQueryParam(url, direction, options);\n\tif (urlByQuery) {\n\t\treturn urlByQuery;\n\t}\n\n\tconst urlByPath = await findUrlByPathSegment(url, direction, options);\n\tif (urlByPath) {\n\t\treturn urlByPath;\n\t}\n\n\treturn null;\n}\n\n/**\n * Asynchronously infers and finds the next page URL based on URL patterns.\n * Increments the page number in the URL and checks if the URL exists.\n * @param url The URL of the current page.\n * @param options Options for URL pattern inference.\n * @returns The next page URL, or null if not found.\n */\nexport function findNextByUrl(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\treturn findUrlByPattern(url, \"next\", options);\n}\n\n/**\n * Asynchronously infers and finds the previous page URL based on URL patterns.\n * @param url The URL of the current page.\n * @param options Options for URL pattern inference.\n * @returns The previous page URL, or null if not found.\n */\nexport function findPrevByUrl(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\treturn findUrlByPattern(url, \"prev\", options);\n}\n"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- const e={REL:{next:/rel\s*=\s*(['"])[^'"]*?\bnext\b[^'"]*?\1/i,prev:/rel\s*=\s*(['"])[^'"]*?\b(prev|previous)\b[^'"]*?\1/i},TEXT:{next:/^\s*(((Next)\s*(page)?|older|forward)|((次|つぎ)(のページ)?(へ)?)|((下|后)\s*(一)?(页|頁))|(다음)|»|>|→)\s*>*$/i,prev:/^\s*<*(((Prev|Previous)\s*(page)?|older|back)|((前)(のページ)?(へ)?)|((上|前)\s*(一)?(页|頁))|(이전)|«|<|←)\s*$/i},CLASS_NAME:{next:/next/i,prev:/prev|previous/i},PAGINATION_LI:{next:/<li[^>]+class\s*=\s*['"][^'"]*(?:current|active)[^'"]*['"][^>]*>.*?<\/li>\s*<li[^>]*>\s*<a\s+(?<attributes>[^>]+)>/is,prev:/<li[^>]*>\s*<a\s+(?<attributes>[^>]+)>.*?<\/a>\s*<\/li>\s*<li[^>]+class\s*=\s*['"][^'"]*(?:current|active)[^'"]*['"][^>]*>/is},PAGINATION_FALLBACK:{next:/(?:<(?:span|a)[^>]+(?:class\s*=\s*['"](?:current|active)['"]|aria-current\s*=\s*['"]page['"])|strong)\s*[^<]*\s*<\/(?:span|a|strong)>\s*<a\s+(?<attributes>[^>]+)>/i,prev:/<a\s+(?<attributes>[^>]+)>.*?<\/a>\s*(?:<(?:span|a)[^>]+(?:class\s*=\s*['"](?:current|active)['"]|aria-current\s*=\s*['"]page['"])|strong)\s*[^<]*/i},POTENTIAL_LINK_TAGS:/<(?:a|link)\s+(?<attributes>[^>]*?)>/gi,ANCHOR_TAG:/<a\s+(?<attributes>[^>]+)>(?<innerText>.*?)<\/a>/gis,ANCHOR_TAG_START:/<a\s+(?<attributes>[^>]+)>/gi,PAGINATION_CONTAINER:/<(?:div|nav|ul)[^>]+(?:class|id)\s*=\s*['"][^'"]*(?:pagination|pager|page-nav)[^'"]*['"][^>]*>(?<containerHtml>[\s\S]*?)<\/(?:div|nav|ul)>/gi,IMG_TAG:/<img[^>]+>/gi,PATH_PAGE_NUMBER:/^(.*[/\-_])(\d+)$/,HTML_TAGS:/<[^>]+>/g,HTML_ENTITIES:/&[a-z]+;|&#[0-9]+;|&#x[0-9a-f]+;/gi},t=8e3,n=new Map;function r(e,t){let r=n.get(t);return r||(r=RegExp(`${t}\\s*=\\s*(['"])(?<value>[^"']*)\\1`,`i`),n.set(t,r)),e.match(r)?.groups?.value??null}function i(e,t,n){let i=r(e,`href`);if(i)try{return new URL(i,t).href}catch(e){let r=e instanceof Error?e.message:String(e);return n?.logger?.(`warn`,`Invalid URL '${i}' for base '${t}': ${r}`),null}return null}async function a(e,n){let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??t);try{let t=await fetch(e,{signal:r.signal});if(!t.ok)return n?.logger?.(`warn`,`Failed to fetch ${e}: ${t.status} ${t.statusText}`),null;let i=t.headers.get(`content-type`);return!i||!i.includes(`text/html`)?(n?.logger?.(`warn`,`URL ${e} did not return HTML content.`),null):await t.text()}catch(t){let r=t instanceof Error?t.message:String(t);return n?.logger?.(`error`,`Error fetching or parsing ${e}: ${r}`),null}finally{clearTimeout(i)}}function o(t,n,r,a){let o=t.matchAll(e.POTENTIAL_LINK_TAGS);for(let e of o){let t=e.groups?.attributes;if(t&&r.test(t)){let e=i(t,n,a);if(e)return e}}return null}function s(t,n,r,a,o){let s=t.matchAll(e.PAGINATION_CONTAINER);for(let e of s){let t=e.groups?.containerHtml;if(!t)continue;let s=t.match(r);if(s?.groups?.attributes){let e=i(s.groups.attributes,n,o);if(e)return e}let c=t.match(a);if(c?.groups?.attributes){let e=i(c.groups.attributes,n,o);if(e)return e}}return null}function c(t,n,r,a){let o=t.matchAll(e.ANCHOR_TAG);for(let t of o){let o=t.groups?.attributes,s=t.groups?.innerText;if(!o||!s)continue;let c=s.replace(e.HTML_TAGS,``).replace(e.HTML_ENTITIES,``).trim();if(c&&r.test(c)){let e=i(o,n,a);if(e)return e}}return null}function l(t,n,a,o,s){let c=t.matchAll(e.ANCHOR_TAG_START);for(let e of c){let t=e.groups?.attributes;if(!t)continue;let c=r(t,`class`),l=r(t,`id`);if(c&&(o??a).test(c)||l&&a.test(l)){let e=i(t,n,s);if(e)return e}}return null}function u(t,n,a,o){let s=t.matchAll(e.ANCHOR_TAG_START);for(let e of s){let t=e.groups?.attributes;if(!t)continue;let s=r(t,`aria-label`);if(s&&a.test(s)){let e=i(t,n,o);if(e)return e}}return null}function d(t,n,a,o){let s=t.matchAll(e.ANCHOR_TAG);for(let t of s){let s=t.groups?.attributes,c=t.groups?.innerText;if(!s||!c)continue;let l=c.matchAll(e.IMG_TAG);for(let[e]of l){let t=r(e,`alt`);if(t&&a.test(t.trim())){let e=i(s,n,o);if(e)return e}}}return null}function f(t,n,r,i){let a=i?.methods??[`rel`,`pagination`,`text`,`className`,`aria-label`,`alt`],f={rel:()=>o(t,n,e.REL[r],i),pagination:()=>s(t,n,e.PAGINATION_LI[r],e.PAGINATION_FALLBACK[r],i),text:()=>c(t,n,e.TEXT[r],i),className:()=>l(t,n,e.CLASS_NAME[r],i?.classNameRegex,i),"aria-label":()=>u(t,n,e.TEXT[r],i),alt:()=>d(t,n,e.TEXT[r],i)};for(let e of a){let t=f[e]();if(t)return t}return null}function p(e,t,n){return f(e,t,`next`,n)}function m(e,t,n){return f(e,t,`prev`,n)}async function h(e,n){let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??t);try{return(await fetch(e,{method:`HEAD`,signal:r.signal})).ok}catch{return!1}finally{clearTimeout(i)}}async function g(e,t,n){try{let r=new URL(e),i=r.searchParams,a=null,o=null;for(let e of[`page`,`p`,`index`]){let t=i.get(e);if(t){let n=parseInt(t,10);if(!Number.isNaN(n)){a=e,o=n;break}}}if(a&&o!==null){let e=t===`next`?o+1:o-1;if(e>0){i.set(a,String(e));let t=r.toString();if(n?.verifyExists===!1||await h(t,n))return t}}}catch(e){let t=e instanceof Error?e.message:String(e);n?.logger?.(`warn`,`Invalid URL provided to findUrlByQueryParam: ${t}`)}return null}async function _(t,n,r){try{let i=new URL(t),a=i.pathname.replace(/\/$/,``).match(e.PATH_PAGE_NUMBER);if(a){let[e,t,o]=a;if(t&&o){let e=parseInt(o,10),a=n===`next`?e+1:e-1;if(a>0){let e=`${t}${a}`,n=`${i.origin}${e}${i.search}${i.hash}`;if(r?.verifyExists===!1||await h(n,r))return n}}}}catch(e){let t=e instanceof Error?e.message:String(e);r?.logger?.(`warn`,`Invalid URL provided to findUrlByPathSegment: ${t}`)}return null}async function v(e,t,n){return await g(e,t,n)||await _(e,t,n)||null}function y(e,t){return v(e,`next`,t)}function b(e,t){return v(e,`prev`,t)}export{a as fetchHtml,p as findNext,y as findNextByUrl,m as findPrev,b as findPrevByUrl};
1
+ const e={REL:{next:/rel\s*=\s*(['"])[^'"]*?\bnext\b[^'"]*?\1/i,prev:/rel\s*=\s*(['"])[^'"]*?\b(prev|previous)\b[^'"]*?\1/i},TEXT:{next:/^\s*(((Next)\s*(page)?|older|forward)|((次|つぎ)(のページ)?(へ)?)|((下|后)\s*(一)?(页|頁))|(다음)|»|>|→)\s*[»>→]*$/i,prev:/^\s*[«<←]*\s*(((Prev|Previous)\s*(page)?|older|back)|((前)(のページ)?(へ)?)|((上|前)\s*(一)?(页|頁))|(이전)|«|<|←)\s*$/i},CLASS_NAME:{next:/next/i,prev:/prev|previous/i},PAGINATION_LI:{next:/<li[^>]+class\s*=\s*['"][^'"]*(?:current|active)[^'"]*['"][^>]*>.*?<\/li>\s*<li[^>]*>\s*<a\s+(?<attributes>[^>]+)>/is,prev:/<li[^>]*>\s*<a\s+(?<attributes>[^>]+)>.*?<\/a>\s*<\/li>\s*<li[^>]+class\s*=\s*['"][^'"]*(?:current|active)[^'"]*['"][^>]*>/is},PAGINATION_FALLBACK:{next:/(?:<(?:span|a)[^>]+(?:class\s*=\s*['"](?:current|active)['"]|aria-current\s*=\s*['"]page['"])|strong)\s*[^<]*\s*<\/(?:span|a|strong)>\s*<a\s+(?<attributes>[^>]+)>/i,prev:/<a\s+(?<attributes>[^>]+)>.*?<\/a>\s*(?:<(?:span|a)[^>]+(?:class\s*=\s*['"](?:current|active)['"]|aria-current\s*=\s*['"]page['"])|strong)\s*[^<]*/i},POTENTIAL_LINK_TAGS:/<(?:a|link)\s+(?<attributes>[^>]*?)>/gi,ANCHOR_TAG:/<a\s+(?<attributes>[^>]+)>(?<innerText>.*?)<\/a>/gis,ANCHOR_TAG_START:/<a\s+(?<attributes>[^>]+)>/gi,PAGINATION_CONTAINER:/<(?:div|nav|ul)[^>]+(?:class|id)\s*=\s*['"][^'"]*(?:pagination|pager|page-nav)[^'"]*['"][^>]*>(?<containerHtml>[\s\S]*?)<\/(?:div|nav|ul)>/gi,IMG_TAG:/<img[^>]+>/gi,PATH_PAGE_NUMBER:/^(.*[/\-_])(\d+)$/,HTML_TAGS:/<[^>]+>/g,HTML_ENTITIES:/&[a-z]+;|&#[0-9]+;|&#x[0-9a-f]+;/gi},t=8e3,n=new Map;function r(e,t){let r=n.get(t);return r||(r=RegExp(`${t}\\s*=\\s*(['"])(?<value>[^"']*)\\1`,`i`),n.set(t,r)),e.match(r)?.groups?.value??null}function i(e,t,n){let i=r(e,`href`);if(i){let e=i.replace(/&amp;/g,`&`);try{return new URL(e,t).href}catch(e){let r=e instanceof Error?e.message:String(e);return n?.logger?.(`warn`,`Invalid URL '${i}' for base '${t}': ${r}`),null}}return null}async function a(e,n){let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??t);try{let t=await fetch(e,{signal:r.signal});if(!t.ok)return n?.logger?.(`warn`,`Failed to fetch ${e}: ${t.status} ${t.statusText}`),null;let i=t.headers.get(`content-type`);return!i||!i.includes(`text/html`)?(n?.logger?.(`warn`,`URL ${e} did not return HTML content.`),null):await t.text()}catch(t){let r=t instanceof Error?t.message:String(t);return n?.logger?.(`error`,`Error fetching or parsing ${e}: ${r}`),null}finally{clearTimeout(i)}}function o(t,n,r,a){let o=t.matchAll(e.POTENTIAL_LINK_TAGS);for(let e of o){let t=e.groups?.attributes;if(t&&r.test(t)){let e=i(t,n,a);if(e)return e}}return null}function s(t,n,r,a,o){let s=t.matchAll(e.PAGINATION_CONTAINER);for(let e of s){let t=e.groups?.containerHtml;if(!t)continue;let s=t.match(r);if(s?.groups?.attributes){let e=i(s.groups.attributes,n,o);if(e)return e}let c=t.match(a);if(c?.groups?.attributes){let e=i(c.groups.attributes,n,o);if(e)return e}}return null}function c(t,n,r,a){let o=t.matchAll(e.ANCHOR_TAG);for(let t of o){let o=t.groups?.attributes,s=t.groups?.innerText;if(!o||!s)continue;let c=s.replace(e.HTML_TAGS,``).replace(e.HTML_ENTITIES,``).trim();if(c&&r.test(c)){console.log(r,c);let e=i(o,n,a);if(e)return e}}return null}function l(t,n,a,o,s){let c=t.matchAll(e.ANCHOR_TAG_START);for(let e of c){let t=e.groups?.attributes;if(!t)continue;let c=r(t,`class`),l=r(t,`id`);if(c&&(o??a).test(c)||l&&a.test(l)){let e=i(t,n,s);if(e)return e}}return null}function u(t,n,a,o){let s=t.matchAll(e.ANCHOR_TAG_START);for(let e of s){let t=e.groups?.attributes;if(!t)continue;let s=r(t,`aria-label`);if(s&&a.test(s)){let e=i(t,n,o);if(e)return e}}return null}function d(t,n,a,o){let s=t.matchAll(e.ANCHOR_TAG);for(let t of s){let s=t.groups?.attributes,c=t.groups?.innerText;if(!s||!c)continue;let l=c.matchAll(e.IMG_TAG);for(let[e]of l){let t=r(e,`alt`);if(t&&a.test(t.trim())){let e=i(s,n,o);if(e)return e}}}return null}function f(t,n,r,i){let a=i?.methods??[`rel`,`pagination`,`text`,`className`,`aria-label`,`alt`],f={rel:()=>o(t,n,e.REL[r],i),pagination:()=>s(t,n,e.PAGINATION_LI[r],e.PAGINATION_FALLBACK[r],i),text:()=>c(t,n,e.TEXT[r],i),className:()=>l(t,n,e.CLASS_NAME[r],i?.classNameRegex,i),"aria-label":()=>u(t,n,e.TEXT[r],i),alt:()=>d(t,n,e.TEXT[r],i)};for(let e of a){let t=f[e]();if(t)return t}return null}function p(e,t,n){return f(e,t,`next`,n)}function m(e,t,n){return f(e,t,`prev`,n)}async function h(e,n){let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??t);try{return(await fetch(e,{method:`HEAD`,signal:r.signal})).ok}catch{return!1}finally{clearTimeout(i)}}async function g(e,t,n){try{let r=new URL(e),i=r.searchParams,a=null,o=null;for(let e of[`page`,`p`,`index`]){let t=i.get(e);if(t){let n=parseInt(t,10);if(!Number.isNaN(n)){a=e,o=n;break}}}if(a&&o!==null){let e=t===`next`?o+1:o-1;if(e>0){i.set(a,String(e));let t=r.toString();if(n?.verifyExists===!1||await h(t,n))return t}}}catch(e){let t=e instanceof Error?e.message:String(e);n?.logger?.(`warn`,`Invalid URL provided to findUrlByQueryParam: ${t}`)}return null}async function _(t,n,r){try{let i=new URL(t),a=i.pathname.replace(/\/$/,``).match(e.PATH_PAGE_NUMBER);if(a){let[e,t,o]=a;if(t&&o){let e=parseInt(o,10),a=n===`next`?e+1:e-1;if(a>0){let e=`${t}${a}`,n=`${i.origin}${e}${i.search}${i.hash}`;if(r?.verifyExists===!1||await h(n,r))return n}}}}catch(e){let t=e instanceof Error?e.message:String(e);r?.logger?.(`warn`,`Invalid URL provided to findUrlByPathSegment: ${t}`)}return null}async function v(e,t,n){return await g(e,t,n)||await _(e,t,n)||null}function y(e,t){return v(e,`next`,t)}function b(e,t){return v(e,`prev`,t)}export{a as fetchHtml,p as findNext,y as findNextByUrl,m as findPrev,b as findPrevByUrl};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["// src/index.ts\n\nexport interface BaseOptions {\n\tlogger?: (level: \"warn\" | \"error\", message: string) => void;\n\ttimeout?: number;\n\tverifyExists?: boolean;\n}\n\nexport type Method =\n\t| \"rel\"\n\t| \"pagination\"\n\t| \"text\"\n\t| \"className\"\n\t| \"aria-label\"\n\t| \"alt\";\n\nexport interface FindNextOptions extends BaseOptions {\n\tmethods?: Method[];\n\tclassNameRegex?: RegExp;\n}\n\ntype Direction = \"next\" | \"prev\";\n\nconst REGEX = {\n\tREL: {\n\t\tnext: /rel\\s*=\\s*(['\"])[^'\"]*?\\bnext\\b[^'\"]*?\\1/i,\n\t\tprev: /rel\\s*=\\s*(['\"])[^'\"]*?\\b(prev|previous)\\b[^'\"]*?\\1/i,\n\t},\n\tTEXT: {\n\t\tnext: /^\\s*(((Next)\\s*(page)?|older|forward)|((次|つぎ)(のページ)?(へ)?)|((下|后)\\s*(一)?(页|頁))|(다음)|»|>|→)\\s*>*$/i,\n\t\tprev: /^\\s*<*(((Prev|Previous)\\s*(page)?|older|back)|((前)(のページ)?(へ)?)|((上|前)\\s*(一)?(页|頁))|(이전)|«|<|←)\\s*$/i,\n\t},\n\tCLASS_NAME: {\n\t\tnext: /next/i,\n\t\tprev: /prev|previous/i,\n\t},\n\tPAGINATION_LI: {\n\t\t// Extracts attributes of an <a> tag within an <li> element that follows a current/active <li> element (e.g., <li class=\"current\">1</li> <li class=\"\"><a href=\"/page/2\">2</a></li>)\n\t\tnext: /<li[^>]+class\\s*=\\s*['\"][^'\"]*(?:current|active)[^'\"]*['\"][^>]*>.*?<\\/li>\\s*<li[^>]*>\\s*<a\\s+(?<attributes>[^>]+)>/is,\n\t\t// Extracts attributes of an <a> tag within an <li> element that precedes a current/active <li> element (e.g., <li class=\"\"><a href=\"/page/1\">1</a></li> <li class=\"current\">2</li>)\n\t\tprev: /<li[^>]*>\\s*<a\\s+(?<attributes>[^>]+)>.*?<\\/a>\\s*<\\/li>\\s*<li[^>]+class\\s*=\\s*['\"][^'\"]*(?:current|active)[^'\"]*['\"][^>]*>/is,\n\t},\n\tPAGINATION_FALLBACK: {\n\t\t// Extracts attributes of an <a> tag that follows a current/active element (span, a, or strong tag with current/active class or aria-current=\"page\").\n\t\t// This handles cases where there's no <li> element structure or for more general pagination.\n\t\t// Example: <span class=\"current\">1</span> <a href=\"/page/2\">2</a>\n\t\tnext: /(?:<(?:span|a)[^>]+(?:class\\s*=\\s*['\"](?:current|active)['\"]|aria-current\\s*=\\s*['\"]page['\"])|strong)\\s*[^<]*\\s*<\\/(?:span|a|strong)>\\s*<a\\s+(?<attributes>[^>]+)>/i,\n\t\t// Extracts attributes of an <a> tag that precedes a current/active element.\n\t\t// Example: <a href=\"/page/1\">1</a> <span class=\"current\">2</span>\n\t\tprev: /<a\\s+(?<attributes>[^>]+)>.*?<\\/a>\\s*(?:<(?:span|a)[^>]+(?:class\\s*=\\s*['\"](?:current|active)['\"]|aria-current\\s*=\\s*['\"]page['\"])|strong)\\s*[^<]*/i,\n\t},\n\t// Common Regex\n\tPOTENTIAL_LINK_TAGS: /<(?:a|link)\\s+(?<attributes>[^>]*?)>/gi,\n\tANCHOR_TAG: /<a\\s+(?<attributes>[^>]+)>(?<innerText>.*?)<\\/a>/gis,\n\tANCHOR_TAG_START: /<a\\s+(?<attributes>[^>]+)>/gi,\n\tPAGINATION_CONTAINER:\n\t\t/<(?:div|nav|ul)[^>]+(?:class|id)\\s*=\\s*['\"][^'\"]*(?:pagination|pager|page-nav)[^'\"]*['\"][^>]*>(?<containerHtml>[\\s\\S]*?)<\\/(?:div|nav|ul)>/gi,\n\tIMG_TAG: /<img[^>]+>/gi,\n\tPATH_PAGE_NUMBER: /^(.*[/\\-_])(\\d+)$/,\n\tHTML_TAGS: /<[^>]+>/g,\n\tHTML_ENTITIES: /&[a-z]+;|&#[0-9]+;|&#x[0-9a-f]+;/gi,\n};\n\nconst DEFAULT_TIMEOUT_MS = 8000;\n\nconst attributeRegexCache = new Map<string, RegExp>();\n\n/**\n * Extracts the value of a specified attribute from an attribute string.\n * @param attributes The string containing attributes.\n * @param attributeName The name of the attribute to extract (e.g., \"href\", \"class\", \"id\").\n * @returns The value of the attribute, or null if not found.\n */\nfunction extractAttribute(\n\tattributes: string,\n\tattributeName: string,\n): string | null {\n\tlet regex = attributeRegexCache.get(attributeName);\n\tif (!regex) {\n\t\tregex = new RegExp(\n\t\t\t`${attributeName}\\\\s*=\\\\s*(['\"])(?<value>[^\"']*)\\\\1`,\n\t\t\t\"i\",\n\t\t);\n\t\tattributeRegexCache.set(attributeName, regex);\n\t}\n\tconst match = attributes.match(regex);\n\treturn match?.groups?.value ?? null;\n}\n\n/**\n * Extracts the href attribute from an attribute string and converts it to an absolute URL.\n * @param attributes The string containing attributes.\n * @param baseUrl The base URL for resolving relative paths.\n * @returns The absolute URL, or null if not found/invalid.\n */\nfunction extractAbsoluteHref(\n\tattributes: string,\n\tbaseUrl: string,\n\toptions?: BaseOptions,\n): string | null {\n\tconst href = extractAttribute(attributes, \"href\");\n\tif (href) {\n\t\ttry {\n\t\t\treturn new URL(href, baseUrl).href;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\toptions?.logger?.(\n\t\t\t\t\"warn\",\n\t\t\t\t`Invalid URL '${href}' for base '${baseUrl}': ${message}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Asynchronously fetches HTML content from a given URL.\n * @param url The URL to fetch.\n * @param options Options.\n * @returns The HTML string, or null if fetching failed.\n */\nexport async function fetchHtml(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(\n\t\t() => controller.abort(),\n\t\toptions?.timeout ?? DEFAULT_TIMEOUT_MS,\n\t);\n\n\ttry {\n\t\tconst response = await fetch(url, { signal: controller.signal });\n\n\t\tif (!response.ok) {\n\t\t\toptions?.logger?.(\n\t\t\t\t\"warn\",\n\t\t\t\t`Failed to fetch ${url}: ${response.status} ${response.statusText}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst contentType = response.headers.get(\"content-type\");\n\t\tif (!contentType || !contentType.includes(\"text/html\")) {\n\t\t\toptions?.logger?.(\"warn\", `URL ${url} did not return HTML content.`);\n\t\t\treturn null;\n\t\t}\n\n\t\treturn await response.text();\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\"error\", `Error fetching or parsing ${url}: ${message}`);\n\t\treturn null;\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t}\n}\n\nfunction findLinkByRel(\n\thtml: string,\n\tbaseUrl: string,\n\trelRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst allPotentialLinks = html.matchAll(REGEX.POTENTIAL_LINK_TAGS);\n\n\tfor (const match of allPotentialLinks) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (relRegex.test(attributes)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByPaginationStructure(\n\thtml: string,\n\tbaseUrl: string,\n\tliRegex: RegExp,\n\tfallbackRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst paginationContainers = html.matchAll(REGEX.PAGINATION_CONTAINER);\n\n\tfor (const match of paginationContainers) {\n\t\tconst containerHtml = match.groups?.containerHtml;\n\n\t\tif (!containerHtml) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst liMatch = containerHtml.match(liRegex);\n\t\tif (liMatch?.groups?.attributes) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(\n\t\t\t\tliMatch.groups.attributes,\n\t\t\t\tbaseUrl,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\n\t\tconst fallbackMatch = containerHtml.match(fallbackRegex);\n\t\tif (fallbackMatch?.groups?.attributes) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(\n\t\t\t\tfallbackMatch.groups.attributes,\n\t\t\t\tbaseUrl,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByText(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tconst innerText = match.groups?.innerText;\n\n\t\tif (!attributes || !innerText) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst cleanText = innerText\n\t\t\t.replace(REGEX.HTML_TAGS, \"\")\n\t\t\t.replace(REGEX.HTML_ENTITIES, \"\")\n\t\t\t.trim();\n\t\tif (!cleanText) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (textRegex.test(cleanText)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByClassName(\n\thtml: string,\n\tbaseUrl: string,\n\tdefaultRegex: RegExp,\n\tclassNameRegex?: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG_START);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst classAttr = extractAttribute(attributes, \"class\");\n\t\tconst idAttr = extractAttribute(attributes, \"id\");\n\n\t\tif (\n\t\t\t(classAttr && (classNameRegex ?? defaultRegex).test(classAttr)) ||\n\t\t\t(idAttr && defaultRegex.test(idAttr))\n\t\t) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByAriaLabel(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG_START);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst ariaLabelAttr = extractAttribute(attributes, \"aria-label\");\n\t\tif (ariaLabelAttr && textRegex.test(ariaLabelAttr)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByAltText(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tconst innerHtml = match.groups?.innerText;\n\n\t\tif (!attributes || !innerHtml) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst imgTags = innerHtml.matchAll(REGEX.IMG_TAG);\n\t\tfor (const [imgTag] of imgTags) {\n\t\t\tconst altText = extractAttribute(imgTag, \"alt\");\n\t\t\tif (altText && textRegex.test(altText.trim())) {\n\t\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\t\tif (absoluteUrl) {\n\t\t\t\t\treturn absoluteUrl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLink(\n\thtml: string,\n\tbaseUrl: string,\n\tdirection: Direction,\n\toptions?: FindNextOptions,\n): string | null {\n\tconst methods: Method[] = options?.methods ?? [\n\t\t\"rel\",\n\t\t\"pagination\",\n\t\t\"text\",\n\t\t\"className\",\n\t\t\"aria-label\",\n\t\t\"alt\",\n\t];\n\n\t// Dispatch table (map of strategies)\n\tconst strategies: { [key in Method]: () => string | null } = {\n\t\trel: () => findLinkByRel(html, baseUrl, REGEX.REL[direction], options),\n\t\tpagination: () =>\n\t\t\tfindLinkByPaginationStructure(\n\t\t\t\thtml,\n\t\t\t\tbaseUrl,\n\t\t\t\tREGEX.PAGINATION_LI[direction],\n\t\t\t\tREGEX.PAGINATION_FALLBACK[direction],\n\t\t\t\toptions,\n\t\t\t),\n\t\ttext: () => findLinkByText(html, baseUrl, REGEX.TEXT[direction], options),\n\t\tclassName: () =>\n\t\t\tfindLinkByClassName(\n\t\t\t\thtml,\n\t\t\t\tbaseUrl,\n\t\t\t\tREGEX.CLASS_NAME[direction],\n\t\t\t\toptions?.classNameRegex,\n\t\t\t\toptions,\n\t\t\t),\n\t\t\"aria-label\": () =>\n\t\t\tfindLinkByAriaLabel(html, baseUrl, REGEX.TEXT[direction], options),\n\t\talt: () => findLinkByAltText(html, baseUrl, REGEX.TEXT[direction], options),\n\t};\n\n\tfor (const method of methods) {\n\t\tconst url = strategies[method]();\n\t\tif (url) {\n\t\t\treturn url;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Attempts multiple strategies in order to find the next page link URL from an HTML string.\n * @param html The HTML string to parse.\n * @param baseUrl The base URL from which the HTML was fetched (for resolving relative paths).\n * @param options Options for specifying search strategies.\n * @returns The URL of the next page, or null if not found.\n */\nexport function findNext(\n\thtml: string,\n\tbaseUrl: string,\n\toptions?: FindNextOptions,\n): string | null {\n\treturn findLink(html, baseUrl, \"next\", options);\n}\n\n/**\n * Attempts multiple strategies in order to find the previous page link URL from an HTML string.\n * @param html The HTML string to parse.\n * @param baseUrl The base URL from which the HTML was fetched.\n * @param options Options for specifying search strategies.\n * @returns The URL of the previous page, or null if not found.\n */\nexport function findPrev(\n\thtml: string,\n\tbaseUrl: string,\n\toptions?: FindNextOptions,\n): string | null {\n\treturn findLink(html, baseUrl, \"prev\", options);\n}\n\n/**\n * Checks if a URL actually exists using a HEAD request.\n * @param url The URL to check.\n * @returns True if the URL exists, false otherwise.\n */\nasync function urlExists(url: string, options?: BaseOptions): Promise<boolean> {\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(\n\t\t() => controller.abort(),\n\t\toptions?.timeout ?? DEFAULT_TIMEOUT_MS,\n\t);\n\n\ttry {\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"HEAD\",\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\treturn response.ok;\n\t} catch {\n\t\treturn false;\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t}\n}\n\nasync function findUrlByQueryParam(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\ttry {\n\t\tconst urlObject = new URL(url);\n\t\tconst params = urlObject.searchParams;\n\t\tlet targetKey: string | null = null;\n\t\tlet currentValue: number | null = null;\n\n\t\tfor (const key of [\"page\", \"p\", \"index\"]) {\n\t\t\tconst value = params.get(key);\n\t\t\tif (value) {\n\t\t\t\tconst num = parseInt(value, 10);\n\t\t\t\tif (!Number.isNaN(num)) {\n\t\t\t\t\ttargetKey = key;\n\t\t\t\t\tcurrentValue = num;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (targetKey && currentValue !== null) {\n\t\t\tconst newNumber =\n\t\t\t\tdirection === \"next\" ? currentValue + 1 : currentValue - 1;\n\n\t\t\tif (newNumber > 0) {\n\t\t\t\tparams.set(targetKey, String(newNumber));\n\t\t\t\tconst newUrl = urlObject.toString();\n\t\t\t\tif (\n\t\t\t\t\toptions?.verifyExists === false ||\n\t\t\t\t\t(await urlExists(newUrl, options))\n\t\t\t\t) {\n\t\t\t\t\treturn newUrl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\n\t\t\t\"warn\",\n\t\t\t`Invalid URL provided to findUrlByQueryParam: ${message}`,\n\t\t);\n\t}\n\n\treturn null;\n}\n\nasync function findUrlByPathSegment(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\ttry {\n\t\tconst urlObject = new URL(url);\n\t\tconst pathname = urlObject.pathname.replace(/\\/$/, \"\");\n\t\tconst pathMatch = pathname.match(REGEX.PATH_PAGE_NUMBER);\n\n\t\tif (pathMatch) {\n\t\t\tconst [_, prefix, currentNumberStr] = pathMatch;\n\t\t\tif (prefix && currentNumberStr) {\n\t\t\t\tconst currentNumber = parseInt(currentNumberStr, 10);\n\t\t\t\tconst newNumber =\n\t\t\t\t\tdirection === \"next\" ? currentNumber + 1 : currentNumber - 1;\n\n\t\t\t\tif (newNumber > 0) {\n\t\t\t\t\tconst newPath = `${prefix}${newNumber}`;\n\t\t\t\t\tconst newUrl = `${urlObject.origin}${newPath}${urlObject.search}${urlObject.hash}`;\n\t\t\t\t\tif (\n\t\t\t\t\t\toptions?.verifyExists === false ||\n\t\t\t\t\t\t(await urlExists(newUrl, options))\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn newUrl;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\n\t\t\t\"warn\",\n\t\t\t`Invalid URL provided to findUrlByPathSegment: ${message}`,\n\t\t);\n\t}\n\n\treturn null;\n}\n\n/**\n * Asynchronously infers and finds the next/previous page URL based on URL patterns.\n * @param url The URL of the current page.\n * @param direction \"next\" or \"prev\".\n * @param options Options for URL pattern inference.\n * @returns The next/previous page URL, or null if not found.\n */\nasync function findUrlByPattern(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\tconst urlByQuery = await findUrlByQueryParam(url, direction, options);\n\tif (urlByQuery) {\n\t\treturn urlByQuery;\n\t}\n\n\tconst urlByPath = await findUrlByPathSegment(url, direction, options);\n\tif (urlByPath) {\n\t\treturn urlByPath;\n\t}\n\n\treturn null;\n}\n\n/**\n * Asynchronously infers and finds the next page URL based on URL patterns.\n * Increments the page number in the URL and checks if the URL exists.\n * @param url The URL of the current page.\n * @param options Options for URL pattern inference.\n * @returns The next page URL, or null if not found.\n */\nexport function findNextByUrl(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\treturn findUrlByPattern(url, \"next\", options);\n}\n\n/**\n * Asynchronously infers and finds the previous page URL based on URL patterns.\n * @param url The URL of the current page.\n * @param options Options for URL pattern inference.\n * @returns The previous page URL, or null if not found.\n */\nexport function findPrevByUrl(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\treturn findUrlByPattern(url, \"prev\", options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["// src/index.ts\n\nexport interface BaseOptions {\n\tlogger?: (level: \"warn\" | \"error\", message: string) => void;\n\ttimeout?: number;\n\tverifyExists?: boolean;\n}\n\nexport type Method =\n\t| \"rel\"\n\t| \"pagination\"\n\t| \"text\"\n\t| \"className\"\n\t| \"aria-label\"\n\t| \"alt\";\n\nexport interface FindNextOptions extends BaseOptions {\n\tmethods?: Method[];\n\tclassNameRegex?: RegExp;\n}\n\ntype Direction = \"next\" | \"prev\";\n\nconst REGEX = {\n\tREL: {\n\t\tnext: /rel\\s*=\\s*(['\"])[^'\"]*?\\bnext\\b[^'\"]*?\\1/i,\n\t\tprev: /rel\\s*=\\s*(['\"])[^'\"]*?\\b(prev|previous)\\b[^'\"]*?\\1/i,\n\t},\n\tTEXT: {\n\t\tnext: /^\\s*(((Next)\\s*(page)?|older|forward)|((次|つぎ)(のページ)?(へ)?)|((下|后)\\s*(一)?(页|頁))|(다음)|»|>|→)\\s*[»>→]*$/i,\n\t\tprev: /^\\s*[«<←]*\\s*(((Prev|Previous)\\s*(page)?|older|back)|((前)(のページ)?(へ)?)|((上|前)\\s*(一)?(页|頁))|(이전)|«|<|←)\\s*$/i,\n\t},\n\tCLASS_NAME: {\n\t\tnext: /next/i,\n\t\tprev: /prev|previous/i,\n\t},\n\tPAGINATION_LI: {\n\t\t// Extracts attributes of an <a> tag within an <li> element that follows a current/active <li> element (e.g., <li class=\"current\">1</li> <li class=\"\"><a href=\"/page/2\">2</a></li>)\n\t\tnext: /<li[^>]+class\\s*=\\s*['\"][^'\"]*(?:current|active)[^'\"]*['\"][^>]*>.*?<\\/li>\\s*<li[^>]*>\\s*<a\\s+(?<attributes>[^>]+)>/is,\n\t\t// Extracts attributes of an <a> tag within an <li> element that precedes a current/active <li> element (e.g., <li class=\"\"><a href=\"/page/1\">1</a></li> <li class=\"current\">2</li>)\n\t\tprev: /<li[^>]*>\\s*<a\\s+(?<attributes>[^>]+)>.*?<\\/a>\\s*<\\/li>\\s*<li[^>]+class\\s*=\\s*['\"][^'\"]*(?:current|active)[^'\"]*['\"][^>]*>/is,\n\t},\n\tPAGINATION_FALLBACK: {\n\t\t// Extracts attributes of an <a> tag that follows a current/active element (span, a, or strong tag with current/active class or aria-current=\"page\").\n\t\t// This handles cases where there's no <li> element structure or for more general pagination.\n\t\t// Example: <span class=\"current\">1</span> <a href=\"/page/2\">2</a>\n\t\tnext: /(?:<(?:span|a)[^>]+(?:class\\s*=\\s*['\"](?:current|active)['\"]|aria-current\\s*=\\s*['\"]page['\"])|strong)\\s*[^<]*\\s*<\\/(?:span|a|strong)>\\s*<a\\s+(?<attributes>[^>]+)>/i,\n\t\t// Extracts attributes of an <a> tag that precedes a current/active element.\n\t\t// Example: <a href=\"/page/1\">1</a> <span class=\"current\">2</span>\n\t\tprev: /<a\\s+(?<attributes>[^>]+)>.*?<\\/a>\\s*(?:<(?:span|a)[^>]+(?:class\\s*=\\s*['\"](?:current|active)['\"]|aria-current\\s*=\\s*['\"]page['\"])|strong)\\s*[^<]*/i,\n\t},\n\t// Common Regex\n\tPOTENTIAL_LINK_TAGS: /<(?:a|link)\\s+(?<attributes>[^>]*?)>/gi,\n\tANCHOR_TAG: /<a\\s+(?<attributes>[^>]+)>(?<innerText>.*?)<\\/a>/gis,\n\tANCHOR_TAG_START: /<a\\s+(?<attributes>[^>]+)>/gi,\n\tPAGINATION_CONTAINER:\n\t\t/<(?:div|nav|ul)[^>]+(?:class|id)\\s*=\\s*['\"][^'\"]*(?:pagination|pager|page-nav)[^'\"]*['\"][^>]*>(?<containerHtml>[\\s\\S]*?)<\\/(?:div|nav|ul)>/gi,\n\tIMG_TAG: /<img[^>]+>/gi,\n\tPATH_PAGE_NUMBER: /^(.*[/\\-_])(\\d+)$/,\n\tHTML_TAGS: /<[^>]+>/g,\n\tHTML_ENTITIES: /&[a-z]+;|&#[0-9]+;|&#x[0-9a-f]+;/gi,\n};\n\nconst DEFAULT_TIMEOUT_MS = 8000;\n\nconst attributeRegexCache = new Map<string, RegExp>();\n\n/**\n * Extracts the value of a specified attribute from an attribute string.\n * @param attributes The string containing attributes.\n * @param attributeName The name of the attribute to extract (e.g., \"href\", \"class\", \"id\").\n * @returns The value of the attribute, or null if not found.\n */\nfunction extractAttribute(\n\tattributes: string,\n\tattributeName: string,\n): string | null {\n\tlet regex = attributeRegexCache.get(attributeName);\n\tif (!regex) {\n\t\tregex = new RegExp(\n\t\t\t`${attributeName}\\\\s*=\\\\s*(['\"])(?<value>[^\"']*)\\\\1`,\n\t\t\t\"i\",\n\t\t);\n\t\tattributeRegexCache.set(attributeName, regex);\n\t}\n\tconst match = attributes.match(regex);\n\treturn match?.groups?.value ?? null;\n}\n\n/**\n * Extracts the href attribute from an attribute string and converts it to an absolute URL.\n * @param attributes The string containing attributes.\n * @param baseUrl The base URL for resolving relative paths.\n * @returns The absolute URL, or null if not found/invalid.\n */\nfunction extractAbsoluteHref(\n\tattributes: string,\n\tbaseUrl: string,\n\toptions?: BaseOptions,\n): string | null {\n\tconst href = extractAttribute(attributes, \"href\");\n\tif (href) {\n\t\tconst decodedHref = href.replace(/&amp;/g, \"&\");\n\t\ttry {\n\t\t\treturn new URL(decodedHref, baseUrl).href;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\toptions?.logger?.(\n\t\t\t\t\"warn\",\n\t\t\t\t`Invalid URL '${href}' for base '${baseUrl}': ${message}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Asynchronously fetches HTML content from a given URL.\n * @param url The URL to fetch.\n * @param options Options.\n * @returns The HTML string, or null if fetching failed.\n */\nexport async function fetchHtml(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(\n\t\t() => controller.abort(),\n\t\toptions?.timeout ?? DEFAULT_TIMEOUT_MS,\n\t);\n\n\ttry {\n\t\tconst response = await fetch(url, { signal: controller.signal });\n\n\t\tif (!response.ok) {\n\t\t\toptions?.logger?.(\n\t\t\t\t\"warn\",\n\t\t\t\t`Failed to fetch ${url}: ${response.status} ${response.statusText}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst contentType = response.headers.get(\"content-type\");\n\t\tif (!contentType || !contentType.includes(\"text/html\")) {\n\t\t\toptions?.logger?.(\"warn\", `URL ${url} did not return HTML content.`);\n\t\t\treturn null;\n\t\t}\n\n\t\treturn await response.text();\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\"error\", `Error fetching or parsing ${url}: ${message}`);\n\t\treturn null;\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t}\n}\n\nfunction findLinkByRel(\n\thtml: string,\n\tbaseUrl: string,\n\trelRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst allPotentialLinks = html.matchAll(REGEX.POTENTIAL_LINK_TAGS);\n\n\tfor (const match of allPotentialLinks) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (relRegex.test(attributes)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByPaginationStructure(\n\thtml: string,\n\tbaseUrl: string,\n\tliRegex: RegExp,\n\tfallbackRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst paginationContainers = html.matchAll(REGEX.PAGINATION_CONTAINER);\n\n\tfor (const match of paginationContainers) {\n\t\tconst containerHtml = match.groups?.containerHtml;\n\n\t\tif (!containerHtml) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst liMatch = containerHtml.match(liRegex);\n\t\tif (liMatch?.groups?.attributes) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(\n\t\t\t\tliMatch.groups.attributes,\n\t\t\t\tbaseUrl,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\n\t\tconst fallbackMatch = containerHtml.match(fallbackRegex);\n\t\tif (fallbackMatch?.groups?.attributes) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(\n\t\t\t\tfallbackMatch.groups.attributes,\n\t\t\t\tbaseUrl,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByText(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tconst innerText = match.groups?.innerText;\n\n\t\tif (!attributes || !innerText) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst cleanText = innerText\n\t\t\t.replace(REGEX.HTML_TAGS, \"\")\n\t\t\t.replace(REGEX.HTML_ENTITIES, \"\")\n\t\t\t.trim();\n\t\tif (!cleanText) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (textRegex.test(cleanText)) {\n\t\t\tconsole.log(textRegex, cleanText);\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByClassName(\n\thtml: string,\n\tbaseUrl: string,\n\tdefaultRegex: RegExp,\n\tclassNameRegex?: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG_START);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst classAttr = extractAttribute(attributes, \"class\");\n\t\tconst idAttr = extractAttribute(attributes, \"id\");\n\n\t\tif (\n\t\t\t(classAttr && (classNameRegex ?? defaultRegex).test(classAttr)) ||\n\t\t\t(idAttr && defaultRegex.test(idAttr))\n\t\t) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByAriaLabel(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG_START);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tif (!attributes) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst ariaLabelAttr = extractAttribute(attributes, \"aria-label\");\n\t\tif (ariaLabelAttr && textRegex.test(ariaLabelAttr)) {\n\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\tif (absoluteUrl) {\n\t\t\t\treturn absoluteUrl;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLinkByAltText(\n\thtml: string,\n\tbaseUrl: string,\n\ttextRegex: RegExp,\n\toptions?: BaseOptions,\n): string | null {\n\tconst anchorTags = html.matchAll(REGEX.ANCHOR_TAG);\n\n\tfor (const match of anchorTags) {\n\t\tconst attributes = match.groups?.attributes;\n\t\tconst innerHtml = match.groups?.innerText;\n\n\t\tif (!attributes || !innerHtml) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst imgTags = innerHtml.matchAll(REGEX.IMG_TAG);\n\t\tfor (const [imgTag] of imgTags) {\n\t\t\tconst altText = extractAttribute(imgTag, \"alt\");\n\t\t\tif (altText && textRegex.test(altText.trim())) {\n\t\t\t\tconst absoluteUrl = extractAbsoluteHref(attributes, baseUrl, options);\n\t\t\t\tif (absoluteUrl) {\n\t\t\t\t\treturn absoluteUrl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction findLink(\n\thtml: string,\n\tbaseUrl: string,\n\tdirection: Direction,\n\toptions?: FindNextOptions,\n): string | null {\n\tconst methods: Method[] = options?.methods ?? [\n\t\t\"rel\",\n\t\t\"pagination\",\n\t\t\"text\",\n\t\t\"className\",\n\t\t\"aria-label\",\n\t\t\"alt\",\n\t];\n\n\t// Dispatch table (map of strategies)\n\tconst strategies: { [key in Method]: () => string | null } = {\n\t\trel: () => findLinkByRel(html, baseUrl, REGEX.REL[direction], options),\n\t\tpagination: () =>\n\t\t\tfindLinkByPaginationStructure(\n\t\t\t\thtml,\n\t\t\t\tbaseUrl,\n\t\t\t\tREGEX.PAGINATION_LI[direction],\n\t\t\t\tREGEX.PAGINATION_FALLBACK[direction],\n\t\t\t\toptions,\n\t\t\t),\n\t\ttext: () => findLinkByText(html, baseUrl, REGEX.TEXT[direction], options),\n\t\tclassName: () =>\n\t\t\tfindLinkByClassName(\n\t\t\t\thtml,\n\t\t\t\tbaseUrl,\n\t\t\t\tREGEX.CLASS_NAME[direction],\n\t\t\t\toptions?.classNameRegex,\n\t\t\t\toptions,\n\t\t\t),\n\t\t\"aria-label\": () =>\n\t\t\tfindLinkByAriaLabel(html, baseUrl, REGEX.TEXT[direction], options),\n\t\talt: () => findLinkByAltText(html, baseUrl, REGEX.TEXT[direction], options),\n\t};\n\n\tfor (const method of methods) {\n\t\tconst url = strategies[method]();\n\t\tif (url) {\n\t\t\treturn url;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Attempts multiple strategies in order to find the next page link URL from an HTML string.\n * @param html The HTML string to parse.\n * @param baseUrl The base URL from which the HTML was fetched (for resolving relative paths).\n * @param options Options for specifying search strategies.\n * @returns The URL of the next page, or null if not found.\n */\nexport function findNext(\n\thtml: string,\n\tbaseUrl: string,\n\toptions?: FindNextOptions,\n): string | null {\n\treturn findLink(html, baseUrl, \"next\", options);\n}\n\n/**\n * Attempts multiple strategies in order to find the previous page link URL from an HTML string.\n * @param html The HTML string to parse.\n * @param baseUrl The base URL from which the HTML was fetched.\n * @param options Options for specifying search strategies.\n * @returns The URL of the previous page, or null if not found.\n */\nexport function findPrev(\n\thtml: string,\n\tbaseUrl: string,\n\toptions?: FindNextOptions,\n): string | null {\n\treturn findLink(html, baseUrl, \"prev\", options);\n}\n\n/**\n * Checks if a URL actually exists using a HEAD request.\n * @param url The URL to check.\n * @returns True if the URL exists, false otherwise.\n */\nasync function urlExists(url: string, options?: BaseOptions): Promise<boolean> {\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(\n\t\t() => controller.abort(),\n\t\toptions?.timeout ?? DEFAULT_TIMEOUT_MS,\n\t);\n\n\ttry {\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"HEAD\",\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\treturn response.ok;\n\t} catch {\n\t\treturn false;\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t}\n}\n\nasync function findUrlByQueryParam(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\ttry {\n\t\tconst urlObject = new URL(url);\n\t\tconst params = urlObject.searchParams;\n\t\tlet targetKey: string | null = null;\n\t\tlet currentValue: number | null = null;\n\n\t\tfor (const key of [\"page\", \"p\", \"index\"]) {\n\t\t\tconst value = params.get(key);\n\t\t\tif (value) {\n\t\t\t\tconst num = parseInt(value, 10);\n\t\t\t\tif (!Number.isNaN(num)) {\n\t\t\t\t\ttargetKey = key;\n\t\t\t\t\tcurrentValue = num;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (targetKey && currentValue !== null) {\n\t\t\tconst newNumber =\n\t\t\t\tdirection === \"next\" ? currentValue + 1 : currentValue - 1;\n\n\t\t\tif (newNumber > 0) {\n\t\t\t\tparams.set(targetKey, String(newNumber));\n\t\t\t\tconst newUrl = urlObject.toString();\n\t\t\t\tif (\n\t\t\t\t\toptions?.verifyExists === false ||\n\t\t\t\t\t(await urlExists(newUrl, options))\n\t\t\t\t) {\n\t\t\t\t\treturn newUrl;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\n\t\t\t\"warn\",\n\t\t\t`Invalid URL provided to findUrlByQueryParam: ${message}`,\n\t\t);\n\t}\n\n\treturn null;\n}\n\nasync function findUrlByPathSegment(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\ttry {\n\t\tconst urlObject = new URL(url);\n\t\tconst pathname = urlObject.pathname.replace(/\\/$/, \"\");\n\t\tconst pathMatch = pathname.match(REGEX.PATH_PAGE_NUMBER);\n\n\t\tif (pathMatch) {\n\t\t\tconst [_, prefix, currentNumberStr] = pathMatch;\n\t\t\tif (prefix && currentNumberStr) {\n\t\t\t\tconst currentNumber = parseInt(currentNumberStr, 10);\n\t\t\t\tconst newNumber =\n\t\t\t\t\tdirection === \"next\" ? currentNumber + 1 : currentNumber - 1;\n\n\t\t\t\tif (newNumber > 0) {\n\t\t\t\t\tconst newPath = `${prefix}${newNumber}`;\n\t\t\t\t\tconst newUrl = `${urlObject.origin}${newPath}${urlObject.search}${urlObject.hash}`;\n\t\t\t\t\tif (\n\t\t\t\t\t\toptions?.verifyExists === false ||\n\t\t\t\t\t\t(await urlExists(newUrl, options))\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn newUrl;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\toptions?.logger?.(\n\t\t\t\"warn\",\n\t\t\t`Invalid URL provided to findUrlByPathSegment: ${message}`,\n\t\t);\n\t}\n\n\treturn null;\n}\n\n/**\n * Asynchronously infers and finds the next/previous page URL based on URL patterns.\n * @param url The URL of the current page.\n * @param direction \"next\" or \"prev\".\n * @param options Options for URL pattern inference.\n * @returns The next/previous page URL, or null if not found.\n */\nasync function findUrlByPattern(\n\turl: string,\n\tdirection: Direction,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\tconst urlByQuery = await findUrlByQueryParam(url, direction, options);\n\tif (urlByQuery) {\n\t\treturn urlByQuery;\n\t}\n\n\tconst urlByPath = await findUrlByPathSegment(url, direction, options);\n\tif (urlByPath) {\n\t\treturn urlByPath;\n\t}\n\n\treturn null;\n}\n\n/**\n * Asynchronously infers and finds the next page URL based on URL patterns.\n * Increments the page number in the URL and checks if the URL exists.\n * @param url The URL of the current page.\n * @param options Options for URL pattern inference.\n * @returns The next page URL, or null if not found.\n */\nexport function findNextByUrl(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\treturn findUrlByPattern(url, \"next\", options);\n}\n\n/**\n * Asynchronously infers and finds the previous page URL based on URL patterns.\n * @param url The URL of the current page.\n * @param options Options for URL pattern inference.\n * @returns The previous page URL, or null if not found.\n */\nexport function findPrevByUrl(\n\turl: string,\n\toptions?: BaseOptions,\n): Promise<string | null> {\n\treturn findUrlByPattern(url, \"prev\", options);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mash43/relnext",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A TypeScript library designed to detect pagination links such as \"next\" and \"previous\" from web page HTML content or URLs.",
5
5
  "author": "mash43",
6
6
  "license": "MIT",