From 1ddf6e26a9ea86b7043fc552f41d98153c9dbbdc Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 7 Sep 2026 12:56:38 +0200 Subject: [PATCH 1/2] chore: reproduce the sanitize-html 2.17.7 crash with a health probe Co-Authored-By: Claude Fable 5 --- package.json | 2 +- pages/api/health.tsx | 13 +++++++++ pnpm-lock.yaml | 64 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 7bb4b307..56fa0b7b 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "recharts": "^2.15.4", "rehype-raw": "^7.0.0", "remark-gfm": "4.0.1", - "sanitize-html": "2.17.5", + "sanitize-html": "2.17.7", "swr": "^2.5.1", "viem": "^2.56.3", "wagmi": "^2.19.5", diff --git a/pages/api/health.tsx b/pages/api/health.tsx index 73120af1..f6630492 100644 --- a/pages/api/health.tsx +++ b/pages/api/health.tsx @@ -23,9 +23,22 @@ const health = async (req: NextApiRequest, res: NextApiResponse) => { const allHealthy = subgraphOk; + // DEBUG (remove before merge): report the runtime and whether the + // sanitize-html require chain loads on it. + let sanitizeHtmlLoads: string; + try { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const s = require("sanitize-html"); + sanitizeHtmlLoads = `ok (sanitized: ${s("x")})`; + } catch (e) { + sanitizeHtmlLoads = `LOAD FAILED: ${(e as Error).message}`; + } + const response = { status: allHealthy ? "healthy" : "degraded", timestamp: new Date().toISOString(), + nodeVersion: process.version, + sanitizeHtml: sanitizeHtmlLoads, checks: { subgraph: subgraphOk ? "ok" : "error", }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2091d73..b3855361 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -148,8 +148,8 @@ importers: specifier: 4.0.1 version: 4.0.1 sanitize-html: - specifier: 2.17.5 - version: 2.17.5 + specifier: 2.17.7 + version: 2.17.7 swr: specifier: ^2.5.1 version: 2.5.1(react@19.2.1) @@ -5701,16 +5701,32 @@ packages: dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dom-serializer@3.1.1: + resolution: {integrity: sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==} + engines: {node: '>=20.19.0'} + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + domelementtype@3.0.0: + resolution: {integrity: sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==} + engines: {node: '>=20.19.0'} + domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + domhandler@6.0.1: + resolution: {integrity: sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==} + engines: {node: '>=20.19.0'} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + domutils@4.0.2: + resolution: {integrity: sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==} + engines: {node: '>=20.19.0'} + dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} @@ -5799,6 +5815,10 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -6495,6 +6515,10 @@ packages: htmlparser2@10.1.0: resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + htmlparser2@12.0.0: + resolution: {integrity: sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==} + engines: {node: '>=20.19.0'} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -8583,8 +8607,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sanitize-html@2.17.5: - resolution: {integrity: sha512-ZmU1joGRrvoyctKIiuwUxqR6moLoU2Wk+2bMccN6f7UwhAmwYDvWziqPxRDDN2Qip62NqnIrVrT9akbL6Wretg==} + sanitize-html@2.17.7: + resolution: {integrity: sha512-PGtEkc9cbnedU3s9TmzDbpsZ8w086g/0Q8k8/oIO1NLNU3i5k9yn835CrjJSajp1KMmkisbO1qPXxNKO3welAg==} + engines: {node: '>=22.12.0'} sax@1.6.1: resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==} @@ -17186,18 +17211,36 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 + dom-serializer@3.1.1: + dependencies: + domelementtype: 3.0.0 + domhandler: 6.0.1 + entities: 8.0.0 + domelementtype@2.3.0: {} + domelementtype@3.0.0: {} + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 + domhandler@6.0.1: + dependencies: + domelementtype: 3.0.0 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 domelementtype: 2.3.0 domhandler: 5.0.3 + domutils@4.0.2: + dependencies: + dom-serializer: 3.1.1 + domelementtype: 3.0.0 + domhandler: 6.0.1 + dot-case@3.0.4: dependencies: no-case: 3.0.4 @@ -17297,6 +17340,8 @@ snapshots: entities@7.0.1: {} + entities@8.0.0: {} + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -18332,6 +18377,13 @@ snapshots: domutils: 3.2.2 entities: 7.0.1 + htmlparser2@12.0.0: + dependencies: + domelementtype: 3.0.0 + domhandler: 6.0.1 + domutils: 4.0.2 + entities: 8.0.0 + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -21051,11 +21103,11 @@ snapshots: safer-buffer@2.1.2: {} - sanitize-html@2.17.5: + sanitize-html@2.17.7: dependencies: deepmerge: 4.3.1 escape-string-regexp: 4.0.0 - htmlparser2: 10.1.0 + htmlparser2: 12.0.0 is-plain-object: 5.1.0 launder: 1.7.1 parse-srcset: 1.0.2 From d16d0ee78fd017261972f88ed9b91c637e496471 Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Mon, 7 Sep 2026 14:10:16 +0200 Subject: [PATCH 2/2] chore: transpile the sanitize-html chain to dodge vercel's require Co-Authored-By: Claude Fable 5 --- next.config.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/next.config.js b/next.config.js index d02ef9bb..56e52d0c 100644 --- a/next.config.js +++ b/next.config.js @@ -2,6 +2,18 @@ const nextConfig = { productionBrowserSourceMaps: true, + // Compile the sanitize-html chain into the build: Vercel's runtime + // require() cannot load ESM-only packages like htmlparser2 >=11 + // (it runs node with --no-experimental-require-module). + transpilePackages: [ + "sanitize-html", + "htmlparser2", + "domelementtype", + "domhandler", + "domutils", + "entities", + ], + turbopack: { rules: { "*.svg": {