fetch-status-codes 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,16 @@ All notable changes to this project will be documented in this file.
6
6
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7
7
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ [0.3.0] - 2025-12-31
10
+ --------------------
11
+
12
+ ### Changed
13
+
14
+ - The default setting for the `resolveRedirects` option is now `false`. This reduces the operation
15
+ time by approximately one second. However, if the old behavior is desired you can manually set the
16
+ option to `true`.
17
+ - Upgraded [jsdom](https://github.com/jsdom/jsdom#readme) from v27.3.0 to v27.4.0.
18
+
9
19
  [0.2.0] - 2025-12-23
10
20
  --------------------
11
21
 
@@ -36,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
36
46
 
37
47
  - Initial release.
38
48
 
49
+ [0.3.0]: https://github.com/jbenner-radham/node-fetch-status-codes/compare/v0.2.0...v0.3.0
39
50
  [0.2.0]: https://github.com/jbenner-radham/node-fetch-status-codes/compare/v0.1.1...v0.2.0
40
51
  [0.1.1]: https://github.com/jbenner-radham/node-fetch-status-codes/compare/v0.1.0...v0.1.1
41
52
  [0.1.0]: https://github.com/jbenner-radham/node-fetch-status-codes/releases/tag/v0.1.0
package/README.md CHANGED
@@ -16,6 +16,8 @@ Usage
16
16
  ```typescript
17
17
  import fetchStatusCodes, { fetchStatusCodeClasses } from 'fetch-status-codes';
18
18
 
19
+ // Calling `fetchStatusCodes()` without any arguments implicitly sets the `resolveRedirects` option
20
+ // to `false`.
19
21
  await fetchStatusCodes();
20
22
  // >>> [
21
23
  // >>> {
@@ -24,14 +26,18 @@ await fetchStatusCodes();
24
26
  // >>> references: [
25
27
  // >>> {
26
28
  // >>> name: 'RFC9110, Section 15.2.1',
27
- // >>> url: 'https://www.rfc-editor.org/rfc/rfc9110.html#section-15.2.1'
29
+ // >>> url: 'https://www.iana.org/go/rfc9110'
28
30
  // >>> }
29
31
  // >>> ]
30
32
  // >>> },
31
33
  // >>> ...
32
34
  // >>> ]
33
35
 
34
- await fetchStatusCodes({ resolveRedirects: false });
36
+ // Calling `fetchStatusCodes()` with the `resolveRedirects` option set to `true` gives you arguably
37
+ // better URLs since they aren't redirects and they will link directly to the relevant section if
38
+ // applicable. However, this will also add approximately one second of additional execution time to
39
+ // the app e.g., ~1.616s without resolution versus ~2.655s with resolution.
40
+ await fetchStatusCodes({ resolveRedirects: true });
35
41
  // >>> [
36
42
  // >>> {
37
43
  // >>> value: 100,
@@ -39,7 +45,7 @@ await fetchStatusCodes({ resolveRedirects: false });
39
45
  // >>> references: [
40
46
  // >>> {
41
47
  // >>> name: 'RFC9110, Section 15.2.1',
42
- // >>> url: 'https://www.iana.org/go/rfc9110'
48
+ // >>> url: 'https://www.rfc-editor.org/rfc/rfc9110.html#section-15.2.1'
43
49
  // >>> }
44
50
  // >>> ]
45
51
  // >>> },
package/dist/cjs/index.js CHANGED
@@ -1,3 +1,3 @@
1
- "use strict";var i=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var g=(e,n)=>{for(var r in n)i(e,r,{get:n[r],enumerable:!0})},x=(e,n,r,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let s of y(n))!w.call(e,s)&&s!==r&&i(e,s,{get:()=>n[s],enumerable:!(o=f(n,s))||o.enumerable});return e};var h=e=>x(i({},"__esModule",{value:!0}),e);var A={};g(A,{default:()=>d,fetchStatusCodeClasses:()=>S});module.exports=h(A);var l=require("jsdom");function C(e){try{return e()}catch{}}async function m(){let r=await(await fetch("https://www.iana.org/assignments/http-status-codes")).text();return new l.JSDOM(r)}async function d({resolveRedirects:e=!0}={}){let r=(await m()).window.document.getElementById("table-http-status-codes-1"),o=Array.from(r.querySelectorAll("tbody > tr")),s=async t=>{if(!e)return t.href;let a=await fetch(t.href,{method:"HEAD",redirect:"manual"}),c=C(()=>new URL(a.status.toString().startsWith("3")&&a.headers.has("Location")?a.headers.get("Location"):t.href))??new URL(t.href),p=/RFC\d+(?:, Section )?((?:\d+)?(?:.\d+){0,2})/,[,u]=p.exec(t.textContent)??[];return u&&(c.hash=`section-${u}`),c.toString()};return Promise.all(o.map(t=>Array.from(t.querySelectorAll("td"))).filter(t=>t.at(1).textContent!=="Unassigned").map(async t=>({value:Number.parseInt(t.at(0).textContent),description:t.at(1).textContent,references:await Promise.all(Array.from(t.at(2).querySelectorAll("a")).map(async a=>({name:a.textContent,url:await s(a)})))})))}async function S(){let e=await m(),n=Array.from(e.window.document.querySelectorAll("dt")).find(o=>o.textContent.trim()==="Note")?.nextElementSibling.textContent.trim(),r=/^(?<value>\dxx): (?<name>[ A-Za-z]+) - (?<description>[ ,A-Za-z]+)$/;return n?.split(`
1
+ "use strict";var i=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var g=(e,n)=>{for(var r in n)i(e,r,{get:n[r],enumerable:!0})},x=(e,n,r,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let s of y(n))!w.call(e,s)&&s!==r&&i(e,s,{get:()=>n[s],enumerable:!(o=p(n,s))||o.enumerable});return e};var h=e=>x(i({},"__esModule",{value:!0}),e);var A={};g(A,{default:()=>d,fetchStatusCodeClasses:()=>S});module.exports=h(A);var l=require("jsdom");function C(e){try{return e()}catch{}}async function m(){let r=await(await fetch("https://www.iana.org/assignments/http-status-codes")).text();return new l.JSDOM(r)}async function d({resolveRedirects:e=!1}={}){let r=(await m()).window.document.getElementById("table-http-status-codes-1"),o=Array.from(r.querySelectorAll("tbody > tr")),s=async t=>{if(!e)return t.href;let a=await fetch(t.href,{method:"HEAD",redirect:"manual"}),c=C(()=>new URL(a.status.toString().startsWith("3")&&a.headers.has("Location")?a.headers.get("Location"):t.href))??new URL(t.href),f=/RFC\d+(?:, Section )?((?:\d+)?(?:.\d+){0,2})/,[,u]=f.exec(t.textContent)??[];return u&&(c.hash=`section-${u}`),c.toString()};return Promise.all(o.map(t=>Array.from(t.querySelectorAll("td"))).filter(t=>t.at(1).textContent!=="Unassigned").map(async t=>({value:Number.parseInt(t.at(0).textContent),description:t.at(1).textContent,references:await Promise.all(Array.from(t.at(2).querySelectorAll("a")).map(async a=>({name:a.textContent,url:await s(a)})))})))}async function S(){let e=await m(),n=Array.from(e.window.document.querySelectorAll("dt")).find(o=>o.textContent.trim()==="Note")?.nextElementSibling.textContent.trim(),r=/^(?<value>\dxx): (?<name>[ A-Za-z]+) - (?<description>[ ,A-Za-z]+)$/;return n?.split(`
2
2
  `).map(o=>{let{value:s="",name:t="",description:a=""}=r.exec(o)?.groups??{};return{value:s,name:t,description:a}})??[]}0&&(module.exports={fetchStatusCodeClasses});
3
3
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["import { JSDOM } from 'jsdom';\n\nexport type Reference = { name: string; url: string };\n\nexport type StatusCode = { value: number; description: string; references: Reference[] };\n\nexport type StatusCodeClass = { value: string; name: string; description: string };\n\nfunction attempt<T>(callback: () => T): T | undefined {\n try {\n return callback();\n } catch { /* Do nothing. */ }\n}\n\nasync function fetchRegistryDom(): Promise<JSDOM> {\n const url = 'https://www.iana.org/assignments/http-status-codes';\n const response = await fetch(url);\n const html = await response.text();\n\n return new JSDOM(html);\n}\n\nexport default async function fetchStatusCodes({ resolveRedirects = true }: {\n resolveRedirects?: boolean;\n} = {}): Promise<StatusCode[]> {\n const dom = await fetchRegistryDom();\n const table = dom.window.document.getElementById('table-http-status-codes-1')!;\n const rows = Array.from(table.querySelectorAll<HTMLTableRowElement>('tbody > tr'));\n const getReferenceLink = async (anchor: HTMLAnchorElement): Promise<string> => {\n if (!resolveRedirects) {\n return anchor.href;\n }\n\n // The IANA links are redirects, but we want direct links with a section hash if applicable.\n const response = await fetch(anchor.href, { method: 'HEAD', redirect: 'manual' });\n\n // The location header returned by some ietf.org pages is relative\n // (e.g., /doc/status-change-http-experiments-to-historic/) so check to make\n // sure the location is a valid URL and fall back to the anchor href if not.\n const url = attempt(\n () => new URL(\n response.status.toString().startsWith('3') && response.headers.has('Location')\n ? response.headers.get('Location')!\n : anchor.href\n )\n ) ?? new URL(anchor.href);\n const pattern = /RFC\\d+(?:, Section )?((?:\\d+)?(?:.\\d+){0,2})/;\n const [, section] = pattern.exec(anchor.textContent) ?? [];\n\n if (section) {\n url.hash = `section-${section}`;\n }\n\n return url.toString();\n };\n\n return Promise.all(rows.map(row => Array.from(row.querySelectorAll('td')))\n .filter(cells => cells.at(1)!.textContent !== 'Unassigned')\n .map(async cells => ({\n value: Number.parseInt(cells.at(0)!.textContent),\n description: cells.at(1)!.textContent,\n references: await Promise.all(Array.from(cells.at(2)!.querySelectorAll('a'))\n .map(async anchor =>\n ({ name: anchor.textContent!, url: await getReferenceLink(anchor) })\n )\n )\n })));\n}\n\nexport async function fetchStatusCodeClasses(): Promise<StatusCodeClass[]> {\n const dom = await fetchRegistryDom();\n const note = Array.from(dom.window.document.querySelectorAll('dt'))\n .find(element => element.textContent.trim() === 'Note')\n ?.nextElementSibling!.textContent.trim();\n const pattern = /^(?<value>\\dxx): (?<name>[ A-Za-z]+) - (?<description>[ ,A-Za-z]+)$/;\n\n return note?.split('\\n').map(line => {\n const { value = '', name = '', description = '' } = pattern.exec(line)?.groups ?? {};\n\n return { value, name, description };\n }) ?? [];\n}\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,2BAAAC,IAAA,eAAAC,EAAAJ,GAAA,IAAAK,EAAsB,iBAQtB,SAASC,EAAWC,EAAkC,CACpD,GAAI,CACF,OAAOA,EAAS,CAClB,MAAQ,CAAoB,CAC9B,CAEA,eAAeC,GAAmC,CAGhD,IAAMC,EAAO,MADI,MAAM,MADX,oDACoB,GACJ,KAAK,EAEjC,OAAO,IAAI,QAAMA,CAAI,CACvB,CAEA,eAAOP,EAAwC,CAAE,iBAAAQ,EAAmB,EAAK,EAErE,CAAC,EAA0B,CAE7B,IAAMC,GADM,MAAMH,EAAiB,GACjB,OAAO,SAAS,eAAe,2BAA2B,EACtEI,EAAO,MAAM,KAAKD,EAAM,iBAAsC,YAAY,CAAC,EAC3EE,EAAmB,MAAOC,GAA+C,CAC7E,GAAI,CAACJ,EACH,OAAOI,EAAO,KAIhB,IAAMC,EAAW,MAAM,MAAMD,EAAO,KAAM,CAAE,OAAQ,OAAQ,SAAU,QAAS,CAAC,EAK1EE,EAAMV,EACV,IAAM,IAAI,IACRS,EAAS,OAAO,SAAS,EAAE,WAAW,GAAG,GAAKA,EAAS,QAAQ,IAAI,UAAU,EACzEA,EAAS,QAAQ,IAAI,UAAU,EAC/BD,EAAO,IACb,CACF,GAAK,IAAI,IAAIA,EAAO,IAAI,EAClBG,EAAU,+CACV,CAAC,CAAEC,CAAO,EAAID,EAAQ,KAAKH,EAAO,WAAW,GAAK,CAAC,EAEzD,OAAII,IACFF,EAAI,KAAO,WAAWE,CAAO,IAGxBF,EAAI,SAAS,CACtB,EAEA,OAAO,QAAQ,IAAIJ,EAAK,IAAIO,GAAO,MAAM,KAAKA,EAAI,iBAAiB,IAAI,CAAC,CAAC,EACtE,OAAOC,GAASA,EAAM,GAAG,CAAC,EAAG,cAAgB,YAAY,EACzD,IAAI,MAAMA,IAAU,CACnB,MAAO,OAAO,SAASA,EAAM,GAAG,CAAC,EAAG,WAAW,EAC/C,YAAaA,EAAM,GAAG,CAAC,EAAG,YAC1B,WAAY,MAAM,QAAQ,IAAI,MAAM,KAAKA,EAAM,GAAG,CAAC,EAAG,iBAAiB,GAAG,CAAC,EACxE,IAAI,MAAMN,IACR,CAAE,KAAMA,EAAO,YAAc,IAAK,MAAMD,EAAiBC,CAAM,CAAE,EACpE,CACF,CACF,EAAE,CAAC,CACP,CAEA,eAAsBX,GAAqD,CACzE,IAAMkB,EAAM,MAAMb,EAAiB,EAC7Bc,EAAO,MAAM,KAAKD,EAAI,OAAO,SAAS,iBAAiB,IAAI,CAAC,EAC/D,KAAKE,GAAWA,EAAQ,YAAY,KAAK,IAAM,MAAM,GACpD,mBAAoB,YAAY,KAAK,EACnCN,EAAU,sEAEhB,OAAOK,GAAM,MAAM;AAAA,CAAI,EAAE,IAAIE,GAAQ,CACnC,GAAM,CAAE,MAAAC,EAAQ,GAAI,KAAAC,EAAO,GAAI,YAAAC,EAAc,EAAG,EAAIV,EAAQ,KAAKO,CAAI,GAAG,QAAU,CAAC,EAEnF,MAAO,CAAE,MAAAC,EAAO,KAAAC,EAAM,YAAAC,CAAY,CACpC,CAAC,GAAK,CAAC,CACT",
4
+ "sourcesContent": ["import { JSDOM } from 'jsdom';\n\nexport type Reference = { name: string; url: string };\n\nexport type StatusCode = { value: number; description: string; references: Reference[] };\n\nexport type StatusCodeClass = { value: string; name: string; description: string };\n\nfunction attempt<T>(callback: () => T): T | undefined {\n try {\n return callback();\n } catch { /* Do nothing. */ }\n}\n\nasync function fetchRegistryDom(): Promise<JSDOM> {\n const url = 'https://www.iana.org/assignments/http-status-codes';\n const response = await fetch(url);\n const html = await response.text();\n\n return new JSDOM(html);\n}\n\nexport default async function fetchStatusCodes({ resolveRedirects = false }: {\n resolveRedirects?: boolean;\n} = {}): Promise<StatusCode[]> {\n const dom = await fetchRegistryDom();\n const table = dom.window.document.getElementById('table-http-status-codes-1')!;\n const rows = Array.from(table.querySelectorAll<HTMLTableRowElement>('tbody > tr'));\n const getReferenceLink = async (anchor: HTMLAnchorElement): Promise<string> => {\n if (!resolveRedirects) {\n return anchor.href;\n }\n\n // The IANA links are redirects, but we want direct links with a section hash if applicable.\n const response = await fetch(anchor.href, { method: 'HEAD', redirect: 'manual' });\n\n // The location header returned by some ietf.org pages is relative\n // (e.g., /doc/status-change-http-experiments-to-historic/) so check to make\n // sure the location is a valid URL and fall back to the anchor href if not.\n const url = attempt(\n () => new URL(\n response.status.toString().startsWith('3') && response.headers.has('Location')\n ? response.headers.get('Location')!\n : anchor.href\n )\n ) ?? new URL(anchor.href);\n const pattern = /RFC\\d+(?:, Section )?((?:\\d+)?(?:.\\d+){0,2})/;\n const [, section] = pattern.exec(anchor.textContent) ?? [];\n\n if (section) {\n url.hash = `section-${section}`;\n }\n\n return url.toString();\n };\n\n return Promise.all(rows.map(row => Array.from(row.querySelectorAll('td')))\n .filter(cells => cells.at(1)!.textContent !== 'Unassigned')\n .map(async cells => ({\n value: Number.parseInt(cells.at(0)!.textContent),\n description: cells.at(1)!.textContent,\n references: await Promise.all(Array.from(cells.at(2)!.querySelectorAll('a'))\n .map(async anchor =>\n ({ name: anchor.textContent!, url: await getReferenceLink(anchor) })\n )\n )\n })));\n}\n\nexport async function fetchStatusCodeClasses(): Promise<StatusCodeClass[]> {\n const dom = await fetchRegistryDom();\n const note = Array.from(dom.window.document.querySelectorAll('dt'))\n .find(element => element.textContent.trim() === 'Note')\n ?.nextElementSibling!.textContent.trim();\n const pattern = /^(?<value>\\dxx): (?<name>[ A-Za-z]+) - (?<description>[ ,A-Za-z]+)$/;\n\n return note?.split('\\n').map(line => {\n const { value = '', name = '', description = '' } = pattern.exec(line)?.groups ?? {};\n\n return { value, name, description };\n }) ?? [];\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,2BAAAC,IAAA,eAAAC,EAAAJ,GAAA,IAAAK,EAAsB,iBAQtB,SAASC,EAAWC,EAAkC,CACpD,GAAI,CACF,OAAOA,EAAS,CAClB,MAAQ,CAAoB,CAC9B,CAEA,eAAeC,GAAmC,CAGhD,IAAMC,EAAO,MADI,MAAM,MADX,oDACoB,GACJ,KAAK,EAEjC,OAAO,IAAI,QAAMA,CAAI,CACvB,CAEA,eAAOP,EAAwC,CAAE,iBAAAQ,EAAmB,EAAM,EAEtE,CAAC,EAA0B,CAE7B,IAAMC,GADM,MAAMH,EAAiB,GACjB,OAAO,SAAS,eAAe,2BAA2B,EACtEI,EAAO,MAAM,KAAKD,EAAM,iBAAsC,YAAY,CAAC,EAC3EE,EAAmB,MAAOC,GAA+C,CAC7E,GAAI,CAACJ,EACH,OAAOI,EAAO,KAIhB,IAAMC,EAAW,MAAM,MAAMD,EAAO,KAAM,CAAE,OAAQ,OAAQ,SAAU,QAAS,CAAC,EAK1EE,EAAMV,EACV,IAAM,IAAI,IACRS,EAAS,OAAO,SAAS,EAAE,WAAW,GAAG,GAAKA,EAAS,QAAQ,IAAI,UAAU,EACzEA,EAAS,QAAQ,IAAI,UAAU,EAC/BD,EAAO,IACb,CACF,GAAK,IAAI,IAAIA,EAAO,IAAI,EAClBG,EAAU,+CACV,CAAC,CAAEC,CAAO,EAAID,EAAQ,KAAKH,EAAO,WAAW,GAAK,CAAC,EAEzD,OAAII,IACFF,EAAI,KAAO,WAAWE,CAAO,IAGxBF,EAAI,SAAS,CACtB,EAEA,OAAO,QAAQ,IAAIJ,EAAK,IAAIO,GAAO,MAAM,KAAKA,EAAI,iBAAiB,IAAI,CAAC,CAAC,EACtE,OAAOC,GAASA,EAAM,GAAG,CAAC,EAAG,cAAgB,YAAY,EACzD,IAAI,MAAMA,IAAU,CACnB,MAAO,OAAO,SAASA,EAAM,GAAG,CAAC,EAAG,WAAW,EAC/C,YAAaA,EAAM,GAAG,CAAC,EAAG,YAC1B,WAAY,MAAM,QAAQ,IAAI,MAAM,KAAKA,EAAM,GAAG,CAAC,EAAG,iBAAiB,GAAG,CAAC,EACxE,IAAI,MAAMN,IACR,CAAE,KAAMA,EAAO,YAAc,IAAK,MAAMD,EAAiBC,CAAM,CAAE,EACpE,CACF,CACF,EAAE,CAAC,CACP,CAEA,eAAsBX,GAAqD,CACzE,IAAMkB,EAAM,MAAMb,EAAiB,EAC7Bc,EAAO,MAAM,KAAKD,EAAI,OAAO,SAAS,iBAAiB,IAAI,CAAC,EAC/D,KAAKE,GAAWA,EAAQ,YAAY,KAAK,IAAM,MAAM,GACpD,mBAAoB,YAAY,KAAK,EACnCN,EAAU,sEAEhB,OAAOK,GAAM,MAAM;AAAA,CAAI,EAAE,IAAIE,GAAQ,CACnC,GAAM,CAAE,MAAAC,EAAQ,GAAI,KAAAC,EAAO,GAAI,YAAAC,EAAc,EAAG,EAAIV,EAAQ,KAAKO,CAAI,GAAG,QAAU,CAAC,EAEnF,MAAO,CAAE,MAAAC,EAAO,KAAAC,EAAM,YAAAC,CAAY,CACpC,CAAC,GAAK,CAAC,CACT",
6
6
  "names": ["index_exports", "__export", "fetchStatusCodes", "fetchStatusCodeClasses", "__toCommonJS", "import_jsdom", "attempt", "callback", "fetchRegistryDom", "html", "resolveRedirects", "table", "rows", "getReferenceLink", "anchor", "response", "url", "pattern", "section", "row", "cells", "dom", "note", "element", "line", "value", "name", "description"]
7
7
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fetch-status-codes",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Fetch the HTTP status codes from the IANA registry.",
5
5
  "keywords": [
6
6
  "class",
@@ -26,7 +26,7 @@
26
26
  "exports": "./index.js",
27
27
  "types": "../types/index.d.ts",
28
28
  "dependencies": {
29
- "jsdom": "^27.3.0"
29
+ "jsdom": "^27.4.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@eslint/compat": "^2.0.0",
package/dist/esm/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import{JSDOM as m}from"jsdom";function d(n){try{return n()}catch{}}async function u(){let r=await(await fetch("https://www.iana.org/assignments/http-status-codes")).text();return new m(r)}async function p({resolveRedirects:n=!0}={}){let r=(await u()).window.document.getElementById("table-http-status-codes-1"),s=Array.from(r.querySelectorAll("tbody > tr")),a=async t=>{if(!n)return t.href;let e=await fetch(t.href,{method:"HEAD",redirect:"manual"}),i=d(()=>new URL(e.status.toString().startsWith("3")&&e.headers.has("Location")?e.headers.get("Location"):t.href))??new URL(t.href),l=/RFC\d+(?:, Section )?((?:\d+)?(?:.\d+){0,2})/,[,c]=l.exec(t.textContent)??[];return c&&(i.hash=`section-${c}`),i.toString()};return Promise.all(s.map(t=>Array.from(t.querySelectorAll("td"))).filter(t=>t.at(1).textContent!=="Unassigned").map(async t=>({value:Number.parseInt(t.at(0).textContent),description:t.at(1).textContent,references:await Promise.all(Array.from(t.at(2).querySelectorAll("a")).map(async e=>({name:e.textContent,url:await a(e)})))})))}async function y(){let n=await u(),o=Array.from(n.window.document.querySelectorAll("dt")).find(s=>s.textContent.trim()==="Note")?.nextElementSibling.textContent.trim(),r=/^(?<value>\dxx): (?<name>[ A-Za-z]+) - (?<description>[ ,A-Za-z]+)$/;return o?.split(`
2
- `).map(s=>{let{value:a="",name:t="",description:e=""}=r.exec(s)?.groups??{};return{value:a,name:t,description:e}})??[]}export{p as default,y as fetchStatusCodeClasses};
1
+ import{JSDOM as m}from"jsdom";function d(n){try{return n()}catch{}}async function u(){let r=await(await fetch("https://www.iana.org/assignments/http-status-codes")).text();return new m(r)}async function f({resolveRedirects:n=!1}={}){let r=(await u()).window.document.getElementById("table-http-status-codes-1"),s=Array.from(r.querySelectorAll("tbody > tr")),a=async t=>{if(!n)return t.href;let e=await fetch(t.href,{method:"HEAD",redirect:"manual"}),i=d(()=>new URL(e.status.toString().startsWith("3")&&e.headers.has("Location")?e.headers.get("Location"):t.href))??new URL(t.href),l=/RFC\d+(?:, Section )?((?:\d+)?(?:.\d+){0,2})/,[,c]=l.exec(t.textContent)??[];return c&&(i.hash=`section-${c}`),i.toString()};return Promise.all(s.map(t=>Array.from(t.querySelectorAll("td"))).filter(t=>t.at(1).textContent!=="Unassigned").map(async t=>({value:Number.parseInt(t.at(0).textContent),description:t.at(1).textContent,references:await Promise.all(Array.from(t.at(2).querySelectorAll("a")).map(async e=>({name:e.textContent,url:await a(e)})))})))}async function y(){let n=await u(),o=Array.from(n.window.document.querySelectorAll("dt")).find(s=>s.textContent.trim()==="Note")?.nextElementSibling.textContent.trim(),r=/^(?<value>\dxx): (?<name>[ A-Za-z]+) - (?<description>[ ,A-Za-z]+)$/;return o?.split(`
2
+ `).map(s=>{let{value:a="",name:t="",description:e=""}=r.exec(s)?.groups??{};return{value:a,name:t,description:e}})??[]}export{f as default,y as fetchStatusCodeClasses};
3
3
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["import { JSDOM } from 'jsdom';\n\nexport type Reference = { name: string; url: string };\n\nexport type StatusCode = { value: number; description: string; references: Reference[] };\n\nexport type StatusCodeClass = { value: string; name: string; description: string };\n\nfunction attempt<T>(callback: () => T): T | undefined {\n try {\n return callback();\n } catch { /* Do nothing. */ }\n}\n\nasync function fetchRegistryDom(): Promise<JSDOM> {\n const url = 'https://www.iana.org/assignments/http-status-codes';\n const response = await fetch(url);\n const html = await response.text();\n\n return new JSDOM(html);\n}\n\nexport default async function fetchStatusCodes({ resolveRedirects = true }: {\n resolveRedirects?: boolean;\n} = {}): Promise<StatusCode[]> {\n const dom = await fetchRegistryDom();\n const table = dom.window.document.getElementById('table-http-status-codes-1')!;\n const rows = Array.from(table.querySelectorAll<HTMLTableRowElement>('tbody > tr'));\n const getReferenceLink = async (anchor: HTMLAnchorElement): Promise<string> => {\n if (!resolveRedirects) {\n return anchor.href;\n }\n\n // The IANA links are redirects, but we want direct links with a section hash if applicable.\n const response = await fetch(anchor.href, { method: 'HEAD', redirect: 'manual' });\n\n // The location header returned by some ietf.org pages is relative\n // (e.g., /doc/status-change-http-experiments-to-historic/) so check to make\n // sure the location is a valid URL and fall back to the anchor href if not.\n const url = attempt(\n () => new URL(\n response.status.toString().startsWith('3') && response.headers.has('Location')\n ? response.headers.get('Location')!\n : anchor.href\n )\n ) ?? new URL(anchor.href);\n const pattern = /RFC\\d+(?:, Section )?((?:\\d+)?(?:.\\d+){0,2})/;\n const [, section] = pattern.exec(anchor.textContent) ?? [];\n\n if (section) {\n url.hash = `section-${section}`;\n }\n\n return url.toString();\n };\n\n return Promise.all(rows.map(row => Array.from(row.querySelectorAll('td')))\n .filter(cells => cells.at(1)!.textContent !== 'Unassigned')\n .map(async cells => ({\n value: Number.parseInt(cells.at(0)!.textContent),\n description: cells.at(1)!.textContent,\n references: await Promise.all(Array.from(cells.at(2)!.querySelectorAll('a'))\n .map(async anchor =>\n ({ name: anchor.textContent!, url: await getReferenceLink(anchor) })\n )\n )\n })));\n}\n\nexport async function fetchStatusCodeClasses(): Promise<StatusCodeClass[]> {\n const dom = await fetchRegistryDom();\n const note = Array.from(dom.window.document.querySelectorAll('dt'))\n .find(element => element.textContent.trim() === 'Note')\n ?.nextElementSibling!.textContent.trim();\n const pattern = /^(?<value>\\dxx): (?<name>[ A-Za-z]+) - (?<description>[ ,A-Za-z]+)$/;\n\n return note?.split('\\n').map(line => {\n const { value = '', name = '', description = '' } = pattern.exec(line)?.groups ?? {};\n\n return { value, name, description };\n }) ?? [];\n}\n"],
5
- "mappings": "AAAA,OAAS,SAAAA,MAAa,QAQtB,SAASC,EAAWC,EAAkC,CACpD,GAAI,CACF,OAAOA,EAAS,CAClB,MAAQ,CAAoB,CAC9B,CAEA,eAAeC,GAAmC,CAGhD,IAAMC,EAAO,MADI,MAAM,MADX,oDACoB,GACJ,KAAK,EAEjC,OAAO,IAAIJ,EAAMI,CAAI,CACvB,CAEA,eAAOC,EAAwC,CAAE,iBAAAC,EAAmB,EAAK,EAErE,CAAC,EAA0B,CAE7B,IAAMC,GADM,MAAMJ,EAAiB,GACjB,OAAO,SAAS,eAAe,2BAA2B,EACtEK,EAAO,MAAM,KAAKD,EAAM,iBAAsC,YAAY,CAAC,EAC3EE,EAAmB,MAAOC,GAA+C,CAC7E,GAAI,CAACJ,EACH,OAAOI,EAAO,KAIhB,IAAMC,EAAW,MAAM,MAAMD,EAAO,KAAM,CAAE,OAAQ,OAAQ,SAAU,QAAS,CAAC,EAK1EE,EAAMX,EACV,IAAM,IAAI,IACRU,EAAS,OAAO,SAAS,EAAE,WAAW,GAAG,GAAKA,EAAS,QAAQ,IAAI,UAAU,EACzEA,EAAS,QAAQ,IAAI,UAAU,EAC/BD,EAAO,IACb,CACF,GAAK,IAAI,IAAIA,EAAO,IAAI,EAClBG,EAAU,+CACV,CAAC,CAAEC,CAAO,EAAID,EAAQ,KAAKH,EAAO,WAAW,GAAK,CAAC,EAEzD,OAAII,IACFF,EAAI,KAAO,WAAWE,CAAO,IAGxBF,EAAI,SAAS,CACtB,EAEA,OAAO,QAAQ,IAAIJ,EAAK,IAAIO,GAAO,MAAM,KAAKA,EAAI,iBAAiB,IAAI,CAAC,CAAC,EACtE,OAAOC,GAASA,EAAM,GAAG,CAAC,EAAG,cAAgB,YAAY,EACzD,IAAI,MAAMA,IAAU,CACnB,MAAO,OAAO,SAASA,EAAM,GAAG,CAAC,EAAG,WAAW,EAC/C,YAAaA,EAAM,GAAG,CAAC,EAAG,YAC1B,WAAY,MAAM,QAAQ,IAAI,MAAM,KAAKA,EAAM,GAAG,CAAC,EAAG,iBAAiB,GAAG,CAAC,EACxE,IAAI,MAAMN,IACR,CAAE,KAAMA,EAAO,YAAc,IAAK,MAAMD,EAAiBC,CAAM,CAAE,EACpE,CACF,CACF,EAAE,CAAC,CACP,CAEA,eAAsBO,GAAqD,CACzE,IAAMC,EAAM,MAAMf,EAAiB,EAC7BgB,EAAO,MAAM,KAAKD,EAAI,OAAO,SAAS,iBAAiB,IAAI,CAAC,EAC/D,KAAKE,GAAWA,EAAQ,YAAY,KAAK,IAAM,MAAM,GACpD,mBAAoB,YAAY,KAAK,EACnCP,EAAU,sEAEhB,OAAOM,GAAM,MAAM;AAAA,CAAI,EAAE,IAAIE,GAAQ,CACnC,GAAM,CAAE,MAAAC,EAAQ,GAAI,KAAAC,EAAO,GAAI,YAAAC,EAAc,EAAG,EAAIX,EAAQ,KAAKQ,CAAI,GAAG,QAAU,CAAC,EAEnF,MAAO,CAAE,MAAAC,EAAO,KAAAC,EAAM,YAAAC,CAAY,CACpC,CAAC,GAAK,CAAC,CACT",
4
+ "sourcesContent": ["import { JSDOM } from 'jsdom';\n\nexport type Reference = { name: string; url: string };\n\nexport type StatusCode = { value: number; description: string; references: Reference[] };\n\nexport type StatusCodeClass = { value: string; name: string; description: string };\n\nfunction attempt<T>(callback: () => T): T | undefined {\n try {\n return callback();\n } catch { /* Do nothing. */ }\n}\n\nasync function fetchRegistryDom(): Promise<JSDOM> {\n const url = 'https://www.iana.org/assignments/http-status-codes';\n const response = await fetch(url);\n const html = await response.text();\n\n return new JSDOM(html);\n}\n\nexport default async function fetchStatusCodes({ resolveRedirects = false }: {\n resolveRedirects?: boolean;\n} = {}): Promise<StatusCode[]> {\n const dom = await fetchRegistryDom();\n const table = dom.window.document.getElementById('table-http-status-codes-1')!;\n const rows = Array.from(table.querySelectorAll<HTMLTableRowElement>('tbody > tr'));\n const getReferenceLink = async (anchor: HTMLAnchorElement): Promise<string> => {\n if (!resolveRedirects) {\n return anchor.href;\n }\n\n // The IANA links are redirects, but we want direct links with a section hash if applicable.\n const response = await fetch(anchor.href, { method: 'HEAD', redirect: 'manual' });\n\n // The location header returned by some ietf.org pages is relative\n // (e.g., /doc/status-change-http-experiments-to-historic/) so check to make\n // sure the location is a valid URL and fall back to the anchor href if not.\n const url = attempt(\n () => new URL(\n response.status.toString().startsWith('3') && response.headers.has('Location')\n ? response.headers.get('Location')!\n : anchor.href\n )\n ) ?? new URL(anchor.href);\n const pattern = /RFC\\d+(?:, Section )?((?:\\d+)?(?:.\\d+){0,2})/;\n const [, section] = pattern.exec(anchor.textContent) ?? [];\n\n if (section) {\n url.hash = `section-${section}`;\n }\n\n return url.toString();\n };\n\n return Promise.all(rows.map(row => Array.from(row.querySelectorAll('td')))\n .filter(cells => cells.at(1)!.textContent !== 'Unassigned')\n .map(async cells => ({\n value: Number.parseInt(cells.at(0)!.textContent),\n description: cells.at(1)!.textContent,\n references: await Promise.all(Array.from(cells.at(2)!.querySelectorAll('a'))\n .map(async anchor =>\n ({ name: anchor.textContent!, url: await getReferenceLink(anchor) })\n )\n )\n })));\n}\n\nexport async function fetchStatusCodeClasses(): Promise<StatusCodeClass[]> {\n const dom = await fetchRegistryDom();\n const note = Array.from(dom.window.document.querySelectorAll('dt'))\n .find(element => element.textContent.trim() === 'Note')\n ?.nextElementSibling!.textContent.trim();\n const pattern = /^(?<value>\\dxx): (?<name>[ A-Za-z]+) - (?<description>[ ,A-Za-z]+)$/;\n\n return note?.split('\\n').map(line => {\n const { value = '', name = '', description = '' } = pattern.exec(line)?.groups ?? {};\n\n return { value, name, description };\n }) ?? [];\n}\n"],
5
+ "mappings": "AAAA,OAAS,SAAAA,MAAa,QAQtB,SAASC,EAAWC,EAAkC,CACpD,GAAI,CACF,OAAOA,EAAS,CAClB,MAAQ,CAAoB,CAC9B,CAEA,eAAeC,GAAmC,CAGhD,IAAMC,EAAO,MADI,MAAM,MADX,oDACoB,GACJ,KAAK,EAEjC,OAAO,IAAIJ,EAAMI,CAAI,CACvB,CAEA,eAAOC,EAAwC,CAAE,iBAAAC,EAAmB,EAAM,EAEtE,CAAC,EAA0B,CAE7B,IAAMC,GADM,MAAMJ,EAAiB,GACjB,OAAO,SAAS,eAAe,2BAA2B,EACtEK,EAAO,MAAM,KAAKD,EAAM,iBAAsC,YAAY,CAAC,EAC3EE,EAAmB,MAAOC,GAA+C,CAC7E,GAAI,CAACJ,EACH,OAAOI,EAAO,KAIhB,IAAMC,EAAW,MAAM,MAAMD,EAAO,KAAM,CAAE,OAAQ,OAAQ,SAAU,QAAS,CAAC,EAK1EE,EAAMX,EACV,IAAM,IAAI,IACRU,EAAS,OAAO,SAAS,EAAE,WAAW,GAAG,GAAKA,EAAS,QAAQ,IAAI,UAAU,EACzEA,EAAS,QAAQ,IAAI,UAAU,EAC/BD,EAAO,IACb,CACF,GAAK,IAAI,IAAIA,EAAO,IAAI,EAClBG,EAAU,+CACV,CAAC,CAAEC,CAAO,EAAID,EAAQ,KAAKH,EAAO,WAAW,GAAK,CAAC,EAEzD,OAAII,IACFF,EAAI,KAAO,WAAWE,CAAO,IAGxBF,EAAI,SAAS,CACtB,EAEA,OAAO,QAAQ,IAAIJ,EAAK,IAAIO,GAAO,MAAM,KAAKA,EAAI,iBAAiB,IAAI,CAAC,CAAC,EACtE,OAAOC,GAASA,EAAM,GAAG,CAAC,EAAG,cAAgB,YAAY,EACzD,IAAI,MAAMA,IAAU,CACnB,MAAO,OAAO,SAASA,EAAM,GAAG,CAAC,EAAG,WAAW,EAC/C,YAAaA,EAAM,GAAG,CAAC,EAAG,YAC1B,WAAY,MAAM,QAAQ,IAAI,MAAM,KAAKA,EAAM,GAAG,CAAC,EAAG,iBAAiB,GAAG,CAAC,EACxE,IAAI,MAAMN,IACR,CAAE,KAAMA,EAAO,YAAc,IAAK,MAAMD,EAAiBC,CAAM,CAAE,EACpE,CACF,CACF,EAAE,CAAC,CACP,CAEA,eAAsBO,GAAqD,CACzE,IAAMC,EAAM,MAAMf,EAAiB,EAC7BgB,EAAO,MAAM,KAAKD,EAAI,OAAO,SAAS,iBAAiB,IAAI,CAAC,EAC/D,KAAKE,GAAWA,EAAQ,YAAY,KAAK,IAAM,MAAM,GACpD,mBAAoB,YAAY,KAAK,EACnCP,EAAU,sEAEhB,OAAOM,GAAM,MAAM;AAAA,CAAI,EAAE,IAAIE,GAAQ,CACnC,GAAM,CAAE,MAAAC,EAAQ,GAAI,KAAAC,EAAO,GAAI,YAAAC,EAAc,EAAG,EAAIX,EAAQ,KAAKQ,CAAI,GAAG,QAAU,CAAC,EAEnF,MAAO,CAAE,MAAAC,EAAO,KAAAC,EAAM,YAAAC,CAAY,CACpC,CAAC,GAAK,CAAC,CACT",
6
6
  "names": ["JSDOM", "attempt", "callback", "fetchRegistryDom", "html", "fetchStatusCodes", "resolveRedirects", "table", "rows", "getReferenceLink", "anchor", "response", "url", "pattern", "section", "row", "cells", "fetchStatusCodeClasses", "dom", "note", "element", "line", "value", "name", "description"]
7
7
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fetch-status-codes",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Fetch the HTTP status codes from the IANA registry.",
5
5
  "keywords": [
6
6
  "class",
@@ -26,7 +26,7 @@
26
26
  "exports": "./index.js",
27
27
  "types": "../types/index.d.ts",
28
28
  "dependencies": {
29
- "jsdom": "^27.3.0"
29
+ "jsdom": "^27.4.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@eslint/compat": "^2.0.0",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC;AAEzF,MAAM,MAAM,eAAe,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAgBnF,wBAA8B,gBAAgB,CAAC,EAAE,gBAAuB,EAAE,GAAE;IAC1E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA2C7B;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAYzE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC;AAEzF,MAAM,MAAM,eAAe,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAgBnF,wBAA8B,gBAAgB,CAAC,EAAE,gBAAwB,EAAE,GAAE;IAC3E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA2C7B;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAYzE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fetch-status-codes",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Fetch the HTTP status codes from the IANA registry.",
5
5
  "keywords": [
6
6
  "class",
@@ -50,7 +50,7 @@
50
50
  "test:watch": "vitest"
51
51
  },
52
52
  "dependencies": {
53
- "jsdom": "^27.3.0"
53
+ "jsdom": "^27.4.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@eslint/compat": "^2.0.0",