Repository: kubernetes-client/javascript
Pull request #2333 (2025-03-27 19:15:06)

begin migration to native fetch()
Refs: https://github.com/kubernetes-client/javascript/issues/2306

A few notes:

- This unblocks the nock update. In fact, I believe it necessitates the update because the older nock version didn't seem to support native fetch.
- There is future room for improvement related to the use of web streams. Native fetch uses web streams, while node-fetch uses Node streams. Just to get this migration going, I used a conversion function (search the diff for `Readable.fromWeb()`), but ideally we would try working with web streams directly to reduce some overhead.
- `node-fetch` is still used in `src/gen/http/isomorphic-fetch.ts`, which I think would need to be resolved in the code generator.
- In `src/util.ts`, there is a `normalizeResponseHeaders()` that apparently exists due to a node-fetch quirk. I did not look into whether or not that is still needed, but keeping it doesn't seem to break anything.
- `FetchError` does not appear to be a thing in native fetch.

We may or may not want to keep this as a draft PR because of this list ~~and pending the CI~~ - what do others think?


Patch:
diff --git a/package-lock.json b/package-lock.json
index 96c0efc5..bb5d14b4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -39,7 +39,7 @@
                 "husky": "^9.0.6",
                 "mocha": "^11.0.1",
                 "mock-fs": "^5.2.0",
-                "nock": "^13.2.9",
+                "nock": "^14.0.2",
                 "prettier": "^3.0.0",
                 "pretty-quick": "^4.0.0",
                 "ts-mockito": "^2.3.1",
@@ -49,6 +49,20 @@
                 "typescript-eslint": "^8.26.0"
             }
         },
+        "node_modules/@asamuzakjp/css-color": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.1.tgz",
+            "integrity": "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@csstools/css-calc": "^2.1.2",
+                "@csstools/css-color-parser": "^3.0.8",
+                "@csstools/css-parser-algorithms": "^3.0.4",
+                "@csstools/css-tokenizer": "^3.0.3",
+                "lru-cache": "^10.4.3"
+            }
+        },
         "node_modules/@bcoe/v8-coverage": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.1.tgz",
@@ -58,6 +72,121 @@
                 "node": ">=18"
             }
         },
+        "node_modules/@csstools/color-helpers": {
+            "version": "5.0.2",
+            "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz",
+            "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT-0",
+            "engines": {
+                "node": ">=18"
+            }
+        },
+        "node_modules/@csstools/css-calc": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz",
+            "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT",
+            "engines": {
+                "node": ">=18"
+            },
+            "peerDependencies": {
+                "@csstools/css-parser-algorithms": "^3.0.4",
+                "@csstools/css-tokenizer": "^3.0.3"
+            }
+        },
+        "node_modules/@csstools/css-color-parser": {
+            "version": "3.0.8",
+            "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz",
+            "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT",
+            "dependencies": {
+                "@csstools/color-helpers": "^5.0.2",
+                "@csstools/css-calc": "^2.1.2"
+            },
+            "engines": {
+                "node": ">=18"
+            },
+            "peerDependencies": {
+                "@csstools/css-parser-algorithms": "^3.0.4",
+                "@csstools/css-tokenizer": "^3.0.3"
+            }
+        },
+        "node_modules/@csstools/css-parser-algorithms": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz",
+            "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT",
+            "engines": {
+                "node": ">=18"
+            },
+            "peerDependencies": {
+                "@csstools/css-tokenizer": "^3.0.3"
+            }
+        },
+        "node_modules/@csstools/css-tokenizer": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz",
+            "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "license": "MIT",
+            "engines": {
+                "node": ">=18"
+            }
+        },
         "node_modules/@esbuild/aix-ppc64": {
             "version": "0.25.0",
             "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
@@ -855,6 +984,25 @@
                 "jsep": "^0.4.0||^1.0.0"
             }
         },
+        "node_modules/@mswjs/interceptors": {
+            "version": "0.38.0",
+            "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.38.0.tgz",
+            "integrity": "sha512-nPHVM+LUl4V1kXPXuTcNN5OMD//ltCQ0lccuEagvidJdpbig3hP3W6/ctWHx6mee7vZIWE0L+Mqj3vx0ASlm/w==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@open-draft/deferred-promise": "^2.2.0",
+                "@open-draft/logger": "^0.3.0",
+                "@open-draft/until": "^2.0.0",
+                "is-node-process": "^1.2.0",
+                "jsdom": "^26.0.0",
+                "outvariant": "^1.4.3",
+                "strict-event-emitter": "^0.5.1"
+            },
+            "engines": {
+                "node": ">=18"
+            }
+        },
         "node_modules/@nodelib/fs.scandir": {
             "version": "2.1.5",
             "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -893,6 +1041,31 @@
                 "node": ">= 8"
             }
         },
+        "node_modules/@open-draft/deferred-promise": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz",
+            "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/@open-draft/logger": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz",
+            "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "is-node-process": "^1.2.0",
+                "outvariant": "^1.4.0"
+            }
+        },
+        "node_modules/@open-draft/until": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz",
+            "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/@pkgjs/parseargs": {
             "version": "0.11.0",
             "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -1600,6 +1773,34 @@
                 "node": ">= 8"
             }
         },
+        "node_modules/cssstyle": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz",
+            "integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@asamuzakjp/css-color": "^3.1.1",
+                "rrweb-cssom": "^0.8.0"
+            },
+            "engines": {
+                "node": ">=18"
+            }
+        },
+        "node_modules/data-urls": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+            "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "whatwg-mimetype": "^4.0.0",
+                "whatwg-url": "^14.0.0"
+            },
+            "engines": {
+                "node": ">=18"
+            }
+        },
         "node_modules/debug": {
             "version": "4.3.5",
             "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
@@ -1621,6 +1822,13 @@
             "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
             "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
         },
+        "node_modules/decimal.js": {
+            "version": "10.5.0",
+            "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz",
+            "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/deep-is": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -2335,12 +2543,53 @@
                 "node": ">=14"
             }
         },
+        "node_modules/html-encoding-sniffer": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+            "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "whatwg-encoding": "^3.1.1"
+            },
+            "engines": {
+                "node": ">=18"
+            }
+        },
         "node_modules/html-escaper": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
             "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
             "dev": true
         },
+        "node_modules/http-proxy-agent": {
+            "version": "7.0.2",
+            "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+            "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "agent-base": "^7.1.0",
+                "debug": "^4.3.4"
+            },
+            "engines": {
+                "node": ">= 14"
+            }
+        },
+        "node_modules/https-proxy-agent": {
+            "version": "7.0.6",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+            "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "agent-base": "^7.1.2",
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 14"
+            }
+        },
         "node_modules/husky": {
             "version": "9.1.7",
             "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
@@ -2356,6 +2605,19 @@
                 "url": "https://github.com/sponsors/typicode"
             }
         },
+        "node_modules/iconv-lite": {
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+            "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "safer-buffer": ">= 2.1.2 < 3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
         "node_modules/ignore": {
             "version": "5.3.2",
             "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -2467,6 +2729,13 @@
                 "node": ">=0.10.0"
             }
         },
+        "node_modules/is-node-process": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz",
+            "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/is-number": {
             "version": "7.0.0",
             "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -2486,6 +2755,13 @@
                 "node": ">=8"
             }
         },
+        "node_modules/is-potential-custom-element-name": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+            "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/is-unicode-supported": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
@@ -2607,6 +2883,47 @@
             "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
             "license": "MIT"
         },
+        "node_modules/jsdom": {
+            "version": "26.0.0",
+            "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz",
+            "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "cssstyle": "^4.2.1",
+                "data-urls": "^5.0.0",
+                "decimal.js": "^10.4.3",
+                "form-data": "^4.0.1",
+                "html-encoding-sniffer": "^4.0.0",
+                "http-proxy-agent": "^7.0.2",
+                "https-proxy-agent": "^7.0.6",
+                "is-potential-custom-element-name": "^1.0.1",
+                "nwsapi": "^2.2.16",
+                "parse5": "^7.2.1",
+                "rrweb-cssom": "^0.8.0",
+                "saxes": "^6.0.0",
+                "symbol-tree": "^3.2.4",
+                "tough-cookie": "^5.0.0",
+                "w3c-xmlserializer": "^5.0.0",
+                "webidl-conversions": "^7.0.0",
+                "whatwg-encoding": "^3.1.1",
+                "whatwg-mimetype": "^4.0.0",
+                "whatwg-url": "^14.1.0",
+                "ws": "^8.18.0",
+                "xml-name-validator": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=18"
+            },
+            "peerDependencies": {
+                "canvas": "^3.0.0"
+            },
+            "peerDependenciesMeta": {
+                "canvas": {
+                    "optional": true
+                }
+            }
+        },
         "node_modules/jsep": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
@@ -2738,12 +3055,10 @@
             }
         },
         "node_modules/lru-cache": {
-            "version": "10.2.0",
-            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
-            "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
-            "engines": {
-                "node": "14 || >=16.14"
-            }
+            "version": "10.4.3",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+            "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+            "license": "ISC"
         },
         "node_modules/lunr": {
             "version": "2.3.9",
@@ -3088,17 +3403,18 @@
             "license": "MIT"
         },
         "node_modules/nock": {
-            "version": "13.5.6",
-            "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz",
-            "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==",
+            "version": "14.0.2",
+            "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.2.tgz",
+            "integrity": "sha512-jDUBpCNY7KLyOF2FTiT4mLRxV2kpjo4YrcGtv3IMCIwqFKxOQFrmeXefvuEiWvS3ApPWQHAkakkkVpoOrDNCVg==",
             "dev": true,
+            "license": "MIT",
             "dependencies": {
-                "debug": "^4.1.0",
+                "@mswjs/interceptors": "^0.38.0",
                 "json-stringify-safe": "^5.0.1",
                 "propagate": "^2.0.0"
             },
             "engines": {
-                "node": ">= 10.13"
+                "node": ">=18.20.0 <20 || >=20.12.1"
             }
         },
         "node_modules/node-fetch": {
@@ -3151,6 +3467,13 @@
                 "node": ">=0.10.0"
             }
         },
+        "node_modules/nwsapi": {
+            "version": "2.2.19",
+            "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.19.tgz",
+            "integrity": "sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/oauth4webapi": {
             "version": "3.3.1",
             "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.3.1.tgz",
@@ -3199,6 +3522,13 @@
                 "node": ">= 0.8.0"
             }
         },
+        "node_modules/outvariant": {
+            "version": "1.4.3",
+            "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz",
+            "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/p-locate": {
             "version": "5.0.0",
             "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
@@ -3248,6 +3578,19 @@
                 "node": ">=6"
             }
         },
+        "node_modules/parse5": {
+            "version": "7.2.1",
+            "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
+            "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "entities": "^4.5.0"
+            },
+            "funding": {
+                "url": "https://github.com/inikulin/parse5?sponsor=1"
+            }
+        },
         "node_modules/path-exists": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -3510,6 +3853,13 @@
                 "url": "https://github.com/sponsors/isaacs"
             }
         },
+        "node_modules/rrweb-cssom": {
+            "version": "0.8.0",
+            "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+            "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/run-parallel": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -3554,6 +3904,26 @@
                 }
             ]
         },
+        "node_modules/safer-buffer": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/saxes": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+            "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+            "dev": true,
+            "license": "ISC",
+            "dependencies": {
+                "xmlchars": "^2.2.0"
+            },
+            "engines": {
+                "node": ">=v12.22.7"
+            }
+        },
         "node_modules/semver": {
             "version": "7.6.3",
             "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
@@ -3652,6 +4022,13 @@
                 "node": ">= 0.10.0"
             }
         },
+        "node_modules/strict-event-emitter": {
+            "version": "0.5.1",
+            "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz",
+            "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/string-width": {
             "version": "4.2.3",
             "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@@ -3729,6 +4106,13 @@
                 "url": "https://github.com/chalk/supports-color?sponsor=1"
             }
         },
+        "node_modules/symbol-tree": {
+            "version": "3.2.4",
+            "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+            "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/tar": {
             "version": "7.4.3",
             "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
@@ -3852,6 +4236,26 @@
             "dev": true,
             "license": "MIT"
         },
+        "node_modules/tldts": {
+            "version": "6.1.85",
+            "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.85.tgz",
+            "integrity": "sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "tldts-core": "^6.1.85"
+            },
+            "bin": {
+                "tldts": "bin/cli.js"
+            }
+        },
+        "node_modules/tldts-core": {
+            "version": "6.1.85",
+            "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.85.tgz",
+            "integrity": "sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/tmp": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -3884,6 +4288,32 @@
                 "node": ">=8.0"
             }
         },
+        "node_modules/tough-cookie": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+            "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+            "dev": true,
+            "license": "BSD-3-Clause",
+            "dependencies": {
+                "tldts": "^6.1.32"
+            },
+            "engines": {
+                "node": ">=16"
+            }
+        },
+        "node_modules/tr46": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz",
+            "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "punycode": "^2.3.1"
+            },
+            "engines": {
+                "node": ">=18"
+            }
+        },
         "node_modules/ts-api-utils": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
@@ -4065,6 +4495,66 @@
                 "node": ">=10.12.0"
             }
         },
+        "node_modules/w3c-xmlserializer": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+            "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "xml-name-validator": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=18"
+            }
+        },
+        "node_modules/webidl-conversions": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+            "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+            "dev": true,
+            "license": "BSD-2-Clause",
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/whatwg-encoding": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+            "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "iconv-lite": "0.6.3"
+            },
+            "engines": {
+                "node": ">=18"
+            }
+        },
+        "node_modules/whatwg-mimetype": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+            "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=18"
+            }
+        },
+        "node_modules/whatwg-url": {
+            "version": "14.2.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+            "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "tr46": "^5.1.0",
+                "webidl-conversions": "^7.0.0"
+            },
+            "engines": {
+                "node": ">=18"
+            }
+        },
         "node_modules/word-wrap": {
             "version": "1.2.5",
             "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
@@ -4141,6 +4631,23 @@
                 }
             }
         },
+        "node_modules/xml-name-validator": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+            "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+            "dev": true,
+            "license": "Apache-2.0",
+            "engines": {
+                "node": ">=18"
+            }
+        },
+        "node_modules/xmlchars": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+            "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+            "dev": true,
+            "license": "MIT"
+        },
         "node_modules/y18n": {
             "version": "5.0.8",
             "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -4243,12 +4750,61 @@
         }
     },
     "dependencies": {
+        "@asamuzakjp/css-color": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.1.tgz",
+            "integrity": "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==",
+            "dev": true,
+            "requires": {
+                "@csstools/css-calc": "^2.1.2",
+                "@csstools/css-color-parser": "^3.0.8",
+                "@csstools/css-parser-algorithms": "^3.0.4",
+                "@csstools/css-tokenizer": "^3.0.3",
+                "lru-cache": "^10.4.3"
+            }
+        },
         "@bcoe/v8-coverage": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.1.tgz",
             "integrity": "sha512-W+a0/JpU28AqH4IKtwUPcEUnUyXMDLALcn5/JLczGGT9fHE2sIby/xP/oQnx3nxkForzgzPy201RAKcB4xPAFQ==",
             "dev": true
         },
+        "@csstools/color-helpers": {
+            "version": "5.0.2",
+            "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz",
+            "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==",
+            "dev": true
+        },
+        "@csstools/css-calc": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz",
+            "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==",
+            "dev": true,
+            "requires": {}
+        },
+        "@csstools/css-color-parser": {
+            "version": "3.0.8",
+            "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz",
+            "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==",
+            "dev": true,
+            "requires": {
+                "@csstools/color-helpers": "^5.0.2",
+                "@csstools/css-calc": "^2.1.2"
+            }
+        },
+        "@csstools/css-parser-algorithms": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz",
+            "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==",
+            "dev": true,
+            "requires": {}
+        },
+        "@csstools/css-tokenizer": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz",
+            "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==",
+            "dev": true
+        },
         "@esbuild/aix-ppc64": {
             "version": "0.25.0",
             "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
@@ -4665,6 +5221,21 @@
             "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==",
             "requires": {}
         },
+        "@mswjs/interceptors": {
+            "version": "0.38.0",
+            "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.38.0.tgz",
+            "integrity": "sha512-nPHVM+LUl4V1kXPXuTcNN5OMD//ltCQ0lccuEagvidJdpbig3hP3W6/ctWHx6mee7vZIWE0L+Mqj3vx0ASlm/w==",
+            "dev": true,
+            "requires": {
+                "@open-draft/deferred-promise": "^2.2.0",
+                "@open-draft/logger": "^0.3.0",
+                "@open-draft/until": "^2.0.0",
+                "is-node-process": "^1.2.0",
+                "jsdom": "^26.0.0",
+                "outvariant": "^1.4.3",
+                "strict-event-emitter": "^0.5.1"
+            }
+        },
         "@nodelib/fs.scandir": {
             "version": "2.1.5",
             "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -4691,6 +5262,28 @@
                 "fastq": "^1.6.0"
             }
         },
+        "@open-draft/deferred-promise": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz",
+            "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==",
+            "dev": true
+        },
+        "@open-draft/logger": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz",
+            "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==",
+            "dev": true,
+            "requires": {
+                "is-node-process": "^1.2.0",
+                "outvariant": "^1.4.0"
+            }
+        },
+        "@open-draft/until": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz",
+            "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==",
+            "dev": true
+        },
         "@pkgjs/parseargs": {
             "version": "0.11.0",
             "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -5185,6 +5778,26 @@
                 }
             }
         },
+        "cssstyle": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz",
+            "integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==",
+            "dev": true,
+            "requires": {
+                "@asamuzakjp/css-color": "^3.1.1",
+                "rrweb-cssom": "^0.8.0"
+            }
+        },
+        "data-urls": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+            "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+            "dev": true,
+            "requires": {
+                "whatwg-mimetype": "^4.0.0",
+                "whatwg-url": "^14.0.0"
+            }
+        },
         "debug": {
             "version": "4.3.5",
             "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
@@ -5200,6 +5813,12 @@
                 }
             }
         },
+        "decimal.js": {
+            "version": "10.5.0",
+            "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz",
+            "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
+            "dev": true
+        },
         "deep-is": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -5677,18 +6296,56 @@
             "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz",
             "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA=="
         },
+        "html-encoding-sniffer": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+            "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+            "dev": true,
+            "requires": {
+                "whatwg-encoding": "^3.1.1"
+            }
+        },
         "html-escaper": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
             "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
             "dev": true
         },
+        "http-proxy-agent": {
+            "version": "7.0.2",
+            "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+            "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+            "dev": true,
+            "requires": {
+                "agent-base": "^7.1.0",
+                "debug": "^4.3.4"
+            }
+        },
+        "https-proxy-agent": {
+            "version": "7.0.6",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+            "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+            "dev": true,
+            "requires": {
+                "agent-base": "^7.1.2",
+                "debug": "4"
+            }
+        },
         "husky": {
             "version": "9.1.7",
             "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
             "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
             "dev": true
         },
+        "iconv-lite": {
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+            "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+            "dev": true,
+            "requires": {
+                "safer-buffer": ">= 2.1.2 < 3.0.0"
+            }
+        },
         "ignore": {
             "version": "5.3.2",
             "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -5770,6 +6427,12 @@
                 "is-extglob": "^2.1.1"
             }
         },
+        "is-node-process": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz",
+            "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==",
+            "dev": true
+        },
         "is-number": {
             "version": "7.0.0",
             "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -5782,6 +6445,12 @@
             "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
             "dev": true
         },
+        "is-potential-custom-element-name": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+            "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+            "dev": true
+        },
         "is-unicode-supported": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
@@ -5871,6 +6540,35 @@
             "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
             "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
         },
+        "jsdom": {
+            "version": "26.0.0",
+            "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz",
+            "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==",
+            "dev": true,
+            "requires": {
+                "cssstyle": "^4.2.1",
+                "data-urls": "^5.0.0",
+                "decimal.js": "^10.4.3",
+                "form-data": "^4.0.1",
+                "html-encoding-sniffer": "^4.0.0",
+                "http-proxy-agent": "^7.0.2",
+                "https-proxy-agent": "^7.0.6",
+                "is-potential-custom-element-name": "^1.0.1",
+                "nwsapi": "^2.2.16",
+                "parse5": "^7.2.1",
+                "rrweb-cssom": "^0.8.0",
+                "saxes": "^6.0.0",
+                "symbol-tree": "^3.2.4",
+                "tough-cookie": "^5.0.0",
+                "w3c-xmlserializer": "^5.0.0",
+                "webidl-conversions": "^7.0.0",
+                "whatwg-encoding": "^3.1.1",
+                "whatwg-mimetype": "^4.0.0",
+                "whatwg-url": "^14.1.0",
+                "ws": "^8.18.0",
+                "xml-name-validator": "^5.0.0"
+            }
+        },
         "jsep": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
@@ -5970,9 +6668,9 @@
             }
         },
         "lru-cache": {
-            "version": "10.2.0",
-            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
-            "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="
+            "version": "10.4.3",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+            "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
         },
         "lunr": {
             "version": "2.3.9",
@@ -6225,12 +6923,12 @@
             "dev": true
         },
         "nock": {
-            "version": "13.5.6",
-            "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz",
-            "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==",
+            "version": "14.0.2",
+            "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.2.tgz",
+            "integrity": "sha512-jDUBpCNY7KLyOF2FTiT4mLRxV2kpjo4YrcGtv3IMCIwqFKxOQFrmeXefvuEiWvS3ApPWQHAkakkkVpoOrDNCVg==",
             "dev": true,
             "requires": {
-                "debug": "^4.1.0",
+                "@mswjs/interceptors": "^0.38.0",
                 "json-stringify-safe": "^5.0.1",
                 "propagate": "^2.0.0"
             }
@@ -6270,6 +6968,12 @@
             "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
             "dev": true
         },
+        "nwsapi": {
+            "version": "2.2.19",
+            "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.19.tgz",
+            "integrity": "sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA==",
+            "dev": true
+        },
         "oauth4webapi": {
             "version": "3.3.1",
             "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.3.1.tgz",
@@ -6306,6 +7010,12 @@
                 "word-wrap": "^1.2.5"
             }
         },
+        "outvariant": {
+            "version": "1.4.3",
+            "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz",
+            "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==",
+            "dev": true
+        },
         "p-locate": {
             "version": "5.0.0",
             "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
@@ -6341,6 +7051,15 @@
                 "callsites": "^3.0.0"
             }
         },
+        "parse5": {
+            "version": "7.2.1",
+            "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
+            "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
+            "dev": true,
+            "requires": {
+                "entities": "^4.5.0"
+            }
+        },
         "path-exists": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -6498,6 +7217,12 @@
                 "glob": "^7.1.3"
             }
         },
+        "rrweb-cssom": {
+            "version": "0.8.0",
+            "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+            "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
+            "dev": true
+        },
         "run-parallel": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -6513,6 +7238,21 @@
             "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
             "dev": true
         },
+        "safer-buffer": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+            "dev": true
+        },
+        "saxes": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+            "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+            "dev": true,
+            "requires": {
+                "xmlchars": "^2.2.0"
+            }
+        },
         "semver": {
             "version": "7.6.3",
             "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
@@ -6575,6 +7315,12 @@
             "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.3.tgz",
             "integrity": "sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw=="
         },
+        "strict-event-emitter": {
+            "version": "0.5.1",
+            "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz",
+            "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
+            "dev": true
+        },
         "string-width": {
             "version": "4.2.3",
             "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@@ -6626,6 +7372,12 @@
                 "has-flag": "^4.0.0"
             }
         },
+        "symbol-tree": {
+            "version": "3.2.4",
+            "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+            "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+            "dev": true
+        },
         "tar": {
             "version": "7.4.3",
             "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
@@ -6711,6 +7463,21 @@
             "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
             "dev": true
         },
+        "tldts": {
+            "version": "6.1.85",
+            "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.85.tgz",
+            "integrity": "sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==",
+            "dev": true,
+            "requires": {
+                "tldts-core": "^6.1.85"
+            }
+        },
+        "tldts-core": {
+            "version": "6.1.85",
+            "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.85.tgz",
+            "integrity": "sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==",
+            "dev": true
+        },
         "tmp": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -6736,6 +7503,24 @@
                 "is-number": "^7.0.0"
             }
         },
+        "tough-cookie": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+            "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+            "dev": true,
+            "requires": {
+                "tldts": "^6.1.32"
+            }
+        },
+        "tr46": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz",
+            "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==",
+            "dev": true,
+            "requires": {
+                "punycode": "^2.3.1"
+            }
+        },
         "ts-api-utils": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
@@ -6859,6 +7644,46 @@
                 "convert-source-map": "^2.0.0"
             }
         },
+        "w3c-xmlserializer": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+            "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+            "dev": true,
+            "requires": {
+                "xml-name-validator": "^5.0.0"
+            }
+        },
+        "webidl-conversions": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+            "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+            "dev": true
+        },
+        "whatwg-encoding": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+            "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+            "dev": true,
+            "requires": {
+                "iconv-lite": "0.6.3"
+            }
+        },
+        "whatwg-mimetype": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+            "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+            "dev": true
+        },
+        "whatwg-url": {
+            "version": "14.2.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+            "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+            "dev": true,
+            "requires": {
+                "tr46": "^5.1.0",
+                "webidl-conversions": "^7.0.0"
+            }
+        },
         "word-wrap": {
             "version": "1.2.5",
             "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
@@ -6903,6 +7728,18 @@
             "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
             "requires": {}
         },
+        "xml-name-validator": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+            "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+            "dev": true
+        },
+        "xmlchars": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+            "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+            "dev": true
+        },
         "y18n": {
             "version": "5.0.8",
             "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/package.json b/package.json
index 6986674e..9e171f75 100644
--- a/package.json
+++ b/package.json
@@ -85,7 +85,7 @@
         "husky": "^9.0.6",
         "mocha": "^11.0.1",
         "mock-fs": "^5.2.0",
-        "nock": "^13.2.9",
+        "nock": "^14.0.2",
         "prettier": "^3.0.0",
         "pretty-quick": "^4.0.0",
         "ts-mockito": "^2.3.1",
diff --git a/src/config.ts b/src/config.ts
index 2545fe9a..aad84dfd 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -4,7 +4,6 @@ import yaml from 'js-yaml';
 import net from 'node:net';
 import path from 'node:path';
 
-import { Headers, RequestInit } from 'node-fetch';
 import { RequestContext } from './api.js';
 import { Authenticator } from './auth.js';
 import { AzureAuth } from './azure_auth.js';
diff --git a/src/health.ts b/src/health.ts
index 59e29aaf..98d81256 100644
--- a/src/health.ts
+++ b/src/health.ts
@@ -1,4 +1,3 @@
-import fetch from 'node-fetch';
 import { KubeConfig } from './config.js';
 import { RequestOptions } from 'node:https';
 
diff --git a/src/index.ts b/src/index.ts
index 10fb300e..c7754305 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -18,6 +18,3 @@ export * from './health.js';
 export * from './middleware.js';
 export * from './patch.js';
 export { type ConfigOptions, type User, type Cluster, type Context } from './config_types.js';
-
-// Export FetchError so that instanceof checks in user code will definitely use the same instance
-export { FetchError } from 'node-fetch';
diff --git a/src/log.ts b/src/log.ts
index 419c8b9a..e810e265 100644
--- a/src/log.ts
+++ b/src/log.ts
@@ -1,5 +1,4 @@
-import fetch from 'node-fetch';
-import { Writable } from 'node:stream';
+import { Readable, Writable } from 'node:stream';
 import { ApiException } from './api.js';
 import { KubeConfig } from './config.js';
 import { V1Status } from './gen/index.js';
@@ -140,7 +139,7 @@ export class Log {
             const status = response.status;
             if (status === 200) {
                 // TODO: the follow search param still has the stream close prematurely based on my testing
-                response.body!.pipe(stream);
+                Readable.fromWeb(response.body!).pipe(stream);
             } else if (status === 500) {
                 const v1status = (await response.json()) as V1Status;
                 const v1code = v1status.code;
diff --git a/src/metrics.ts b/src/metrics.ts
index 5e720865..182a407d 100644
--- a/src/metrics.ts
+++ b/src/metrics.ts
@@ -1,4 +1,3 @@
-import fetch from 'node-fetch';
 import { KubeConfig } from './config.js';
 import { ApiException, V1Status } from './gen/index.js';
 import { normalizeResponseHeaders } from './util.js';
diff --git a/src/util.ts b/src/util.ts
index 791d89c7..f970f997 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -1,4 +1,3 @@
-import { Response } from 'node-fetch';
 import { CoreV1Api, V1Container, V1Pod } from './gen/index.js';
 
 export async function podsForNode(api: CoreV1Api, nodeName: string): Promise<V1Pod[]> {
diff --git a/src/watch.ts b/src/watch.ts
index ac842180..301aa53f 100644
--- a/src/watch.ts
+++ b/src/watch.ts
@@ -1,6 +1,6 @@
 import { STATUS_CODES } from 'node:http';
 import { createInterface } from 'node:readline';
-import fetch from 'node-fetch';
+import { Readable } from 'node:stream';
 import { KubeConfig } from './config.js';
 
 export class Watch {
@@ -55,7 +55,7 @@ export class Watch {
             const response = await fetch(watchURL, requestInit);
 
             if (response.status === 200) {
-                const body = response.body!;
+                const body = Readable.fromWeb(response.body!);
 
                 body.on('error', doneCallOnce);
                 body.on('close', () => doneCallOnce(null));


Test patch:
diff --git a/src/config_test.ts b/src/config_test.ts
index 9992937a..6fdca20a 100644
--- a/src/config_test.ts
+++ b/src/config_test.ts
@@ -10,7 +10,6 @@ import { mock } from 'node:test';
 import mockfs from 'mock-fs';
 
 import { Authenticator } from './auth.js';
-import { Headers } from 'node-fetch';
 import { HttpMethod } from './index.js';
 import { assertRequestAgentsEqual, assertRequestOptionsEqual } from './test/match-buffer.js';
 import { CoreV1Api, RequestContext } from './api.js';
@@ -295,7 +294,7 @@ describe('KubeConfig', () => {
             strictEqual(headers.get('list'), 'a, b');
             strictEqual(headers.get('number'), '5');
             strictEqual(headers.get('string'), 'str');
-            assertRequestAgentsEqual(requestInit.agent as Agent, expectedAgent);
+            assertRequestAgentsEqual((requestInit as any).agent as Agent, expectedAgent);
         });
     });
 
diff --git a/src/metrics_test.ts b/src/metrics_test.ts
index cd5ef9a9..45999fc2 100644
--- a/src/metrics_test.ts
+++ b/src/metrics_test.ts
@@ -148,7 +148,7 @@ describe('Metrics', () => {
             const metricsClient = new Metrics(kc);
             await rejects(metricsClient.getPodMetrics(), (err) => {
                 ok(err instanceof ApiException);
-                match(err.message, /connect ECONNREFUSED 127.0.0.1:51011/);
+                match(err.message, /Error occurred in metrics request: fetch failed/);
                 return true;
             });
         });
diff --git a/src/util_test.ts b/src/util_test.ts
index 47cb7073..84c0eb29 100644
--- a/src/util_test.ts
+++ b/src/util_test.ts
@@ -1,5 +1,4 @@
 import { deepStrictEqual, strictEqual, throws } from 'node:assert';
-import { Response } from 'node-fetch';
 import { CoreV1Api, V1Container, V1Pod } from './api.js';
 import {
     normalizeResponseHeaders,